Roo/data/JsonReader.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     
8042     getItems : function()
8043     {
8044         var r=new Roo.util.MixedCollection(false, function(o){
8045             return o.id || (o.id = Roo.id());
8046         });
8047         var iter = function(el) {
8048             if (el.inputEl) {
8049                 r.add(el);
8050             }
8051             if (!el.items) {
8052                 return;
8053             }
8054             Roo.each(el.items,function(e) {
8055                 iter(e);
8056             });
8057
8058
8059         };
8060
8061         iter(this);
8062         return r;
8063         
8064     }
8065
8066 });
8067
8068 Roo.apply(Roo.bootstrap.Form, {
8069     
8070     popover : {
8071         
8072         padding : 5,
8073         
8074         isApplied : false,
8075         
8076         isMasked : false,
8077         
8078         form : false,
8079         
8080         target : false,
8081         
8082         toolTip : false,
8083         
8084         intervalID : false,
8085         
8086         maskEl : false,
8087         
8088         apply : function()
8089         {
8090             if(this.isApplied){
8091                 return;
8092             }
8093             
8094             this.maskEl = {
8095                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8096                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8097                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8098                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8099             };
8100             
8101             this.maskEl.top.enableDisplayMode("block");
8102             this.maskEl.left.enableDisplayMode("block");
8103             this.maskEl.bottom.enableDisplayMode("block");
8104             this.maskEl.right.enableDisplayMode("block");
8105             
8106             this.toolTip = new Roo.bootstrap.Tooltip({
8107                 cls : 'roo-form-error-popover',
8108                 alignment : {
8109                     'left' : ['r-l', [-2,0], 'right'],
8110                     'right' : ['l-r', [2,0], 'left'],
8111                     'bottom' : ['tl-bl', [0,2], 'top'],
8112                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8113                 }
8114             });
8115             
8116             this.toolTip.render(Roo.get(document.body));
8117
8118             this.toolTip.el.enableDisplayMode("block");
8119             
8120             Roo.get(document.body).on('click', function(){
8121                 this.unmask();
8122             }, this);
8123             
8124             Roo.get(document.body).on('touchstart', function(){
8125                 this.unmask();
8126             }, this);
8127             
8128             this.isApplied = true
8129         },
8130         
8131         mask : function(form, target)
8132         {
8133             this.form = form;
8134             
8135             this.target = target;
8136             
8137             if(!this.form.errorMask || !target.el){
8138                 return;
8139             }
8140             
8141             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8142             
8143             Roo.log(scrollable);
8144             
8145             var ot = this.target.el.calcOffsetsTo(scrollable);
8146             
8147             var scrollTo = ot[1] - this.form.maskOffset;
8148             
8149             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8150             
8151             scrollable.scrollTo('top', scrollTo);
8152             
8153             var box = this.target.el.getBox();
8154             Roo.log(box);
8155             var zIndex = Roo.bootstrap.Modal.zIndex++;
8156
8157             
8158             this.maskEl.top.setStyle('position', 'absolute');
8159             this.maskEl.top.setStyle('z-index', zIndex);
8160             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8161             this.maskEl.top.setLeft(0);
8162             this.maskEl.top.setTop(0);
8163             this.maskEl.top.show();
8164             
8165             this.maskEl.left.setStyle('position', 'absolute');
8166             this.maskEl.left.setStyle('z-index', zIndex);
8167             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8168             this.maskEl.left.setLeft(0);
8169             this.maskEl.left.setTop(box.y - this.padding);
8170             this.maskEl.left.show();
8171
8172             this.maskEl.bottom.setStyle('position', 'absolute');
8173             this.maskEl.bottom.setStyle('z-index', zIndex);
8174             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8175             this.maskEl.bottom.setLeft(0);
8176             this.maskEl.bottom.setTop(box.bottom + this.padding);
8177             this.maskEl.bottom.show();
8178
8179             this.maskEl.right.setStyle('position', 'absolute');
8180             this.maskEl.right.setStyle('z-index', zIndex);
8181             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8182             this.maskEl.right.setLeft(box.right + this.padding);
8183             this.maskEl.right.setTop(box.y - this.padding);
8184             this.maskEl.right.show();
8185
8186             this.toolTip.bindEl = this.target.el;
8187
8188             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8189
8190             var tip = this.target.blankText;
8191
8192             if(this.target.getValue() !== '' ) {
8193                 
8194                 if (this.target.invalidText.length) {
8195                     tip = this.target.invalidText;
8196                 } else if (this.target.regexText.length){
8197                     tip = this.target.regexText;
8198                 }
8199             }
8200
8201             this.toolTip.show(tip);
8202
8203             this.intervalID = window.setInterval(function() {
8204                 Roo.bootstrap.Form.popover.unmask();
8205             }, 10000);
8206
8207             window.onwheel = function(){ return false;};
8208             
8209             (function(){ this.isMasked = true; }).defer(500, this);
8210             
8211         },
8212         
8213         unmask : function()
8214         {
8215             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8216                 return;
8217             }
8218             
8219             this.maskEl.top.setStyle('position', 'absolute');
8220             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.top.hide();
8222
8223             this.maskEl.left.setStyle('position', 'absolute');
8224             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8225             this.maskEl.left.hide();
8226
8227             this.maskEl.bottom.setStyle('position', 'absolute');
8228             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8229             this.maskEl.bottom.hide();
8230
8231             this.maskEl.right.setStyle('position', 'absolute');
8232             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8233             this.maskEl.right.hide();
8234             
8235             this.toolTip.hide();
8236             
8237             this.toolTip.el.hide();
8238             
8239             window.onwheel = function(){ return true;};
8240             
8241             if(this.intervalID){
8242                 window.clearInterval(this.intervalID);
8243                 this.intervalID = false;
8244             }
8245             
8246             this.isMasked = false;
8247             
8248         }
8249         
8250     }
8251     
8252 });
8253
8254 /*
8255  * Based on:
8256  * Ext JS Library 1.1.1
8257  * Copyright(c) 2006-2007, Ext JS, LLC.
8258  *
8259  * Originally Released Under LGPL - original licence link has changed is not relivant.
8260  *
8261  * Fork - LGPL
8262  * <script type="text/javascript">
8263  */
8264 /**
8265  * @class Roo.form.VTypes
8266  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8267  * @singleton
8268  */
8269 Roo.form.VTypes = function(){
8270     // closure these in so they are only created once.
8271     var alpha = /^[a-zA-Z_]+$/;
8272     var alphanum = /^[a-zA-Z0-9_]+$/;
8273     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8274     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8275
8276     // All these messages and functions are configurable
8277     return {
8278         /**
8279          * The function used to validate email addresses
8280          * @param {String} value The email address
8281          */
8282         'email' : function(v){
8283             return email.test(v);
8284         },
8285         /**
8286          * The error text to display when the email validation function returns false
8287          * @type String
8288          */
8289         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8290         /**
8291          * The keystroke filter mask to be applied on email input
8292          * @type RegExp
8293          */
8294         'emailMask' : /[a-z0-9_\.\-@]/i,
8295
8296         /**
8297          * The function used to validate URLs
8298          * @param {String} value The URL
8299          */
8300         'url' : function(v){
8301             return url.test(v);
8302         },
8303         /**
8304          * The error text to display when the url validation function returns false
8305          * @type String
8306          */
8307         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8308         
8309         /**
8310          * The function used to validate alpha values
8311          * @param {String} value The value
8312          */
8313         'alpha' : function(v){
8314             return alpha.test(v);
8315         },
8316         /**
8317          * The error text to display when the alpha validation function returns false
8318          * @type String
8319          */
8320         'alphaText' : 'This field should only contain letters and _',
8321         /**
8322          * The keystroke filter mask to be applied on alpha input
8323          * @type RegExp
8324          */
8325         'alphaMask' : /[a-z_]/i,
8326
8327         /**
8328          * The function used to validate alphanumeric values
8329          * @param {String} value The value
8330          */
8331         'alphanum' : function(v){
8332             return alphanum.test(v);
8333         },
8334         /**
8335          * The error text to display when the alphanumeric validation function returns false
8336          * @type String
8337          */
8338         'alphanumText' : 'This field should only contain letters, numbers and _',
8339         /**
8340          * The keystroke filter mask to be applied on alphanumeric input
8341          * @type RegExp
8342          */
8343         'alphanumMask' : /[a-z0-9_]/i
8344     };
8345 }();/*
8346  * - LGPL
8347  *
8348  * Input
8349  * 
8350  */
8351
8352 /**
8353  * @class Roo.bootstrap.Input
8354  * @extends Roo.bootstrap.Component
8355  * Bootstrap Input class
8356  * @cfg {Boolean} disabled is it disabled
8357  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8358  * @cfg {String} name name of the input
8359  * @cfg {string} fieldLabel - the label associated
8360  * @cfg {string} placeholder - placeholder to put in text.
8361  * @cfg {string}  before - input group add on before
8362  * @cfg {string} after - input group add on after
8363  * @cfg {string} size - (lg|sm) or leave empty..
8364  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8365  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8366  * @cfg {Number} md colspan out of 12 for computer-sized screens
8367  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8368  * @cfg {string} value default value of the input
8369  * @cfg {Number} labelWidth set the width of label 
8370  * @cfg {Number} labellg set the width of label (1-12)
8371  * @cfg {Number} labelmd set the width of label (1-12)
8372  * @cfg {Number} labelsm set the width of label (1-12)
8373  * @cfg {Number} labelxs set the width of label (1-12)
8374  * @cfg {String} labelAlign (top|left)
8375  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8376  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8377  * @cfg {String} indicatorpos (left|right) default left
8378
8379  * @cfg {String} align (left|center|right) Default left
8380  * @cfg {Boolean} forceFeedback (true|false) Default false
8381  * 
8382  * 
8383  * 
8384  * 
8385  * @constructor
8386  * Create a new Input
8387  * @param {Object} config The config object
8388  */
8389
8390 Roo.bootstrap.Input = function(config){
8391     
8392     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8393     
8394     this.addEvents({
8395         /**
8396          * @event focus
8397          * Fires when this field receives input focus.
8398          * @param {Roo.form.Field} this
8399          */
8400         focus : true,
8401         /**
8402          * @event blur
8403          * Fires when this field loses input focus.
8404          * @param {Roo.form.Field} this
8405          */
8406         blur : true,
8407         /**
8408          * @event specialkey
8409          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8410          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8411          * @param {Roo.form.Field} this
8412          * @param {Roo.EventObject} e The event object
8413          */
8414         specialkey : true,
8415         /**
8416          * @event change
8417          * Fires just before the field blurs if the field value has changed.
8418          * @param {Roo.form.Field} this
8419          * @param {Mixed} newValue The new value
8420          * @param {Mixed} oldValue The original value
8421          */
8422         change : true,
8423         /**
8424          * @event invalid
8425          * Fires after the field has been marked as invalid.
8426          * @param {Roo.form.Field} this
8427          * @param {String} msg The validation message
8428          */
8429         invalid : true,
8430         /**
8431          * @event valid
8432          * Fires after the field has been validated with no errors.
8433          * @param {Roo.form.Field} this
8434          */
8435         valid : true,
8436          /**
8437          * @event keyup
8438          * Fires after the key up
8439          * @param {Roo.form.Field} this
8440          * @param {Roo.EventObject}  e The event Object
8441          */
8442         keyup : true
8443     });
8444 };
8445
8446 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8447      /**
8448      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8449       automatic validation (defaults to "keyup").
8450      */
8451     validationEvent : "keyup",
8452      /**
8453      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8454      */
8455     validateOnBlur : true,
8456     /**
8457      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8458      */
8459     validationDelay : 250,
8460      /**
8461      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8462      */
8463     focusClass : "x-form-focus",  // not needed???
8464     
8465        
8466     /**
8467      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8468      */
8469     invalidClass : "has-warning",
8470     
8471     /**
8472      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8473      */
8474     validClass : "has-success",
8475     
8476     /**
8477      * @cfg {Boolean} hasFeedback (true|false) default true
8478      */
8479     hasFeedback : true,
8480     
8481     /**
8482      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8483      */
8484     invalidFeedbackClass : "glyphicon-warning-sign",
8485     
8486     /**
8487      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8488      */
8489     validFeedbackClass : "glyphicon-ok",
8490     
8491     /**
8492      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8493      */
8494     selectOnFocus : false,
8495     
8496      /**
8497      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8498      */
8499     maskRe : null,
8500        /**
8501      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8502      */
8503     vtype : null,
8504     
8505       /**
8506      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8507      */
8508     disableKeyFilter : false,
8509     
8510        /**
8511      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8512      */
8513     disabled : false,
8514      /**
8515      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8516      */
8517     allowBlank : true,
8518     /**
8519      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8520      */
8521     blankText : "Please complete this mandatory field",
8522     
8523      /**
8524      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8525      */
8526     minLength : 0,
8527     /**
8528      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8529      */
8530     maxLength : Number.MAX_VALUE,
8531     /**
8532      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8533      */
8534     minLengthText : "The minimum length for this field is {0}",
8535     /**
8536      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8537      */
8538     maxLengthText : "The maximum length for this field is {0}",
8539   
8540     
8541     /**
8542      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8543      * If available, this function will be called only after the basic validators all return true, and will be passed the
8544      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8545      */
8546     validator : null,
8547     /**
8548      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8549      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8550      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8551      */
8552     regex : null,
8553     /**
8554      * @cfg {String} regexText -- Depricated - use Invalid Text
8555      */
8556     regexText : "",
8557     
8558     /**
8559      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8560      */
8561     invalidText : "",
8562     
8563     
8564     
8565     autocomplete: false,
8566     
8567     
8568     fieldLabel : '',
8569     inputType : 'text',
8570     
8571     name : false,
8572     placeholder: false,
8573     before : false,
8574     after : false,
8575     size : false,
8576     hasFocus : false,
8577     preventMark: false,
8578     isFormField : true,
8579     value : '',
8580     labelWidth : 2,
8581     labelAlign : false,
8582     readOnly : false,
8583     align : false,
8584     formatedValue : false,
8585     forceFeedback : false,
8586     
8587     indicatorpos : 'left',
8588     
8589     labellg : 0,
8590     labelmd : 0,
8591     labelsm : 0,
8592     labelxs : 0,
8593     
8594     parentLabelAlign : function()
8595     {
8596         var parent = this;
8597         while (parent.parent()) {
8598             parent = parent.parent();
8599             if (typeof(parent.labelAlign) !='undefined') {
8600                 return parent.labelAlign;
8601             }
8602         }
8603         return 'left';
8604         
8605     },
8606     
8607     getAutoCreate : function()
8608     {
8609         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8610         
8611         var id = Roo.id();
8612         
8613         var cfg = {};
8614         
8615         if(this.inputType != 'hidden'){
8616             cfg.cls = 'form-group' //input-group
8617         }
8618         
8619         var input =  {
8620             tag: 'input',
8621             id : id,
8622             type : this.inputType,
8623             value : this.value,
8624             cls : 'form-control',
8625             placeholder : this.placeholder || '',
8626             autocomplete : this.autocomplete || 'new-password'
8627         };
8628         
8629         if(this.align){
8630             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8631         }
8632         
8633         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8634             input.maxLength = this.maxLength;
8635         }
8636         
8637         if (this.disabled) {
8638             input.disabled=true;
8639         }
8640         
8641         if (this.readOnly) {
8642             input.readonly=true;
8643         }
8644         
8645         if (this.name) {
8646             input.name = this.name;
8647         }
8648         
8649         if (this.size) {
8650             input.cls += ' input-' + this.size;
8651         }
8652         
8653         var settings=this;
8654         ['xs','sm','md','lg'].map(function(size){
8655             if (settings[size]) {
8656                 cfg.cls += ' col-' + size + '-' + settings[size];
8657             }
8658         });
8659         
8660         var inputblock = input;
8661         
8662         var feedback = {
8663             tag: 'span',
8664             cls: 'glyphicon form-control-feedback'
8665         };
8666             
8667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8668             
8669             inputblock = {
8670                 cls : 'has-feedback',
8671                 cn :  [
8672                     input,
8673                     feedback
8674                 ] 
8675             };  
8676         }
8677         
8678         if (this.before || this.after) {
8679             
8680             inputblock = {
8681                 cls : 'input-group',
8682                 cn :  [] 
8683             };
8684             
8685             if (this.before && typeof(this.before) == 'string') {
8686                 
8687                 inputblock.cn.push({
8688                     tag :'span',
8689                     cls : 'roo-input-before input-group-addon',
8690                     html : this.before
8691                 });
8692             }
8693             if (this.before && typeof(this.before) == 'object') {
8694                 this.before = Roo.factory(this.before);
8695                 
8696                 inputblock.cn.push({
8697                     tag :'span',
8698                     cls : 'roo-input-before input-group-' +
8699                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8700                 });
8701             }
8702             
8703             inputblock.cn.push(input);
8704             
8705             if (this.after && typeof(this.after) == 'string') {
8706                 inputblock.cn.push({
8707                     tag :'span',
8708                     cls : 'roo-input-after input-group-addon',
8709                     html : this.after
8710                 });
8711             }
8712             if (this.after && typeof(this.after) == 'object') {
8713                 this.after = Roo.factory(this.after);
8714                 
8715                 inputblock.cn.push({
8716                     tag :'span',
8717                     cls : 'roo-input-after input-group-' +
8718                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8719                 });
8720             }
8721             
8722             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8723                 inputblock.cls += ' has-feedback';
8724                 inputblock.cn.push(feedback);
8725             }
8726         };
8727         
8728         if (align ==='left' && this.fieldLabel.length) {
8729             
8730             cfg.cls += ' roo-form-group-label-left';
8731             
8732             cfg.cn = [
8733                 {
8734                     tag : 'i',
8735                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8736                     tooltip : 'This field is required'
8737                 },
8738                 {
8739                     tag: 'label',
8740                     'for' :  id,
8741                     cls : 'control-label',
8742                     html : this.fieldLabel
8743
8744                 },
8745                 {
8746                     cls : "", 
8747                     cn: [
8748                         inputblock
8749                     ]
8750                 }
8751             ];
8752             
8753             var labelCfg = cfg.cn[1];
8754             var contentCfg = cfg.cn[2];
8755             
8756             if(this.indicatorpos == 'right'){
8757                 cfg.cn = [
8758                     {
8759                         tag: 'label',
8760                         'for' :  id,
8761                         cls : 'control-label',
8762                         cn : [
8763                             {
8764                                 tag : 'span',
8765                                 html : this.fieldLabel
8766                             },
8767                             {
8768                                 tag : 'i',
8769                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8770                                 tooltip : 'This field is required'
8771                             }
8772                         ]
8773                     },
8774                     {
8775                         cls : "",
8776                         cn: [
8777                             inputblock
8778                         ]
8779                     }
8780
8781                 ];
8782                 
8783                 labelCfg = cfg.cn[0];
8784                 contentCfg = cfg.cn[1];
8785             
8786             }
8787             
8788             if(this.labelWidth > 12){
8789                 labelCfg.style = "width: " + this.labelWidth + 'px';
8790             }
8791             
8792             if(this.labelWidth < 13 && this.labelmd == 0){
8793                 this.labelmd = this.labelWidth;
8794             }
8795             
8796             if(this.labellg > 0){
8797                 labelCfg.cls += ' col-lg-' + this.labellg;
8798                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8799             }
8800             
8801             if(this.labelmd > 0){
8802                 labelCfg.cls += ' col-md-' + this.labelmd;
8803                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8804             }
8805             
8806             if(this.labelsm > 0){
8807                 labelCfg.cls += ' col-sm-' + this.labelsm;
8808                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8809             }
8810             
8811             if(this.labelxs > 0){
8812                 labelCfg.cls += ' col-xs-' + this.labelxs;
8813                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8814             }
8815             
8816             
8817         } else if ( this.fieldLabel.length) {
8818                 
8819             cfg.cn = [
8820                 {
8821                     tag : 'i',
8822                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8823                     tooltip : 'This field is required'
8824                 },
8825                 {
8826                     tag: 'label',
8827                    //cls : 'input-group-addon',
8828                     html : this.fieldLabel
8829
8830                 },
8831
8832                inputblock
8833
8834            ];
8835            
8836            if(this.indicatorpos == 'right'){
8837                 
8838                 cfg.cn = [
8839                     {
8840                         tag: 'label',
8841                        //cls : 'input-group-addon',
8842                         html : this.fieldLabel
8843
8844                     },
8845                     {
8846                         tag : 'i',
8847                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8848                         tooltip : 'This field is required'
8849                     },
8850
8851                    inputblock
8852
8853                ];
8854
8855             }
8856
8857         } else {
8858             
8859             cfg.cn = [
8860
8861                     inputblock
8862
8863             ];
8864                 
8865                 
8866         };
8867         
8868         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8869            cfg.cls += ' navbar-form';
8870         }
8871         
8872         if (this.parentType === 'NavGroup') {
8873            cfg.cls += ' navbar-form';
8874            cfg.tag = 'li';
8875         }
8876         
8877         return cfg;
8878         
8879     },
8880     /**
8881      * return the real input element.
8882      */
8883     inputEl: function ()
8884     {
8885         return this.el.select('input.form-control',true).first();
8886     },
8887     
8888     tooltipEl : function()
8889     {
8890         return this.inputEl();
8891     },
8892     
8893     indicatorEl : function()
8894     {
8895         var indicator = this.el.select('i.roo-required-indicator',true).first();
8896         
8897         if(!indicator){
8898             return false;
8899         }
8900         
8901         return indicator;
8902         
8903     },
8904     
8905     setDisabled : function(v)
8906     {
8907         var i  = this.inputEl().dom;
8908         if (!v) {
8909             i.removeAttribute('disabled');
8910             return;
8911             
8912         }
8913         i.setAttribute('disabled','true');
8914     },
8915     initEvents : function()
8916     {
8917           
8918         this.inputEl().on("keydown" , this.fireKey,  this);
8919         this.inputEl().on("focus", this.onFocus,  this);
8920         this.inputEl().on("blur", this.onBlur,  this);
8921         
8922         this.inputEl().relayEvent('keyup', this);
8923         
8924         this.indicator = this.indicatorEl();
8925         
8926         if(this.indicator){
8927             this.indicator.addClass('invisible');
8928             
8929         }
8930  
8931         // reference to original value for reset
8932         this.originalValue = this.getValue();
8933         //Roo.form.TextField.superclass.initEvents.call(this);
8934         if(this.validationEvent == 'keyup'){
8935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8936             this.inputEl().on('keyup', this.filterValidation, this);
8937         }
8938         else if(this.validationEvent !== false){
8939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8940         }
8941         
8942         if(this.selectOnFocus){
8943             this.on("focus", this.preFocus, this);
8944             
8945         }
8946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8947             this.inputEl().on("keypress", this.filterKeys, this);
8948         } else {
8949             this.inputEl().relayEvent('keypress', this);
8950         }
8951        /* if(this.grow){
8952             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8953             this.el.on("click", this.autoSize,  this);
8954         }
8955         */
8956         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8957             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8958         }
8959         
8960         if (typeof(this.before) == 'object') {
8961             this.before.render(this.el.select('.roo-input-before',true).first());
8962         }
8963         if (typeof(this.after) == 'object') {
8964             this.after.render(this.el.select('.roo-input-after',true).first());
8965         }
8966         
8967         
8968     },
8969     filterValidation : function(e){
8970         if(!e.isNavKeyPress()){
8971             this.validationTask.delay(this.validationDelay);
8972         }
8973     },
8974      /**
8975      * Validates the field value
8976      * @return {Boolean} True if the value is valid, else false
8977      */
8978     validate : function(){
8979         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8980         if(this.disabled || this.validateValue(this.getRawValue())){
8981             this.markValid();
8982             return true;
8983         }
8984         
8985         this.markInvalid();
8986         return false;
8987     },
8988     
8989     
8990     /**
8991      * Validates a value according to the field's validation rules and marks the field as invalid
8992      * if the validation fails
8993      * @param {Mixed} value The value to validate
8994      * @return {Boolean} True if the value is valid, else false
8995      */
8996     validateValue : function(value){
8997         if(value.length < 1)  { // if it's blank
8998             if(this.allowBlank){
8999                 return true;
9000             }            
9001             return this.inputEl().hasClass('hide') ? true : false;
9002         }
9003         
9004         if(value.length < this.minLength){
9005             return false;
9006         }
9007         if(value.length > this.maxLength){
9008             return false;
9009         }
9010         if(this.vtype){
9011             var vt = Roo.form.VTypes;
9012             if(!vt[this.vtype](value, this)){
9013                 return false;
9014             }
9015         }
9016         if(typeof this.validator == "function"){
9017             var msg = this.validator(value);
9018             if(msg !== true){
9019                 return false;
9020             }
9021             if (typeof(msg) == 'string') {
9022                 this.invalidText = msg;
9023             }
9024         }
9025         
9026         if(this.regex && !this.regex.test(value)){
9027             return false;
9028         }
9029         
9030         return true;
9031     },
9032
9033     
9034     
9035      // private
9036     fireKey : function(e){
9037         //Roo.log('field ' + e.getKey());
9038         if(e.isNavKeyPress()){
9039             this.fireEvent("specialkey", this, e);
9040         }
9041     },
9042     focus : function (selectText){
9043         if(this.rendered){
9044             this.inputEl().focus();
9045             if(selectText === true){
9046                 this.inputEl().dom.select();
9047             }
9048         }
9049         return this;
9050     } ,
9051     
9052     onFocus : function(){
9053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9054            // this.el.addClass(this.focusClass);
9055         }
9056         if(!this.hasFocus){
9057             this.hasFocus = true;
9058             this.startValue = this.getValue();
9059             this.fireEvent("focus", this);
9060         }
9061     },
9062     
9063     beforeBlur : Roo.emptyFn,
9064
9065     
9066     // private
9067     onBlur : function(){
9068         this.beforeBlur();
9069         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9070             //this.el.removeClass(this.focusClass);
9071         }
9072         this.hasFocus = false;
9073         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9074             this.validate();
9075         }
9076         var v = this.getValue();
9077         if(String(v) !== String(this.startValue)){
9078             this.fireEvent('change', this, v, this.startValue);
9079         }
9080         this.fireEvent("blur", this);
9081     },
9082     
9083     /**
9084      * Resets the current field value to the originally loaded value and clears any validation messages
9085      */
9086     reset : function(){
9087         this.setValue(this.originalValue);
9088         this.validate();
9089     },
9090      /**
9091      * Returns the name of the field
9092      * @return {Mixed} name The name field
9093      */
9094     getName: function(){
9095         return this.name;
9096     },
9097      /**
9098      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9099      * @return {Mixed} value The field value
9100      */
9101     getValue : function(){
9102         
9103         var v = this.inputEl().getValue();
9104         
9105         return v;
9106     },
9107     /**
9108      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9109      * @return {Mixed} value The field value
9110      */
9111     getRawValue : function(){
9112         var v = this.inputEl().getValue();
9113         
9114         return v;
9115     },
9116     
9117     /**
9118      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9119      * @param {Mixed} value The value to set
9120      */
9121     setRawValue : function(v){
9122         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9123     },
9124     
9125     selectText : function(start, end){
9126         var v = this.getRawValue();
9127         if(v.length > 0){
9128             start = start === undefined ? 0 : start;
9129             end = end === undefined ? v.length : end;
9130             var d = this.inputEl().dom;
9131             if(d.setSelectionRange){
9132                 d.setSelectionRange(start, end);
9133             }else if(d.createTextRange){
9134                 var range = d.createTextRange();
9135                 range.moveStart("character", start);
9136                 range.moveEnd("character", v.length-end);
9137                 range.select();
9138             }
9139         }
9140     },
9141     
9142     /**
9143      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9144      * @param {Mixed} value The value to set
9145      */
9146     setValue : function(v){
9147         this.value = v;
9148         if(this.rendered){
9149             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9150             this.validate();
9151         }
9152     },
9153     
9154     /*
9155     processValue : function(value){
9156         if(this.stripCharsRe){
9157             var newValue = value.replace(this.stripCharsRe, '');
9158             if(newValue !== value){
9159                 this.setRawValue(newValue);
9160                 return newValue;
9161             }
9162         }
9163         return value;
9164     },
9165   */
9166     preFocus : function(){
9167         
9168         if(this.selectOnFocus){
9169             this.inputEl().dom.select();
9170         }
9171     },
9172     filterKeys : function(e){
9173         var k = e.getKey();
9174         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9175             return;
9176         }
9177         var c = e.getCharCode(), cc = String.fromCharCode(c);
9178         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9179             return;
9180         }
9181         if(!this.maskRe.test(cc)){
9182             e.stopEvent();
9183         }
9184     },
9185      /**
9186      * Clear any invalid styles/messages for this field
9187      */
9188     clearInvalid : function(){
9189         
9190         if(!this.el || this.preventMark){ // not rendered
9191             return;
9192         }
9193         
9194      
9195         this.el.removeClass(this.invalidClass);
9196         
9197         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9198             
9199             var feedback = this.el.select('.form-control-feedback', true).first();
9200             
9201             if(feedback){
9202                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9203             }
9204             
9205         }
9206         
9207         this.fireEvent('valid', this);
9208     },
9209     
9210      /**
9211      * Mark this field as valid
9212      */
9213     markValid : function()
9214     {
9215         if(!this.el  || this.preventMark){ // not rendered...
9216             return;
9217         }
9218         
9219         this.el.removeClass([this.invalidClass, this.validClass]);
9220         
9221         var feedback = this.el.select('.form-control-feedback', true).first();
9222             
9223         if(feedback){
9224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9225         }
9226
9227         if(this.disabled){
9228             return;
9229         }
9230         
9231         if(this.allowBlank && !this.getRawValue().length){
9232             return;
9233         }
9234         
9235         if(this.indicator){
9236             this.indicator.removeClass('visible');
9237             this.indicator.addClass('invisible');
9238         }
9239         
9240         this.el.addClass(this.validClass);
9241         
9242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9243             
9244             var feedback = this.el.select('.form-control-feedback', true).first();
9245             
9246             if(feedback){
9247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9248                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9249             }
9250             
9251         }
9252         
9253         this.fireEvent('valid', this);
9254     },
9255     
9256      /**
9257      * Mark this field as invalid
9258      * @param {String} msg The validation message
9259      */
9260     markInvalid : function(msg)
9261     {
9262         if(!this.el  || this.preventMark){ // not rendered
9263             return;
9264         }
9265         
9266         this.el.removeClass([this.invalidClass, this.validClass]);
9267         
9268         var feedback = this.el.select('.form-control-feedback', true).first();
9269             
9270         if(feedback){
9271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272         }
9273
9274         if(this.disabled){
9275             return;
9276         }
9277         
9278         if(this.allowBlank && !this.getRawValue().length){
9279             return;
9280         }
9281         
9282         if(this.indicator){
9283             this.indicator.removeClass('invisible');
9284             this.indicator.addClass('visible');
9285         }
9286         
9287         this.el.addClass(this.invalidClass);
9288         
9289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9290             
9291             var feedback = this.el.select('.form-control-feedback', true).first();
9292             
9293             if(feedback){
9294                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9295                 
9296                 if(this.getValue().length || this.forceFeedback){
9297                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9298                 }
9299                 
9300             }
9301             
9302         }
9303         
9304         this.fireEvent('invalid', this, msg);
9305     },
9306     // private
9307     SafariOnKeyDown : function(event)
9308     {
9309         // this is a workaround for a password hang bug on chrome/ webkit.
9310         if (this.inputEl().dom.type != 'password') {
9311             return;
9312         }
9313         
9314         var isSelectAll = false;
9315         
9316         if(this.inputEl().dom.selectionEnd > 0){
9317             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9318         }
9319         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9320             event.preventDefault();
9321             this.setValue('');
9322             return;
9323         }
9324         
9325         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9326             
9327             event.preventDefault();
9328             // this is very hacky as keydown always get's upper case.
9329             //
9330             var cc = String.fromCharCode(event.getCharCode());
9331             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9332             
9333         }
9334     },
9335     adjustWidth : function(tag, w){
9336         tag = tag.toLowerCase();
9337         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9338             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9339                 if(tag == 'input'){
9340                     return w + 2;
9341                 }
9342                 if(tag == 'textarea'){
9343                     return w-2;
9344                 }
9345             }else if(Roo.isOpera){
9346                 if(tag == 'input'){
9347                     return w + 2;
9348                 }
9349                 if(tag == 'textarea'){
9350                     return w-2;
9351                 }
9352             }
9353         }
9354         return w;
9355     },
9356     
9357     setFieldLabel : function(v)
9358     {
9359         if(!this.rendered){
9360             return;
9361         }
9362         
9363         this.fieldLabel = v;
9364         
9365         if(this.indicator){
9366             var ar = this.el.select('label > span',true);
9367             if (!ar.length) {
9368                 Roo.log("could not find label > span on element");
9369                 Roo.log(this);
9370                 return;
9371             }
9372             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9373             return;
9374         }
9375         
9376         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9377     }
9378 });
9379
9380  
9381 /*
9382  * - LGPL
9383  *
9384  * Input
9385  * 
9386  */
9387
9388 /**
9389  * @class Roo.bootstrap.TextArea
9390  * @extends Roo.bootstrap.Input
9391  * Bootstrap TextArea class
9392  * @cfg {Number} cols Specifies the visible width of a text area
9393  * @cfg {Number} rows Specifies the visible number of lines in a text area
9394  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9395  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9396  * @cfg {string} html text
9397  * 
9398  * @constructor
9399  * Create a new TextArea
9400  * @param {Object} config The config object
9401  */
9402
9403 Roo.bootstrap.TextArea = function(config){
9404     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9405    
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9409      
9410     cols : false,
9411     rows : 5,
9412     readOnly : false,
9413     warp : 'soft',
9414     resize : false,
9415     value: false,
9416     html: false,
9417     
9418     getAutoCreate : function(){
9419         
9420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9421         
9422         var id = Roo.id();
9423         
9424         var cfg = {};
9425         
9426         if(this.inputType != 'hidden'){
9427             cfg.cls = 'form-group' //input-group
9428         }
9429         
9430         var input =  {
9431             tag: 'textarea',
9432             id : id,
9433             warp : this.warp,
9434             rows : this.rows,
9435             value : this.value || '',
9436             html: this.html || '',
9437             cls : 'form-control',
9438             placeholder : this.placeholder || '' 
9439             
9440         };
9441         
9442         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9443             input.maxLength = this.maxLength;
9444         }
9445         
9446         if(this.resize){
9447             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9448         }
9449         
9450         if(this.cols){
9451             input.cols = this.cols;
9452         }
9453         
9454         if (this.readOnly) {
9455             input.readonly = true;
9456         }
9457         
9458         if (this.name) {
9459             input.name = this.name;
9460         }
9461         
9462         if (this.size) {
9463             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9464         }
9465         
9466         var settings=this;
9467         ['xs','sm','md','lg'].map(function(size){
9468             if (settings[size]) {
9469                 cfg.cls += ' col-' + size + '-' + settings[size];
9470             }
9471         });
9472         
9473         var inputblock = input;
9474         
9475         if(this.hasFeedback && !this.allowBlank){
9476             
9477             var feedback = {
9478                 tag: 'span',
9479                 cls: 'glyphicon form-control-feedback'
9480             };
9481
9482             inputblock = {
9483                 cls : 'has-feedback',
9484                 cn :  [
9485                     input,
9486                     feedback
9487                 ] 
9488             };  
9489         }
9490         
9491         
9492         if (this.before || this.after) {
9493             
9494             inputblock = {
9495                 cls : 'input-group',
9496                 cn :  [] 
9497             };
9498             if (this.before) {
9499                 inputblock.cn.push({
9500                     tag :'span',
9501                     cls : 'input-group-addon',
9502                     html : this.before
9503                 });
9504             }
9505             
9506             inputblock.cn.push(input);
9507             
9508             if(this.hasFeedback && !this.allowBlank){
9509                 inputblock.cls += ' has-feedback';
9510                 inputblock.cn.push(feedback);
9511             }
9512             
9513             if (this.after) {
9514                 inputblock.cn.push({
9515                     tag :'span',
9516                     cls : 'input-group-addon',
9517                     html : this.after
9518                 });
9519             }
9520             
9521         }
9522         
9523         if (align ==='left' && this.fieldLabel.length) {
9524             cfg.cn = [
9525                 {
9526                     tag: 'label',
9527                     'for' :  id,
9528                     cls : 'control-label',
9529                     html : this.fieldLabel
9530                 },
9531                 {
9532                     cls : "",
9533                     cn: [
9534                         inputblock
9535                     ]
9536                 }
9537
9538             ];
9539             
9540             if(this.labelWidth > 12){
9541                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9542             }
9543
9544             if(this.labelWidth < 13 && this.labelmd == 0){
9545                 this.labelmd = this.labelWidth;
9546             }
9547
9548             if(this.labellg > 0){
9549                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9550                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9551             }
9552
9553             if(this.labelmd > 0){
9554                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9555                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9556             }
9557
9558             if(this.labelsm > 0){
9559                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9560                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9561             }
9562
9563             if(this.labelxs > 0){
9564                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9565                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9566             }
9567             
9568         } else if ( this.fieldLabel.length) {
9569             cfg.cn = [
9570
9571                {
9572                    tag: 'label',
9573                    //cls : 'input-group-addon',
9574                    html : this.fieldLabel
9575
9576                },
9577
9578                inputblock
9579
9580            ];
9581
9582         } else {
9583
9584             cfg.cn = [
9585
9586                 inputblock
9587
9588             ];
9589                 
9590         }
9591         
9592         if (this.disabled) {
9593             input.disabled=true;
9594         }
9595         
9596         return cfg;
9597         
9598     },
9599     /**
9600      * return the real textarea element.
9601      */
9602     inputEl: function ()
9603     {
9604         return this.el.select('textarea.form-control',true).first();
9605     },
9606     
9607     /**
9608      * Clear any invalid styles/messages for this field
9609      */
9610     clearInvalid : function()
9611     {
9612         
9613         if(!this.el || this.preventMark){ // not rendered
9614             return;
9615         }
9616         
9617         var label = this.el.select('label', true).first();
9618         var icon = this.el.select('i.fa-star', true).first();
9619         
9620         if(label && icon){
9621             icon.remove();
9622         }
9623         
9624         this.el.removeClass(this.invalidClass);
9625         
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9627             
9628             var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630             if(feedback){
9631                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9632             }
9633             
9634         }
9635         
9636         this.fireEvent('valid', this);
9637     },
9638     
9639      /**
9640      * Mark this field as valid
9641      */
9642     markValid : function()
9643     {
9644         if(!this.el  || this.preventMark){ // not rendered
9645             return;
9646         }
9647         
9648         this.el.removeClass([this.invalidClass, this.validClass]);
9649         
9650         var feedback = this.el.select('.form-control-feedback', true).first();
9651             
9652         if(feedback){
9653             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9654         }
9655
9656         if(this.disabled || this.allowBlank){
9657             return;
9658         }
9659         
9660         var label = this.el.select('label', true).first();
9661         var icon = this.el.select('i.fa-star', true).first();
9662         
9663         if(label && icon){
9664             icon.remove();
9665         }
9666         
9667         this.el.addClass(this.validClass);
9668         
9669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9670             
9671             var feedback = this.el.select('.form-control-feedback', true).first();
9672             
9673             if(feedback){
9674                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9675                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9676             }
9677             
9678         }
9679         
9680         this.fireEvent('valid', this);
9681     },
9682     
9683      /**
9684      * Mark this field as invalid
9685      * @param {String} msg The validation message
9686      */
9687     markInvalid : function(msg)
9688     {
9689         if(!this.el  || this.preventMark){ // not rendered
9690             return;
9691         }
9692         
9693         this.el.removeClass([this.invalidClass, this.validClass]);
9694         
9695         var feedback = this.el.select('.form-control-feedback', true).first();
9696             
9697         if(feedback){
9698             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9699         }
9700
9701         if(this.disabled || this.allowBlank){
9702             return;
9703         }
9704         
9705         var label = this.el.select('label', true).first();
9706         var icon = this.el.select('i.fa-star', true).first();
9707         
9708         if(!this.getValue().length && label && !icon){
9709             this.el.createChild({
9710                 tag : 'i',
9711                 cls : 'text-danger fa fa-lg fa-star',
9712                 tooltip : 'This field is required',
9713                 style : 'margin-right:5px;'
9714             }, label, true);
9715         }
9716
9717         this.el.addClass(this.invalidClass);
9718         
9719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9720             
9721             var feedback = this.el.select('.form-control-feedback', true).first();
9722             
9723             if(feedback){
9724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725                 
9726                 if(this.getValue().length || this.forceFeedback){
9727                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9728                 }
9729                 
9730             }
9731             
9732         }
9733         
9734         this.fireEvent('invalid', this, msg);
9735     }
9736 });
9737
9738  
9739 /*
9740  * - LGPL
9741  *
9742  * trigger field - base class for combo..
9743  * 
9744  */
9745  
9746 /**
9747  * @class Roo.bootstrap.TriggerField
9748  * @extends Roo.bootstrap.Input
9749  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9750  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9751  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9752  * for which you can provide a custom implementation.  For example:
9753  * <pre><code>
9754 var trigger = new Roo.bootstrap.TriggerField();
9755 trigger.onTriggerClick = myTriggerFn;
9756 trigger.applyTo('my-field');
9757 </code></pre>
9758  *
9759  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9760  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9761  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9762  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9763  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9764
9765  * @constructor
9766  * Create a new TriggerField.
9767  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9768  * to the base TextField)
9769  */
9770 Roo.bootstrap.TriggerField = function(config){
9771     this.mimicing = false;
9772     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9773 };
9774
9775 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9776     /**
9777      * @cfg {String} triggerClass A CSS class to apply to the trigger
9778      */
9779      /**
9780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9781      */
9782     hideTrigger:false,
9783
9784     /**
9785      * @cfg {Boolean} removable (true|false) special filter default false
9786      */
9787     removable : false,
9788     
9789     /** @cfg {Boolean} grow @hide */
9790     /** @cfg {Number} growMin @hide */
9791     /** @cfg {Number} growMax @hide */
9792
9793     /**
9794      * @hide 
9795      * @method
9796      */
9797     autoSize: Roo.emptyFn,
9798     // private
9799     monitorTab : true,
9800     // private
9801     deferHeight : true,
9802
9803     
9804     actionMode : 'wrap',
9805     
9806     caret : false,
9807     
9808     
9809     getAutoCreate : function(){
9810        
9811         var align = this.labelAlign || this.parentLabelAlign();
9812         
9813         var id = Roo.id();
9814         
9815         var cfg = {
9816             cls: 'form-group' //input-group
9817         };
9818         
9819         
9820         var input =  {
9821             tag: 'input',
9822             id : id,
9823             type : this.inputType,
9824             cls : 'form-control',
9825             autocomplete: 'new-password',
9826             placeholder : this.placeholder || '' 
9827             
9828         };
9829         if (this.name) {
9830             input.name = this.name;
9831         }
9832         if (this.size) {
9833             input.cls += ' input-' + this.size;
9834         }
9835         
9836         if (this.disabled) {
9837             input.disabled=true;
9838         }
9839         
9840         var inputblock = input;
9841         
9842         if(this.hasFeedback && !this.allowBlank){
9843             
9844             var feedback = {
9845                 tag: 'span',
9846                 cls: 'glyphicon form-control-feedback'
9847             };
9848             
9849             if(this.removable && !this.editable && !this.tickable){
9850                 inputblock = {
9851                     cls : 'has-feedback',
9852                     cn :  [
9853                         inputblock,
9854                         {
9855                             tag: 'button',
9856                             html : 'x',
9857                             cls : 'roo-combo-removable-btn close'
9858                         },
9859                         feedback
9860                     ] 
9861                 };
9862             } else {
9863                 inputblock = {
9864                     cls : 'has-feedback',
9865                     cn :  [
9866                         inputblock,
9867                         feedback
9868                     ] 
9869                 };
9870             }
9871
9872         } else {
9873             if(this.removable && !this.editable && !this.tickable){
9874                 inputblock = {
9875                     cls : 'roo-removable',
9876                     cn :  [
9877                         inputblock,
9878                         {
9879                             tag: 'button',
9880                             html : 'x',
9881                             cls : 'roo-combo-removable-btn close'
9882                         }
9883                     ] 
9884                 };
9885             }
9886         }
9887         
9888         if (this.before || this.after) {
9889             
9890             inputblock = {
9891                 cls : 'input-group',
9892                 cn :  [] 
9893             };
9894             if (this.before) {
9895                 inputblock.cn.push({
9896                     tag :'span',
9897                     cls : 'input-group-addon',
9898                     html : this.before
9899                 });
9900             }
9901             
9902             inputblock.cn.push(input);
9903             
9904             if(this.hasFeedback && !this.allowBlank){
9905                 inputblock.cls += ' has-feedback';
9906                 inputblock.cn.push(feedback);
9907             }
9908             
9909             if (this.after) {
9910                 inputblock.cn.push({
9911                     tag :'span',
9912                     cls : 'input-group-addon',
9913                     html : this.after
9914                 });
9915             }
9916             
9917         };
9918         
9919         var box = {
9920             tag: 'div',
9921             cn: [
9922                 {
9923                     tag: 'input',
9924                     type : 'hidden',
9925                     cls: 'form-hidden-field'
9926                 },
9927                 inputblock
9928             ]
9929             
9930         };
9931         
9932         if(this.multiple){
9933             box = {
9934                 tag: 'div',
9935                 cn: [
9936                     {
9937                         tag: 'input',
9938                         type : 'hidden',
9939                         cls: 'form-hidden-field'
9940                     },
9941                     {
9942                         tag: 'ul',
9943                         cls: 'roo-select2-choices',
9944                         cn:[
9945                             {
9946                                 tag: 'li',
9947                                 cls: 'roo-select2-search-field',
9948                                 cn: [
9949
9950                                     inputblock
9951                                 ]
9952                             }
9953                         ]
9954                     }
9955                 ]
9956             }
9957         };
9958         
9959         var combobox = {
9960             cls: 'roo-select2-container input-group',
9961             cn: [
9962                 box
9963 //                {
9964 //                    tag: 'ul',
9965 //                    cls: 'typeahead typeahead-long dropdown-menu',
9966 //                    style: 'display:none'
9967 //                }
9968             ]
9969         };
9970         
9971         if(!this.multiple && this.showToggleBtn){
9972             
9973             var caret = {
9974                         tag: 'span',
9975                         cls: 'caret'
9976              };
9977             if (this.caret != false) {
9978                 caret = {
9979                      tag: 'i',
9980                      cls: 'fa fa-' + this.caret
9981                 };
9982                 
9983             }
9984             
9985             combobox.cn.push({
9986                 tag :'span',
9987                 cls : 'input-group-addon btn dropdown-toggle',
9988                 cn : [
9989                     caret,
9990                     {
9991                         tag: 'span',
9992                         cls: 'combobox-clear',
9993                         cn  : [
9994                             {
9995                                 tag : 'i',
9996                                 cls: 'icon-remove'
9997                             }
9998                         ]
9999                     }
10000                 ]
10001
10002             })
10003         }
10004         
10005         if(this.multiple){
10006             combobox.cls += ' roo-select2-container-multi';
10007         }
10008         
10009         if (align ==='left' && this.fieldLabel.length) {
10010             
10011             cfg.cls += ' roo-form-group-label-left';
10012
10013             cfg.cn = [
10014                 {
10015                     tag : 'i',
10016                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10017                     tooltip : 'This field is required'
10018                 },
10019                 {
10020                     tag: 'label',
10021                     'for' :  id,
10022                     cls : 'control-label',
10023                     html : this.fieldLabel
10024
10025                 },
10026                 {
10027                     cls : "", 
10028                     cn: [
10029                         combobox
10030                     ]
10031                 }
10032
10033             ];
10034             
10035             var labelCfg = cfg.cn[1];
10036             var contentCfg = cfg.cn[2];
10037             
10038             if(this.indicatorpos == 'right'){
10039                 cfg.cn = [
10040                     {
10041                         tag: 'label',
10042                         'for' :  id,
10043                         cls : 'control-label',
10044                         cn : [
10045                             {
10046                                 tag : 'span',
10047                                 html : this.fieldLabel
10048                             },
10049                             {
10050                                 tag : 'i',
10051                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10052                                 tooltip : 'This field is required'
10053                             }
10054                         ]
10055                     },
10056                     {
10057                         cls : "", 
10058                         cn: [
10059                             combobox
10060                         ]
10061                     }
10062
10063                 ];
10064                 
10065                 labelCfg = cfg.cn[0];
10066                 contentCfg = cfg.cn[1];
10067             }
10068             
10069             if(this.labelWidth > 12){
10070                 labelCfg.style = "width: " + this.labelWidth + 'px';
10071             }
10072             
10073             if(this.labelWidth < 13 && this.labelmd == 0){
10074                 this.labelmd = this.labelWidth;
10075             }
10076             
10077             if(this.labellg > 0){
10078                 labelCfg.cls += ' col-lg-' + this.labellg;
10079                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10080             }
10081             
10082             if(this.labelmd > 0){
10083                 labelCfg.cls += ' col-md-' + this.labelmd;
10084                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10085             }
10086             
10087             if(this.labelsm > 0){
10088                 labelCfg.cls += ' col-sm-' + this.labelsm;
10089                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10090             }
10091             
10092             if(this.labelxs > 0){
10093                 labelCfg.cls += ' col-xs-' + this.labelxs;
10094                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10095             }
10096             
10097         } else if ( this.fieldLabel.length) {
10098 //                Roo.log(" label");
10099             cfg.cn = [
10100                 {
10101                    tag : 'i',
10102                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10103                    tooltip : 'This field is required'
10104                },
10105                {
10106                    tag: 'label',
10107                    //cls : 'input-group-addon',
10108                    html : this.fieldLabel
10109
10110                },
10111
10112                combobox
10113
10114             ];
10115             
10116             if(this.indicatorpos == 'right'){
10117                 
10118                 cfg.cn = [
10119                     {
10120                        tag: 'label',
10121                        cn : [
10122                            {
10123                                tag : 'span',
10124                                html : this.fieldLabel
10125                            },
10126                            {
10127                               tag : 'i',
10128                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10129                               tooltip : 'This field is required'
10130                            }
10131                        ]
10132
10133                     },
10134                     combobox
10135
10136                 ];
10137
10138             }
10139
10140         } else {
10141             
10142 //                Roo.log(" no label && no align");
10143                 cfg = combobox
10144                      
10145                 
10146         }
10147         
10148         var settings=this;
10149         ['xs','sm','md','lg'].map(function(size){
10150             if (settings[size]) {
10151                 cfg.cls += ' col-' + size + '-' + settings[size];
10152             }
10153         });
10154         
10155         return cfg;
10156         
10157     },
10158     
10159     
10160     
10161     // private
10162     onResize : function(w, h){
10163 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10164 //        if(typeof w == 'number'){
10165 //            var x = w - this.trigger.getWidth();
10166 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10167 //            this.trigger.setStyle('left', x+'px');
10168 //        }
10169     },
10170
10171     // private
10172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10173
10174     // private
10175     getResizeEl : function(){
10176         return this.inputEl();
10177     },
10178
10179     // private
10180     getPositionEl : function(){
10181         return this.inputEl();
10182     },
10183
10184     // private
10185     alignErrorIcon : function(){
10186         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10187     },
10188
10189     // private
10190     initEvents : function(){
10191         
10192         this.createList();
10193         
10194         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10195         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10196         if(!this.multiple && this.showToggleBtn){
10197             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10198             if(this.hideTrigger){
10199                 this.trigger.setDisplayed(false);
10200             }
10201             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10202         }
10203         
10204         if(this.multiple){
10205             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10206         }
10207         
10208         if(this.removable && !this.editable && !this.tickable){
10209             var close = this.closeTriggerEl();
10210             
10211             if(close){
10212                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10213                 close.on('click', this.removeBtnClick, this, close);
10214             }
10215         }
10216         
10217         //this.trigger.addClassOnOver('x-form-trigger-over');
10218         //this.trigger.addClassOnClick('x-form-trigger-click');
10219         
10220         //if(!this.width){
10221         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10222         //}
10223     },
10224     
10225     closeTriggerEl : function()
10226     {
10227         var close = this.el.select('.roo-combo-removable-btn', true).first();
10228         return close ? close : false;
10229     },
10230     
10231     removeBtnClick : function(e, h, el)
10232     {
10233         e.preventDefault();
10234         
10235         if(this.fireEvent("remove", this) !== false){
10236             this.reset();
10237             this.fireEvent("afterremove", this)
10238         }
10239     },
10240     
10241     createList : function()
10242     {
10243         this.list = Roo.get(document.body).createChild({
10244             tag: 'ul',
10245             cls: 'typeahead typeahead-long dropdown-menu',
10246             style: 'display:none'
10247         });
10248         
10249         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10250         
10251     },
10252
10253     // private
10254     initTrigger : function(){
10255        
10256     },
10257
10258     // private
10259     onDestroy : function(){
10260         if(this.trigger){
10261             this.trigger.removeAllListeners();
10262           //  this.trigger.remove();
10263         }
10264         //if(this.wrap){
10265         //    this.wrap.remove();
10266         //}
10267         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10268     },
10269
10270     // private
10271     onFocus : function(){
10272         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10273         /*
10274         if(!this.mimicing){
10275             this.wrap.addClass('x-trigger-wrap-focus');
10276             this.mimicing = true;
10277             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10278             if(this.monitorTab){
10279                 this.el.on("keydown", this.checkTab, this);
10280             }
10281         }
10282         */
10283     },
10284
10285     // private
10286     checkTab : function(e){
10287         if(e.getKey() == e.TAB){
10288             this.triggerBlur();
10289         }
10290     },
10291
10292     // private
10293     onBlur : function(){
10294         // do nothing
10295     },
10296
10297     // private
10298     mimicBlur : function(e, t){
10299         /*
10300         if(!this.wrap.contains(t) && this.validateBlur()){
10301             this.triggerBlur();
10302         }
10303         */
10304     },
10305
10306     // private
10307     triggerBlur : function(){
10308         this.mimicing = false;
10309         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10310         if(this.monitorTab){
10311             this.el.un("keydown", this.checkTab, this);
10312         }
10313         //this.wrap.removeClass('x-trigger-wrap-focus');
10314         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10315     },
10316
10317     // private
10318     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10319     validateBlur : function(e, t){
10320         return true;
10321     },
10322
10323     // private
10324     onDisable : function(){
10325         this.inputEl().dom.disabled = true;
10326         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10327         //if(this.wrap){
10328         //    this.wrap.addClass('x-item-disabled');
10329         //}
10330     },
10331
10332     // private
10333     onEnable : function(){
10334         this.inputEl().dom.disabled = false;
10335         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10336         //if(this.wrap){
10337         //    this.el.removeClass('x-item-disabled');
10338         //}
10339     },
10340
10341     // private
10342     onShow : function(){
10343         var ae = this.getActionEl();
10344         
10345         if(ae){
10346             ae.dom.style.display = '';
10347             ae.dom.style.visibility = 'visible';
10348         }
10349     },
10350
10351     // private
10352     
10353     onHide : function(){
10354         var ae = this.getActionEl();
10355         ae.dom.style.display = 'none';
10356     },
10357
10358     /**
10359      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10360      * by an implementing function.
10361      * @method
10362      * @param {EventObject} e
10363      */
10364     onTriggerClick : Roo.emptyFn
10365 });
10366  /*
10367  * Based on:
10368  * Ext JS Library 1.1.1
10369  * Copyright(c) 2006-2007, Ext JS, LLC.
10370  *
10371  * Originally Released Under LGPL - original licence link has changed is not relivant.
10372  *
10373  * Fork - LGPL
10374  * <script type="text/javascript">
10375  */
10376
10377
10378 /**
10379  * @class Roo.data.SortTypes
10380  * @singleton
10381  * Defines the default sorting (casting?) comparison functions used when sorting data.
10382  */
10383 Roo.data.SortTypes = {
10384     /**
10385      * Default sort that does nothing
10386      * @param {Mixed} s The value being converted
10387      * @return {Mixed} The comparison value
10388      */
10389     none : function(s){
10390         return s;
10391     },
10392     
10393     /**
10394      * The regular expression used to strip tags
10395      * @type {RegExp}
10396      * @property
10397      */
10398     stripTagsRE : /<\/?[^>]+>/gi,
10399     
10400     /**
10401      * Strips all HTML tags to sort on text only
10402      * @param {Mixed} s The value being converted
10403      * @return {String} The comparison value
10404      */
10405     asText : function(s){
10406         return String(s).replace(this.stripTagsRE, "");
10407     },
10408     
10409     /**
10410      * Strips all HTML tags to sort on text only - Case insensitive
10411      * @param {Mixed} s The value being converted
10412      * @return {String} The comparison value
10413      */
10414     asUCText : function(s){
10415         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10416     },
10417     
10418     /**
10419      * Case insensitive string
10420      * @param {Mixed} s The value being converted
10421      * @return {String} The comparison value
10422      */
10423     asUCString : function(s) {
10424         return String(s).toUpperCase();
10425     },
10426     
10427     /**
10428      * Date sorting
10429      * @param {Mixed} s The value being converted
10430      * @return {Number} The comparison value
10431      */
10432     asDate : function(s) {
10433         if(!s){
10434             return 0;
10435         }
10436         if(s instanceof Date){
10437             return s.getTime();
10438         }
10439         return Date.parse(String(s));
10440     },
10441     
10442     /**
10443      * Float sorting
10444      * @param {Mixed} s The value being converted
10445      * @return {Float} The comparison value
10446      */
10447     asFloat : function(s) {
10448         var val = parseFloat(String(s).replace(/,/g, ""));
10449         if(isNaN(val)) {
10450             val = 0;
10451         }
10452         return val;
10453     },
10454     
10455     /**
10456      * Integer sorting
10457      * @param {Mixed} s The value being converted
10458      * @return {Number} The comparison value
10459      */
10460     asInt : function(s) {
10461         var val = parseInt(String(s).replace(/,/g, ""));
10462         if(isNaN(val)) {
10463             val = 0;
10464         }
10465         return val;
10466     }
10467 };/*
10468  * Based on:
10469  * Ext JS Library 1.1.1
10470  * Copyright(c) 2006-2007, Ext JS, LLC.
10471  *
10472  * Originally Released Under LGPL - original licence link has changed is not relivant.
10473  *
10474  * Fork - LGPL
10475  * <script type="text/javascript">
10476  */
10477
10478 /**
10479 * @class Roo.data.Record
10480  * Instances of this class encapsulate both record <em>definition</em> information, and record
10481  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10482  * to access Records cached in an {@link Roo.data.Store} object.<br>
10483  * <p>
10484  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10485  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10486  * objects.<br>
10487  * <p>
10488  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10489  * @constructor
10490  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10491  * {@link #create}. The parameters are the same.
10492  * @param {Array} data An associative Array of data values keyed by the field name.
10493  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10494  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10495  * not specified an integer id is generated.
10496  */
10497 Roo.data.Record = function(data, id){
10498     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10499     this.data = data;
10500 };
10501
10502 /**
10503  * Generate a constructor for a specific record layout.
10504  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10505  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10506  * Each field definition object may contain the following properties: <ul>
10507  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10508  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10509  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10510  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10511  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10512  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10513  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10514  * this may be omitted.</p></li>
10515  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10516  * <ul><li>auto (Default, implies no conversion)</li>
10517  * <li>string</li>
10518  * <li>int</li>
10519  * <li>float</li>
10520  * <li>boolean</li>
10521  * <li>date</li></ul></p></li>
10522  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10523  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10524  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10525  * by the Reader into an object that will be stored in the Record. It is passed the
10526  * following parameters:<ul>
10527  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10528  * </ul></p></li>
10529  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10530  * </ul>
10531  * <br>usage:<br><pre><code>
10532 var TopicRecord = Roo.data.Record.create(
10533     {name: 'title', mapping: 'topic_title'},
10534     {name: 'author', mapping: 'username'},
10535     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10536     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10537     {name: 'lastPoster', mapping: 'user2'},
10538     {name: 'excerpt', mapping: 'post_text'}
10539 );
10540
10541 var myNewRecord = new TopicRecord({
10542     title: 'Do my job please',
10543     author: 'noobie',
10544     totalPosts: 1,
10545     lastPost: new Date(),
10546     lastPoster: 'Animal',
10547     excerpt: 'No way dude!'
10548 });
10549 myStore.add(myNewRecord);
10550 </code></pre>
10551  * @method create
10552  * @static
10553  */
10554 Roo.data.Record.create = function(o){
10555     var f = function(){
10556         f.superclass.constructor.apply(this, arguments);
10557     };
10558     Roo.extend(f, Roo.data.Record);
10559     var p = f.prototype;
10560     p.fields = new Roo.util.MixedCollection(false, function(field){
10561         return field.name;
10562     });
10563     for(var i = 0, len = o.length; i < len; i++){
10564         p.fields.add(new Roo.data.Field(o[i]));
10565     }
10566     f.getField = function(name){
10567         return p.fields.get(name);  
10568     };
10569     return f;
10570 };
10571
10572 Roo.data.Record.AUTO_ID = 1000;
10573 Roo.data.Record.EDIT = 'edit';
10574 Roo.data.Record.REJECT = 'reject';
10575 Roo.data.Record.COMMIT = 'commit';
10576
10577 Roo.data.Record.prototype = {
10578     /**
10579      * Readonly flag - true if this record has been modified.
10580      * @type Boolean
10581      */
10582     dirty : false,
10583     editing : false,
10584     error: null,
10585     modified: null,
10586
10587     // private
10588     join : function(store){
10589         this.store = store;
10590     },
10591
10592     /**
10593      * Set the named field to the specified value.
10594      * @param {String} name The name of the field to set.
10595      * @param {Object} value The value to set the field to.
10596      */
10597     set : function(name, value){
10598         if(this.data[name] == value){
10599             return;
10600         }
10601         this.dirty = true;
10602         if(!this.modified){
10603             this.modified = {};
10604         }
10605         if(typeof this.modified[name] == 'undefined'){
10606             this.modified[name] = this.data[name];
10607         }
10608         this.data[name] = value;
10609         if(!this.editing && this.store){
10610             this.store.afterEdit(this);
10611         }       
10612     },
10613
10614     /**
10615      * Get the value of the named field.
10616      * @param {String} name The name of the field to get the value of.
10617      * @return {Object} The value of the field.
10618      */
10619     get : function(name){
10620         return this.data[name]; 
10621     },
10622
10623     // private
10624     beginEdit : function(){
10625         this.editing = true;
10626         this.modified = {}; 
10627     },
10628
10629     // private
10630     cancelEdit : function(){
10631         this.editing = false;
10632         delete this.modified;
10633     },
10634
10635     // private
10636     endEdit : function(){
10637         this.editing = false;
10638         if(this.dirty && this.store){
10639             this.store.afterEdit(this);
10640         }
10641     },
10642
10643     /**
10644      * Usually called by the {@link Roo.data.Store} which owns the Record.
10645      * Rejects all changes made to the Record since either creation, or the last commit operation.
10646      * Modified fields are reverted to their original values.
10647      * <p>
10648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10649      * of reject operations.
10650      */
10651     reject : function(){
10652         var m = this.modified;
10653         for(var n in m){
10654             if(typeof m[n] != "function"){
10655                 this.data[n] = m[n];
10656             }
10657         }
10658         this.dirty = false;
10659         delete this.modified;
10660         this.editing = false;
10661         if(this.store){
10662             this.store.afterReject(this);
10663         }
10664     },
10665
10666     /**
10667      * Usually called by the {@link Roo.data.Store} which owns the Record.
10668      * Commits all changes made to the Record since either creation, or the last commit operation.
10669      * <p>
10670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10671      * of commit operations.
10672      */
10673     commit : function(){
10674         this.dirty = false;
10675         delete this.modified;
10676         this.editing = false;
10677         if(this.store){
10678             this.store.afterCommit(this);
10679         }
10680     },
10681
10682     // private
10683     hasError : function(){
10684         return this.error != null;
10685     },
10686
10687     // private
10688     clearError : function(){
10689         this.error = null;
10690     },
10691
10692     /**
10693      * Creates a copy of this record.
10694      * @param {String} id (optional) A new record id if you don't want to use this record's id
10695      * @return {Record}
10696      */
10697     copy : function(newId) {
10698         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10699     }
10700 };/*
10701  * Based on:
10702  * Ext JS Library 1.1.1
10703  * Copyright(c) 2006-2007, Ext JS, LLC.
10704  *
10705  * Originally Released Under LGPL - original licence link has changed is not relivant.
10706  *
10707  * Fork - LGPL
10708  * <script type="text/javascript">
10709  */
10710
10711
10712
10713 /**
10714  * @class Roo.data.Store
10715  * @extends Roo.util.Observable
10716  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10717  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10718  * <p>
10719  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10720  * has no knowledge of the format of the data returned by the Proxy.<br>
10721  * <p>
10722  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10723  * instances from the data object. These records are cached and made available through accessor functions.
10724  * @constructor
10725  * Creates a new Store.
10726  * @param {Object} config A config object containing the objects needed for the Store to access data,
10727  * and read the data into Records.
10728  */
10729 Roo.data.Store = function(config){
10730     this.data = new Roo.util.MixedCollection(false);
10731     this.data.getKey = function(o){
10732         return o.id;
10733     };
10734     this.baseParams = {};
10735     // private
10736     this.paramNames = {
10737         "start" : "start",
10738         "limit" : "limit",
10739         "sort" : "sort",
10740         "dir" : "dir",
10741         "multisort" : "_multisort"
10742     };
10743
10744     if(config && config.data){
10745         this.inlineData = config.data;
10746         delete config.data;
10747     }
10748
10749     Roo.apply(this, config);
10750     
10751     if(this.reader){ // reader passed
10752         this.reader = Roo.factory(this.reader, Roo.data);
10753         this.reader.xmodule = this.xmodule || false;
10754         if(!this.recordType){
10755             this.recordType = this.reader.recordType;
10756         }
10757         if(this.reader.onMetaChange){
10758             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10759         }
10760     }
10761
10762     if(this.recordType){
10763         this.fields = this.recordType.prototype.fields;
10764     }
10765     this.modified = [];
10766
10767     this.addEvents({
10768         /**
10769          * @event datachanged
10770          * Fires when the data cache has changed, and a widget which is using this Store
10771          * as a Record cache should refresh its view.
10772          * @param {Store} this
10773          */
10774         datachanged : true,
10775         /**
10776          * @event metachange
10777          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10778          * @param {Store} this
10779          * @param {Object} meta The JSON metadata
10780          */
10781         metachange : true,
10782         /**
10783          * @event add
10784          * Fires when Records have been added to the Store
10785          * @param {Store} this
10786          * @param {Roo.data.Record[]} records The array of Records added
10787          * @param {Number} index The index at which the record(s) were added
10788          */
10789         add : true,
10790         /**
10791          * @event remove
10792          * Fires when a Record has been removed from the Store
10793          * @param {Store} this
10794          * @param {Roo.data.Record} record The Record that was removed
10795          * @param {Number} index The index at which the record was removed
10796          */
10797         remove : true,
10798         /**
10799          * @event update
10800          * Fires when a Record has been updated
10801          * @param {Store} this
10802          * @param {Roo.data.Record} record The Record that was updated
10803          * @param {String} operation The update operation being performed.  Value may be one of:
10804          * <pre><code>
10805  Roo.data.Record.EDIT
10806  Roo.data.Record.REJECT
10807  Roo.data.Record.COMMIT
10808          * </code></pre>
10809          */
10810         update : true,
10811         /**
10812          * @event clear
10813          * Fires when the data cache has been cleared.
10814          * @param {Store} this
10815          */
10816         clear : true,
10817         /**
10818          * @event beforeload
10819          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10820          * the load action will be canceled.
10821          * @param {Store} this
10822          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10823          */
10824         beforeload : true,
10825         /**
10826          * @event beforeloadadd
10827          * Fires after a new set of Records has been loaded.
10828          * @param {Store} this
10829          * @param {Roo.data.Record[]} records The Records that were loaded
10830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10831          */
10832         beforeloadadd : true,
10833         /**
10834          * @event load
10835          * Fires after a new set of Records has been loaded, before they are added to the store.
10836          * @param {Store} this
10837          * @param {Roo.data.Record[]} records The Records that were loaded
10838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10839          * @params {Object} return from reader
10840          */
10841         load : true,
10842         /**
10843          * @event loadexception
10844          * Fires if an exception occurs in the Proxy during loading.
10845          * Called with the signature of the Proxy's "loadexception" event.
10846          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10847          * 
10848          * @param {Proxy} 
10849          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10850          * @param {Object} load options 
10851          * @param {Object} jsonData from your request (normally this contains the Exception)
10852          */
10853         loadexception : true
10854     });
10855     
10856     if(this.proxy){
10857         this.proxy = Roo.factory(this.proxy, Roo.data);
10858         this.proxy.xmodule = this.xmodule || false;
10859         this.relayEvents(this.proxy,  ["loadexception"]);
10860     }
10861     this.sortToggle = {};
10862     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10863
10864     Roo.data.Store.superclass.constructor.call(this);
10865
10866     if(this.inlineData){
10867         this.loadData(this.inlineData);
10868         delete this.inlineData;
10869     }
10870 };
10871
10872 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10873      /**
10874     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10875     * without a remote query - used by combo/forms at present.
10876     */
10877     
10878     /**
10879     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10880     */
10881     /**
10882     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10883     */
10884     /**
10885     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10886     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10887     */
10888     /**
10889     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10890     * on any HTTP request
10891     */
10892     /**
10893     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10894     */
10895     /**
10896     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10897     */
10898     multiSort: false,
10899     /**
10900     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10901     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10902     */
10903     remoteSort : false,
10904
10905     /**
10906     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10907      * loaded or when a record is removed. (defaults to false).
10908     */
10909     pruneModifiedRecords : false,
10910
10911     // private
10912     lastOptions : null,
10913
10914     /**
10915      * Add Records to the Store and fires the add event.
10916      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10917      */
10918     add : function(records){
10919         records = [].concat(records);
10920         for(var i = 0, len = records.length; i < len; i++){
10921             records[i].join(this);
10922         }
10923         var index = this.data.length;
10924         this.data.addAll(records);
10925         this.fireEvent("add", this, records, index);
10926     },
10927
10928     /**
10929      * Remove a Record from the Store and fires the remove event.
10930      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10931      */
10932     remove : function(record){
10933         var index = this.data.indexOf(record);
10934         this.data.removeAt(index);
10935         if(this.pruneModifiedRecords){
10936             this.modified.remove(record);
10937         }
10938         this.fireEvent("remove", this, record, index);
10939     },
10940
10941     /**
10942      * Remove all Records from the Store and fires the clear event.
10943      */
10944     removeAll : function(){
10945         this.data.clear();
10946         if(this.pruneModifiedRecords){
10947             this.modified = [];
10948         }
10949         this.fireEvent("clear", this);
10950     },
10951
10952     /**
10953      * Inserts Records to the Store at the given index and fires the add event.
10954      * @param {Number} index The start index at which to insert the passed Records.
10955      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10956      */
10957     insert : function(index, records){
10958         records = [].concat(records);
10959         for(var i = 0, len = records.length; i < len; i++){
10960             this.data.insert(index, records[i]);
10961             records[i].join(this);
10962         }
10963         this.fireEvent("add", this, records, index);
10964     },
10965
10966     /**
10967      * Get the index within the cache of the passed Record.
10968      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10969      * @return {Number} The index of the passed Record. Returns -1 if not found.
10970      */
10971     indexOf : function(record){
10972         return this.data.indexOf(record);
10973     },
10974
10975     /**
10976      * Get the index within the cache of the Record with the passed id.
10977      * @param {String} id The id of the Record to find.
10978      * @return {Number} The index of the Record. Returns -1 if not found.
10979      */
10980     indexOfId : function(id){
10981         return this.data.indexOfKey(id);
10982     },
10983
10984     /**
10985      * Get the Record with the specified id.
10986      * @param {String} id The id of the Record to find.
10987      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10988      */
10989     getById : function(id){
10990         return this.data.key(id);
10991     },
10992
10993     /**
10994      * Get the Record at the specified index.
10995      * @param {Number} index The index of the Record to find.
10996      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10997      */
10998     getAt : function(index){
10999         return this.data.itemAt(index);
11000     },
11001
11002     /**
11003      * Returns a range of Records between specified indices.
11004      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11005      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11006      * @return {Roo.data.Record[]} An array of Records
11007      */
11008     getRange : function(start, end){
11009         return this.data.getRange(start, end);
11010     },
11011
11012     // private
11013     storeOptions : function(o){
11014         o = Roo.apply({}, o);
11015         delete o.callback;
11016         delete o.scope;
11017         this.lastOptions = o;
11018     },
11019
11020     /**
11021      * Loads the Record cache from the configured Proxy using the configured Reader.
11022      * <p>
11023      * If using remote paging, then the first load call must specify the <em>start</em>
11024      * and <em>limit</em> properties in the options.params property to establish the initial
11025      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11026      * <p>
11027      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11028      * and this call will return before the new data has been loaded. Perform any post-processing
11029      * in a callback function, or in a "load" event handler.</strong>
11030      * <p>
11031      * @param {Object} options An object containing properties which control loading options:<ul>
11032      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11033      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11034      * passed the following arguments:<ul>
11035      * <li>r : Roo.data.Record[]</li>
11036      * <li>options: Options object from the load call</li>
11037      * <li>success: Boolean success indicator</li></ul></li>
11038      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11039      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11040      * </ul>
11041      */
11042     load : function(options){
11043         options = options || {};
11044         if(this.fireEvent("beforeload", this, options) !== false){
11045             this.storeOptions(options);
11046             var p = Roo.apply(options.params || {}, this.baseParams);
11047             // if meta was not loaded from remote source.. try requesting it.
11048             if (!this.reader.metaFromRemote) {
11049                 p._requestMeta = 1;
11050             }
11051             if(this.sortInfo && this.remoteSort){
11052                 var pn = this.paramNames;
11053                 p[pn["sort"]] = this.sortInfo.field;
11054                 p[pn["dir"]] = this.sortInfo.direction;
11055             }
11056             if (this.multiSort) {
11057                 var pn = this.paramNames;
11058                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11059             }
11060             
11061             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11062         }
11063     },
11064
11065     /**
11066      * Reloads the Record cache from the configured Proxy using the configured Reader and
11067      * the options from the last load operation performed.
11068      * @param {Object} options (optional) An object containing properties which may override the options
11069      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11070      * the most recently used options are reused).
11071      */
11072     reload : function(options){
11073         this.load(Roo.applyIf(options||{}, this.lastOptions));
11074     },
11075
11076     // private
11077     // Called as a callback by the Reader during a load operation.
11078     loadRecords : function(o, options, success){
11079         if(!o || success === false){
11080             if(success !== false){
11081                 this.fireEvent("load", this, [], options, o);
11082             }
11083             if(options.callback){
11084                 options.callback.call(options.scope || this, [], options, false);
11085             }
11086             return;
11087         }
11088         // if data returned failure - throw an exception.
11089         if (o.success === false) {
11090             // show a message if no listener is registered.
11091             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11092                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11093             }
11094             // loadmask wil be hooked into this..
11095             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11096             return;
11097         }
11098         var r = o.records, t = o.totalRecords || r.length;
11099         
11100         this.fireEvent("beforeloadadd", this, r, options, o);
11101         
11102         if(!options || options.add !== true){
11103             if(this.pruneModifiedRecords){
11104                 this.modified = [];
11105             }
11106             for(var i = 0, len = r.length; i < len; i++){
11107                 r[i].join(this);
11108             }
11109             if(this.snapshot){
11110                 this.data = this.snapshot;
11111                 delete this.snapshot;
11112             }
11113             this.data.clear();
11114             this.data.addAll(r);
11115             this.totalLength = t;
11116             this.applySort();
11117             this.fireEvent("datachanged", this);
11118         }else{
11119             this.totalLength = Math.max(t, this.data.length+r.length);
11120             this.add(r);
11121         }
11122         
11123         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11124                 
11125             var e = new Roo.data.Record({});
11126
11127             e.set(this.parent.displayField, this.parent.emptyTitle);
11128             e.set(this.parent.valueField, '');
11129
11130             this.insert(0, e);
11131         }
11132             
11133         this.fireEvent("load", this, r, options, o);
11134         if(options.callback){
11135             options.callback.call(options.scope || this, r, options, true);
11136         }
11137     },
11138
11139
11140     /**
11141      * Loads data from a passed data block. A Reader which understands the format of the data
11142      * must have been configured in the constructor.
11143      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11144      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11145      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11146      */
11147     loadData : function(o, append){
11148         var r = this.reader.readRecords(o);
11149         this.loadRecords(r, {add: append}, true);
11150     },
11151
11152     /**
11153      * Gets the number of cached records.
11154      * <p>
11155      * <em>If using paging, this may not be the total size of the dataset. If the data object
11156      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11157      * the data set size</em>
11158      */
11159     getCount : function(){
11160         return this.data.length || 0;
11161     },
11162
11163     /**
11164      * Gets the total number of records in the dataset as returned by the server.
11165      * <p>
11166      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11167      * the dataset size</em>
11168      */
11169     getTotalCount : function(){
11170         return this.totalLength || 0;
11171     },
11172
11173     /**
11174      * Returns the sort state of the Store as an object with two properties:
11175      * <pre><code>
11176  field {String} The name of the field by which the Records are sorted
11177  direction {String} The sort order, "ASC" or "DESC"
11178      * </code></pre>
11179      */
11180     getSortState : function(){
11181         return this.sortInfo;
11182     },
11183
11184     // private
11185     applySort : function(){
11186         if(this.sortInfo && !this.remoteSort){
11187             var s = this.sortInfo, f = s.field;
11188             var st = this.fields.get(f).sortType;
11189             var fn = function(r1, r2){
11190                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11191                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11192             };
11193             this.data.sort(s.direction, fn);
11194             if(this.snapshot && this.snapshot != this.data){
11195                 this.snapshot.sort(s.direction, fn);
11196             }
11197         }
11198     },
11199
11200     /**
11201      * Sets the default sort column and order to be used by the next load operation.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     setDefaultSort : function(field, dir){
11206         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11207     },
11208
11209     /**
11210      * Sort the Records.
11211      * If remote sorting is used, the sort is performed on the server, and the cache is
11212      * reloaded. If local sorting is used, the cache is sorted internally.
11213      * @param {String} fieldName The name of the field to sort by.
11214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11215      */
11216     sort : function(fieldName, dir){
11217         var f = this.fields.get(fieldName);
11218         if(!dir){
11219             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11220             
11221             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11222                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11223             }else{
11224                 dir = f.sortDir;
11225             }
11226         }
11227         this.sortToggle[f.name] = dir;
11228         this.sortInfo = {field: f.name, direction: dir};
11229         if(!this.remoteSort){
11230             this.applySort();
11231             this.fireEvent("datachanged", this);
11232         }else{
11233             this.load(this.lastOptions);
11234         }
11235     },
11236
11237     /**
11238      * Calls the specified function for each of the Records in the cache.
11239      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11240      * Returning <em>false</em> aborts and exits the iteration.
11241      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11242      */
11243     each : function(fn, scope){
11244         this.data.each(fn, scope);
11245     },
11246
11247     /**
11248      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11249      * (e.g., during paging).
11250      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11251      */
11252     getModifiedRecords : function(){
11253         return this.modified;
11254     },
11255
11256     // private
11257     createFilterFn : function(property, value, anyMatch){
11258         if(!value.exec){ // not a regex
11259             value = String(value);
11260             if(value.length == 0){
11261                 return false;
11262             }
11263             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11264         }
11265         return function(r){
11266             return value.test(r.data[property]);
11267         };
11268     },
11269
11270     /**
11271      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11272      * @param {String} property A field on your records
11273      * @param {Number} start The record index to start at (defaults to 0)
11274      * @param {Number} end The last record index to include (defaults to length - 1)
11275      * @return {Number} The sum
11276      */
11277     sum : function(property, start, end){
11278         var rs = this.data.items, v = 0;
11279         start = start || 0;
11280         end = (end || end === 0) ? end : rs.length-1;
11281
11282         for(var i = start; i <= end; i++){
11283             v += (rs[i].data[property] || 0);
11284         }
11285         return v;
11286     },
11287
11288     /**
11289      * Filter the records by a specified property.
11290      * @param {String} field A field on your records
11291      * @param {String/RegExp} value Either a string that the field
11292      * should start with or a RegExp to test against the field
11293      * @param {Boolean} anyMatch True to match any part not just the beginning
11294      */
11295     filter : function(property, value, anyMatch){
11296         var fn = this.createFilterFn(property, value, anyMatch);
11297         return fn ? this.filterBy(fn) : this.clearFilter();
11298     },
11299
11300     /**
11301      * Filter by a function. The specified function will be called with each
11302      * record in this data source. If the function returns true the record is included,
11303      * otherwise it is filtered.
11304      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11305      * @param {Object} scope (optional) The scope of the function (defaults to this)
11306      */
11307     filterBy : function(fn, scope){
11308         this.snapshot = this.snapshot || this.data;
11309         this.data = this.queryBy(fn, scope||this);
11310         this.fireEvent("datachanged", this);
11311     },
11312
11313     /**
11314      * Query the records by a specified property.
11315      * @param {String} field A field on your records
11316      * @param {String/RegExp} value Either a string that the field
11317      * should start with or a RegExp to test against the field
11318      * @param {Boolean} anyMatch True to match any part not just the beginning
11319      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11320      */
11321     query : function(property, value, anyMatch){
11322         var fn = this.createFilterFn(property, value, anyMatch);
11323         return fn ? this.queryBy(fn) : this.data.clone();
11324     },
11325
11326     /**
11327      * Query by a function. The specified function will be called with each
11328      * record in this data source. If the function returns true the record is included
11329      * in the results.
11330      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11331      * @param {Object} scope (optional) The scope of the function (defaults to this)
11332       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11333      **/
11334     queryBy : function(fn, scope){
11335         var data = this.snapshot || this.data;
11336         return data.filterBy(fn, scope||this);
11337     },
11338
11339     /**
11340      * Collects unique values for a particular dataIndex from this store.
11341      * @param {String} dataIndex The property to collect
11342      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11343      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11344      * @return {Array} An array of the unique values
11345      **/
11346     collect : function(dataIndex, allowNull, bypassFilter){
11347         var d = (bypassFilter === true && this.snapshot) ?
11348                 this.snapshot.items : this.data.items;
11349         var v, sv, r = [], l = {};
11350         for(var i = 0, len = d.length; i < len; i++){
11351             v = d[i].data[dataIndex];
11352             sv = String(v);
11353             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11354                 l[sv] = true;
11355                 r[r.length] = v;
11356             }
11357         }
11358         return r;
11359     },
11360
11361     /**
11362      * Revert to a view of the Record cache with no filtering applied.
11363      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11364      */
11365     clearFilter : function(suppressEvent){
11366         if(this.snapshot && this.snapshot != this.data){
11367             this.data = this.snapshot;
11368             delete this.snapshot;
11369             if(suppressEvent !== true){
11370                 this.fireEvent("datachanged", this);
11371             }
11372         }
11373     },
11374
11375     // private
11376     afterEdit : function(record){
11377         if(this.modified.indexOf(record) == -1){
11378             this.modified.push(record);
11379         }
11380         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11381     },
11382     
11383     // private
11384     afterReject : function(record){
11385         this.modified.remove(record);
11386         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11387     },
11388
11389     // private
11390     afterCommit : function(record){
11391         this.modified.remove(record);
11392         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11393     },
11394
11395     /**
11396      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11397      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11398      */
11399     commitChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].commit();
11404         }
11405     },
11406
11407     /**
11408      * Cancel outstanding changes on all changed records.
11409      */
11410     rejectChanges : function(){
11411         var m = this.modified.slice(0);
11412         this.modified = [];
11413         for(var i = 0, len = m.length; i < len; i++){
11414             m[i].reject();
11415         }
11416     },
11417
11418     onMetaChange : function(meta, rtype, o){
11419         this.recordType = rtype;
11420         this.fields = rtype.prototype.fields;
11421         delete this.snapshot;
11422         this.sortInfo = meta.sortInfo || this.sortInfo;
11423         this.modified = [];
11424         this.fireEvent('metachange', this, this.reader.meta);
11425     },
11426     
11427     moveIndex : function(data, type)
11428     {
11429         var index = this.indexOf(data);
11430         
11431         var newIndex = index + type;
11432         
11433         this.remove(data);
11434         
11435         this.insert(newIndex, data);
11436         
11437     }
11438 });/*
11439  * Based on:
11440  * Ext JS Library 1.1.1
11441  * Copyright(c) 2006-2007, Ext JS, LLC.
11442  *
11443  * Originally Released Under LGPL - original licence link has changed is not relivant.
11444  *
11445  * Fork - LGPL
11446  * <script type="text/javascript">
11447  */
11448
11449 /**
11450  * @class Roo.data.SimpleStore
11451  * @extends Roo.data.Store
11452  * Small helper class to make creating Stores from Array data easier.
11453  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11454  * @cfg {Array} fields An array of field definition objects, or field name strings.
11455  * @cfg {Array} data The multi-dimensional array of data
11456  * @constructor
11457  * @param {Object} config
11458  */
11459 Roo.data.SimpleStore = function(config){
11460     Roo.data.SimpleStore.superclass.constructor.call(this, {
11461         isLocal : true,
11462         reader: new Roo.data.ArrayReader({
11463                 id: config.id
11464             },
11465             Roo.data.Record.create(config.fields)
11466         ),
11467         proxy : new Roo.data.MemoryProxy(config.data)
11468     });
11469     this.load();
11470 };
11471 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11472  * Based on:
11473  * Ext JS Library 1.1.1
11474  * Copyright(c) 2006-2007, Ext JS, LLC.
11475  *
11476  * Originally Released Under LGPL - original licence link has changed is not relivant.
11477  *
11478  * Fork - LGPL
11479  * <script type="text/javascript">
11480  */
11481
11482 /**
11483 /**
11484  * @extends Roo.data.Store
11485  * @class Roo.data.JsonStore
11486  * Small helper class to make creating Stores for JSON data easier. <br/>
11487 <pre><code>
11488 var store = new Roo.data.JsonStore({
11489     url: 'get-images.php',
11490     root: 'images',
11491     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11492 });
11493 </code></pre>
11494  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11495  * JsonReader and HttpProxy (unless inline data is provided).</b>
11496  * @cfg {Array} fields An array of field definition objects, or field name strings.
11497  * @constructor
11498  * @param {Object} config
11499  */
11500 Roo.data.JsonStore = function(c){
11501     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11502         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11503         reader: new Roo.data.JsonReader(c, c.fields)
11504     }));
11505 };
11506 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11507  * Based on:
11508  * Ext JS Library 1.1.1
11509  * Copyright(c) 2006-2007, Ext JS, LLC.
11510  *
11511  * Originally Released Under LGPL - original licence link has changed is not relivant.
11512  *
11513  * Fork - LGPL
11514  * <script type="text/javascript">
11515  */
11516
11517  
11518 Roo.data.Field = function(config){
11519     if(typeof config == "string"){
11520         config = {name: config};
11521     }
11522     Roo.apply(this, config);
11523     
11524     if(!this.type){
11525         this.type = "auto";
11526     }
11527     
11528     var st = Roo.data.SortTypes;
11529     // named sortTypes are supported, here we look them up
11530     if(typeof this.sortType == "string"){
11531         this.sortType = st[this.sortType];
11532     }
11533     
11534     // set default sortType for strings and dates
11535     if(!this.sortType){
11536         switch(this.type){
11537             case "string":
11538                 this.sortType = st.asUCString;
11539                 break;
11540             case "date":
11541                 this.sortType = st.asDate;
11542                 break;
11543             default:
11544                 this.sortType = st.none;
11545         }
11546     }
11547
11548     // define once
11549     var stripRe = /[\$,%]/g;
11550
11551     // prebuilt conversion function for this field, instead of
11552     // switching every time we're reading a value
11553     if(!this.convert){
11554         var cv, dateFormat = this.dateFormat;
11555         switch(this.type){
11556             case "":
11557             case "auto":
11558             case undefined:
11559                 cv = function(v){ return v; };
11560                 break;
11561             case "string":
11562                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11563                 break;
11564             case "int":
11565                 cv = function(v){
11566                     return v !== undefined && v !== null && v !== '' ?
11567                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11568                     };
11569                 break;
11570             case "float":
11571                 cv = function(v){
11572                     return v !== undefined && v !== null && v !== '' ?
11573                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11574                     };
11575                 break;
11576             case "bool":
11577             case "boolean":
11578                 cv = function(v){ return v === true || v === "true" || v == 1; };
11579                 break;
11580             case "date":
11581                 cv = function(v){
11582                     if(!v){
11583                         return '';
11584                     }
11585                     if(v instanceof Date){
11586                         return v;
11587                     }
11588                     if(dateFormat){
11589                         if(dateFormat == "timestamp"){
11590                             return new Date(v*1000);
11591                         }
11592                         return Date.parseDate(v, dateFormat);
11593                     }
11594                     var parsed = Date.parse(v);
11595                     return parsed ? new Date(parsed) : null;
11596                 };
11597              break;
11598             
11599         }
11600         this.convert = cv;
11601     }
11602 };
11603
11604 Roo.data.Field.prototype = {
11605     dateFormat: null,
11606     defaultValue: "",
11607     mapping: null,
11608     sortType : null,
11609     sortDir : "ASC"
11610 };/*
11611  * Based on:
11612  * Ext JS Library 1.1.1
11613  * Copyright(c) 2006-2007, Ext JS, LLC.
11614  *
11615  * Originally Released Under LGPL - original licence link has changed is not relivant.
11616  *
11617  * Fork - LGPL
11618  * <script type="text/javascript">
11619  */
11620  
11621 // Base class for reading structured data from a data source.  This class is intended to be
11622 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11623
11624 /**
11625  * @class Roo.data.DataReader
11626  * Base class for reading structured data from a data source.  This class is intended to be
11627  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11628  */
11629
11630 Roo.data.DataReader = function(meta, recordType){
11631     
11632     this.meta = meta;
11633     
11634     this.recordType = recordType instanceof Array ? 
11635         Roo.data.Record.create(recordType) : recordType;
11636 };
11637
11638 Roo.data.DataReader.prototype = {
11639      /**
11640      * Create an empty record
11641      * @param {Object} data (optional) - overlay some values
11642      * @return {Roo.data.Record} record created.
11643      */
11644     newRow :  function(d) {
11645         var da =  {};
11646         this.recordType.prototype.fields.each(function(c) {
11647             switch( c.type) {
11648                 case 'int' : da[c.name] = 0; break;
11649                 case 'date' : da[c.name] = new Date(); break;
11650                 case 'float' : da[c.name] = 0.0; break;
11651                 case 'boolean' : da[c.name] = false; break;
11652                 default : da[c.name] = ""; break;
11653             }
11654             
11655         });
11656         return new this.recordType(Roo.apply(da, d));
11657     }
11658     
11659 };/*
11660  * Based on:
11661  * Ext JS Library 1.1.1
11662  * Copyright(c) 2006-2007, Ext JS, LLC.
11663  *
11664  * Originally Released Under LGPL - original licence link has changed is not relivant.
11665  *
11666  * Fork - LGPL
11667  * <script type="text/javascript">
11668  */
11669
11670 /**
11671  * @class Roo.data.DataProxy
11672  * @extends Roo.data.Observable
11673  * This class is an abstract base class for implementations which provide retrieval of
11674  * unformatted data objects.<br>
11675  * <p>
11676  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11677  * (of the appropriate type which knows how to parse the data object) to provide a block of
11678  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11679  * <p>
11680  * Custom implementations must implement the load method as described in
11681  * {@link Roo.data.HttpProxy#load}.
11682  */
11683 Roo.data.DataProxy = function(){
11684     this.addEvents({
11685         /**
11686          * @event beforeload
11687          * Fires before a network request is made to retrieve a data object.
11688          * @param {Object} This DataProxy object.
11689          * @param {Object} params The params parameter to the load function.
11690          */
11691         beforeload : true,
11692         /**
11693          * @event load
11694          * Fires before the load method's callback is called.
11695          * @param {Object} This DataProxy object.
11696          * @param {Object} o The data object.
11697          * @param {Object} arg The callback argument object passed to the load function.
11698          */
11699         load : true,
11700         /**
11701          * @event loadexception
11702          * Fires if an Exception occurs during data retrieval.
11703          * @param {Object} This DataProxy object.
11704          * @param {Object} o The data object.
11705          * @param {Object} arg The callback argument object passed to the load function.
11706          * @param {Object} e The Exception.
11707          */
11708         loadexception : true
11709     });
11710     Roo.data.DataProxy.superclass.constructor.call(this);
11711 };
11712
11713 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11714
11715     /**
11716      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11717      */
11718 /*
11719  * Based on:
11720  * Ext JS Library 1.1.1
11721  * Copyright(c) 2006-2007, Ext JS, LLC.
11722  *
11723  * Originally Released Under LGPL - original licence link has changed is not relivant.
11724  *
11725  * Fork - LGPL
11726  * <script type="text/javascript">
11727  */
11728 /**
11729  * @class Roo.data.MemoryProxy
11730  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11731  * to the Reader when its load method is called.
11732  * @constructor
11733  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11734  */
11735 Roo.data.MemoryProxy = function(data){
11736     if (data.data) {
11737         data = data.data;
11738     }
11739     Roo.data.MemoryProxy.superclass.constructor.call(this);
11740     this.data = data;
11741 };
11742
11743 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11744     
11745     /**
11746      * Load data from the requested source (in this case an in-memory
11747      * data object passed to the constructor), read the data object into
11748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11749      * process that block using the passed callback.
11750      * @param {Object} params This parameter is not used by the MemoryProxy class.
11751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11752      * object into a block of Roo.data.Records.
11753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11754      * The function must be passed <ul>
11755      * <li>The Record block object</li>
11756      * <li>The "arg" argument from the load function</li>
11757      * <li>A boolean success indicator</li>
11758      * </ul>
11759      * @param {Object} scope The scope in which to call the callback
11760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11761      */
11762     load : function(params, reader, callback, scope, arg){
11763         params = params || {};
11764         var result;
11765         try {
11766             result = reader.readRecords(this.data);
11767         }catch(e){
11768             this.fireEvent("loadexception", this, arg, null, e);
11769             callback.call(scope, null, arg, false);
11770             return;
11771         }
11772         callback.call(scope, result, arg, true);
11773     },
11774     
11775     // private
11776     update : function(params, records){
11777         
11778     }
11779 });/*
11780  * Based on:
11781  * Ext JS Library 1.1.1
11782  * Copyright(c) 2006-2007, Ext JS, LLC.
11783  *
11784  * Originally Released Under LGPL - original licence link has changed is not relivant.
11785  *
11786  * Fork - LGPL
11787  * <script type="text/javascript">
11788  */
11789 /**
11790  * @class Roo.data.HttpProxy
11791  * @extends Roo.data.DataProxy
11792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11793  * configured to reference a certain URL.<br><br>
11794  * <p>
11795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11796  * from which the running page was served.<br><br>
11797  * <p>
11798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11799  * <p>
11800  * Be aware that to enable the browser to parse an XML document, the server must set
11801  * the Content-Type header in the HTTP response to "text/xml".
11802  * @constructor
11803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11805  * will be used to make the request.
11806  */
11807 Roo.data.HttpProxy = function(conn){
11808     Roo.data.HttpProxy.superclass.constructor.call(this);
11809     // is conn a conn config or a real conn?
11810     this.conn = conn;
11811     this.useAjax = !conn || !conn.events;
11812   
11813 };
11814
11815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11816     // thse are take from connection...
11817     
11818     /**
11819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11820      */
11821     /**
11822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11823      * extra parameters to each request made by this object. (defaults to undefined)
11824      */
11825     /**
11826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11827      *  to each request made by this object. (defaults to undefined)
11828      */
11829     /**
11830      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11831      */
11832     /**
11833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11834      */
11835      /**
11836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11837      * @type Boolean
11838      */
11839   
11840
11841     /**
11842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11843      * @type Boolean
11844      */
11845     /**
11846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11848      * a finer-grained basis than the DataProxy events.
11849      */
11850     getConnection : function(){
11851         return this.useAjax ? Roo.Ajax : this.conn;
11852     },
11853
11854     /**
11855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11857      * process that block using the passed callback.
11858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11859      * for the request to the remote server.
11860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11861      * object into a block of Roo.data.Records.
11862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11863      * The function must be passed <ul>
11864      * <li>The Record block object</li>
11865      * <li>The "arg" argument from the load function</li>
11866      * <li>A boolean success indicator</li>
11867      * </ul>
11868      * @param {Object} scope The scope in which to call the callback
11869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11870      */
11871     load : function(params, reader, callback, scope, arg){
11872         if(this.fireEvent("beforeload", this, params) !== false){
11873             var  o = {
11874                 params : params || {},
11875                 request: {
11876                     callback : callback,
11877                     scope : scope,
11878                     arg : arg
11879                 },
11880                 reader: reader,
11881                 callback : this.loadResponse,
11882                 scope: this
11883             };
11884             if(this.useAjax){
11885                 Roo.applyIf(o, this.conn);
11886                 if(this.activeRequest){
11887                     Roo.Ajax.abort(this.activeRequest);
11888                 }
11889                 this.activeRequest = Roo.Ajax.request(o);
11890             }else{
11891                 this.conn.request(o);
11892             }
11893         }else{
11894             callback.call(scope||this, null, arg, false);
11895         }
11896     },
11897
11898     // private
11899     loadResponse : function(o, success, response){
11900         delete this.activeRequest;
11901         if(!success){
11902             this.fireEvent("loadexception", this, o, response);
11903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11904             return;
11905         }
11906         var result;
11907         try {
11908             result = o.reader.read(response);
11909         }catch(e){
11910             this.fireEvent("loadexception", this, o, response, e);
11911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11912             return;
11913         }
11914         
11915         this.fireEvent("load", this, o, o.request.arg);
11916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11917     },
11918
11919     // private
11920     update : function(dataSet){
11921
11922     },
11923
11924     // private
11925     updateResponse : function(dataSet){
11926
11927     }
11928 });/*
11929  * Based on:
11930  * Ext JS Library 1.1.1
11931  * Copyright(c) 2006-2007, Ext JS, LLC.
11932  *
11933  * Originally Released Under LGPL - original licence link has changed is not relivant.
11934  *
11935  * Fork - LGPL
11936  * <script type="text/javascript">
11937  */
11938
11939 /**
11940  * @class Roo.data.ScriptTagProxy
11941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11942  * other than the originating domain of the running page.<br><br>
11943  * <p>
11944  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11946  * <p>
11947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11948  * source code that is used as the source inside a &lt;script> tag.<br><br>
11949  * <p>
11950  * In order for the browser to process the returned data, the server must wrap the data object
11951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11953  * depending on whether the callback name was passed:
11954  * <p>
11955  * <pre><code>
11956 boolean scriptTag = false;
11957 String cb = request.getParameter("callback");
11958 if (cb != null) {
11959     scriptTag = true;
11960     response.setContentType("text/javascript");
11961 } else {
11962     response.setContentType("application/x-json");
11963 }
11964 Writer out = response.getWriter();
11965 if (scriptTag) {
11966     out.write(cb + "(");
11967 }
11968 out.print(dataBlock.toJsonString());
11969 if (scriptTag) {
11970     out.write(");");
11971 }
11972 </pre></code>
11973  *
11974  * @constructor
11975  * @param {Object} config A configuration object.
11976  */
11977 Roo.data.ScriptTagProxy = function(config){
11978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11979     Roo.apply(this, config);
11980     this.head = document.getElementsByTagName("head")[0];
11981 };
11982
11983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11984
11985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11986     /**
11987      * @cfg {String} url The URL from which to request the data object.
11988      */
11989     /**
11990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11991      */
11992     timeout : 30000,
11993     /**
11994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11995      * the server the name of the callback function set up by the load call to process the returned data object.
11996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11997      * javascript output which calls this named function passing the data object as its only parameter.
11998      */
11999     callbackParam : "callback",
12000     /**
12001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12002      * name to the request.
12003      */
12004     nocache : true,
12005
12006     /**
12007      * Load data from the configured URL, read the data object into
12008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12009      * process that block using the passed callback.
12010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12011      * for the request to the remote server.
12012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12013      * object into a block of Roo.data.Records.
12014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12015      * The function must be passed <ul>
12016      * <li>The Record block object</li>
12017      * <li>The "arg" argument from the load function</li>
12018      * <li>A boolean success indicator</li>
12019      * </ul>
12020      * @param {Object} scope The scope in which to call the callback
12021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12022      */
12023     load : function(params, reader, callback, scope, arg){
12024         if(this.fireEvent("beforeload", this, params) !== false){
12025
12026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12027
12028             var url = this.url;
12029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12030             if(this.nocache){
12031                 url += "&_dc=" + (new Date().getTime());
12032             }
12033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12034             var trans = {
12035                 id : transId,
12036                 cb : "stcCallback"+transId,
12037                 scriptId : "stcScript"+transId,
12038                 params : params,
12039                 arg : arg,
12040                 url : url,
12041                 callback : callback,
12042                 scope : scope,
12043                 reader : reader
12044             };
12045             var conn = this;
12046
12047             window[trans.cb] = function(o){
12048                 conn.handleResponse(o, trans);
12049             };
12050
12051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12052
12053             if(this.autoAbort !== false){
12054                 this.abort();
12055             }
12056
12057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12058
12059             var script = document.createElement("script");
12060             script.setAttribute("src", url);
12061             script.setAttribute("type", "text/javascript");
12062             script.setAttribute("id", trans.scriptId);
12063             this.head.appendChild(script);
12064
12065             this.trans = trans;
12066         }else{
12067             callback.call(scope||this, null, arg, false);
12068         }
12069     },
12070
12071     // private
12072     isLoading : function(){
12073         return this.trans ? true : false;
12074     },
12075
12076     /**
12077      * Abort the current server request.
12078      */
12079     abort : function(){
12080         if(this.isLoading()){
12081             this.destroyTrans(this.trans);
12082         }
12083     },
12084
12085     // private
12086     destroyTrans : function(trans, isLoaded){
12087         this.head.removeChild(document.getElementById(trans.scriptId));
12088         clearTimeout(trans.timeoutId);
12089         if(isLoaded){
12090             window[trans.cb] = undefined;
12091             try{
12092                 delete window[trans.cb];
12093             }catch(e){}
12094         }else{
12095             // if hasn't been loaded, wait for load to remove it to prevent script error
12096             window[trans.cb] = function(){
12097                 window[trans.cb] = undefined;
12098                 try{
12099                     delete window[trans.cb];
12100                 }catch(e){}
12101             };
12102         }
12103     },
12104
12105     // private
12106     handleResponse : function(o, trans){
12107         this.trans = false;
12108         this.destroyTrans(trans, true);
12109         var result;
12110         try {
12111             result = trans.reader.readRecords(o);
12112         }catch(e){
12113             this.fireEvent("loadexception", this, o, trans.arg, e);
12114             trans.callback.call(trans.scope||window, null, trans.arg, false);
12115             return;
12116         }
12117         this.fireEvent("load", this, o, trans.arg);
12118         trans.callback.call(trans.scope||window, result, trans.arg, true);
12119     },
12120
12121     // private
12122     handleFailure : function(trans){
12123         this.trans = false;
12124         this.destroyTrans(trans, false);
12125         this.fireEvent("loadexception", this, null, trans.arg);
12126         trans.callback.call(trans.scope||window, null, trans.arg, false);
12127     }
12128 });/*
12129  * Based on:
12130  * Ext JS Library 1.1.1
12131  * Copyright(c) 2006-2007, Ext JS, LLC.
12132  *
12133  * Originally Released Under LGPL - original licence link has changed is not relivant.
12134  *
12135  * Fork - LGPL
12136  * <script type="text/javascript">
12137  */
12138
12139 /**
12140  * @class Roo.data.JsonReader
12141  * @extends Roo.data.DataReader
12142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12143  * based on mappings in a provided Roo.data.Record constructor.
12144  * 
12145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12146  * in the reply previously. 
12147  * 
12148  * <p>
12149  * Example code:
12150  * <pre><code>
12151 var RecordDef = Roo.data.Record.create([
12152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12154 ]);
12155 var myReader = new Roo.data.JsonReader({
12156     totalProperty: "results",    // The property which contains the total dataset size (optional)
12157     root: "rows",                // The property which contains an Array of row objects
12158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12159 }, RecordDef);
12160 </code></pre>
12161  * <p>
12162  * This would consume a JSON file like this:
12163  * <pre><code>
12164 { 'results': 2, 'rows': [
12165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12167 }
12168 </code></pre>
12169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12171  * paged from the remote server.
12172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12173  * @cfg {String} root name of the property which contains the Array of row objects.
12174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12175  * @cfg {Array} fields Array of field definition objects
12176  * @constructor
12177  * Create a new JsonReader
12178  * @param {Object} meta Metadata configuration options
12179  * @param {Object} recordType Either an Array of field definition objects,
12180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12181  */
12182 Roo.data.JsonReader = function(meta, recordType){
12183     
12184     meta = meta || {};
12185     // set some defaults:
12186     Roo.applyIf(meta, {
12187         totalProperty: 'total',
12188         successProperty : 'success',
12189         root : 'data',
12190         id : 'id'
12191     });
12192     
12193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12194 };
12195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12196     
12197     /**
12198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12199      * Used by Store query builder to append _requestMeta to params.
12200      * 
12201      */
12202     metaFromRemote : false,
12203     /**
12204      * This method is only used by a DataProxy which has retrieved data from a remote server.
12205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12206      * @return {Object} data A data block which is used by an Roo.data.Store object as
12207      * a cache of Roo.data.Records.
12208      */
12209     read : function(response){
12210         var json = response.responseText;
12211        
12212         var o = /* eval:var:o */ eval("("+json+")");
12213         if(!o) {
12214             throw {message: "JsonReader.read: Json object not found"};
12215         }
12216         
12217         if(o.metaData){
12218             
12219             delete this.ef;
12220             this.metaFromRemote = true;
12221             this.meta = o.metaData;
12222             this.recordType = Roo.data.Record.create(o.metaData.fields);
12223             this.onMetaChange(this.meta, this.recordType, o);
12224         }
12225         return this.readRecords(o);
12226     },
12227
12228     // private function a store will implement
12229     onMetaChange : function(meta, recordType, o){
12230
12231     },
12232
12233     /**
12234          * @ignore
12235          */
12236     simpleAccess: function(obj, subsc) {
12237         return obj[subsc];
12238     },
12239
12240         /**
12241          * @ignore
12242          */
12243     getJsonAccessor: function(){
12244         var re = /[\[\.]/;
12245         return function(expr) {
12246             try {
12247                 return(re.test(expr))
12248                     ? new Function("obj", "return obj." + expr)
12249                     : function(obj){
12250                         return obj[expr];
12251                     };
12252             } catch(e){}
12253             return Roo.emptyFn;
12254         };
12255     }(),
12256
12257     /**
12258      * Create a data block containing Roo.data.Records from an XML document.
12259      * @param {Object} o An object which contains an Array of row objects in the property specified
12260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12261      * which contains the total size of the dataset.
12262      * @return {Object} data A data block which is used by an Roo.data.Store object as
12263      * a cache of Roo.data.Records.
12264      */
12265     readRecords : function(o){
12266         /**
12267          * After any data loads, the raw JSON data is available for further custom processing.
12268          * @type Object
12269          */
12270         this.o = o;
12271         var s = this.meta, Record = this.recordType,
12272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12273
12274         Roo.log('----------------------');
12275         Roo.log(s);
12276         Roo.log(Record);
12277         Roo.log(f);
12278
12279
12280 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12281         if (!this.ef) {
12282             if(s.totalProperty) {
12283                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12284                 }
12285                 if(s.successProperty) {
12286                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12287                 }
12288                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12289                 if (s.id) {
12290                         var g = this.getJsonAccessor(s.id);
12291                         this.getId = function(rec) {
12292                                 var r = g(rec);  
12293                                 return (r === undefined || r === "") ? null : r;
12294                         };
12295                 } else {
12296                         this.getId = function(){return null;};
12297                 }
12298             this.ef = [];
12299             for(var jj = 0; jj < fl; jj++){
12300                 f = fi[jj];
12301                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12302                 this.ef[jj] = this.getJsonAccessor(map);
12303             }
12304         }
12305
12306         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12307         if(s.totalProperty){
12308             var vt = parseInt(this.getTotal(o), 10);
12309             if(!isNaN(vt)){
12310                 totalRecords = vt;
12311             }
12312         }
12313         if(s.successProperty){
12314             var vs = this.getSuccess(o);
12315             if(vs === false || vs === 'false'){
12316                 success = false;
12317             }
12318         }
12319         var records = [];
12320         for(var i = 0; i < c; i++){
12321                 var n = root[i];
12322             var values = {};
12323             var id = this.getId(n);
12324             for(var j = 0; j < fl; j++){
12325                 f = fi[j];
12326             var v = this.ef[j](n);
12327             if (!f.convert) {
12328                 Roo.log('missing convert for ' + f.name);
12329                 Roo.log(f);
12330                 continue;
12331             }
12332             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12333             }
12334             var record = new Record(values, id);
12335             record.json = n;
12336             records[i] = record;
12337         }
12338         return {
12339             raw : o,
12340             success : success,
12341             records : records,
12342             totalRecords : totalRecords
12343         };
12344     }
12345 });/*
12346  * Based on:
12347  * Ext JS Library 1.1.1
12348  * Copyright(c) 2006-2007, Ext JS, LLC.
12349  *
12350  * Originally Released Under LGPL - original licence link has changed is not relivant.
12351  *
12352  * Fork - LGPL
12353  * <script type="text/javascript">
12354  */
12355
12356 /**
12357  * @class Roo.data.ArrayReader
12358  * @extends Roo.data.DataReader
12359  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12360  * Each element of that Array represents a row of data fields. The
12361  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12362  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12363  * <p>
12364  * Example code:.
12365  * <pre><code>
12366 var RecordDef = Roo.data.Record.create([
12367     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12368     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12369 ]);
12370 var myReader = new Roo.data.ArrayReader({
12371     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12372 }, RecordDef);
12373 </code></pre>
12374  * <p>
12375  * This would consume an Array like this:
12376  * <pre><code>
12377 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12378   </code></pre>
12379  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12380  * @constructor
12381  * Create a new JsonReader
12382  * @param {Object} meta Metadata configuration options.
12383  * @param {Object} recordType Either an Array of field definition objects
12384  * as specified to {@link Roo.data.Record#create},
12385  * or an {@link Roo.data.Record} object
12386  * created using {@link Roo.data.Record#create}.
12387  */
12388 Roo.data.ArrayReader = function(meta, recordType){
12389     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12390 };
12391
12392 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12393     /**
12394      * Create a data block containing Roo.data.Records from an XML document.
12395      * @param {Object} o An Array of row objects which represents the dataset.
12396      * @return {Object} data A data block which is used by an Roo.data.Store object as
12397      * a cache of Roo.data.Records.
12398      */
12399     readRecords : function(o){
12400         var sid = this.meta ? this.meta.id : null;
12401         var recordType = this.recordType, fields = recordType.prototype.fields;
12402         var records = [];
12403         var root = o;
12404             for(var i = 0; i < root.length; i++){
12405                     var n = root[i];
12406                 var values = {};
12407                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12408                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12409                 var f = fields.items[j];
12410                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12411                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12412                 v = f.convert(v);
12413                 values[f.name] = v;
12414             }
12415                 var record = new recordType(values, id);
12416                 record.json = n;
12417                 records[records.length] = record;
12418             }
12419             return {
12420                 records : records,
12421                 totalRecords : records.length
12422             };
12423     }
12424 });/*
12425  * - LGPL
12426  * * 
12427  */
12428
12429 /**
12430  * @class Roo.bootstrap.ComboBox
12431  * @extends Roo.bootstrap.TriggerField
12432  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12433  * @cfg {Boolean} append (true|false) default false
12434  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12435  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12436  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12437  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12438  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12439  * @cfg {Boolean} animate default true
12440  * @cfg {Boolean} emptyResultText only for touch device
12441  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12442  * @cfg {String} emptyTitle default ''
12443  * @constructor
12444  * Create a new ComboBox.
12445  * @param {Object} config Configuration options
12446  */
12447 Roo.bootstrap.ComboBox = function(config){
12448     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12449     this.addEvents({
12450         /**
12451          * @event expand
12452          * Fires when the dropdown list is expanded
12453         * @param {Roo.bootstrap.ComboBox} combo This combo box
12454         */
12455         'expand' : true,
12456         /**
12457          * @event collapse
12458          * Fires when the dropdown list is collapsed
12459         * @param {Roo.bootstrap.ComboBox} combo This combo box
12460         */
12461         'collapse' : true,
12462         /**
12463          * @event beforeselect
12464          * Fires before a list item is selected. Return false to cancel the selection.
12465         * @param {Roo.bootstrap.ComboBox} combo This combo box
12466         * @param {Roo.data.Record} record The data record returned from the underlying store
12467         * @param {Number} index The index of the selected item in the dropdown list
12468         */
12469         'beforeselect' : true,
12470         /**
12471          * @event select
12472          * Fires when a list item is selected
12473         * @param {Roo.bootstrap.ComboBox} combo This combo box
12474         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12475         * @param {Number} index The index of the selected item in the dropdown list
12476         */
12477         'select' : true,
12478         /**
12479          * @event beforequery
12480          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12481          * The event object passed has these properties:
12482         * @param {Roo.bootstrap.ComboBox} combo This combo box
12483         * @param {String} query The query
12484         * @param {Boolean} forceAll true to force "all" query
12485         * @param {Boolean} cancel true to cancel the query
12486         * @param {Object} e The query event object
12487         */
12488         'beforequery': true,
12489          /**
12490          * @event add
12491          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12492         * @param {Roo.bootstrap.ComboBox} combo This combo box
12493         */
12494         'add' : true,
12495         /**
12496          * @event edit
12497          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12498         * @param {Roo.bootstrap.ComboBox} combo This combo box
12499         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12500         */
12501         'edit' : true,
12502         /**
12503          * @event remove
12504          * Fires when the remove value from the combobox array
12505         * @param {Roo.bootstrap.ComboBox} combo This combo box
12506         */
12507         'remove' : true,
12508         /**
12509          * @event afterremove
12510          * Fires when the remove value from the combobox array
12511         * @param {Roo.bootstrap.ComboBox} combo This combo box
12512         */
12513         'afterremove' : true,
12514         /**
12515          * @event specialfilter
12516          * Fires when specialfilter
12517             * @param {Roo.bootstrap.ComboBox} combo This combo box
12518             */
12519         'specialfilter' : true,
12520         /**
12521          * @event tick
12522          * Fires when tick the element
12523             * @param {Roo.bootstrap.ComboBox} combo This combo box
12524             */
12525         'tick' : true,
12526         /**
12527          * @event touchviewdisplay
12528          * Fires when touch view require special display (default is using displayField)
12529             * @param {Roo.bootstrap.ComboBox} combo This combo box
12530             * @param {Object} cfg set html .
12531             */
12532         'touchviewdisplay' : true
12533         
12534     });
12535     
12536     this.item = [];
12537     this.tickItems = [];
12538     
12539     this.selectedIndex = -1;
12540     if(this.mode == 'local'){
12541         if(config.queryDelay === undefined){
12542             this.queryDelay = 10;
12543         }
12544         if(config.minChars === undefined){
12545             this.minChars = 0;
12546         }
12547     }
12548 };
12549
12550 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12551      
12552     /**
12553      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12554      * rendering into an Roo.Editor, defaults to false)
12555      */
12556     /**
12557      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12558      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12559      */
12560     /**
12561      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12562      */
12563     /**
12564      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12565      * the dropdown list (defaults to undefined, with no header element)
12566      */
12567
12568      /**
12569      * @cfg {String/Roo.Template} tpl The template to use to render the output
12570      */
12571      
12572      /**
12573      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12574      */
12575     listWidth: undefined,
12576     /**
12577      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12578      * mode = 'remote' or 'text' if mode = 'local')
12579      */
12580     displayField: undefined,
12581     
12582     /**
12583      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12584      * mode = 'remote' or 'value' if mode = 'local'). 
12585      * Note: use of a valueField requires the user make a selection
12586      * in order for a value to be mapped.
12587      */
12588     valueField: undefined,
12589     /**
12590      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12591      */
12592     modalTitle : '',
12593     
12594     /**
12595      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12596      * field's data value (defaults to the underlying DOM element's name)
12597      */
12598     hiddenName: undefined,
12599     /**
12600      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12601      */
12602     listClass: '',
12603     /**
12604      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12605      */
12606     selectedClass: 'active',
12607     
12608     /**
12609      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12610      */
12611     shadow:'sides',
12612     /**
12613      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12614      * anchor positions (defaults to 'tl-bl')
12615      */
12616     listAlign: 'tl-bl?',
12617     /**
12618      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12619      */
12620     maxHeight: 300,
12621     /**
12622      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12623      * query specified by the allQuery config option (defaults to 'query')
12624      */
12625     triggerAction: 'query',
12626     /**
12627      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12628      * (defaults to 4, does not apply if editable = false)
12629      */
12630     minChars : 4,
12631     /**
12632      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12633      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12634      */
12635     typeAhead: false,
12636     /**
12637      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12638      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12639      */
12640     queryDelay: 500,
12641     /**
12642      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12643      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12644      */
12645     pageSize: 0,
12646     /**
12647      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12648      * when editable = true (defaults to false)
12649      */
12650     selectOnFocus:false,
12651     /**
12652      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12653      */
12654     queryParam: 'query',
12655     /**
12656      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12657      * when mode = 'remote' (defaults to 'Loading...')
12658      */
12659     loadingText: 'Loading...',
12660     /**
12661      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12662      */
12663     resizable: false,
12664     /**
12665      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12666      */
12667     handleHeight : 8,
12668     /**
12669      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12670      * traditional select (defaults to true)
12671      */
12672     editable: true,
12673     /**
12674      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12675      */
12676     allQuery: '',
12677     /**
12678      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12679      */
12680     mode: 'remote',
12681     /**
12682      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12683      * listWidth has a higher value)
12684      */
12685     minListWidth : 70,
12686     /**
12687      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12688      * allow the user to set arbitrary text into the field (defaults to false)
12689      */
12690     forceSelection:false,
12691     /**
12692      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12693      * if typeAhead = true (defaults to 250)
12694      */
12695     typeAheadDelay : 250,
12696     /**
12697      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12698      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12699      */
12700     valueNotFoundText : undefined,
12701     /**
12702      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12703      */
12704     blockFocus : false,
12705     
12706     /**
12707      * @cfg {Boolean} disableClear Disable showing of clear button.
12708      */
12709     disableClear : false,
12710     /**
12711      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12712      */
12713     alwaysQuery : false,
12714     
12715     /**
12716      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12717      */
12718     multiple : false,
12719     
12720     /**
12721      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12722      */
12723     invalidClass : "has-warning",
12724     
12725     /**
12726      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12727      */
12728     validClass : "has-success",
12729     
12730     /**
12731      * @cfg {Boolean} specialFilter (true|false) special filter default false
12732      */
12733     specialFilter : false,
12734     
12735     /**
12736      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12737      */
12738     mobileTouchView : true,
12739     
12740     /**
12741      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12742      */
12743     useNativeIOS : false,
12744     
12745     ios_options : false,
12746     
12747     //private
12748     addicon : false,
12749     editicon: false,
12750     
12751     page: 0,
12752     hasQuery: false,
12753     append: false,
12754     loadNext: false,
12755     autoFocus : true,
12756     tickable : false,
12757     btnPosition : 'right',
12758     triggerList : true,
12759     showToggleBtn : true,
12760     animate : true,
12761     emptyResultText: 'Empty',
12762     triggerText : 'Select',
12763     emptyTitle : '',
12764     
12765     // element that contains real text value.. (when hidden is used..)
12766     
12767     getAutoCreate : function()
12768     {   
12769         var cfg = false;
12770         //render
12771         /*
12772          * Render classic select for iso
12773          */
12774         
12775         if(Roo.isIOS && this.useNativeIOS){
12776             cfg = this.getAutoCreateNativeIOS();
12777             return cfg;
12778         }
12779         
12780         /*
12781          * Touch Devices
12782          */
12783         
12784         if(Roo.isTouch && this.mobileTouchView){
12785             cfg = this.getAutoCreateTouchView();
12786             return cfg;;
12787         }
12788         
12789         /*
12790          *  Normal ComboBox
12791          */
12792         if(!this.tickable){
12793             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12794             return cfg;
12795         }
12796         
12797         /*
12798          *  ComboBox with tickable selections
12799          */
12800              
12801         var align = this.labelAlign || this.parentLabelAlign();
12802         
12803         cfg = {
12804             cls : 'form-group roo-combobox-tickable' //input-group
12805         };
12806         
12807         var btn_text_select = '';
12808         var btn_text_done = '';
12809         var btn_text_cancel = '';
12810         
12811         if (this.btn_text_show) {
12812             btn_text_select = 'Select';
12813             btn_text_done = 'Done';
12814             btn_text_cancel = 'Cancel'; 
12815         }
12816         
12817         var buttons = {
12818             tag : 'div',
12819             cls : 'tickable-buttons',
12820             cn : [
12821                 {
12822                     tag : 'button',
12823                     type : 'button',
12824                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12825                     //html : this.triggerText
12826                     html: btn_text_select
12827                 },
12828                 {
12829                     tag : 'button',
12830                     type : 'button',
12831                     name : 'ok',
12832                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12833                     //html : 'Done'
12834                     html: btn_text_done
12835                 },
12836                 {
12837                     tag : 'button',
12838                     type : 'button',
12839                     name : 'cancel',
12840                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12841                     //html : 'Cancel'
12842                     html: btn_text_cancel
12843                 }
12844             ]
12845         };
12846         
12847         if(this.editable){
12848             buttons.cn.unshift({
12849                 tag: 'input',
12850                 cls: 'roo-select2-search-field-input'
12851             });
12852         }
12853         
12854         var _this = this;
12855         
12856         Roo.each(buttons.cn, function(c){
12857             if (_this.size) {
12858                 c.cls += ' btn-' + _this.size;
12859             }
12860
12861             if (_this.disabled) {
12862                 c.disabled = true;
12863             }
12864         });
12865         
12866         var box = {
12867             tag: 'div',
12868             cn: [
12869                 {
12870                     tag: 'input',
12871                     type : 'hidden',
12872                     cls: 'form-hidden-field'
12873                 },
12874                 {
12875                     tag: 'ul',
12876                     cls: 'roo-select2-choices',
12877                     cn:[
12878                         {
12879                             tag: 'li',
12880                             cls: 'roo-select2-search-field',
12881                             cn: [
12882                                 buttons
12883                             ]
12884                         }
12885                     ]
12886                 }
12887             ]
12888         };
12889         
12890         var combobox = {
12891             cls: 'roo-select2-container input-group roo-select2-container-multi',
12892             cn: [
12893                 box
12894 //                {
12895 //                    tag: 'ul',
12896 //                    cls: 'typeahead typeahead-long dropdown-menu',
12897 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12898 //                }
12899             ]
12900         };
12901         
12902         if(this.hasFeedback && !this.allowBlank){
12903             
12904             var feedback = {
12905                 tag: 'span',
12906                 cls: 'glyphicon form-control-feedback'
12907             };
12908
12909             combobox.cn.push(feedback);
12910         }
12911         
12912         
12913         if (align ==='left' && this.fieldLabel.length) {
12914             
12915             cfg.cls += ' roo-form-group-label-left';
12916             
12917             cfg.cn = [
12918                 {
12919                     tag : 'i',
12920                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12921                     tooltip : 'This field is required'
12922                 },
12923                 {
12924                     tag: 'label',
12925                     'for' :  id,
12926                     cls : 'control-label',
12927                     html : this.fieldLabel
12928
12929                 },
12930                 {
12931                     cls : "", 
12932                     cn: [
12933                         combobox
12934                     ]
12935                 }
12936
12937             ];
12938             
12939             var labelCfg = cfg.cn[1];
12940             var contentCfg = cfg.cn[2];
12941             
12942
12943             if(this.indicatorpos == 'right'){
12944                 
12945                 cfg.cn = [
12946                     {
12947                         tag: 'label',
12948                         'for' :  id,
12949                         cls : 'control-label',
12950                         cn : [
12951                             {
12952                                 tag : 'span',
12953                                 html : this.fieldLabel
12954                             },
12955                             {
12956                                 tag : 'i',
12957                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12958                                 tooltip : 'This field is required'
12959                             }
12960                         ]
12961                     },
12962                     {
12963                         cls : "",
12964                         cn: [
12965                             combobox
12966                         ]
12967                     }
12968
12969                 ];
12970                 
12971                 
12972                 
12973                 labelCfg = cfg.cn[0];
12974                 contentCfg = cfg.cn[1];
12975             
12976             }
12977             
12978             if(this.labelWidth > 12){
12979                 labelCfg.style = "width: " + this.labelWidth + 'px';
12980             }
12981             
12982             if(this.labelWidth < 13 && this.labelmd == 0){
12983                 this.labelmd = this.labelWidth;
12984             }
12985             
12986             if(this.labellg > 0){
12987                 labelCfg.cls += ' col-lg-' + this.labellg;
12988                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12989             }
12990             
12991             if(this.labelmd > 0){
12992                 labelCfg.cls += ' col-md-' + this.labelmd;
12993                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12994             }
12995             
12996             if(this.labelsm > 0){
12997                 labelCfg.cls += ' col-sm-' + this.labelsm;
12998                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12999             }
13000             
13001             if(this.labelxs > 0){
13002                 labelCfg.cls += ' col-xs-' + this.labelxs;
13003                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13004             }
13005                 
13006                 
13007         } else if ( this.fieldLabel.length) {
13008 //                Roo.log(" label");
13009                  cfg.cn = [
13010                     {
13011                         tag : 'i',
13012                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13013                         tooltip : 'This field is required'
13014                     },
13015                     {
13016                         tag: 'label',
13017                         //cls : 'input-group-addon',
13018                         html : this.fieldLabel
13019                     },
13020                     combobox
13021                 ];
13022                 
13023                 if(this.indicatorpos == 'right'){
13024                     cfg.cn = [
13025                         {
13026                             tag: 'label',
13027                             //cls : 'input-group-addon',
13028                             html : this.fieldLabel
13029                         },
13030                         {
13031                             tag : 'i',
13032                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13033                             tooltip : 'This field is required'
13034                         },
13035                         combobox
13036                     ];
13037                     
13038                 }
13039
13040         } else {
13041             
13042 //                Roo.log(" no label && no align");
13043                 cfg = combobox
13044                      
13045                 
13046         }
13047          
13048         var settings=this;
13049         ['xs','sm','md','lg'].map(function(size){
13050             if (settings[size]) {
13051                 cfg.cls += ' col-' + size + '-' + settings[size];
13052             }
13053         });
13054         
13055         return cfg;
13056         
13057     },
13058     
13059     _initEventsCalled : false,
13060     
13061     // private
13062     initEvents: function()
13063     {   
13064         if (this._initEventsCalled) { // as we call render... prevent looping...
13065             return;
13066         }
13067         this._initEventsCalled = true;
13068         
13069         if (!this.store) {
13070             throw "can not find store for combo";
13071         }
13072         
13073         this.indicator = this.indicatorEl();
13074         
13075         this.store = Roo.factory(this.store, Roo.data);
13076         this.store.parent = this;
13077         
13078         // if we are building from html. then this element is so complex, that we can not really
13079         // use the rendered HTML.
13080         // so we have to trash and replace the previous code.
13081         if (Roo.XComponent.build_from_html) {
13082             // remove this element....
13083             var e = this.el.dom, k=0;
13084             while (e ) { e = e.previousSibling;  ++k;}
13085
13086             this.el.remove();
13087             
13088             this.el=false;
13089             this.rendered = false;
13090             
13091             this.render(this.parent().getChildContainer(true), k);
13092         }
13093         
13094         if(Roo.isIOS && this.useNativeIOS){
13095             this.initIOSView();
13096             return;
13097         }
13098         
13099         /*
13100          * Touch Devices
13101          */
13102         
13103         if(Roo.isTouch && this.mobileTouchView){
13104             this.initTouchView();
13105             return;
13106         }
13107         
13108         if(this.tickable){
13109             this.initTickableEvents();
13110             return;
13111         }
13112         
13113         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13114         
13115         if(this.hiddenName){
13116             
13117             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13118             
13119             this.hiddenField.dom.value =
13120                 this.hiddenValue !== undefined ? this.hiddenValue :
13121                 this.value !== undefined ? this.value : '';
13122
13123             // prevent input submission
13124             this.el.dom.removeAttribute('name');
13125             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13126              
13127              
13128         }
13129         //if(Roo.isGecko){
13130         //    this.el.dom.setAttribute('autocomplete', 'off');
13131         //}
13132         
13133         var cls = 'x-combo-list';
13134         
13135         //this.list = new Roo.Layer({
13136         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13137         //});
13138         
13139         var _this = this;
13140         
13141         (function(){
13142             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13143             _this.list.setWidth(lw);
13144         }).defer(100);
13145         
13146         this.list.on('mouseover', this.onViewOver, this);
13147         this.list.on('mousemove', this.onViewMove, this);
13148         this.list.on('scroll', this.onViewScroll, this);
13149         
13150         /*
13151         this.list.swallowEvent('mousewheel');
13152         this.assetHeight = 0;
13153
13154         if(this.title){
13155             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13156             this.assetHeight += this.header.getHeight();
13157         }
13158
13159         this.innerList = this.list.createChild({cls:cls+'-inner'});
13160         this.innerList.on('mouseover', this.onViewOver, this);
13161         this.innerList.on('mousemove', this.onViewMove, this);
13162         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13163         
13164         if(this.allowBlank && !this.pageSize && !this.disableClear){
13165             this.footer = this.list.createChild({cls:cls+'-ft'});
13166             this.pageTb = new Roo.Toolbar(this.footer);
13167            
13168         }
13169         if(this.pageSize){
13170             this.footer = this.list.createChild({cls:cls+'-ft'});
13171             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13172                     {pageSize: this.pageSize});
13173             
13174         }
13175         
13176         if (this.pageTb && this.allowBlank && !this.disableClear) {
13177             var _this = this;
13178             this.pageTb.add(new Roo.Toolbar.Fill(), {
13179                 cls: 'x-btn-icon x-btn-clear',
13180                 text: '&#160;',
13181                 handler: function()
13182                 {
13183                     _this.collapse();
13184                     _this.clearValue();
13185                     _this.onSelect(false, -1);
13186                 }
13187             });
13188         }
13189         if (this.footer) {
13190             this.assetHeight += this.footer.getHeight();
13191         }
13192         */
13193             
13194         if(!this.tpl){
13195             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13196         }
13197
13198         this.view = new Roo.View(this.list, this.tpl, {
13199             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13200         });
13201         //this.view.wrapEl.setDisplayed(false);
13202         this.view.on('click', this.onViewClick, this);
13203         
13204         
13205         this.store.on('beforeload', this.onBeforeLoad, this);
13206         this.store.on('load', this.onLoad, this);
13207         this.store.on('loadexception', this.onLoadException, this);
13208         /*
13209         if(this.resizable){
13210             this.resizer = new Roo.Resizable(this.list,  {
13211                pinned:true, handles:'se'
13212             });
13213             this.resizer.on('resize', function(r, w, h){
13214                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13215                 this.listWidth = w;
13216                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13217                 this.restrictHeight();
13218             }, this);
13219             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13220         }
13221         */
13222         if(!this.editable){
13223             this.editable = true;
13224             this.setEditable(false);
13225         }
13226         
13227         /*
13228         
13229         if (typeof(this.events.add.listeners) != 'undefined') {
13230             
13231             this.addicon = this.wrap.createChild(
13232                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13233        
13234             this.addicon.on('click', function(e) {
13235                 this.fireEvent('add', this);
13236             }, this);
13237         }
13238         if (typeof(this.events.edit.listeners) != 'undefined') {
13239             
13240             this.editicon = this.wrap.createChild(
13241                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13242             if (this.addicon) {
13243                 this.editicon.setStyle('margin-left', '40px');
13244             }
13245             this.editicon.on('click', function(e) {
13246                 
13247                 // we fire even  if inothing is selected..
13248                 this.fireEvent('edit', this, this.lastData );
13249                 
13250             }, this);
13251         }
13252         */
13253         
13254         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13255             "up" : function(e){
13256                 this.inKeyMode = true;
13257                 this.selectPrev();
13258             },
13259
13260             "down" : function(e){
13261                 if(!this.isExpanded()){
13262                     this.onTriggerClick();
13263                 }else{
13264                     this.inKeyMode = true;
13265                     this.selectNext();
13266                 }
13267             },
13268
13269             "enter" : function(e){
13270 //                this.onViewClick();
13271                 //return true;
13272                 this.collapse();
13273                 
13274                 if(this.fireEvent("specialkey", this, e)){
13275                     this.onViewClick(false);
13276                 }
13277                 
13278                 return true;
13279             },
13280
13281             "esc" : function(e){
13282                 this.collapse();
13283             },
13284
13285             "tab" : function(e){
13286                 this.collapse();
13287                 
13288                 if(this.fireEvent("specialkey", this, e)){
13289                     this.onViewClick(false);
13290                 }
13291                 
13292                 return true;
13293             },
13294
13295             scope : this,
13296
13297             doRelay : function(foo, bar, hname){
13298                 if(hname == 'down' || this.scope.isExpanded()){
13299                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13300                 }
13301                 return true;
13302             },
13303
13304             forceKeyDown: true
13305         });
13306         
13307         
13308         this.queryDelay = Math.max(this.queryDelay || 10,
13309                 this.mode == 'local' ? 10 : 250);
13310         
13311         
13312         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13313         
13314         if(this.typeAhead){
13315             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13316         }
13317         if(this.editable !== false){
13318             this.inputEl().on("keyup", this.onKeyUp, this);
13319         }
13320         if(this.forceSelection){
13321             this.inputEl().on('blur', this.doForce, this);
13322         }
13323         
13324         if(this.multiple){
13325             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13326             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13327         }
13328     },
13329     
13330     initTickableEvents: function()
13331     {   
13332         this.createList();
13333         
13334         if(this.hiddenName){
13335             
13336             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13337             
13338             this.hiddenField.dom.value =
13339                 this.hiddenValue !== undefined ? this.hiddenValue :
13340                 this.value !== undefined ? this.value : '';
13341
13342             // prevent input submission
13343             this.el.dom.removeAttribute('name');
13344             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13345              
13346              
13347         }
13348         
13349 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13350         
13351         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13352         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13353         if(this.triggerList){
13354             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13355         }
13356          
13357         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13358         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13359         
13360         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13361         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13362         
13363         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13364         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13365         
13366         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13367         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13368         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13369         
13370         this.okBtn.hide();
13371         this.cancelBtn.hide();
13372         
13373         var _this = this;
13374         
13375         (function(){
13376             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13377             _this.list.setWidth(lw);
13378         }).defer(100);
13379         
13380         this.list.on('mouseover', this.onViewOver, this);
13381         this.list.on('mousemove', this.onViewMove, this);
13382         
13383         this.list.on('scroll', this.onViewScroll, this);
13384         
13385         if(!this.tpl){
13386             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>';
13387         }
13388
13389         this.view = new Roo.View(this.list, this.tpl, {
13390             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13391         });
13392         
13393         //this.view.wrapEl.setDisplayed(false);
13394         this.view.on('click', this.onViewClick, this);
13395         
13396         
13397         
13398         this.store.on('beforeload', this.onBeforeLoad, this);
13399         this.store.on('load', this.onLoad, this);
13400         this.store.on('loadexception', this.onLoadException, this);
13401         
13402         if(this.editable){
13403             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13404                 "up" : function(e){
13405                     this.inKeyMode = true;
13406                     this.selectPrev();
13407                 },
13408
13409                 "down" : function(e){
13410                     this.inKeyMode = true;
13411                     this.selectNext();
13412                 },
13413
13414                 "enter" : function(e){
13415                     if(this.fireEvent("specialkey", this, e)){
13416                         this.onViewClick(false);
13417                     }
13418                     
13419                     return true;
13420                 },
13421
13422                 "esc" : function(e){
13423                     this.onTickableFooterButtonClick(e, false, false);
13424                 },
13425
13426                 "tab" : function(e){
13427                     this.fireEvent("specialkey", this, e);
13428                     
13429                     this.onTickableFooterButtonClick(e, false, false);
13430                     
13431                     return true;
13432                 },
13433
13434                 scope : this,
13435
13436                 doRelay : function(e, fn, key){
13437                     if(this.scope.isExpanded()){
13438                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13439                     }
13440                     return true;
13441                 },
13442
13443                 forceKeyDown: true
13444             });
13445         }
13446         
13447         this.queryDelay = Math.max(this.queryDelay || 10,
13448                 this.mode == 'local' ? 10 : 250);
13449         
13450         
13451         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13452         
13453         if(this.typeAhead){
13454             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13455         }
13456         
13457         if(this.editable !== false){
13458             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13459         }
13460         
13461         this.indicator = this.indicatorEl();
13462         
13463         if(this.indicator){
13464             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13465             this.indicator.hide();
13466         }
13467         
13468     },
13469
13470     onDestroy : function(){
13471         if(this.view){
13472             this.view.setStore(null);
13473             this.view.el.removeAllListeners();
13474             this.view.el.remove();
13475             this.view.purgeListeners();
13476         }
13477         if(this.list){
13478             this.list.dom.innerHTML  = '';
13479         }
13480         
13481         if(this.store){
13482             this.store.un('beforeload', this.onBeforeLoad, this);
13483             this.store.un('load', this.onLoad, this);
13484             this.store.un('loadexception', this.onLoadException, this);
13485         }
13486         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13487     },
13488
13489     // private
13490     fireKey : function(e){
13491         if(e.isNavKeyPress() && !this.list.isVisible()){
13492             this.fireEvent("specialkey", this, e);
13493         }
13494     },
13495
13496     // private
13497     onResize: function(w, h){
13498 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13499 //        
13500 //        if(typeof w != 'number'){
13501 //            // we do not handle it!?!?
13502 //            return;
13503 //        }
13504 //        var tw = this.trigger.getWidth();
13505 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13506 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13507 //        var x = w - tw;
13508 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13509 //            
13510 //        //this.trigger.setStyle('left', x+'px');
13511 //        
13512 //        if(this.list && this.listWidth === undefined){
13513 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13514 //            this.list.setWidth(lw);
13515 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13516 //        }
13517         
13518     
13519         
13520     },
13521
13522     /**
13523      * Allow or prevent the user from directly editing the field text.  If false is passed,
13524      * the user will only be able to select from the items defined in the dropdown list.  This method
13525      * is the runtime equivalent of setting the 'editable' config option at config time.
13526      * @param {Boolean} value True to allow the user to directly edit the field text
13527      */
13528     setEditable : function(value){
13529         if(value == this.editable){
13530             return;
13531         }
13532         this.editable = value;
13533         if(!value){
13534             this.inputEl().dom.setAttribute('readOnly', true);
13535             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13536             this.inputEl().addClass('x-combo-noedit');
13537         }else{
13538             this.inputEl().dom.setAttribute('readOnly', false);
13539             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13540             this.inputEl().removeClass('x-combo-noedit');
13541         }
13542     },
13543
13544     // private
13545     
13546     onBeforeLoad : function(combo,opts){
13547         if(!this.hasFocus){
13548             return;
13549         }
13550          if (!opts.add) {
13551             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13552          }
13553         this.restrictHeight();
13554         this.selectedIndex = -1;
13555     },
13556
13557     // private
13558     onLoad : function(){
13559         
13560         this.hasQuery = false;
13561         
13562         if(!this.hasFocus){
13563             return;
13564         }
13565         
13566         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13567             this.loading.hide();
13568         }
13569         
13570         if(this.store.getCount() > 0){
13571             
13572             this.expand();
13573             this.restrictHeight();
13574             if(this.lastQuery == this.allQuery){
13575                 if(this.editable && !this.tickable){
13576                     this.inputEl().dom.select();
13577                 }
13578                 
13579                 if(
13580                     !this.selectByValue(this.value, true) &&
13581                     this.autoFocus && 
13582                     (
13583                         !this.store.lastOptions ||
13584                         typeof(this.store.lastOptions.add) == 'undefined' || 
13585                         this.store.lastOptions.add != true
13586                     )
13587                 ){
13588                     this.select(0, true);
13589                 }
13590             }else{
13591                 if(this.autoFocus){
13592                     this.selectNext();
13593                 }
13594                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13595                     this.taTask.delay(this.typeAheadDelay);
13596                 }
13597             }
13598         }else{
13599             this.onEmptyResults();
13600         }
13601         
13602         //this.el.focus();
13603     },
13604     // private
13605     onLoadException : function()
13606     {
13607         this.hasQuery = false;
13608         
13609         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13610             this.loading.hide();
13611         }
13612         
13613         if(this.tickable && this.editable){
13614             return;
13615         }
13616         
13617         this.collapse();
13618         // only causes errors at present
13619         //Roo.log(this.store.reader.jsonData);
13620         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13621             // fixme
13622             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13623         //}
13624         
13625         
13626     },
13627     // private
13628     onTypeAhead : function(){
13629         if(this.store.getCount() > 0){
13630             var r = this.store.getAt(0);
13631             var newValue = r.data[this.displayField];
13632             var len = newValue.length;
13633             var selStart = this.getRawValue().length;
13634             
13635             if(selStart != len){
13636                 this.setRawValue(newValue);
13637                 this.selectText(selStart, newValue.length);
13638             }
13639         }
13640     },
13641
13642     // private
13643     onSelect : function(record, index){
13644         
13645         if(this.fireEvent('beforeselect', this, record, index) !== false){
13646         
13647             this.setFromData(index > -1 ? record.data : false);
13648             
13649             this.collapse();
13650             this.fireEvent('select', this, record, index);
13651         }
13652     },
13653
13654     /**
13655      * Returns the currently selected field value or empty string if no value is set.
13656      * @return {String} value The selected value
13657      */
13658     getValue : function()
13659     {
13660         if(Roo.isIOS && this.useNativeIOS){
13661             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13662         }
13663         
13664         if(this.multiple){
13665             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13666         }
13667         
13668         if(this.valueField){
13669             return typeof this.value != 'undefined' ? this.value : '';
13670         }else{
13671             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13672         }
13673     },
13674     
13675     getRawValue : function()
13676     {
13677         if(Roo.isIOS && this.useNativeIOS){
13678             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13679         }
13680         
13681         var v = this.inputEl().getValue();
13682         
13683         return v;
13684     },
13685
13686     /**
13687      * Clears any text/value currently set in the field
13688      */
13689     clearValue : function(){
13690         
13691         if(this.hiddenField){
13692             this.hiddenField.dom.value = '';
13693         }
13694         this.value = '';
13695         this.setRawValue('');
13696         this.lastSelectionText = '';
13697         this.lastData = false;
13698         
13699         var close = this.closeTriggerEl();
13700         
13701         if(close){
13702             close.hide();
13703         }
13704         
13705         this.validate();
13706         
13707     },
13708
13709     /**
13710      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13711      * will be displayed in the field.  If the value does not match the data value of an existing item,
13712      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13713      * Otherwise the field will be blank (although the value will still be set).
13714      * @param {String} value The value to match
13715      */
13716     setValue : function(v)
13717     {
13718         if(Roo.isIOS && this.useNativeIOS){
13719             this.setIOSValue(v);
13720             return;
13721         }
13722         
13723         if(this.multiple){
13724             this.syncValue();
13725             return;
13726         }
13727         
13728         var text = v;
13729         if(this.valueField){
13730             var r = this.findRecord(this.valueField, v);
13731             if(r){
13732                 text = r.data[this.displayField];
13733             }else if(this.valueNotFoundText !== undefined){
13734                 text = this.valueNotFoundText;
13735             }
13736         }
13737         this.lastSelectionText = text;
13738         if(this.hiddenField){
13739             this.hiddenField.dom.value = v;
13740         }
13741         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13742         this.value = v;
13743         
13744         var close = this.closeTriggerEl();
13745         
13746         if(close){
13747             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13748         }
13749         
13750         this.validate();
13751     },
13752     /**
13753      * @property {Object} the last set data for the element
13754      */
13755     
13756     lastData : false,
13757     /**
13758      * Sets the value of the field based on a object which is related to the record format for the store.
13759      * @param {Object} value the value to set as. or false on reset?
13760      */
13761     setFromData : function(o){
13762         
13763         if(this.multiple){
13764             this.addItem(o);
13765             return;
13766         }
13767             
13768         var dv = ''; // display value
13769         var vv = ''; // value value..
13770         this.lastData = o;
13771         if (this.displayField) {
13772             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13773         } else {
13774             // this is an error condition!!!
13775             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13776         }
13777         
13778         if(this.valueField){
13779             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13780         }
13781         
13782         var close = this.closeTriggerEl();
13783         
13784         if(close){
13785             if(dv.length || vv * 1 > 0){
13786                 close.show() ;
13787                 this.blockFocus=true;
13788             } else {
13789                 close.hide();
13790             }             
13791         }
13792         
13793         if(this.hiddenField){
13794             this.hiddenField.dom.value = vv;
13795             
13796             this.lastSelectionText = dv;
13797             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13798             this.value = vv;
13799             return;
13800         }
13801         // no hidden field.. - we store the value in 'value', but still display
13802         // display field!!!!
13803         this.lastSelectionText = dv;
13804         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13805         this.value = vv;
13806         
13807         
13808         
13809     },
13810     // private
13811     reset : function(){
13812         // overridden so that last data is reset..
13813         
13814         if(this.multiple){
13815             this.clearItem();
13816             return;
13817         }
13818         
13819         this.setValue(this.originalValue);
13820         //this.clearInvalid();
13821         this.lastData = false;
13822         if (this.view) {
13823             this.view.clearSelections();
13824         }
13825         
13826         this.validate();
13827     },
13828     // private
13829     findRecord : function(prop, value){
13830         var record;
13831         if(this.store.getCount() > 0){
13832             this.store.each(function(r){
13833                 if(r.data[prop] == value){
13834                     record = r;
13835                     return false;
13836                 }
13837                 return true;
13838             });
13839         }
13840         return record;
13841     },
13842     
13843     getName: function()
13844     {
13845         // returns hidden if it's set..
13846         if (!this.rendered) {return ''};
13847         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13848         
13849     },
13850     // private
13851     onViewMove : function(e, t){
13852         this.inKeyMode = false;
13853     },
13854
13855     // private
13856     onViewOver : function(e, t){
13857         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13858             return;
13859         }
13860         var item = this.view.findItemFromChild(t);
13861         
13862         if(item){
13863             var index = this.view.indexOf(item);
13864             this.select(index, false);
13865         }
13866     },
13867
13868     // private
13869     onViewClick : function(view, doFocus, el, e)
13870     {
13871         var index = this.view.getSelectedIndexes()[0];
13872         
13873         var r = this.store.getAt(index);
13874         
13875         if(this.tickable){
13876             
13877             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13878                 return;
13879             }
13880             
13881             var rm = false;
13882             var _this = this;
13883             
13884             Roo.each(this.tickItems, function(v,k){
13885                 
13886                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13887                     Roo.log(v);
13888                     _this.tickItems.splice(k, 1);
13889                     
13890                     if(typeof(e) == 'undefined' && view == false){
13891                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13892                     }
13893                     
13894                     rm = true;
13895                     return;
13896                 }
13897             });
13898             
13899             if(rm){
13900                 return;
13901             }
13902             
13903             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13904                 this.tickItems.push(r.data);
13905             }
13906             
13907             if(typeof(e) == 'undefined' && view == false){
13908                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13909             }
13910                     
13911             return;
13912         }
13913         
13914         if(r){
13915             this.onSelect(r, index);
13916         }
13917         if(doFocus !== false && !this.blockFocus){
13918             this.inputEl().focus();
13919         }
13920     },
13921
13922     // private
13923     restrictHeight : function(){
13924         //this.innerList.dom.style.height = '';
13925         //var inner = this.innerList.dom;
13926         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13927         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13928         //this.list.beginUpdate();
13929         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13930         this.list.alignTo(this.inputEl(), this.listAlign);
13931         this.list.alignTo(this.inputEl(), this.listAlign);
13932         //this.list.endUpdate();
13933     },
13934
13935     // private
13936     onEmptyResults : function(){
13937         
13938         if(this.tickable && this.editable){
13939             this.restrictHeight();
13940             return;
13941         }
13942         
13943         this.collapse();
13944     },
13945
13946     /**
13947      * Returns true if the dropdown list is expanded, else false.
13948      */
13949     isExpanded : function(){
13950         return this.list.isVisible();
13951     },
13952
13953     /**
13954      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13955      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13956      * @param {String} value The data value of the item to select
13957      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13958      * selected item if it is not currently in view (defaults to true)
13959      * @return {Boolean} True if the value matched an item in the list, else false
13960      */
13961     selectByValue : function(v, scrollIntoView){
13962         if(v !== undefined && v !== null){
13963             var r = this.findRecord(this.valueField || this.displayField, v);
13964             if(r){
13965                 this.select(this.store.indexOf(r), scrollIntoView);
13966                 return true;
13967             }
13968         }
13969         return false;
13970     },
13971
13972     /**
13973      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13974      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13975      * @param {Number} index The zero-based index of the list item to select
13976      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13977      * selected item if it is not currently in view (defaults to true)
13978      */
13979     select : function(index, scrollIntoView){
13980         this.selectedIndex = index;
13981         this.view.select(index);
13982         if(scrollIntoView !== false){
13983             var el = this.view.getNode(index);
13984             /*
13985              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13986              */
13987             if(el){
13988                 this.list.scrollChildIntoView(el, false);
13989             }
13990         }
13991     },
13992
13993     // private
13994     selectNext : function(){
13995         var ct = this.store.getCount();
13996         if(ct > 0){
13997             if(this.selectedIndex == -1){
13998                 this.select(0);
13999             }else if(this.selectedIndex < ct-1){
14000                 this.select(this.selectedIndex+1);
14001             }
14002         }
14003     },
14004
14005     // private
14006     selectPrev : function(){
14007         var ct = this.store.getCount();
14008         if(ct > 0){
14009             if(this.selectedIndex == -1){
14010                 this.select(0);
14011             }else if(this.selectedIndex != 0){
14012                 this.select(this.selectedIndex-1);
14013             }
14014         }
14015     },
14016
14017     // private
14018     onKeyUp : function(e){
14019         if(this.editable !== false && !e.isSpecialKey()){
14020             this.lastKey = e.getKey();
14021             this.dqTask.delay(this.queryDelay);
14022         }
14023     },
14024
14025     // private
14026     validateBlur : function(){
14027         return !this.list || !this.list.isVisible();   
14028     },
14029
14030     // private
14031     initQuery : function(){
14032         
14033         var v = this.getRawValue();
14034         
14035         if(this.tickable && this.editable){
14036             v = this.tickableInputEl().getValue();
14037         }
14038         
14039         this.doQuery(v);
14040     },
14041
14042     // private
14043     doForce : function(){
14044         if(this.inputEl().dom.value.length > 0){
14045             this.inputEl().dom.value =
14046                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14047              
14048         }
14049     },
14050
14051     /**
14052      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14053      * query allowing the query action to be canceled if needed.
14054      * @param {String} query The SQL query to execute
14055      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14056      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14057      * saved in the current store (defaults to false)
14058      */
14059     doQuery : function(q, forceAll){
14060         
14061         if(q === undefined || q === null){
14062             q = '';
14063         }
14064         var qe = {
14065             query: q,
14066             forceAll: forceAll,
14067             combo: this,
14068             cancel:false
14069         };
14070         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14071             return false;
14072         }
14073         q = qe.query;
14074         
14075         forceAll = qe.forceAll;
14076         if(forceAll === true || (q.length >= this.minChars)){
14077             
14078             this.hasQuery = true;
14079             
14080             if(this.lastQuery != q || this.alwaysQuery){
14081                 this.lastQuery = q;
14082                 if(this.mode == 'local'){
14083                     this.selectedIndex = -1;
14084                     if(forceAll){
14085                         this.store.clearFilter();
14086                     }else{
14087                         
14088                         if(this.specialFilter){
14089                             this.fireEvent('specialfilter', this);
14090                             this.onLoad();
14091                             return;
14092                         }
14093                         
14094                         this.store.filter(this.displayField, q);
14095                     }
14096                     
14097                     this.store.fireEvent("datachanged", this.store);
14098                     
14099                     this.onLoad();
14100                     
14101                     
14102                 }else{
14103                     
14104                     this.store.baseParams[this.queryParam] = q;
14105                     
14106                     var options = {params : this.getParams(q)};
14107                     
14108                     if(this.loadNext){
14109                         options.add = true;
14110                         options.params.start = this.page * this.pageSize;
14111                     }
14112                     
14113                     this.store.load(options);
14114                     
14115                     /*
14116                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14117                      *  we should expand the list on onLoad
14118                      *  so command out it
14119                      */
14120 //                    this.expand();
14121                 }
14122             }else{
14123                 this.selectedIndex = -1;
14124                 this.onLoad();   
14125             }
14126         }
14127         
14128         this.loadNext = false;
14129     },
14130     
14131     // private
14132     getParams : function(q){
14133         var p = {};
14134         //p[this.queryParam] = q;
14135         
14136         if(this.pageSize){
14137             p.start = 0;
14138             p.limit = this.pageSize;
14139         }
14140         return p;
14141     },
14142
14143     /**
14144      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14145      */
14146     collapse : function(){
14147         if(!this.isExpanded()){
14148             return;
14149         }
14150         
14151         this.list.hide();
14152         
14153         this.hasFocus = false;
14154         
14155         if(this.tickable){
14156             this.okBtn.hide();
14157             this.cancelBtn.hide();
14158             this.trigger.show();
14159             
14160             if(this.editable){
14161                 this.tickableInputEl().dom.value = '';
14162                 this.tickableInputEl().blur();
14163             }
14164             
14165         }
14166         
14167         Roo.get(document).un('mousedown', this.collapseIf, this);
14168         Roo.get(document).un('mousewheel', this.collapseIf, this);
14169         if (!this.editable) {
14170             Roo.get(document).un('keydown', this.listKeyPress, this);
14171         }
14172         this.fireEvent('collapse', this);
14173         
14174         this.validate();
14175     },
14176
14177     // private
14178     collapseIf : function(e){
14179         var in_combo  = e.within(this.el);
14180         var in_list =  e.within(this.list);
14181         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14182         
14183         if (in_combo || in_list || is_list) {
14184             //e.stopPropagation();
14185             return;
14186         }
14187         
14188         if(this.tickable){
14189             this.onTickableFooterButtonClick(e, false, false);
14190         }
14191
14192         this.collapse();
14193         
14194     },
14195
14196     /**
14197      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14198      */
14199     expand : function(){
14200        
14201         if(this.isExpanded() || !this.hasFocus){
14202             return;
14203         }
14204         
14205         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14206         this.list.setWidth(lw);
14207         
14208         Roo.log('expand');
14209         
14210         this.list.show();
14211         
14212         this.restrictHeight();
14213         
14214         if(this.tickable){
14215             
14216             this.tickItems = Roo.apply([], this.item);
14217             
14218             this.okBtn.show();
14219             this.cancelBtn.show();
14220             this.trigger.hide();
14221             
14222             if(this.editable){
14223                 this.tickableInputEl().focus();
14224             }
14225             
14226         }
14227         
14228         Roo.get(document).on('mousedown', this.collapseIf, this);
14229         Roo.get(document).on('mousewheel', this.collapseIf, this);
14230         if (!this.editable) {
14231             Roo.get(document).on('keydown', this.listKeyPress, this);
14232         }
14233         
14234         this.fireEvent('expand', this);
14235     },
14236
14237     // private
14238     // Implements the default empty TriggerField.onTriggerClick function
14239     onTriggerClick : function(e)
14240     {
14241         Roo.log('trigger click');
14242         
14243         if(this.disabled || !this.triggerList){
14244             return;
14245         }
14246         
14247         this.page = 0;
14248         this.loadNext = false;
14249         
14250         if(this.isExpanded()){
14251             this.collapse();
14252             if (!this.blockFocus) {
14253                 this.inputEl().focus();
14254             }
14255             
14256         }else {
14257             this.hasFocus = true;
14258             if(this.triggerAction == 'all') {
14259                 this.doQuery(this.allQuery, true);
14260             } else {
14261                 this.doQuery(this.getRawValue());
14262             }
14263             if (!this.blockFocus) {
14264                 this.inputEl().focus();
14265             }
14266         }
14267     },
14268     
14269     onTickableTriggerClick : function(e)
14270     {
14271         if(this.disabled){
14272             return;
14273         }
14274         
14275         this.page = 0;
14276         this.loadNext = false;
14277         this.hasFocus = true;
14278         
14279         if(this.triggerAction == 'all') {
14280             this.doQuery(this.allQuery, true);
14281         } else {
14282             this.doQuery(this.getRawValue());
14283         }
14284     },
14285     
14286     onSearchFieldClick : function(e)
14287     {
14288         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14289             this.onTickableFooterButtonClick(e, false, false);
14290             return;
14291         }
14292         
14293         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14294             return;
14295         }
14296         
14297         this.page = 0;
14298         this.loadNext = false;
14299         this.hasFocus = true;
14300         
14301         if(this.triggerAction == 'all') {
14302             this.doQuery(this.allQuery, true);
14303         } else {
14304             this.doQuery(this.getRawValue());
14305         }
14306     },
14307     
14308     listKeyPress : function(e)
14309     {
14310         //Roo.log('listkeypress');
14311         // scroll to first matching element based on key pres..
14312         if (e.isSpecialKey()) {
14313             return false;
14314         }
14315         var k = String.fromCharCode(e.getKey()).toUpperCase();
14316         //Roo.log(k);
14317         var match  = false;
14318         var csel = this.view.getSelectedNodes();
14319         var cselitem = false;
14320         if (csel.length) {
14321             var ix = this.view.indexOf(csel[0]);
14322             cselitem  = this.store.getAt(ix);
14323             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14324                 cselitem = false;
14325             }
14326             
14327         }
14328         
14329         this.store.each(function(v) { 
14330             if (cselitem) {
14331                 // start at existing selection.
14332                 if (cselitem.id == v.id) {
14333                     cselitem = false;
14334                 }
14335                 return true;
14336             }
14337                 
14338             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14339                 match = this.store.indexOf(v);
14340                 return false;
14341             }
14342             return true;
14343         }, this);
14344         
14345         if (match === false) {
14346             return true; // no more action?
14347         }
14348         // scroll to?
14349         this.view.select(match);
14350         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14351         sn.scrollIntoView(sn.dom.parentNode, false);
14352     },
14353     
14354     onViewScroll : function(e, t){
14355         
14356         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){
14357             return;
14358         }
14359         
14360         this.hasQuery = true;
14361         
14362         this.loading = this.list.select('.loading', true).first();
14363         
14364         if(this.loading === null){
14365             this.list.createChild({
14366                 tag: 'div',
14367                 cls: 'loading roo-select2-more-results roo-select2-active',
14368                 html: 'Loading more results...'
14369             });
14370             
14371             this.loading = this.list.select('.loading', true).first();
14372             
14373             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14374             
14375             this.loading.hide();
14376         }
14377         
14378         this.loading.show();
14379         
14380         var _combo = this;
14381         
14382         this.page++;
14383         this.loadNext = true;
14384         
14385         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14386         
14387         return;
14388     },
14389     
14390     addItem : function(o)
14391     {   
14392         var dv = ''; // display value
14393         
14394         if (this.displayField) {
14395             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14396         } else {
14397             // this is an error condition!!!
14398             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14399         }
14400         
14401         if(!dv.length){
14402             return;
14403         }
14404         
14405         var choice = this.choices.createChild({
14406             tag: 'li',
14407             cls: 'roo-select2-search-choice',
14408             cn: [
14409                 {
14410                     tag: 'div',
14411                     html: dv
14412                 },
14413                 {
14414                     tag: 'a',
14415                     href: '#',
14416                     cls: 'roo-select2-search-choice-close fa fa-times',
14417                     tabindex: '-1'
14418                 }
14419             ]
14420             
14421         }, this.searchField);
14422         
14423         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14424         
14425         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14426         
14427         this.item.push(o);
14428         
14429         this.lastData = o;
14430         
14431         this.syncValue();
14432         
14433         this.inputEl().dom.value = '';
14434         
14435         this.validate();
14436     },
14437     
14438     onRemoveItem : function(e, _self, o)
14439     {
14440         e.preventDefault();
14441         
14442         this.lastItem = Roo.apply([], this.item);
14443         
14444         var index = this.item.indexOf(o.data) * 1;
14445         
14446         if( index < 0){
14447             Roo.log('not this item?!');
14448             return;
14449         }
14450         
14451         this.item.splice(index, 1);
14452         o.item.remove();
14453         
14454         this.syncValue();
14455         
14456         this.fireEvent('remove', this, e);
14457         
14458         this.validate();
14459         
14460     },
14461     
14462     syncValue : function()
14463     {
14464         if(!this.item.length){
14465             this.clearValue();
14466             return;
14467         }
14468             
14469         var value = [];
14470         var _this = this;
14471         Roo.each(this.item, function(i){
14472             if(_this.valueField){
14473                 value.push(i[_this.valueField]);
14474                 return;
14475             }
14476
14477             value.push(i);
14478         });
14479
14480         this.value = value.join(',');
14481
14482         if(this.hiddenField){
14483             this.hiddenField.dom.value = this.value;
14484         }
14485         
14486         this.store.fireEvent("datachanged", this.store);
14487         
14488         this.validate();
14489     },
14490     
14491     clearItem : function()
14492     {
14493         if(!this.multiple){
14494             return;
14495         }
14496         
14497         this.item = [];
14498         
14499         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14500            c.remove();
14501         });
14502         
14503         this.syncValue();
14504         
14505         this.validate();
14506         
14507         if(this.tickable && !Roo.isTouch){
14508             this.view.refresh();
14509         }
14510     },
14511     
14512     inputEl: function ()
14513     {
14514         if(Roo.isIOS && this.useNativeIOS){
14515             return this.el.select('select.roo-ios-select', true).first();
14516         }
14517         
14518         if(Roo.isTouch && this.mobileTouchView){
14519             return this.el.select('input.form-control',true).first();
14520         }
14521         
14522         if(this.tickable){
14523             return this.searchField;
14524         }
14525         
14526         return this.el.select('input.form-control',true).first();
14527     },
14528     
14529     onTickableFooterButtonClick : function(e, btn, el)
14530     {
14531         e.preventDefault();
14532         
14533         this.lastItem = Roo.apply([], this.item);
14534         
14535         if(btn && btn.name == 'cancel'){
14536             this.tickItems = Roo.apply([], this.item);
14537             this.collapse();
14538             return;
14539         }
14540         
14541         this.clearItem();
14542         
14543         var _this = this;
14544         
14545         Roo.each(this.tickItems, function(o){
14546             _this.addItem(o);
14547         });
14548         
14549         this.collapse();
14550         
14551     },
14552     
14553     validate : function()
14554     {
14555         var v = this.getRawValue();
14556         
14557         if(this.multiple){
14558             v = this.getValue();
14559         }
14560         
14561         if(this.disabled || this.allowBlank || v.length){
14562             this.markValid();
14563             return true;
14564         }
14565         
14566         this.markInvalid();
14567         return false;
14568     },
14569     
14570     tickableInputEl : function()
14571     {
14572         if(!this.tickable || !this.editable){
14573             return this.inputEl();
14574         }
14575         
14576         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14577     },
14578     
14579     
14580     getAutoCreateTouchView : function()
14581     {
14582         var id = Roo.id();
14583         
14584         var cfg = {
14585             cls: 'form-group' //input-group
14586         };
14587         
14588         var input =  {
14589             tag: 'input',
14590             id : id,
14591             type : this.inputType,
14592             cls : 'form-control x-combo-noedit',
14593             autocomplete: 'new-password',
14594             placeholder : this.placeholder || '',
14595             readonly : true
14596         };
14597         
14598         if (this.name) {
14599             input.name = this.name;
14600         }
14601         
14602         if (this.size) {
14603             input.cls += ' input-' + this.size;
14604         }
14605         
14606         if (this.disabled) {
14607             input.disabled = true;
14608         }
14609         
14610         var inputblock = {
14611             cls : '',
14612             cn : [
14613                 input
14614             ]
14615         };
14616         
14617         if(this.before){
14618             inputblock.cls += ' input-group';
14619             
14620             inputblock.cn.unshift({
14621                 tag :'span',
14622                 cls : 'input-group-addon',
14623                 html : this.before
14624             });
14625         }
14626         
14627         if(this.removable && !this.multiple){
14628             inputblock.cls += ' roo-removable';
14629             
14630             inputblock.cn.push({
14631                 tag: 'button',
14632                 html : 'x',
14633                 cls : 'roo-combo-removable-btn close'
14634             });
14635         }
14636
14637         if(this.hasFeedback && !this.allowBlank){
14638             
14639             inputblock.cls += ' has-feedback';
14640             
14641             inputblock.cn.push({
14642                 tag: 'span',
14643                 cls: 'glyphicon form-control-feedback'
14644             });
14645             
14646         }
14647         
14648         if (this.after) {
14649             
14650             inputblock.cls += (this.before) ? '' : ' input-group';
14651             
14652             inputblock.cn.push({
14653                 tag :'span',
14654                 cls : 'input-group-addon',
14655                 html : this.after
14656             });
14657         }
14658
14659         var box = {
14660             tag: 'div',
14661             cn: [
14662                 {
14663                     tag: 'input',
14664                     type : 'hidden',
14665                     cls: 'form-hidden-field'
14666                 },
14667                 inputblock
14668             ]
14669             
14670         };
14671         
14672         if(this.multiple){
14673             box = {
14674                 tag: 'div',
14675                 cn: [
14676                     {
14677                         tag: 'input',
14678                         type : 'hidden',
14679                         cls: 'form-hidden-field'
14680                     },
14681                     {
14682                         tag: 'ul',
14683                         cls: 'roo-select2-choices',
14684                         cn:[
14685                             {
14686                                 tag: 'li',
14687                                 cls: 'roo-select2-search-field',
14688                                 cn: [
14689
14690                                     inputblock
14691                                 ]
14692                             }
14693                         ]
14694                     }
14695                 ]
14696             }
14697         };
14698         
14699         var combobox = {
14700             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14701             cn: [
14702                 box
14703             ]
14704         };
14705         
14706         if(!this.multiple && this.showToggleBtn){
14707             
14708             var caret = {
14709                         tag: 'span',
14710                         cls: 'caret'
14711             };
14712             
14713             if (this.caret != false) {
14714                 caret = {
14715                      tag: 'i',
14716                      cls: 'fa fa-' + this.caret
14717                 };
14718                 
14719             }
14720             
14721             combobox.cn.push({
14722                 tag :'span',
14723                 cls : 'input-group-addon btn dropdown-toggle',
14724                 cn : [
14725                     caret,
14726                     {
14727                         tag: 'span',
14728                         cls: 'combobox-clear',
14729                         cn  : [
14730                             {
14731                                 tag : 'i',
14732                                 cls: 'icon-remove'
14733                             }
14734                         ]
14735                     }
14736                 ]
14737
14738             })
14739         }
14740         
14741         if(this.multiple){
14742             combobox.cls += ' roo-select2-container-multi';
14743         }
14744         
14745         var align = this.labelAlign || this.parentLabelAlign();
14746         
14747         if (align ==='left' && this.fieldLabel.length) {
14748
14749             cfg.cn = [
14750                 {
14751                    tag : 'i',
14752                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14753                    tooltip : 'This field is required'
14754                 },
14755                 {
14756                     tag: 'label',
14757                     cls : 'control-label',
14758                     html : this.fieldLabel
14759
14760                 },
14761                 {
14762                     cls : '', 
14763                     cn: [
14764                         combobox
14765                     ]
14766                 }
14767             ];
14768             
14769             var labelCfg = cfg.cn[1];
14770             var contentCfg = cfg.cn[2];
14771             
14772
14773             if(this.indicatorpos == 'right'){
14774                 cfg.cn = [
14775                     {
14776                         tag: 'label',
14777                         'for' :  id,
14778                         cls : 'control-label',
14779                         cn : [
14780                             {
14781                                 tag : 'span',
14782                                 html : this.fieldLabel
14783                             },
14784                             {
14785                                 tag : 'i',
14786                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14787                                 tooltip : 'This field is required'
14788                             }
14789                         ]
14790                     },
14791                     {
14792                         cls : "",
14793                         cn: [
14794                             combobox
14795                         ]
14796                     }
14797
14798                 ];
14799                 
14800                 labelCfg = cfg.cn[0];
14801                 contentCfg = cfg.cn[1];
14802             }
14803             
14804            
14805             
14806             if(this.labelWidth > 12){
14807                 labelCfg.style = "width: " + this.labelWidth + 'px';
14808             }
14809             
14810             if(this.labelWidth < 13 && this.labelmd == 0){
14811                 this.labelmd = this.labelWidth;
14812             }
14813             
14814             if(this.labellg > 0){
14815                 labelCfg.cls += ' col-lg-' + this.labellg;
14816                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14817             }
14818             
14819             if(this.labelmd > 0){
14820                 labelCfg.cls += ' col-md-' + this.labelmd;
14821                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14822             }
14823             
14824             if(this.labelsm > 0){
14825                 labelCfg.cls += ' col-sm-' + this.labelsm;
14826                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14827             }
14828             
14829             if(this.labelxs > 0){
14830                 labelCfg.cls += ' col-xs-' + this.labelxs;
14831                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14832             }
14833                 
14834                 
14835         } else if ( this.fieldLabel.length) {
14836             cfg.cn = [
14837                 {
14838                    tag : 'i',
14839                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14840                    tooltip : 'This field is required'
14841                 },
14842                 {
14843                     tag: 'label',
14844                     cls : 'control-label',
14845                     html : this.fieldLabel
14846
14847                 },
14848                 {
14849                     cls : '', 
14850                     cn: [
14851                         combobox
14852                     ]
14853                 }
14854             ];
14855             
14856             if(this.indicatorpos == 'right'){
14857                 cfg.cn = [
14858                     {
14859                         tag: 'label',
14860                         cls : 'control-label',
14861                         html : this.fieldLabel,
14862                         cn : [
14863                             {
14864                                tag : 'i',
14865                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14866                                tooltip : 'This field is required'
14867                             }
14868                         ]
14869                     },
14870                     {
14871                         cls : '', 
14872                         cn: [
14873                             combobox
14874                         ]
14875                     }
14876                 ];
14877             }
14878         } else {
14879             cfg.cn = combobox;    
14880         }
14881         
14882         
14883         var settings = this;
14884         
14885         ['xs','sm','md','lg'].map(function(size){
14886             if (settings[size]) {
14887                 cfg.cls += ' col-' + size + '-' + settings[size];
14888             }
14889         });
14890         
14891         return cfg;
14892     },
14893     
14894     initTouchView : function()
14895     {
14896         this.renderTouchView();
14897         
14898         this.touchViewEl.on('scroll', function(){
14899             this.el.dom.scrollTop = 0;
14900         }, this);
14901         
14902         this.originalValue = this.getValue();
14903         
14904         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14905         
14906         this.inputEl().on("click", this.showTouchView, this);
14907         if (this.triggerEl) {
14908             this.triggerEl.on("click", this.showTouchView, this);
14909         }
14910         
14911         
14912         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14913         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14914         
14915         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14916         
14917         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14918         this.store.on('load', this.onTouchViewLoad, this);
14919         this.store.on('loadexception', this.onTouchViewLoadException, this);
14920         
14921         if(this.hiddenName){
14922             
14923             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14924             
14925             this.hiddenField.dom.value =
14926                 this.hiddenValue !== undefined ? this.hiddenValue :
14927                 this.value !== undefined ? this.value : '';
14928         
14929             this.el.dom.removeAttribute('name');
14930             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14931         }
14932         
14933         if(this.multiple){
14934             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14935             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14936         }
14937         
14938         if(this.removable && !this.multiple){
14939             var close = this.closeTriggerEl();
14940             if(close){
14941                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14942                 close.on('click', this.removeBtnClick, this, close);
14943             }
14944         }
14945         /*
14946          * fix the bug in Safari iOS8
14947          */
14948         this.inputEl().on("focus", function(e){
14949             document.activeElement.blur();
14950         }, this);
14951         
14952         return;
14953         
14954         
14955     },
14956     
14957     renderTouchView : function()
14958     {
14959         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14960         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14961         
14962         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14963         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14964         
14965         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14966         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14967         this.touchViewBodyEl.setStyle('overflow', 'auto');
14968         
14969         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14970         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14971         
14972         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14973         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14974         
14975     },
14976     
14977     showTouchView : function()
14978     {
14979         if(this.disabled){
14980             return;
14981         }
14982         
14983         this.touchViewHeaderEl.hide();
14984
14985         if(this.modalTitle.length){
14986             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14987             this.touchViewHeaderEl.show();
14988         }
14989
14990         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14991         this.touchViewEl.show();
14992
14993         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14994         
14995         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14996         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14997
14998         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14999
15000         if(this.modalTitle.length){
15001             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15002         }
15003         
15004         this.touchViewBodyEl.setHeight(bodyHeight);
15005
15006         if(this.animate){
15007             var _this = this;
15008             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15009         }else{
15010             this.touchViewEl.addClass('in');
15011         }
15012
15013         this.doTouchViewQuery();
15014         
15015     },
15016     
15017     hideTouchView : function()
15018     {
15019         this.touchViewEl.removeClass('in');
15020
15021         if(this.animate){
15022             var _this = this;
15023             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15024         }else{
15025             this.touchViewEl.setStyle('display', 'none');
15026         }
15027         
15028     },
15029     
15030     setTouchViewValue : function()
15031     {
15032         if(this.multiple){
15033             this.clearItem();
15034         
15035             var _this = this;
15036
15037             Roo.each(this.tickItems, function(o){
15038                 this.addItem(o);
15039             }, this);
15040         }
15041         
15042         this.hideTouchView();
15043     },
15044     
15045     doTouchViewQuery : function()
15046     {
15047         var qe = {
15048             query: '',
15049             forceAll: true,
15050             combo: this,
15051             cancel:false
15052         };
15053         
15054         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15055             return false;
15056         }
15057         
15058         if(!this.alwaysQuery || this.mode == 'local'){
15059             this.onTouchViewLoad();
15060             return;
15061         }
15062         
15063         this.store.load();
15064     },
15065     
15066     onTouchViewBeforeLoad : function(combo,opts)
15067     {
15068         return;
15069     },
15070
15071     // private
15072     onTouchViewLoad : function()
15073     {
15074         if(this.store.getCount() < 1){
15075             this.onTouchViewEmptyResults();
15076             return;
15077         }
15078         
15079         this.clearTouchView();
15080         
15081         var rawValue = this.getRawValue();
15082         
15083         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15084         
15085         this.tickItems = [];
15086         
15087         this.store.data.each(function(d, rowIndex){
15088             var row = this.touchViewListGroup.createChild(template);
15089             
15090             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15091                 row.addClass(d.data.cls);
15092             }
15093             
15094             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15095                 var cfg = {
15096                     data : d.data,
15097                     html : d.data[this.displayField]
15098                 };
15099                 
15100                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15101                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15102                 }
15103             }
15104             row.removeClass('selected');
15105             if(!this.multiple && this.valueField &&
15106                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15107             {
15108                 // radio buttons..
15109                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15110                 row.addClass('selected');
15111             }
15112             
15113             if(this.multiple && this.valueField &&
15114                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15115             {
15116                 
15117                 // checkboxes...
15118                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15119                 this.tickItems.push(d.data);
15120             }
15121             
15122             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15123             
15124         }, this);
15125         
15126         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15127         
15128         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15129
15130         if(this.modalTitle.length){
15131             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15132         }
15133
15134         var listHeight = this.touchViewListGroup.getHeight();
15135         
15136         var _this = this;
15137         
15138         if(firstChecked && listHeight > bodyHeight){
15139             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15140         }
15141         
15142     },
15143     
15144     onTouchViewLoadException : function()
15145     {
15146         this.hideTouchView();
15147     },
15148     
15149     onTouchViewEmptyResults : function()
15150     {
15151         this.clearTouchView();
15152         
15153         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15154         
15155         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15156         
15157     },
15158     
15159     clearTouchView : function()
15160     {
15161         this.touchViewListGroup.dom.innerHTML = '';
15162     },
15163     
15164     onTouchViewClick : function(e, el, o)
15165     {
15166         e.preventDefault();
15167         
15168         var row = o.row;
15169         var rowIndex = o.rowIndex;
15170         
15171         var r = this.store.getAt(rowIndex);
15172         
15173         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15174             
15175             if(!this.multiple){
15176                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15177                     c.dom.removeAttribute('checked');
15178                 }, this);
15179
15180                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15181
15182                 this.setFromData(r.data);
15183
15184                 var close = this.closeTriggerEl();
15185
15186                 if(close){
15187                     close.show();
15188                 }
15189
15190                 this.hideTouchView();
15191
15192                 this.fireEvent('select', this, r, rowIndex);
15193
15194                 return;
15195             }
15196
15197             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15198                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15199                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15200                 return;
15201             }
15202
15203             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15204             this.addItem(r.data);
15205             this.tickItems.push(r.data);
15206         }
15207     },
15208     
15209     getAutoCreateNativeIOS : function()
15210     {
15211         var cfg = {
15212             cls: 'form-group' //input-group,
15213         };
15214         
15215         var combobox =  {
15216             tag: 'select',
15217             cls : 'roo-ios-select'
15218         };
15219         
15220         if (this.name) {
15221             combobox.name = this.name;
15222         }
15223         
15224         if (this.disabled) {
15225             combobox.disabled = true;
15226         }
15227         
15228         var settings = this;
15229         
15230         ['xs','sm','md','lg'].map(function(size){
15231             if (settings[size]) {
15232                 cfg.cls += ' col-' + size + '-' + settings[size];
15233             }
15234         });
15235         
15236         cfg.cn = combobox;
15237         
15238         return cfg;
15239         
15240     },
15241     
15242     initIOSView : function()
15243     {
15244         this.store.on('load', this.onIOSViewLoad, this);
15245         
15246         return;
15247     },
15248     
15249     onIOSViewLoad : function()
15250     {
15251         if(this.store.getCount() < 1){
15252             return;
15253         }
15254         
15255         this.clearIOSView();
15256         
15257         if(this.allowBlank) {
15258             
15259             var default_text = '-- SELECT --';
15260             
15261             if(this.placeholder.length){
15262                 default_text = this.placeholder;
15263             }
15264             
15265             if(this.emptyTitle.length){
15266                 default_text += ' - ' + this.emptyTitle + ' -';
15267             }
15268             
15269             var opt = this.inputEl().createChild({
15270                 tag: 'option',
15271                 value : 0,
15272                 html : default_text
15273             });
15274             
15275             var o = {};
15276             o[this.valueField] = 0;
15277             o[this.displayField] = default_text;
15278             
15279             this.ios_options.push({
15280                 data : o,
15281                 el : opt
15282             });
15283             
15284         }
15285         
15286         this.store.data.each(function(d, rowIndex){
15287             
15288             var html = '';
15289             
15290             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15291                 html = d.data[this.displayField];
15292             }
15293             
15294             var value = '';
15295             
15296             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15297                 value = d.data[this.valueField];
15298             }
15299             
15300             var option = {
15301                 tag: 'option',
15302                 value : value,
15303                 html : html
15304             };
15305             
15306             if(this.value == d.data[this.valueField]){
15307                 option['selected'] = true;
15308             }
15309             
15310             var opt = this.inputEl().createChild(option);
15311             
15312             this.ios_options.push({
15313                 data : d.data,
15314                 el : opt
15315             });
15316             
15317         }, this);
15318         
15319         this.inputEl().on('change', function(){
15320            this.fireEvent('select', this);
15321         }, this);
15322         
15323     },
15324     
15325     clearIOSView: function()
15326     {
15327         this.inputEl().dom.innerHTML = '';
15328         
15329         this.ios_options = [];
15330     },
15331     
15332     setIOSValue: function(v)
15333     {
15334         this.value = v;
15335         
15336         if(!this.ios_options){
15337             return;
15338         }
15339         
15340         Roo.each(this.ios_options, function(opts){
15341            
15342            opts.el.dom.removeAttribute('selected');
15343            
15344            if(opts.data[this.valueField] != v){
15345                return;
15346            }
15347            
15348            opts.el.dom.setAttribute('selected', true);
15349            
15350         }, this);
15351     }
15352
15353     /** 
15354     * @cfg {Boolean} grow 
15355     * @hide 
15356     */
15357     /** 
15358     * @cfg {Number} growMin 
15359     * @hide 
15360     */
15361     /** 
15362     * @cfg {Number} growMax 
15363     * @hide 
15364     */
15365     /**
15366      * @hide
15367      * @method autoSize
15368      */
15369 });
15370
15371 Roo.apply(Roo.bootstrap.ComboBox,  {
15372     
15373     header : {
15374         tag: 'div',
15375         cls: 'modal-header',
15376         cn: [
15377             {
15378                 tag: 'h4',
15379                 cls: 'modal-title'
15380             }
15381         ]
15382     },
15383     
15384     body : {
15385         tag: 'div',
15386         cls: 'modal-body',
15387         cn: [
15388             {
15389                 tag: 'ul',
15390                 cls: 'list-group'
15391             }
15392         ]
15393     },
15394     
15395     listItemRadio : {
15396         tag: 'li',
15397         cls: 'list-group-item',
15398         cn: [
15399             {
15400                 tag: 'span',
15401                 cls: 'roo-combobox-list-group-item-value'
15402             },
15403             {
15404                 tag: 'div',
15405                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15406                 cn: [
15407                     {
15408                         tag: 'input',
15409                         type: 'radio'
15410                     },
15411                     {
15412                         tag: 'label'
15413                     }
15414                 ]
15415             }
15416         ]
15417     },
15418     
15419     listItemCheckbox : {
15420         tag: 'li',
15421         cls: 'list-group-item',
15422         cn: [
15423             {
15424                 tag: 'span',
15425                 cls: 'roo-combobox-list-group-item-value'
15426             },
15427             {
15428                 tag: 'div',
15429                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15430                 cn: [
15431                     {
15432                         tag: 'input',
15433                         type: 'checkbox'
15434                     },
15435                     {
15436                         tag: 'label'
15437                     }
15438                 ]
15439             }
15440         ]
15441     },
15442     
15443     emptyResult : {
15444         tag: 'div',
15445         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15446     },
15447     
15448     footer : {
15449         tag: 'div',
15450         cls: 'modal-footer',
15451         cn: [
15452             {
15453                 tag: 'div',
15454                 cls: 'row',
15455                 cn: [
15456                     {
15457                         tag: 'div',
15458                         cls: 'col-xs-6 text-left',
15459                         cn: {
15460                             tag: 'button',
15461                             cls: 'btn btn-danger roo-touch-view-cancel',
15462                             html: 'Cancel'
15463                         }
15464                     },
15465                     {
15466                         tag: 'div',
15467                         cls: 'col-xs-6 text-right',
15468                         cn: {
15469                             tag: 'button',
15470                             cls: 'btn btn-success roo-touch-view-ok',
15471                             html: 'OK'
15472                         }
15473                     }
15474                 ]
15475             }
15476         ]
15477         
15478     }
15479 });
15480
15481 Roo.apply(Roo.bootstrap.ComboBox,  {
15482     
15483     touchViewTemplate : {
15484         tag: 'div',
15485         cls: 'modal fade roo-combobox-touch-view',
15486         cn: [
15487             {
15488                 tag: 'div',
15489                 cls: 'modal-dialog',
15490                 style : 'position:fixed', // we have to fix position....
15491                 cn: [
15492                     {
15493                         tag: 'div',
15494                         cls: 'modal-content',
15495                         cn: [
15496                             Roo.bootstrap.ComboBox.header,
15497                             Roo.bootstrap.ComboBox.body,
15498                             Roo.bootstrap.ComboBox.footer
15499                         ]
15500                     }
15501                 ]
15502             }
15503         ]
15504     }
15505 });/*
15506  * Based on:
15507  * Ext JS Library 1.1.1
15508  * Copyright(c) 2006-2007, Ext JS, LLC.
15509  *
15510  * Originally Released Under LGPL - original licence link has changed is not relivant.
15511  *
15512  * Fork - LGPL
15513  * <script type="text/javascript">
15514  */
15515
15516 /**
15517  * @class Roo.View
15518  * @extends Roo.util.Observable
15519  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15520  * This class also supports single and multi selection modes. <br>
15521  * Create a data model bound view:
15522  <pre><code>
15523  var store = new Roo.data.Store(...);
15524
15525  var view = new Roo.View({
15526     el : "my-element",
15527     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15528  
15529     singleSelect: true,
15530     selectedClass: "ydataview-selected",
15531     store: store
15532  });
15533
15534  // listen for node click?
15535  view.on("click", function(vw, index, node, e){
15536  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15537  });
15538
15539  // load XML data
15540  dataModel.load("foobar.xml");
15541  </code></pre>
15542  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15543  * <br><br>
15544  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15545  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15546  * 
15547  * Note: old style constructor is still suported (container, template, config)
15548  * 
15549  * @constructor
15550  * Create a new View
15551  * @param {Object} config The config object
15552  * 
15553  */
15554 Roo.View = function(config, depreciated_tpl, depreciated_config){
15555     
15556     this.parent = false;
15557     
15558     if (typeof(depreciated_tpl) == 'undefined') {
15559         // new way.. - universal constructor.
15560         Roo.apply(this, config);
15561         this.el  = Roo.get(this.el);
15562     } else {
15563         // old format..
15564         this.el  = Roo.get(config);
15565         this.tpl = depreciated_tpl;
15566         Roo.apply(this, depreciated_config);
15567     }
15568     this.wrapEl  = this.el.wrap().wrap();
15569     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15570     
15571     
15572     if(typeof(this.tpl) == "string"){
15573         this.tpl = new Roo.Template(this.tpl);
15574     } else {
15575         // support xtype ctors..
15576         this.tpl = new Roo.factory(this.tpl, Roo);
15577     }
15578     
15579     
15580     this.tpl.compile();
15581     
15582     /** @private */
15583     this.addEvents({
15584         /**
15585          * @event beforeclick
15586          * Fires before a click is processed. Returns false to cancel the default action.
15587          * @param {Roo.View} this
15588          * @param {Number} index The index of the target node
15589          * @param {HTMLElement} node The target node
15590          * @param {Roo.EventObject} e The raw event object
15591          */
15592             "beforeclick" : true,
15593         /**
15594          * @event click
15595          * Fires when a template node is clicked.
15596          * @param {Roo.View} this
15597          * @param {Number} index The index of the target node
15598          * @param {HTMLElement} node The target node
15599          * @param {Roo.EventObject} e The raw event object
15600          */
15601             "click" : true,
15602         /**
15603          * @event dblclick
15604          * Fires when a template node is double clicked.
15605          * @param {Roo.View} this
15606          * @param {Number} index The index of the target node
15607          * @param {HTMLElement} node The target node
15608          * @param {Roo.EventObject} e The raw event object
15609          */
15610             "dblclick" : true,
15611         /**
15612          * @event contextmenu
15613          * Fires when a template node is right clicked.
15614          * @param {Roo.View} this
15615          * @param {Number} index The index of the target node
15616          * @param {HTMLElement} node The target node
15617          * @param {Roo.EventObject} e The raw event object
15618          */
15619             "contextmenu" : true,
15620         /**
15621          * @event selectionchange
15622          * Fires when the selected nodes change.
15623          * @param {Roo.View} this
15624          * @param {Array} selections Array of the selected nodes
15625          */
15626             "selectionchange" : true,
15627     
15628         /**
15629          * @event beforeselect
15630          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15631          * @param {Roo.View} this
15632          * @param {HTMLElement} node The node to be selected
15633          * @param {Array} selections Array of currently selected nodes
15634          */
15635             "beforeselect" : true,
15636         /**
15637          * @event preparedata
15638          * Fires on every row to render, to allow you to change the data.
15639          * @param {Roo.View} this
15640          * @param {Object} data to be rendered (change this)
15641          */
15642           "preparedata" : true
15643           
15644           
15645         });
15646
15647
15648
15649     this.el.on({
15650         "click": this.onClick,
15651         "dblclick": this.onDblClick,
15652         "contextmenu": this.onContextMenu,
15653         scope:this
15654     });
15655
15656     this.selections = [];
15657     this.nodes = [];
15658     this.cmp = new Roo.CompositeElementLite([]);
15659     if(this.store){
15660         this.store = Roo.factory(this.store, Roo.data);
15661         this.setStore(this.store, true);
15662     }
15663     
15664     if ( this.footer && this.footer.xtype) {
15665            
15666          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15667         
15668         this.footer.dataSource = this.store;
15669         this.footer.container = fctr;
15670         this.footer = Roo.factory(this.footer, Roo);
15671         fctr.insertFirst(this.el);
15672         
15673         // this is a bit insane - as the paging toolbar seems to detach the el..
15674 //        dom.parentNode.parentNode.parentNode
15675          // they get detached?
15676     }
15677     
15678     
15679     Roo.View.superclass.constructor.call(this);
15680     
15681     
15682 };
15683
15684 Roo.extend(Roo.View, Roo.util.Observable, {
15685     
15686      /**
15687      * @cfg {Roo.data.Store} store Data store to load data from.
15688      */
15689     store : false,
15690     
15691     /**
15692      * @cfg {String|Roo.Element} el The container element.
15693      */
15694     el : '',
15695     
15696     /**
15697      * @cfg {String|Roo.Template} tpl The template used by this View 
15698      */
15699     tpl : false,
15700     /**
15701      * @cfg {String} dataName the named area of the template to use as the data area
15702      *                          Works with domtemplates roo-name="name"
15703      */
15704     dataName: false,
15705     /**
15706      * @cfg {String} selectedClass The css class to add to selected nodes
15707      */
15708     selectedClass : "x-view-selected",
15709      /**
15710      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15711      */
15712     emptyText : "",
15713     
15714     /**
15715      * @cfg {String} text to display on mask (default Loading)
15716      */
15717     mask : false,
15718     /**
15719      * @cfg {Boolean} multiSelect Allow multiple selection
15720      */
15721     multiSelect : false,
15722     /**
15723      * @cfg {Boolean} singleSelect Allow single selection
15724      */
15725     singleSelect:  false,
15726     
15727     /**
15728      * @cfg {Boolean} toggleSelect - selecting 
15729      */
15730     toggleSelect : false,
15731     
15732     /**
15733      * @cfg {Boolean} tickable - selecting 
15734      */
15735     tickable : false,
15736     
15737     /**
15738      * Returns the element this view is bound to.
15739      * @return {Roo.Element}
15740      */
15741     getEl : function(){
15742         return this.wrapEl;
15743     },
15744     
15745     
15746
15747     /**
15748      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15749      */
15750     refresh : function(){
15751         //Roo.log('refresh');
15752         var t = this.tpl;
15753         
15754         // if we are using something like 'domtemplate', then
15755         // the what gets used is:
15756         // t.applySubtemplate(NAME, data, wrapping data..)
15757         // the outer template then get' applied with
15758         //     the store 'extra data'
15759         // and the body get's added to the
15760         //      roo-name="data" node?
15761         //      <span class='roo-tpl-{name}'></span> ?????
15762         
15763         
15764         
15765         this.clearSelections();
15766         this.el.update("");
15767         var html = [];
15768         var records = this.store.getRange();
15769         if(records.length < 1) {
15770             
15771             // is this valid??  = should it render a template??
15772             
15773             this.el.update(this.emptyText);
15774             return;
15775         }
15776         var el = this.el;
15777         if (this.dataName) {
15778             this.el.update(t.apply(this.store.meta)); //????
15779             el = this.el.child('.roo-tpl-' + this.dataName);
15780         }
15781         
15782         for(var i = 0, len = records.length; i < len; i++){
15783             var data = this.prepareData(records[i].data, i, records[i]);
15784             this.fireEvent("preparedata", this, data, i, records[i]);
15785             
15786             var d = Roo.apply({}, data);
15787             
15788             if(this.tickable){
15789                 Roo.apply(d, {'roo-id' : Roo.id()});
15790                 
15791                 var _this = this;
15792             
15793                 Roo.each(this.parent.item, function(item){
15794                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15795                         return;
15796                     }
15797                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15798                 });
15799             }
15800             
15801             html[html.length] = Roo.util.Format.trim(
15802                 this.dataName ?
15803                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15804                     t.apply(d)
15805             );
15806         }
15807         
15808         
15809         
15810         el.update(html.join(""));
15811         this.nodes = el.dom.childNodes;
15812         this.updateIndexes(0);
15813     },
15814     
15815
15816     /**
15817      * Function to override to reformat the data that is sent to
15818      * the template for each node.
15819      * DEPRICATED - use the preparedata event handler.
15820      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15821      * a JSON object for an UpdateManager bound view).
15822      */
15823     prepareData : function(data, index, record)
15824     {
15825         this.fireEvent("preparedata", this, data, index, record);
15826         return data;
15827     },
15828
15829     onUpdate : function(ds, record){
15830         // Roo.log('on update');   
15831         this.clearSelections();
15832         var index = this.store.indexOf(record);
15833         var n = this.nodes[index];
15834         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15835         n.parentNode.removeChild(n);
15836         this.updateIndexes(index, index);
15837     },
15838
15839     
15840     
15841 // --------- FIXME     
15842     onAdd : function(ds, records, index)
15843     {
15844         //Roo.log(['on Add', ds, records, index] );        
15845         this.clearSelections();
15846         if(this.nodes.length == 0){
15847             this.refresh();
15848             return;
15849         }
15850         var n = this.nodes[index];
15851         for(var i = 0, len = records.length; i < len; i++){
15852             var d = this.prepareData(records[i].data, i, records[i]);
15853             if(n){
15854                 this.tpl.insertBefore(n, d);
15855             }else{
15856                 
15857                 this.tpl.append(this.el, d);
15858             }
15859         }
15860         this.updateIndexes(index);
15861     },
15862
15863     onRemove : function(ds, record, index){
15864        // Roo.log('onRemove');
15865         this.clearSelections();
15866         var el = this.dataName  ?
15867             this.el.child('.roo-tpl-' + this.dataName) :
15868             this.el; 
15869         
15870         el.dom.removeChild(this.nodes[index]);
15871         this.updateIndexes(index);
15872     },
15873
15874     /**
15875      * Refresh an individual node.
15876      * @param {Number} index
15877      */
15878     refreshNode : function(index){
15879         this.onUpdate(this.store, this.store.getAt(index));
15880     },
15881
15882     updateIndexes : function(startIndex, endIndex){
15883         var ns = this.nodes;
15884         startIndex = startIndex || 0;
15885         endIndex = endIndex || ns.length - 1;
15886         for(var i = startIndex; i <= endIndex; i++){
15887             ns[i].nodeIndex = i;
15888         }
15889     },
15890
15891     /**
15892      * Changes the data store this view uses and refresh the view.
15893      * @param {Store} store
15894      */
15895     setStore : function(store, initial){
15896         if(!initial && this.store){
15897             this.store.un("datachanged", this.refresh);
15898             this.store.un("add", this.onAdd);
15899             this.store.un("remove", this.onRemove);
15900             this.store.un("update", this.onUpdate);
15901             this.store.un("clear", this.refresh);
15902             this.store.un("beforeload", this.onBeforeLoad);
15903             this.store.un("load", this.onLoad);
15904             this.store.un("loadexception", this.onLoad);
15905         }
15906         if(store){
15907           
15908             store.on("datachanged", this.refresh, this);
15909             store.on("add", this.onAdd, this);
15910             store.on("remove", this.onRemove, this);
15911             store.on("update", this.onUpdate, this);
15912             store.on("clear", this.refresh, this);
15913             store.on("beforeload", this.onBeforeLoad, this);
15914             store.on("load", this.onLoad, this);
15915             store.on("loadexception", this.onLoad, this);
15916         }
15917         
15918         if(store){
15919             this.refresh();
15920         }
15921     },
15922     /**
15923      * onbeforeLoad - masks the loading area.
15924      *
15925      */
15926     onBeforeLoad : function(store,opts)
15927     {
15928          //Roo.log('onBeforeLoad');   
15929         if (!opts.add) {
15930             this.el.update("");
15931         }
15932         this.el.mask(this.mask ? this.mask : "Loading" ); 
15933     },
15934     onLoad : function ()
15935     {
15936         this.el.unmask();
15937     },
15938     
15939
15940     /**
15941      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15942      * @param {HTMLElement} node
15943      * @return {HTMLElement} The template node
15944      */
15945     findItemFromChild : function(node){
15946         var el = this.dataName  ?
15947             this.el.child('.roo-tpl-' + this.dataName,true) :
15948             this.el.dom; 
15949         
15950         if(!node || node.parentNode == el){
15951                     return node;
15952             }
15953             var p = node.parentNode;
15954             while(p && p != el){
15955             if(p.parentNode == el){
15956                 return p;
15957             }
15958             p = p.parentNode;
15959         }
15960             return null;
15961     },
15962
15963     /** @ignore */
15964     onClick : function(e){
15965         var item = this.findItemFromChild(e.getTarget());
15966         if(item){
15967             var index = this.indexOf(item);
15968             if(this.onItemClick(item, index, e) !== false){
15969                 this.fireEvent("click", this, index, item, e);
15970             }
15971         }else{
15972             this.clearSelections();
15973         }
15974     },
15975
15976     /** @ignore */
15977     onContextMenu : function(e){
15978         var item = this.findItemFromChild(e.getTarget());
15979         if(item){
15980             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15981         }
15982     },
15983
15984     /** @ignore */
15985     onDblClick : function(e){
15986         var item = this.findItemFromChild(e.getTarget());
15987         if(item){
15988             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15989         }
15990     },
15991
15992     onItemClick : function(item, index, e)
15993     {
15994         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15995             return false;
15996         }
15997         if (this.toggleSelect) {
15998             var m = this.isSelected(item) ? 'unselect' : 'select';
15999             //Roo.log(m);
16000             var _t = this;
16001             _t[m](item, true, false);
16002             return true;
16003         }
16004         if(this.multiSelect || this.singleSelect){
16005             if(this.multiSelect && e.shiftKey && this.lastSelection){
16006                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16007             }else{
16008                 this.select(item, this.multiSelect && e.ctrlKey);
16009                 this.lastSelection = item;
16010             }
16011             
16012             if(!this.tickable){
16013                 e.preventDefault();
16014             }
16015             
16016         }
16017         return true;
16018     },
16019
16020     /**
16021      * Get the number of selected nodes.
16022      * @return {Number}
16023      */
16024     getSelectionCount : function(){
16025         return this.selections.length;
16026     },
16027
16028     /**
16029      * Get the currently selected nodes.
16030      * @return {Array} An array of HTMLElements
16031      */
16032     getSelectedNodes : function(){
16033         return this.selections;
16034     },
16035
16036     /**
16037      * Get the indexes of the selected nodes.
16038      * @return {Array}
16039      */
16040     getSelectedIndexes : function(){
16041         var indexes = [], s = this.selections;
16042         for(var i = 0, len = s.length; i < len; i++){
16043             indexes.push(s[i].nodeIndex);
16044         }
16045         return indexes;
16046     },
16047
16048     /**
16049      * Clear all selections
16050      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16051      */
16052     clearSelections : function(suppressEvent){
16053         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16054             this.cmp.elements = this.selections;
16055             this.cmp.removeClass(this.selectedClass);
16056             this.selections = [];
16057             if(!suppressEvent){
16058                 this.fireEvent("selectionchange", this, this.selections);
16059             }
16060         }
16061     },
16062
16063     /**
16064      * Returns true if the passed node is selected
16065      * @param {HTMLElement/Number} node The node or node index
16066      * @return {Boolean}
16067      */
16068     isSelected : function(node){
16069         var s = this.selections;
16070         if(s.length < 1){
16071             return false;
16072         }
16073         node = this.getNode(node);
16074         return s.indexOf(node) !== -1;
16075     },
16076
16077     /**
16078      * Selects nodes.
16079      * @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
16080      * @param {Boolean} keepExisting (optional) true to keep existing selections
16081      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16082      */
16083     select : function(nodeInfo, keepExisting, suppressEvent){
16084         if(nodeInfo instanceof Array){
16085             if(!keepExisting){
16086                 this.clearSelections(true);
16087             }
16088             for(var i = 0, len = nodeInfo.length; i < len; i++){
16089                 this.select(nodeInfo[i], true, true);
16090             }
16091             return;
16092         } 
16093         var node = this.getNode(nodeInfo);
16094         if(!node || this.isSelected(node)){
16095             return; // already selected.
16096         }
16097         if(!keepExisting){
16098             this.clearSelections(true);
16099         }
16100         
16101         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16102             Roo.fly(node).addClass(this.selectedClass);
16103             this.selections.push(node);
16104             if(!suppressEvent){
16105                 this.fireEvent("selectionchange", this, this.selections);
16106             }
16107         }
16108         
16109         
16110     },
16111       /**
16112      * Unselects nodes.
16113      * @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
16114      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16115      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16116      */
16117     unselect : function(nodeInfo, keepExisting, suppressEvent)
16118     {
16119         if(nodeInfo instanceof Array){
16120             Roo.each(this.selections, function(s) {
16121                 this.unselect(s, nodeInfo);
16122             }, this);
16123             return;
16124         }
16125         var node = this.getNode(nodeInfo);
16126         if(!node || !this.isSelected(node)){
16127             //Roo.log("not selected");
16128             return; // not selected.
16129         }
16130         // fireevent???
16131         var ns = [];
16132         Roo.each(this.selections, function(s) {
16133             if (s == node ) {
16134                 Roo.fly(node).removeClass(this.selectedClass);
16135
16136                 return;
16137             }
16138             ns.push(s);
16139         },this);
16140         
16141         this.selections= ns;
16142         this.fireEvent("selectionchange", this, this.selections);
16143     },
16144
16145     /**
16146      * Gets a template node.
16147      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16148      * @return {HTMLElement} The node or null if it wasn't found
16149      */
16150     getNode : function(nodeInfo){
16151         if(typeof nodeInfo == "string"){
16152             return document.getElementById(nodeInfo);
16153         }else if(typeof nodeInfo == "number"){
16154             return this.nodes[nodeInfo];
16155         }
16156         return nodeInfo;
16157     },
16158
16159     /**
16160      * Gets a range template nodes.
16161      * @param {Number} startIndex
16162      * @param {Number} endIndex
16163      * @return {Array} An array of nodes
16164      */
16165     getNodes : function(start, end){
16166         var ns = this.nodes;
16167         start = start || 0;
16168         end = typeof end == "undefined" ? ns.length - 1 : end;
16169         var nodes = [];
16170         if(start <= end){
16171             for(var i = start; i <= end; i++){
16172                 nodes.push(ns[i]);
16173             }
16174         } else{
16175             for(var i = start; i >= end; i--){
16176                 nodes.push(ns[i]);
16177             }
16178         }
16179         return nodes;
16180     },
16181
16182     /**
16183      * Finds the index of the passed node
16184      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16185      * @return {Number} The index of the node or -1
16186      */
16187     indexOf : function(node){
16188         node = this.getNode(node);
16189         if(typeof node.nodeIndex == "number"){
16190             return node.nodeIndex;
16191         }
16192         var ns = this.nodes;
16193         for(var i = 0, len = ns.length; i < len; i++){
16194             if(ns[i] == node){
16195                 return i;
16196             }
16197         }
16198         return -1;
16199     }
16200 });
16201 /*
16202  * - LGPL
16203  *
16204  * based on jquery fullcalendar
16205  * 
16206  */
16207
16208 Roo.bootstrap = Roo.bootstrap || {};
16209 /**
16210  * @class Roo.bootstrap.Calendar
16211  * @extends Roo.bootstrap.Component
16212  * Bootstrap Calendar class
16213  * @cfg {Boolean} loadMask (true|false) default false
16214  * @cfg {Object} header generate the user specific header of the calendar, default false
16215
16216  * @constructor
16217  * Create a new Container
16218  * @param {Object} config The config object
16219  */
16220
16221
16222
16223 Roo.bootstrap.Calendar = function(config){
16224     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16225      this.addEvents({
16226         /**
16227              * @event select
16228              * Fires when a date is selected
16229              * @param {DatePicker} this
16230              * @param {Date} date The selected date
16231              */
16232         'select': true,
16233         /**
16234              * @event monthchange
16235              * Fires when the displayed month changes 
16236              * @param {DatePicker} this
16237              * @param {Date} date The selected month
16238              */
16239         'monthchange': true,
16240         /**
16241              * @event evententer
16242              * Fires when mouse over an event
16243              * @param {Calendar} this
16244              * @param {event} Event
16245              */
16246         'evententer': true,
16247         /**
16248              * @event eventleave
16249              * Fires when the mouse leaves an
16250              * @param {Calendar} this
16251              * @param {event}
16252              */
16253         'eventleave': true,
16254         /**
16255              * @event eventclick
16256              * Fires when the mouse click an
16257              * @param {Calendar} this
16258              * @param {event}
16259              */
16260         'eventclick': true
16261         
16262     });
16263
16264 };
16265
16266 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16267     
16268      /**
16269      * @cfg {Number} startDay
16270      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16271      */
16272     startDay : 0,
16273     
16274     loadMask : false,
16275     
16276     header : false,
16277       
16278     getAutoCreate : function(){
16279         
16280         
16281         var fc_button = function(name, corner, style, content ) {
16282             return Roo.apply({},{
16283                 tag : 'span',
16284                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16285                          (corner.length ?
16286                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16287                             ''
16288                         ),
16289                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16290                 unselectable: 'on'
16291             });
16292         };
16293         
16294         var header = {};
16295         
16296         if(!this.header){
16297             header = {
16298                 tag : 'table',
16299                 cls : 'fc-header',
16300                 style : 'width:100%',
16301                 cn : [
16302                     {
16303                         tag: 'tr',
16304                         cn : [
16305                             {
16306                                 tag : 'td',
16307                                 cls : 'fc-header-left',
16308                                 cn : [
16309                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16310                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16311                                     { tag: 'span', cls: 'fc-header-space' },
16312                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16313
16314
16315                                 ]
16316                             },
16317
16318                             {
16319                                 tag : 'td',
16320                                 cls : 'fc-header-center',
16321                                 cn : [
16322                                     {
16323                                         tag: 'span',
16324                                         cls: 'fc-header-title',
16325                                         cn : {
16326                                             tag: 'H2',
16327                                             html : 'month / year'
16328                                         }
16329                                     }
16330
16331                                 ]
16332                             },
16333                             {
16334                                 tag : 'td',
16335                                 cls : 'fc-header-right',
16336                                 cn : [
16337                               /*      fc_button('month', 'left', '', 'month' ),
16338                                     fc_button('week', '', '', 'week' ),
16339                                     fc_button('day', 'right', '', 'day' )
16340                                 */    
16341
16342                                 ]
16343                             }
16344
16345                         ]
16346                     }
16347                 ]
16348             };
16349         }
16350         
16351         header = this.header;
16352         
16353        
16354         var cal_heads = function() {
16355             var ret = [];
16356             // fixme - handle this.
16357             
16358             for (var i =0; i < Date.dayNames.length; i++) {
16359                 var d = Date.dayNames[i];
16360                 ret.push({
16361                     tag: 'th',
16362                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16363                     html : d.substring(0,3)
16364                 });
16365                 
16366             }
16367             ret[0].cls += ' fc-first';
16368             ret[6].cls += ' fc-last';
16369             return ret;
16370         };
16371         var cal_cell = function(n) {
16372             return  {
16373                 tag: 'td',
16374                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16375                 cn : [
16376                     {
16377                         cn : [
16378                             {
16379                                 cls: 'fc-day-number',
16380                                 html: 'D'
16381                             },
16382                             {
16383                                 cls: 'fc-day-content',
16384                              
16385                                 cn : [
16386                                      {
16387                                         style: 'position: relative;' // height: 17px;
16388                                     }
16389                                 ]
16390                             }
16391                             
16392                             
16393                         ]
16394                     }
16395                 ]
16396                 
16397             }
16398         };
16399         var cal_rows = function() {
16400             
16401             var ret = [];
16402             for (var r = 0; r < 6; r++) {
16403                 var row= {
16404                     tag : 'tr',
16405                     cls : 'fc-week',
16406                     cn : []
16407                 };
16408                 
16409                 for (var i =0; i < Date.dayNames.length; i++) {
16410                     var d = Date.dayNames[i];
16411                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16412
16413                 }
16414                 row.cn[0].cls+=' fc-first';
16415                 row.cn[0].cn[0].style = 'min-height:90px';
16416                 row.cn[6].cls+=' fc-last';
16417                 ret.push(row);
16418                 
16419             }
16420             ret[0].cls += ' fc-first';
16421             ret[4].cls += ' fc-prev-last';
16422             ret[5].cls += ' fc-last';
16423             return ret;
16424             
16425         };
16426         
16427         var cal_table = {
16428             tag: 'table',
16429             cls: 'fc-border-separate',
16430             style : 'width:100%',
16431             cellspacing  : 0,
16432             cn : [
16433                 { 
16434                     tag: 'thead',
16435                     cn : [
16436                         { 
16437                             tag: 'tr',
16438                             cls : 'fc-first fc-last',
16439                             cn : cal_heads()
16440                         }
16441                     ]
16442                 },
16443                 { 
16444                     tag: 'tbody',
16445                     cn : cal_rows()
16446                 }
16447                   
16448             ]
16449         };
16450          
16451          var cfg = {
16452             cls : 'fc fc-ltr',
16453             cn : [
16454                 header,
16455                 {
16456                     cls : 'fc-content',
16457                     style : "position: relative;",
16458                     cn : [
16459                         {
16460                             cls : 'fc-view fc-view-month fc-grid',
16461                             style : 'position: relative',
16462                             unselectable : 'on',
16463                             cn : [
16464                                 {
16465                                     cls : 'fc-event-container',
16466                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16467                                 },
16468                                 cal_table
16469                             ]
16470                         }
16471                     ]
16472     
16473                 }
16474            ] 
16475             
16476         };
16477         
16478          
16479         
16480         return cfg;
16481     },
16482     
16483     
16484     initEvents : function()
16485     {
16486         if(!this.store){
16487             throw "can not find store for calendar";
16488         }
16489         
16490         var mark = {
16491             tag: "div",
16492             cls:"x-dlg-mask",
16493             style: "text-align:center",
16494             cn: [
16495                 {
16496                     tag: "div",
16497                     style: "background-color:white;width:50%;margin:250 auto",
16498                     cn: [
16499                         {
16500                             tag: "img",
16501                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16502                         },
16503                         {
16504                             tag: "span",
16505                             html: "Loading"
16506                         }
16507                         
16508                     ]
16509                 }
16510             ]
16511         };
16512         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16513         
16514         var size = this.el.select('.fc-content', true).first().getSize();
16515         this.maskEl.setSize(size.width, size.height);
16516         this.maskEl.enableDisplayMode("block");
16517         if(!this.loadMask){
16518             this.maskEl.hide();
16519         }
16520         
16521         this.store = Roo.factory(this.store, Roo.data);
16522         this.store.on('load', this.onLoad, this);
16523         this.store.on('beforeload', this.onBeforeLoad, this);
16524         
16525         this.resize();
16526         
16527         this.cells = this.el.select('.fc-day',true);
16528         //Roo.log(this.cells);
16529         this.textNodes = this.el.query('.fc-day-number');
16530         this.cells.addClassOnOver('fc-state-hover');
16531         
16532         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16533         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16534         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16535         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16536         
16537         this.on('monthchange', this.onMonthChange, this);
16538         
16539         this.update(new Date().clearTime());
16540     },
16541     
16542     resize : function() {
16543         var sz  = this.el.getSize();
16544         
16545         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16546         this.el.select('.fc-day-content div',true).setHeight(34);
16547     },
16548     
16549     
16550     // private
16551     showPrevMonth : function(e){
16552         this.update(this.activeDate.add("mo", -1));
16553     },
16554     showToday : function(e){
16555         this.update(new Date().clearTime());
16556     },
16557     // private
16558     showNextMonth : function(e){
16559         this.update(this.activeDate.add("mo", 1));
16560     },
16561
16562     // private
16563     showPrevYear : function(){
16564         this.update(this.activeDate.add("y", -1));
16565     },
16566
16567     // private
16568     showNextYear : function(){
16569         this.update(this.activeDate.add("y", 1));
16570     },
16571
16572     
16573    // private
16574     update : function(date)
16575     {
16576         var vd = this.activeDate;
16577         this.activeDate = date;
16578 //        if(vd && this.el){
16579 //            var t = date.getTime();
16580 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16581 //                Roo.log('using add remove');
16582 //                
16583 //                this.fireEvent('monthchange', this, date);
16584 //                
16585 //                this.cells.removeClass("fc-state-highlight");
16586 //                this.cells.each(function(c){
16587 //                   if(c.dateValue == t){
16588 //                       c.addClass("fc-state-highlight");
16589 //                       setTimeout(function(){
16590 //                            try{c.dom.firstChild.focus();}catch(e){}
16591 //                       }, 50);
16592 //                       return false;
16593 //                   }
16594 //                   return true;
16595 //                });
16596 //                return;
16597 //            }
16598 //        }
16599         
16600         var days = date.getDaysInMonth();
16601         
16602         var firstOfMonth = date.getFirstDateOfMonth();
16603         var startingPos = firstOfMonth.getDay()-this.startDay;
16604         
16605         if(startingPos < this.startDay){
16606             startingPos += 7;
16607         }
16608         
16609         var pm = date.add(Date.MONTH, -1);
16610         var prevStart = pm.getDaysInMonth()-startingPos;
16611 //        
16612         this.cells = this.el.select('.fc-day',true);
16613         this.textNodes = this.el.query('.fc-day-number');
16614         this.cells.addClassOnOver('fc-state-hover');
16615         
16616         var cells = this.cells.elements;
16617         var textEls = this.textNodes;
16618         
16619         Roo.each(cells, function(cell){
16620             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16621         });
16622         
16623         days += startingPos;
16624
16625         // convert everything to numbers so it's fast
16626         var day = 86400000;
16627         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16628         //Roo.log(d);
16629         //Roo.log(pm);
16630         //Roo.log(prevStart);
16631         
16632         var today = new Date().clearTime().getTime();
16633         var sel = date.clearTime().getTime();
16634         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16635         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16636         var ddMatch = this.disabledDatesRE;
16637         var ddText = this.disabledDatesText;
16638         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16639         var ddaysText = this.disabledDaysText;
16640         var format = this.format;
16641         
16642         var setCellClass = function(cal, cell){
16643             cell.row = 0;
16644             cell.events = [];
16645             cell.more = [];
16646             //Roo.log('set Cell Class');
16647             cell.title = "";
16648             var t = d.getTime();
16649             
16650             //Roo.log(d);
16651             
16652             cell.dateValue = t;
16653             if(t == today){
16654                 cell.className += " fc-today";
16655                 cell.className += " fc-state-highlight";
16656                 cell.title = cal.todayText;
16657             }
16658             if(t == sel){
16659                 // disable highlight in other month..
16660                 //cell.className += " fc-state-highlight";
16661                 
16662             }
16663             // disabling
16664             if(t < min) {
16665                 cell.className = " fc-state-disabled";
16666                 cell.title = cal.minText;
16667                 return;
16668             }
16669             if(t > max) {
16670                 cell.className = " fc-state-disabled";
16671                 cell.title = cal.maxText;
16672                 return;
16673             }
16674             if(ddays){
16675                 if(ddays.indexOf(d.getDay()) != -1){
16676                     cell.title = ddaysText;
16677                     cell.className = " fc-state-disabled";
16678                 }
16679             }
16680             if(ddMatch && format){
16681                 var fvalue = d.dateFormat(format);
16682                 if(ddMatch.test(fvalue)){
16683                     cell.title = ddText.replace("%0", fvalue);
16684                     cell.className = " fc-state-disabled";
16685                 }
16686             }
16687             
16688             if (!cell.initialClassName) {
16689                 cell.initialClassName = cell.dom.className;
16690             }
16691             
16692             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16693         };
16694
16695         var i = 0;
16696         
16697         for(; i < startingPos; i++) {
16698             textEls[i].innerHTML = (++prevStart);
16699             d.setDate(d.getDate()+1);
16700             
16701             cells[i].className = "fc-past fc-other-month";
16702             setCellClass(this, cells[i]);
16703         }
16704         
16705         var intDay = 0;
16706         
16707         for(; i < days; i++){
16708             intDay = i - startingPos + 1;
16709             textEls[i].innerHTML = (intDay);
16710             d.setDate(d.getDate()+1);
16711             
16712             cells[i].className = ''; // "x-date-active";
16713             setCellClass(this, cells[i]);
16714         }
16715         var extraDays = 0;
16716         
16717         for(; i < 42; i++) {
16718             textEls[i].innerHTML = (++extraDays);
16719             d.setDate(d.getDate()+1);
16720             
16721             cells[i].className = "fc-future fc-other-month";
16722             setCellClass(this, cells[i]);
16723         }
16724         
16725         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16726         
16727         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16728         
16729         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16730         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16731         
16732         if(totalRows != 6){
16733             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16734             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16735         }
16736         
16737         this.fireEvent('monthchange', this, date);
16738         
16739         
16740         /*
16741         if(!this.internalRender){
16742             var main = this.el.dom.firstChild;
16743             var w = main.offsetWidth;
16744             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16745             Roo.fly(main).setWidth(w);
16746             this.internalRender = true;
16747             // opera does not respect the auto grow header center column
16748             // then, after it gets a width opera refuses to recalculate
16749             // without a second pass
16750             if(Roo.isOpera && !this.secondPass){
16751                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16752                 this.secondPass = true;
16753                 this.update.defer(10, this, [date]);
16754             }
16755         }
16756         */
16757         
16758     },
16759     
16760     findCell : function(dt) {
16761         dt = dt.clearTime().getTime();
16762         var ret = false;
16763         this.cells.each(function(c){
16764             //Roo.log("check " +c.dateValue + '?=' + dt);
16765             if(c.dateValue == dt){
16766                 ret = c;
16767                 return false;
16768             }
16769             return true;
16770         });
16771         
16772         return ret;
16773     },
16774     
16775     findCells : function(ev) {
16776         var s = ev.start.clone().clearTime().getTime();
16777        // Roo.log(s);
16778         var e= ev.end.clone().clearTime().getTime();
16779        // Roo.log(e);
16780         var ret = [];
16781         this.cells.each(function(c){
16782              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16783             
16784             if(c.dateValue > e){
16785                 return ;
16786             }
16787             if(c.dateValue < s){
16788                 return ;
16789             }
16790             ret.push(c);
16791         });
16792         
16793         return ret;    
16794     },
16795     
16796 //    findBestRow: function(cells)
16797 //    {
16798 //        var ret = 0;
16799 //        
16800 //        for (var i =0 ; i < cells.length;i++) {
16801 //            ret  = Math.max(cells[i].rows || 0,ret);
16802 //        }
16803 //        return ret;
16804 //        
16805 //    },
16806     
16807     
16808     addItem : function(ev)
16809     {
16810         // look for vertical location slot in
16811         var cells = this.findCells(ev);
16812         
16813 //        ev.row = this.findBestRow(cells);
16814         
16815         // work out the location.
16816         
16817         var crow = false;
16818         var rows = [];
16819         for(var i =0; i < cells.length; i++) {
16820             
16821             cells[i].row = cells[0].row;
16822             
16823             if(i == 0){
16824                 cells[i].row = cells[i].row + 1;
16825             }
16826             
16827             if (!crow) {
16828                 crow = {
16829                     start : cells[i],
16830                     end :  cells[i]
16831                 };
16832                 continue;
16833             }
16834             if (crow.start.getY() == cells[i].getY()) {
16835                 // on same row.
16836                 crow.end = cells[i];
16837                 continue;
16838             }
16839             // different row.
16840             rows.push(crow);
16841             crow = {
16842                 start: cells[i],
16843                 end : cells[i]
16844             };
16845             
16846         }
16847         
16848         rows.push(crow);
16849         ev.els = [];
16850         ev.rows = rows;
16851         ev.cells = cells;
16852         
16853         cells[0].events.push(ev);
16854         
16855         this.calevents.push(ev);
16856     },
16857     
16858     clearEvents: function() {
16859         
16860         if(!this.calevents){
16861             return;
16862         }
16863         
16864         Roo.each(this.cells.elements, function(c){
16865             c.row = 0;
16866             c.events = [];
16867             c.more = [];
16868         });
16869         
16870         Roo.each(this.calevents, function(e) {
16871             Roo.each(e.els, function(el) {
16872                 el.un('mouseenter' ,this.onEventEnter, this);
16873                 el.un('mouseleave' ,this.onEventLeave, this);
16874                 el.remove();
16875             },this);
16876         },this);
16877         
16878         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16879             e.remove();
16880         });
16881         
16882     },
16883     
16884     renderEvents: function()
16885     {   
16886         var _this = this;
16887         
16888         this.cells.each(function(c) {
16889             
16890             if(c.row < 5){
16891                 return;
16892             }
16893             
16894             var ev = c.events;
16895             
16896             var r = 4;
16897             if(c.row != c.events.length){
16898                 r = 4 - (4 - (c.row - c.events.length));
16899             }
16900             
16901             c.events = ev.slice(0, r);
16902             c.more = ev.slice(r);
16903             
16904             if(c.more.length && c.more.length == 1){
16905                 c.events.push(c.more.pop());
16906             }
16907             
16908             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16909             
16910         });
16911             
16912         this.cells.each(function(c) {
16913             
16914             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16915             
16916             
16917             for (var e = 0; e < c.events.length; e++){
16918                 var ev = c.events[e];
16919                 var rows = ev.rows;
16920                 
16921                 for(var i = 0; i < rows.length; i++) {
16922                 
16923                     // how many rows should it span..
16924
16925                     var  cfg = {
16926                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16927                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16928
16929                         unselectable : "on",
16930                         cn : [
16931                             {
16932                                 cls: 'fc-event-inner',
16933                                 cn : [
16934     //                                {
16935     //                                  tag:'span',
16936     //                                  cls: 'fc-event-time',
16937     //                                  html : cells.length > 1 ? '' : ev.time
16938     //                                },
16939                                     {
16940                                       tag:'span',
16941                                       cls: 'fc-event-title',
16942                                       html : String.format('{0}', ev.title)
16943                                     }
16944
16945
16946                                 ]
16947                             },
16948                             {
16949                                 cls: 'ui-resizable-handle ui-resizable-e',
16950                                 html : '&nbsp;&nbsp;&nbsp'
16951                             }
16952
16953                         ]
16954                     };
16955
16956                     if (i == 0) {
16957                         cfg.cls += ' fc-event-start';
16958                     }
16959                     if ((i+1) == rows.length) {
16960                         cfg.cls += ' fc-event-end';
16961                     }
16962
16963                     var ctr = _this.el.select('.fc-event-container',true).first();
16964                     var cg = ctr.createChild(cfg);
16965
16966                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16967                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16968
16969                     var r = (c.more.length) ? 1 : 0;
16970                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16971                     cg.setWidth(ebox.right - sbox.x -2);
16972
16973                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16974                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16975                     cg.on('click', _this.onEventClick, _this, ev);
16976
16977                     ev.els.push(cg);
16978                     
16979                 }
16980                 
16981             }
16982             
16983             
16984             if(c.more.length){
16985                 var  cfg = {
16986                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16987                     style : 'position: absolute',
16988                     unselectable : "on",
16989                     cn : [
16990                         {
16991                             cls: 'fc-event-inner',
16992                             cn : [
16993                                 {
16994                                   tag:'span',
16995                                   cls: 'fc-event-title',
16996                                   html : 'More'
16997                                 }
16998
16999
17000                             ]
17001                         },
17002                         {
17003                             cls: 'ui-resizable-handle ui-resizable-e',
17004                             html : '&nbsp;&nbsp;&nbsp'
17005                         }
17006
17007                     ]
17008                 };
17009
17010                 var ctr = _this.el.select('.fc-event-container',true).first();
17011                 var cg = ctr.createChild(cfg);
17012
17013                 var sbox = c.select('.fc-day-content',true).first().getBox();
17014                 var ebox = c.select('.fc-day-content',true).first().getBox();
17015                 //Roo.log(cg);
17016                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17017                 cg.setWidth(ebox.right - sbox.x -2);
17018
17019                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17020                 
17021             }
17022             
17023         });
17024         
17025         
17026         
17027     },
17028     
17029     onEventEnter: function (e, el,event,d) {
17030         this.fireEvent('evententer', this, el, event);
17031     },
17032     
17033     onEventLeave: function (e, el,event,d) {
17034         this.fireEvent('eventleave', this, el, event);
17035     },
17036     
17037     onEventClick: function (e, el,event,d) {
17038         this.fireEvent('eventclick', this, el, event);
17039     },
17040     
17041     onMonthChange: function () {
17042         this.store.load();
17043     },
17044     
17045     onMoreEventClick: function(e, el, more)
17046     {
17047         var _this = this;
17048         
17049         this.calpopover.placement = 'right';
17050         this.calpopover.setTitle('More');
17051         
17052         this.calpopover.setContent('');
17053         
17054         var ctr = this.calpopover.el.select('.popover-content', true).first();
17055         
17056         Roo.each(more, function(m){
17057             var cfg = {
17058                 cls : 'fc-event-hori fc-event-draggable',
17059                 html : m.title
17060             };
17061             var cg = ctr.createChild(cfg);
17062             
17063             cg.on('click', _this.onEventClick, _this, m);
17064         });
17065         
17066         this.calpopover.show(el);
17067         
17068         
17069     },
17070     
17071     onLoad: function () 
17072     {   
17073         this.calevents = [];
17074         var cal = this;
17075         
17076         if(this.store.getCount() > 0){
17077             this.store.data.each(function(d){
17078                cal.addItem({
17079                     id : d.data.id,
17080                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17081                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17082                     time : d.data.start_time,
17083                     title : d.data.title,
17084                     description : d.data.description,
17085                     venue : d.data.venue
17086                 });
17087             });
17088         }
17089         
17090         this.renderEvents();
17091         
17092         if(this.calevents.length && this.loadMask){
17093             this.maskEl.hide();
17094         }
17095     },
17096     
17097     onBeforeLoad: function()
17098     {
17099         this.clearEvents();
17100         if(this.loadMask){
17101             this.maskEl.show();
17102         }
17103     }
17104 });
17105
17106  
17107  /*
17108  * - LGPL
17109  *
17110  * element
17111  * 
17112  */
17113
17114 /**
17115  * @class Roo.bootstrap.Popover
17116  * @extends Roo.bootstrap.Component
17117  * Bootstrap Popover class
17118  * @cfg {String} html contents of the popover   (or false to use children..)
17119  * @cfg {String} title of popover (or false to hide)
17120  * @cfg {String} placement how it is placed
17121  * @cfg {String} trigger click || hover (or false to trigger manually)
17122  * @cfg {String} over what (parent or false to trigger manually.)
17123  * @cfg {Number} delay - delay before showing
17124  
17125  * @constructor
17126  * Create a new Popover
17127  * @param {Object} config The config object
17128  */
17129
17130 Roo.bootstrap.Popover = function(config){
17131     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17132     
17133     this.addEvents({
17134         // raw events
17135          /**
17136          * @event show
17137          * After the popover show
17138          * 
17139          * @param {Roo.bootstrap.Popover} this
17140          */
17141         "show" : true,
17142         /**
17143          * @event hide
17144          * After the popover hide
17145          * 
17146          * @param {Roo.bootstrap.Popover} this
17147          */
17148         "hide" : true
17149     });
17150 };
17151
17152 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17153     
17154     title: 'Fill in a title',
17155     html: false,
17156     
17157     placement : 'right',
17158     trigger : 'hover', // hover
17159     
17160     delay : 0,
17161     
17162     over: 'parent',
17163     
17164     can_build_overlaid : false,
17165     
17166     getChildContainer : function()
17167     {
17168         return this.el.select('.popover-content',true).first();
17169     },
17170     
17171     getAutoCreate : function(){
17172          
17173         var cfg = {
17174            cls : 'popover roo-dynamic',
17175            style: 'display:block',
17176            cn : [
17177                 {
17178                     cls : 'arrow'
17179                 },
17180                 {
17181                     cls : 'popover-inner',
17182                     cn : [
17183                         {
17184                             tag: 'h3',
17185                             cls: 'popover-title',
17186                             html : this.title
17187                         },
17188                         {
17189                             cls : 'popover-content',
17190                             html : this.html
17191                         }
17192                     ]
17193                     
17194                 }
17195            ]
17196         };
17197         
17198         return cfg;
17199     },
17200     setTitle: function(str)
17201     {
17202         this.title = str;
17203         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17204     },
17205     setContent: function(str)
17206     {
17207         this.html = str;
17208         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17209     },
17210     // as it get's added to the bottom of the page.
17211     onRender : function(ct, position)
17212     {
17213         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17214         if(!this.el){
17215             var cfg = Roo.apply({},  this.getAutoCreate());
17216             cfg.id = Roo.id();
17217             
17218             if (this.cls) {
17219                 cfg.cls += ' ' + this.cls;
17220             }
17221             if (this.style) {
17222                 cfg.style = this.style;
17223             }
17224             //Roo.log("adding to ");
17225             this.el = Roo.get(document.body).createChild(cfg, position);
17226 //            Roo.log(this.el);
17227         }
17228         this.initEvents();
17229     },
17230     
17231     initEvents : function()
17232     {
17233         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17234         this.el.enableDisplayMode('block');
17235         this.el.hide();
17236         if (this.over === false) {
17237             return; 
17238         }
17239         if (this.triggers === false) {
17240             return;
17241         }
17242         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17243         var triggers = this.trigger ? this.trigger.split(' ') : [];
17244         Roo.each(triggers, function(trigger) {
17245         
17246             if (trigger == 'click') {
17247                 on_el.on('click', this.toggle, this);
17248             } else if (trigger != 'manual') {
17249                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17250                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17251       
17252                 on_el.on(eventIn  ,this.enter, this);
17253                 on_el.on(eventOut, this.leave, this);
17254             }
17255         }, this);
17256         
17257     },
17258     
17259     
17260     // private
17261     timeout : null,
17262     hoverState : null,
17263     
17264     toggle : function () {
17265         this.hoverState == 'in' ? this.leave() : this.enter();
17266     },
17267     
17268     enter : function () {
17269         
17270         clearTimeout(this.timeout);
17271     
17272         this.hoverState = 'in';
17273     
17274         if (!this.delay || !this.delay.show) {
17275             this.show();
17276             return;
17277         }
17278         var _t = this;
17279         this.timeout = setTimeout(function () {
17280             if (_t.hoverState == 'in') {
17281                 _t.show();
17282             }
17283         }, this.delay.show)
17284     },
17285     
17286     leave : function() {
17287         clearTimeout(this.timeout);
17288     
17289         this.hoverState = 'out';
17290     
17291         if (!this.delay || !this.delay.hide) {
17292             this.hide();
17293             return;
17294         }
17295         var _t = this;
17296         this.timeout = setTimeout(function () {
17297             if (_t.hoverState == 'out') {
17298                 _t.hide();
17299             }
17300         }, this.delay.hide)
17301     },
17302     
17303     show : function (on_el)
17304     {
17305         if (!on_el) {
17306             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17307         }
17308         
17309         // set content.
17310         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17311         if (this.html !== false) {
17312             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17313         }
17314         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17315         if (!this.title.length) {
17316             this.el.select('.popover-title',true).hide();
17317         }
17318         
17319         var placement = typeof this.placement == 'function' ?
17320             this.placement.call(this, this.el, on_el) :
17321             this.placement;
17322             
17323         var autoToken = /\s?auto?\s?/i;
17324         var autoPlace = autoToken.test(placement);
17325         if (autoPlace) {
17326             placement = placement.replace(autoToken, '') || 'top';
17327         }
17328         
17329         //this.el.detach()
17330         //this.el.setXY([0,0]);
17331         this.el.show();
17332         this.el.dom.style.display='block';
17333         this.el.addClass(placement);
17334         
17335         //this.el.appendTo(on_el);
17336         
17337         var p = this.getPosition();
17338         var box = this.el.getBox();
17339         
17340         if (autoPlace) {
17341             // fixme..
17342         }
17343         var align = Roo.bootstrap.Popover.alignment[placement];
17344         
17345 //        Roo.log(align);
17346         this.el.alignTo(on_el, align[0],align[1]);
17347         //var arrow = this.el.select('.arrow',true).first();
17348         //arrow.set(align[2], 
17349         
17350         this.el.addClass('in');
17351         
17352         
17353         if (this.el.hasClass('fade')) {
17354             // fade it?
17355         }
17356         
17357         this.hoverState = 'in';
17358         
17359         this.fireEvent('show', this);
17360         
17361     },
17362     hide : function()
17363     {
17364         this.el.setXY([0,0]);
17365         this.el.removeClass('in');
17366         this.el.hide();
17367         this.hoverState = null;
17368         
17369         this.fireEvent('hide', this);
17370     }
17371     
17372 });
17373
17374 Roo.bootstrap.Popover.alignment = {
17375     'left' : ['r-l', [-10,0], 'right'],
17376     'right' : ['l-r', [10,0], 'left'],
17377     'bottom' : ['t-b', [0,10], 'top'],
17378     'top' : [ 'b-t', [0,-10], 'bottom']
17379 };
17380
17381  /*
17382  * - LGPL
17383  *
17384  * Progress
17385  * 
17386  */
17387
17388 /**
17389  * @class Roo.bootstrap.Progress
17390  * @extends Roo.bootstrap.Component
17391  * Bootstrap Progress class
17392  * @cfg {Boolean} striped striped of the progress bar
17393  * @cfg {Boolean} active animated of the progress bar
17394  * 
17395  * 
17396  * @constructor
17397  * Create a new Progress
17398  * @param {Object} config The config object
17399  */
17400
17401 Roo.bootstrap.Progress = function(config){
17402     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17403 };
17404
17405 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17406     
17407     striped : false,
17408     active: false,
17409     
17410     getAutoCreate : function(){
17411         var cfg = {
17412             tag: 'div',
17413             cls: 'progress'
17414         };
17415         
17416         
17417         if(this.striped){
17418             cfg.cls += ' progress-striped';
17419         }
17420       
17421         if(this.active){
17422             cfg.cls += ' active';
17423         }
17424         
17425         
17426         return cfg;
17427     }
17428    
17429 });
17430
17431  
17432
17433  /*
17434  * - LGPL
17435  *
17436  * ProgressBar
17437  * 
17438  */
17439
17440 /**
17441  * @class Roo.bootstrap.ProgressBar
17442  * @extends Roo.bootstrap.Component
17443  * Bootstrap ProgressBar class
17444  * @cfg {Number} aria_valuenow aria-value now
17445  * @cfg {Number} aria_valuemin aria-value min
17446  * @cfg {Number} aria_valuemax aria-value max
17447  * @cfg {String} label label for the progress bar
17448  * @cfg {String} panel (success | info | warning | danger )
17449  * @cfg {String} role role of the progress bar
17450  * @cfg {String} sr_only text
17451  * 
17452  * 
17453  * @constructor
17454  * Create a new ProgressBar
17455  * @param {Object} config The config object
17456  */
17457
17458 Roo.bootstrap.ProgressBar = function(config){
17459     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17460 };
17461
17462 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17463     
17464     aria_valuenow : 0,
17465     aria_valuemin : 0,
17466     aria_valuemax : 100,
17467     label : false,
17468     panel : false,
17469     role : false,
17470     sr_only: false,
17471     
17472     getAutoCreate : function()
17473     {
17474         
17475         var cfg = {
17476             tag: 'div',
17477             cls: 'progress-bar',
17478             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17479         };
17480         
17481         if(this.sr_only){
17482             cfg.cn = {
17483                 tag: 'span',
17484                 cls: 'sr-only',
17485                 html: this.sr_only
17486             }
17487         }
17488         
17489         if(this.role){
17490             cfg.role = this.role;
17491         }
17492         
17493         if(this.aria_valuenow){
17494             cfg['aria-valuenow'] = this.aria_valuenow;
17495         }
17496         
17497         if(this.aria_valuemin){
17498             cfg['aria-valuemin'] = this.aria_valuemin;
17499         }
17500         
17501         if(this.aria_valuemax){
17502             cfg['aria-valuemax'] = this.aria_valuemax;
17503         }
17504         
17505         if(this.label && !this.sr_only){
17506             cfg.html = this.label;
17507         }
17508         
17509         if(this.panel){
17510             cfg.cls += ' progress-bar-' + this.panel;
17511         }
17512         
17513         return cfg;
17514     },
17515     
17516     update : function(aria_valuenow)
17517     {
17518         this.aria_valuenow = aria_valuenow;
17519         
17520         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17521     }
17522    
17523 });
17524
17525  
17526
17527  /*
17528  * - LGPL
17529  *
17530  * column
17531  * 
17532  */
17533
17534 /**
17535  * @class Roo.bootstrap.TabGroup
17536  * @extends Roo.bootstrap.Column
17537  * Bootstrap Column class
17538  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17539  * @cfg {Boolean} carousel true to make the group behave like a carousel
17540  * @cfg {Boolean} bullets show bullets for the panels
17541  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17542  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17543  * @cfg {Boolean} showarrow (true|false) show arrow default true
17544  * 
17545  * @constructor
17546  * Create a new TabGroup
17547  * @param {Object} config The config object
17548  */
17549
17550 Roo.bootstrap.TabGroup = function(config){
17551     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17552     if (!this.navId) {
17553         this.navId = Roo.id();
17554     }
17555     this.tabs = [];
17556     Roo.bootstrap.TabGroup.register(this);
17557     
17558 };
17559
17560 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17561     
17562     carousel : false,
17563     transition : false,
17564     bullets : 0,
17565     timer : 0,
17566     autoslide : false,
17567     slideFn : false,
17568     slideOnTouch : false,
17569     showarrow : true,
17570     
17571     getAutoCreate : function()
17572     {
17573         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17574         
17575         cfg.cls += ' tab-content';
17576         
17577         if (this.carousel) {
17578             cfg.cls += ' carousel slide';
17579             
17580             cfg.cn = [{
17581                cls : 'carousel-inner',
17582                cn : []
17583             }];
17584         
17585             if(this.bullets  && !Roo.isTouch){
17586                 
17587                 var bullets = {
17588                     cls : 'carousel-bullets',
17589                     cn : []
17590                 };
17591                
17592                 if(this.bullets_cls){
17593                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17594                 }
17595                 
17596                 bullets.cn.push({
17597                     cls : 'clear'
17598                 });
17599                 
17600                 cfg.cn[0].cn.push(bullets);
17601             }
17602             
17603             if(this.showarrow){
17604                 cfg.cn[0].cn.push({
17605                     tag : 'div',
17606                     class : 'carousel-arrow',
17607                     cn : [
17608                         {
17609                             tag : 'div',
17610                             class : 'carousel-prev',
17611                             cn : [
17612                                 {
17613                                     tag : 'i',
17614                                     class : 'fa fa-chevron-left'
17615                                 }
17616                             ]
17617                         },
17618                         {
17619                             tag : 'div',
17620                             class : 'carousel-next',
17621                             cn : [
17622                                 {
17623                                     tag : 'i',
17624                                     class : 'fa fa-chevron-right'
17625                                 }
17626                             ]
17627                         }
17628                     ]
17629                 });
17630             }
17631             
17632         }
17633         
17634         return cfg;
17635     },
17636     
17637     initEvents:  function()
17638     {
17639 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17640 //            this.el.on("touchstart", this.onTouchStart, this);
17641 //        }
17642         
17643         if(this.autoslide){
17644             var _this = this;
17645             
17646             this.slideFn = window.setInterval(function() {
17647                 _this.showPanelNext();
17648             }, this.timer);
17649         }
17650         
17651         if(this.showarrow){
17652             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17653             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17654         }
17655         
17656         
17657     },
17658     
17659 //    onTouchStart : function(e, el, o)
17660 //    {
17661 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17662 //            return;
17663 //        }
17664 //        
17665 //        this.showPanelNext();
17666 //    },
17667     
17668     
17669     getChildContainer : function()
17670     {
17671         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17672     },
17673     
17674     /**
17675     * register a Navigation item
17676     * @param {Roo.bootstrap.NavItem} the navitem to add
17677     */
17678     register : function(item)
17679     {
17680         this.tabs.push( item);
17681         item.navId = this.navId; // not really needed..
17682         this.addBullet();
17683     
17684     },
17685     
17686     getActivePanel : function()
17687     {
17688         var r = false;
17689         Roo.each(this.tabs, function(t) {
17690             if (t.active) {
17691                 r = t;
17692                 return false;
17693             }
17694             return null;
17695         });
17696         return r;
17697         
17698     },
17699     getPanelByName : function(n)
17700     {
17701         var r = false;
17702         Roo.each(this.tabs, function(t) {
17703             if (t.tabId == n) {
17704                 r = t;
17705                 return false;
17706             }
17707             return null;
17708         });
17709         return r;
17710     },
17711     indexOfPanel : function(p)
17712     {
17713         var r = false;
17714         Roo.each(this.tabs, function(t,i) {
17715             if (t.tabId == p.tabId) {
17716                 r = i;
17717                 return false;
17718             }
17719             return null;
17720         });
17721         return r;
17722     },
17723     /**
17724      * show a specific panel
17725      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17726      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17727      */
17728     showPanel : function (pan)
17729     {
17730         if(this.transition || typeof(pan) == 'undefined'){
17731             Roo.log("waiting for the transitionend");
17732             return;
17733         }
17734         
17735         if (typeof(pan) == 'number') {
17736             pan = this.tabs[pan];
17737         }
17738         
17739         if (typeof(pan) == 'string') {
17740             pan = this.getPanelByName(pan);
17741         }
17742         
17743         var cur = this.getActivePanel();
17744         
17745         if(!pan || !cur){
17746             Roo.log('pan or acitve pan is undefined');
17747             return false;
17748         }
17749         
17750         if (pan.tabId == this.getActivePanel().tabId) {
17751             return true;
17752         }
17753         
17754         if (false === cur.fireEvent('beforedeactivate')) {
17755             return false;
17756         }
17757         
17758         if(this.bullets > 0 && !Roo.isTouch){
17759             this.setActiveBullet(this.indexOfPanel(pan));
17760         }
17761         
17762         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17763             
17764             this.transition = true;
17765             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17766             var lr = dir == 'next' ? 'left' : 'right';
17767             pan.el.addClass(dir); // or prev
17768             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17769             cur.el.addClass(lr); // or right
17770             pan.el.addClass(lr);
17771             
17772             var _this = this;
17773             cur.el.on('transitionend', function() {
17774                 Roo.log("trans end?");
17775                 
17776                 pan.el.removeClass([lr,dir]);
17777                 pan.setActive(true);
17778                 
17779                 cur.el.removeClass([lr]);
17780                 cur.setActive(false);
17781                 
17782                 _this.transition = false;
17783                 
17784             }, this, { single:  true } );
17785             
17786             return true;
17787         }
17788         
17789         cur.setActive(false);
17790         pan.setActive(true);
17791         
17792         return true;
17793         
17794     },
17795     showPanelNext : function()
17796     {
17797         var i = this.indexOfPanel(this.getActivePanel());
17798         
17799         if (i >= this.tabs.length - 1 && !this.autoslide) {
17800             return;
17801         }
17802         
17803         if (i >= this.tabs.length - 1 && this.autoslide) {
17804             i = -1;
17805         }
17806         
17807         this.showPanel(this.tabs[i+1]);
17808     },
17809     
17810     showPanelPrev : function()
17811     {
17812         var i = this.indexOfPanel(this.getActivePanel());
17813         
17814         if (i  < 1 && !this.autoslide) {
17815             return;
17816         }
17817         
17818         if (i < 1 && this.autoslide) {
17819             i = this.tabs.length;
17820         }
17821         
17822         this.showPanel(this.tabs[i-1]);
17823     },
17824     
17825     
17826     addBullet: function()
17827     {
17828         if(!this.bullets || Roo.isTouch){
17829             return;
17830         }
17831         var ctr = this.el.select('.carousel-bullets',true).first();
17832         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17833         var bullet = ctr.createChild({
17834             cls : 'bullet bullet-' + i
17835         },ctr.dom.lastChild);
17836         
17837         
17838         var _this = this;
17839         
17840         bullet.on('click', (function(e, el, o, ii, t){
17841
17842             e.preventDefault();
17843
17844             this.showPanel(ii);
17845
17846             if(this.autoslide && this.slideFn){
17847                 clearInterval(this.slideFn);
17848                 this.slideFn = window.setInterval(function() {
17849                     _this.showPanelNext();
17850                 }, this.timer);
17851             }
17852
17853         }).createDelegate(this, [i, bullet], true));
17854                 
17855         
17856     },
17857      
17858     setActiveBullet : function(i)
17859     {
17860         if(Roo.isTouch){
17861             return;
17862         }
17863         
17864         Roo.each(this.el.select('.bullet', true).elements, function(el){
17865             el.removeClass('selected');
17866         });
17867
17868         var bullet = this.el.select('.bullet-' + i, true).first();
17869         
17870         if(!bullet){
17871             return;
17872         }
17873         
17874         bullet.addClass('selected');
17875     }
17876     
17877     
17878   
17879 });
17880
17881  
17882
17883  
17884  
17885 Roo.apply(Roo.bootstrap.TabGroup, {
17886     
17887     groups: {},
17888      /**
17889     * register a Navigation Group
17890     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17891     */
17892     register : function(navgrp)
17893     {
17894         this.groups[navgrp.navId] = navgrp;
17895         
17896     },
17897     /**
17898     * fetch a Navigation Group based on the navigation ID
17899     * if one does not exist , it will get created.
17900     * @param {string} the navgroup to add
17901     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17902     */
17903     get: function(navId) {
17904         if (typeof(this.groups[navId]) == 'undefined') {
17905             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17906         }
17907         return this.groups[navId] ;
17908     }
17909     
17910     
17911     
17912 });
17913
17914  /*
17915  * - LGPL
17916  *
17917  * TabPanel
17918  * 
17919  */
17920
17921 /**
17922  * @class Roo.bootstrap.TabPanel
17923  * @extends Roo.bootstrap.Component
17924  * Bootstrap TabPanel class
17925  * @cfg {Boolean} active panel active
17926  * @cfg {String} html panel content
17927  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17928  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17929  * @cfg {String} href click to link..
17930  * 
17931  * 
17932  * @constructor
17933  * Create a new TabPanel
17934  * @param {Object} config The config object
17935  */
17936
17937 Roo.bootstrap.TabPanel = function(config){
17938     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17939     this.addEvents({
17940         /**
17941              * @event changed
17942              * Fires when the active status changes
17943              * @param {Roo.bootstrap.TabPanel} this
17944              * @param {Boolean} state the new state
17945             
17946          */
17947         'changed': true,
17948         /**
17949              * @event beforedeactivate
17950              * Fires before a tab is de-activated - can be used to do validation on a form.
17951              * @param {Roo.bootstrap.TabPanel} this
17952              * @return {Boolean} false if there is an error
17953             
17954          */
17955         'beforedeactivate': true
17956      });
17957     
17958     this.tabId = this.tabId || Roo.id();
17959   
17960 };
17961
17962 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17963     
17964     active: false,
17965     html: false,
17966     tabId: false,
17967     navId : false,
17968     href : '',
17969     
17970     getAutoCreate : function(){
17971         var cfg = {
17972             tag: 'div',
17973             // item is needed for carousel - not sure if it has any effect otherwise
17974             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17975             html: this.html || ''
17976         };
17977         
17978         if(this.active){
17979             cfg.cls += ' active';
17980         }
17981         
17982         if(this.tabId){
17983             cfg.tabId = this.tabId;
17984         }
17985         
17986         
17987         return cfg;
17988     },
17989     
17990     initEvents:  function()
17991     {
17992         var p = this.parent();
17993         
17994         this.navId = this.navId || p.navId;
17995         
17996         if (typeof(this.navId) != 'undefined') {
17997             // not really needed.. but just in case.. parent should be a NavGroup.
17998             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17999             
18000             tg.register(this);
18001             
18002             var i = tg.tabs.length - 1;
18003             
18004             if(this.active && tg.bullets > 0 && i < tg.bullets){
18005                 tg.setActiveBullet(i);
18006             }
18007         }
18008         
18009         this.el.on('click', this.onClick, this);
18010         
18011         if(Roo.isTouch){
18012             this.el.on("touchstart", this.onTouchStart, this);
18013             this.el.on("touchmove", this.onTouchMove, this);
18014             this.el.on("touchend", this.onTouchEnd, this);
18015         }
18016         
18017     },
18018     
18019     onRender : function(ct, position)
18020     {
18021         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18022     },
18023     
18024     setActive : function(state)
18025     {
18026         Roo.log("panel - set active " + this.tabId + "=" + state);
18027         
18028         this.active = state;
18029         if (!state) {
18030             this.el.removeClass('active');
18031             
18032         } else  if (!this.el.hasClass('active')) {
18033             this.el.addClass('active');
18034         }
18035         
18036         this.fireEvent('changed', this, state);
18037     },
18038     
18039     onClick : function(e)
18040     {
18041         e.preventDefault();
18042         
18043         if(!this.href.length){
18044             return;
18045         }
18046         
18047         window.location.href = this.href;
18048     },
18049     
18050     startX : 0,
18051     startY : 0,
18052     endX : 0,
18053     endY : 0,
18054     swiping : false,
18055     
18056     onTouchStart : function(e)
18057     {
18058         this.swiping = false;
18059         
18060         this.startX = e.browserEvent.touches[0].clientX;
18061         this.startY = e.browserEvent.touches[0].clientY;
18062     },
18063     
18064     onTouchMove : function(e)
18065     {
18066         this.swiping = true;
18067         
18068         this.endX = e.browserEvent.touches[0].clientX;
18069         this.endY = e.browserEvent.touches[0].clientY;
18070     },
18071     
18072     onTouchEnd : function(e)
18073     {
18074         if(!this.swiping){
18075             this.onClick(e);
18076             return;
18077         }
18078         
18079         var tabGroup = this.parent();
18080         
18081         if(this.endX > this.startX){ // swiping right
18082             tabGroup.showPanelPrev();
18083             return;
18084         }
18085         
18086         if(this.startX > this.endX){ // swiping left
18087             tabGroup.showPanelNext();
18088             return;
18089         }
18090     }
18091     
18092     
18093 });
18094  
18095
18096  
18097
18098  /*
18099  * - LGPL
18100  *
18101  * DateField
18102  * 
18103  */
18104
18105 /**
18106  * @class Roo.bootstrap.DateField
18107  * @extends Roo.bootstrap.Input
18108  * Bootstrap DateField class
18109  * @cfg {Number} weekStart default 0
18110  * @cfg {String} viewMode default empty, (months|years)
18111  * @cfg {String} minViewMode default empty, (months|years)
18112  * @cfg {Number} startDate default -Infinity
18113  * @cfg {Number} endDate default Infinity
18114  * @cfg {Boolean} todayHighlight default false
18115  * @cfg {Boolean} todayBtn default false
18116  * @cfg {Boolean} calendarWeeks default false
18117  * @cfg {Object} daysOfWeekDisabled default empty
18118  * @cfg {Boolean} singleMode default false (true | false)
18119  * 
18120  * @cfg {Boolean} keyboardNavigation default true
18121  * @cfg {String} language default en
18122  * 
18123  * @constructor
18124  * Create a new DateField
18125  * @param {Object} config The config object
18126  */
18127
18128 Roo.bootstrap.DateField = function(config){
18129     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18130      this.addEvents({
18131             /**
18132              * @event show
18133              * Fires when this field show.
18134              * @param {Roo.bootstrap.DateField} this
18135              * @param {Mixed} date The date value
18136              */
18137             show : true,
18138             /**
18139              * @event show
18140              * Fires when this field hide.
18141              * @param {Roo.bootstrap.DateField} this
18142              * @param {Mixed} date The date value
18143              */
18144             hide : true,
18145             /**
18146              * @event select
18147              * Fires when select a date.
18148              * @param {Roo.bootstrap.DateField} this
18149              * @param {Mixed} date The date value
18150              */
18151             select : true,
18152             /**
18153              * @event beforeselect
18154              * Fires when before select a date.
18155              * @param {Roo.bootstrap.DateField} this
18156              * @param {Mixed} date The date value
18157              */
18158             beforeselect : true
18159         });
18160 };
18161
18162 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18163     
18164     /**
18165      * @cfg {String} format
18166      * The default date format string which can be overriden for localization support.  The format must be
18167      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18168      */
18169     format : "m/d/y",
18170     /**
18171      * @cfg {String} altFormats
18172      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18173      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18174      */
18175     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18176     
18177     weekStart : 0,
18178     
18179     viewMode : '',
18180     
18181     minViewMode : '',
18182     
18183     todayHighlight : false,
18184     
18185     todayBtn: false,
18186     
18187     language: 'en',
18188     
18189     keyboardNavigation: true,
18190     
18191     calendarWeeks: false,
18192     
18193     startDate: -Infinity,
18194     
18195     endDate: Infinity,
18196     
18197     daysOfWeekDisabled: [],
18198     
18199     _events: [],
18200     
18201     singleMode : false,
18202     
18203     UTCDate: function()
18204     {
18205         return new Date(Date.UTC.apply(Date, arguments));
18206     },
18207     
18208     UTCToday: function()
18209     {
18210         var today = new Date();
18211         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18212     },
18213     
18214     getDate: function() {
18215             var d = this.getUTCDate();
18216             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18217     },
18218     
18219     getUTCDate: function() {
18220             return this.date;
18221     },
18222     
18223     setDate: function(d) {
18224             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18225     },
18226     
18227     setUTCDate: function(d) {
18228             this.date = d;
18229             this.setValue(this.formatDate(this.date));
18230     },
18231         
18232     onRender: function(ct, position)
18233     {
18234         
18235         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18236         
18237         this.language = this.language || 'en';
18238         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18239         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18240         
18241         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18242         this.format = this.format || 'm/d/y';
18243         this.isInline = false;
18244         this.isInput = true;
18245         this.component = this.el.select('.add-on', true).first() || false;
18246         this.component = (this.component && this.component.length === 0) ? false : this.component;
18247         this.hasInput = this.component && this.inputEl().length;
18248         
18249         if (typeof(this.minViewMode === 'string')) {
18250             switch (this.minViewMode) {
18251                 case 'months':
18252                     this.minViewMode = 1;
18253                     break;
18254                 case 'years':
18255                     this.minViewMode = 2;
18256                     break;
18257                 default:
18258                     this.minViewMode = 0;
18259                     break;
18260             }
18261         }
18262         
18263         if (typeof(this.viewMode === 'string')) {
18264             switch (this.viewMode) {
18265                 case 'months':
18266                     this.viewMode = 1;
18267                     break;
18268                 case 'years':
18269                     this.viewMode = 2;
18270                     break;
18271                 default:
18272                     this.viewMode = 0;
18273                     break;
18274             }
18275         }
18276                 
18277         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18278         
18279 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18280         
18281         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18282         
18283         this.picker().on('mousedown', this.onMousedown, this);
18284         this.picker().on('click', this.onClick, this);
18285         
18286         this.picker().addClass('datepicker-dropdown');
18287         
18288         this.startViewMode = this.viewMode;
18289         
18290         if(this.singleMode){
18291             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18292                 v.setVisibilityMode(Roo.Element.DISPLAY);
18293                 v.hide();
18294             });
18295             
18296             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18297                 v.setStyle('width', '189px');
18298             });
18299         }
18300         
18301         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18302             if(!this.calendarWeeks){
18303                 v.remove();
18304                 return;
18305             }
18306             
18307             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18308             v.attr('colspan', function(i, val){
18309                 return parseInt(val) + 1;
18310             });
18311         });
18312                         
18313         
18314         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18315         
18316         this.setStartDate(this.startDate);
18317         this.setEndDate(this.endDate);
18318         
18319         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18320         
18321         this.fillDow();
18322         this.fillMonths();
18323         this.update();
18324         this.showMode();
18325         
18326         if(this.isInline) {
18327             this.show();
18328         }
18329     },
18330     
18331     picker : function()
18332     {
18333         return this.pickerEl;
18334 //        return this.el.select('.datepicker', true).first();
18335     },
18336     
18337     fillDow: function()
18338     {
18339         var dowCnt = this.weekStart;
18340         
18341         var dow = {
18342             tag: 'tr',
18343             cn: [
18344                 
18345             ]
18346         };
18347         
18348         if(this.calendarWeeks){
18349             dow.cn.push({
18350                 tag: 'th',
18351                 cls: 'cw',
18352                 html: '&nbsp;'
18353             })
18354         }
18355         
18356         while (dowCnt < this.weekStart + 7) {
18357             dow.cn.push({
18358                 tag: 'th',
18359                 cls: 'dow',
18360                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18361             });
18362         }
18363         
18364         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18365     },
18366     
18367     fillMonths: function()
18368     {    
18369         var i = 0;
18370         var months = this.picker().select('>.datepicker-months td', true).first();
18371         
18372         months.dom.innerHTML = '';
18373         
18374         while (i < 12) {
18375             var month = {
18376                 tag: 'span',
18377                 cls: 'month',
18378                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18379             };
18380             
18381             months.createChild(month);
18382         }
18383         
18384     },
18385     
18386     update: function()
18387     {
18388         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;
18389         
18390         if (this.date < this.startDate) {
18391             this.viewDate = new Date(this.startDate);
18392         } else if (this.date > this.endDate) {
18393             this.viewDate = new Date(this.endDate);
18394         } else {
18395             this.viewDate = new Date(this.date);
18396         }
18397         
18398         this.fill();
18399     },
18400     
18401     fill: function() 
18402     {
18403         var d = new Date(this.viewDate),
18404                 year = d.getUTCFullYear(),
18405                 month = d.getUTCMonth(),
18406                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18407                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18408                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18409                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18410                 currentDate = this.date && this.date.valueOf(),
18411                 today = this.UTCToday();
18412         
18413         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18414         
18415 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18416         
18417 //        this.picker.select('>tfoot th.today').
18418 //                                              .text(dates[this.language].today)
18419 //                                              .toggle(this.todayBtn !== false);
18420     
18421         this.updateNavArrows();
18422         this.fillMonths();
18423                                                 
18424         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18425         
18426         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18427          
18428         prevMonth.setUTCDate(day);
18429         
18430         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18431         
18432         var nextMonth = new Date(prevMonth);
18433         
18434         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18435         
18436         nextMonth = nextMonth.valueOf();
18437         
18438         var fillMonths = false;
18439         
18440         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18441         
18442         while(prevMonth.valueOf() < nextMonth) {
18443             var clsName = '';
18444             
18445             if (prevMonth.getUTCDay() === this.weekStart) {
18446                 if(fillMonths){
18447                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18448                 }
18449                     
18450                 fillMonths = {
18451                     tag: 'tr',
18452                     cn: []
18453                 };
18454                 
18455                 if(this.calendarWeeks){
18456                     // ISO 8601: First week contains first thursday.
18457                     // ISO also states week starts on Monday, but we can be more abstract here.
18458                     var
18459                     // Start of current week: based on weekstart/current date
18460                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18461                     // Thursday of this week
18462                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18463                     // First Thursday of year, year from thursday
18464                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18465                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18466                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18467                     
18468                     fillMonths.cn.push({
18469                         tag: 'td',
18470                         cls: 'cw',
18471                         html: calWeek
18472                     });
18473                 }
18474             }
18475             
18476             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18477                 clsName += ' old';
18478             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18479                 clsName += ' new';
18480             }
18481             if (this.todayHighlight &&
18482                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18483                 prevMonth.getUTCMonth() == today.getMonth() &&
18484                 prevMonth.getUTCDate() == today.getDate()) {
18485                 clsName += ' today';
18486             }
18487             
18488             if (currentDate && prevMonth.valueOf() === currentDate) {
18489                 clsName += ' active';
18490             }
18491             
18492             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18493                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18494                     clsName += ' disabled';
18495             }
18496             
18497             fillMonths.cn.push({
18498                 tag: 'td',
18499                 cls: 'day ' + clsName,
18500                 html: prevMonth.getDate()
18501             });
18502             
18503             prevMonth.setDate(prevMonth.getDate()+1);
18504         }
18505           
18506         var currentYear = this.date && this.date.getUTCFullYear();
18507         var currentMonth = this.date && this.date.getUTCMonth();
18508         
18509         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18510         
18511         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18512             v.removeClass('active');
18513             
18514             if(currentYear === year && k === currentMonth){
18515                 v.addClass('active');
18516             }
18517             
18518             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18519                 v.addClass('disabled');
18520             }
18521             
18522         });
18523         
18524         
18525         year = parseInt(year/10, 10) * 10;
18526         
18527         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18528         
18529         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18530         
18531         year -= 1;
18532         for (var i = -1; i < 11; i++) {
18533             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18534                 tag: 'span',
18535                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18536                 html: year
18537             });
18538             
18539             year += 1;
18540         }
18541     },
18542     
18543     showMode: function(dir) 
18544     {
18545         if (dir) {
18546             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18547         }
18548         
18549         Roo.each(this.picker().select('>div',true).elements, function(v){
18550             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18551             v.hide();
18552         });
18553         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18554     },
18555     
18556     place: function()
18557     {
18558         if(this.isInline) {
18559             return;
18560         }
18561         
18562         this.picker().removeClass(['bottom', 'top']);
18563         
18564         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18565             /*
18566              * place to the top of element!
18567              *
18568              */
18569             
18570             this.picker().addClass('top');
18571             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18572             
18573             return;
18574         }
18575         
18576         this.picker().addClass('bottom');
18577         
18578         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18579     },
18580     
18581     parseDate : function(value)
18582     {
18583         if(!value || value instanceof Date){
18584             return value;
18585         }
18586         var v = Date.parseDate(value, this.format);
18587         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18588             v = Date.parseDate(value, 'Y-m-d');
18589         }
18590         if(!v && this.altFormats){
18591             if(!this.altFormatsArray){
18592                 this.altFormatsArray = this.altFormats.split("|");
18593             }
18594             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18595                 v = Date.parseDate(value, this.altFormatsArray[i]);
18596             }
18597         }
18598         return v;
18599     },
18600     
18601     formatDate : function(date, fmt)
18602     {   
18603         return (!date || !(date instanceof Date)) ?
18604         date : date.dateFormat(fmt || this.format);
18605     },
18606     
18607     onFocus : function()
18608     {
18609         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18610         this.show();
18611     },
18612     
18613     onBlur : function()
18614     {
18615         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18616         
18617         var d = this.inputEl().getValue();
18618         
18619         this.setValue(d);
18620                 
18621         this.hide();
18622     },
18623     
18624     show : function()
18625     {
18626         this.picker().show();
18627         this.update();
18628         this.place();
18629         
18630         this.fireEvent('show', this, this.date);
18631     },
18632     
18633     hide : function()
18634     {
18635         if(this.isInline) {
18636             return;
18637         }
18638         this.picker().hide();
18639         this.viewMode = this.startViewMode;
18640         this.showMode();
18641         
18642         this.fireEvent('hide', this, this.date);
18643         
18644     },
18645     
18646     onMousedown: function(e)
18647     {
18648         e.stopPropagation();
18649         e.preventDefault();
18650     },
18651     
18652     keyup: function(e)
18653     {
18654         Roo.bootstrap.DateField.superclass.keyup.call(this);
18655         this.update();
18656     },
18657
18658     setValue: function(v)
18659     {
18660         if(this.fireEvent('beforeselect', this, v) !== false){
18661             var d = new Date(this.parseDate(v) ).clearTime();
18662         
18663             if(isNaN(d.getTime())){
18664                 this.date = this.viewDate = '';
18665                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18666                 return;
18667             }
18668
18669             v = this.formatDate(d);
18670
18671             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18672
18673             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18674
18675             this.update();
18676
18677             this.fireEvent('select', this, this.date);
18678         }
18679     },
18680     
18681     getValue: function()
18682     {
18683         return this.formatDate(this.date);
18684     },
18685     
18686     fireKey: function(e)
18687     {
18688         if (!this.picker().isVisible()){
18689             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18690                 this.show();
18691             }
18692             return;
18693         }
18694         
18695         var dateChanged = false,
18696         dir, day, month,
18697         newDate, newViewDate;
18698         
18699         switch(e.keyCode){
18700             case 27: // escape
18701                 this.hide();
18702                 e.preventDefault();
18703                 break;
18704             case 37: // left
18705             case 39: // right
18706                 if (!this.keyboardNavigation) {
18707                     break;
18708                 }
18709                 dir = e.keyCode == 37 ? -1 : 1;
18710                 
18711                 if (e.ctrlKey){
18712                     newDate = this.moveYear(this.date, dir);
18713                     newViewDate = this.moveYear(this.viewDate, dir);
18714                 } else if (e.shiftKey){
18715                     newDate = this.moveMonth(this.date, dir);
18716                     newViewDate = this.moveMonth(this.viewDate, dir);
18717                 } else {
18718                     newDate = new Date(this.date);
18719                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18720                     newViewDate = new Date(this.viewDate);
18721                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18722                 }
18723                 if (this.dateWithinRange(newDate)){
18724                     this.date = newDate;
18725                     this.viewDate = newViewDate;
18726                     this.setValue(this.formatDate(this.date));
18727 //                    this.update();
18728                     e.preventDefault();
18729                     dateChanged = true;
18730                 }
18731                 break;
18732             case 38: // up
18733             case 40: // down
18734                 if (!this.keyboardNavigation) {
18735                     break;
18736                 }
18737                 dir = e.keyCode == 38 ? -1 : 1;
18738                 if (e.ctrlKey){
18739                     newDate = this.moveYear(this.date, dir);
18740                     newViewDate = this.moveYear(this.viewDate, dir);
18741                 } else if (e.shiftKey){
18742                     newDate = this.moveMonth(this.date, dir);
18743                     newViewDate = this.moveMonth(this.viewDate, dir);
18744                 } else {
18745                     newDate = new Date(this.date);
18746                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18747                     newViewDate = new Date(this.viewDate);
18748                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18749                 }
18750                 if (this.dateWithinRange(newDate)){
18751                     this.date = newDate;
18752                     this.viewDate = newViewDate;
18753                     this.setValue(this.formatDate(this.date));
18754 //                    this.update();
18755                     e.preventDefault();
18756                     dateChanged = true;
18757                 }
18758                 break;
18759             case 13: // enter
18760                 this.setValue(this.formatDate(this.date));
18761                 this.hide();
18762                 e.preventDefault();
18763                 break;
18764             case 9: // tab
18765                 this.setValue(this.formatDate(this.date));
18766                 this.hide();
18767                 break;
18768             case 16: // shift
18769             case 17: // ctrl
18770             case 18: // alt
18771                 break;
18772             default :
18773                 this.hide();
18774                 
18775         }
18776     },
18777     
18778     
18779     onClick: function(e) 
18780     {
18781         e.stopPropagation();
18782         e.preventDefault();
18783         
18784         var target = e.getTarget();
18785         
18786         if(target.nodeName.toLowerCase() === 'i'){
18787             target = Roo.get(target).dom.parentNode;
18788         }
18789         
18790         var nodeName = target.nodeName;
18791         var className = target.className;
18792         var html = target.innerHTML;
18793         //Roo.log(nodeName);
18794         
18795         switch(nodeName.toLowerCase()) {
18796             case 'th':
18797                 switch(className) {
18798                     case 'switch':
18799                         this.showMode(1);
18800                         break;
18801                     case 'prev':
18802                     case 'next':
18803                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18804                         switch(this.viewMode){
18805                                 case 0:
18806                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18807                                         break;
18808                                 case 1:
18809                                 case 2:
18810                                         this.viewDate = this.moveYear(this.viewDate, dir);
18811                                         break;
18812                         }
18813                         this.fill();
18814                         break;
18815                     case 'today':
18816                         var date = new Date();
18817                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18818 //                        this.fill()
18819                         this.setValue(this.formatDate(this.date));
18820                         
18821                         this.hide();
18822                         break;
18823                 }
18824                 break;
18825             case 'span':
18826                 if (className.indexOf('disabled') < 0) {
18827                     this.viewDate.setUTCDate(1);
18828                     if (className.indexOf('month') > -1) {
18829                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18830                     } else {
18831                         var year = parseInt(html, 10) || 0;
18832                         this.viewDate.setUTCFullYear(year);
18833                         
18834                     }
18835                     
18836                     if(this.singleMode){
18837                         this.setValue(this.formatDate(this.viewDate));
18838                         this.hide();
18839                         return;
18840                     }
18841                     
18842                     this.showMode(-1);
18843                     this.fill();
18844                 }
18845                 break;
18846                 
18847             case 'td':
18848                 //Roo.log(className);
18849                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18850                     var day = parseInt(html, 10) || 1;
18851                     var year = this.viewDate.getUTCFullYear(),
18852                         month = this.viewDate.getUTCMonth();
18853
18854                     if (className.indexOf('old') > -1) {
18855                         if(month === 0 ){
18856                             month = 11;
18857                             year -= 1;
18858                         }else{
18859                             month -= 1;
18860                         }
18861                     } else if (className.indexOf('new') > -1) {
18862                         if (month == 11) {
18863                             month = 0;
18864                             year += 1;
18865                         } else {
18866                             month += 1;
18867                         }
18868                     }
18869                     //Roo.log([year,month,day]);
18870                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18871                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18872 //                    this.fill();
18873                     //Roo.log(this.formatDate(this.date));
18874                     this.setValue(this.formatDate(this.date));
18875                     this.hide();
18876                 }
18877                 break;
18878         }
18879     },
18880     
18881     setStartDate: function(startDate)
18882     {
18883         this.startDate = startDate || -Infinity;
18884         if (this.startDate !== -Infinity) {
18885             this.startDate = this.parseDate(this.startDate);
18886         }
18887         this.update();
18888         this.updateNavArrows();
18889     },
18890
18891     setEndDate: function(endDate)
18892     {
18893         this.endDate = endDate || Infinity;
18894         if (this.endDate !== Infinity) {
18895             this.endDate = this.parseDate(this.endDate);
18896         }
18897         this.update();
18898         this.updateNavArrows();
18899     },
18900     
18901     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18902     {
18903         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18904         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18905             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18906         }
18907         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18908             return parseInt(d, 10);
18909         });
18910         this.update();
18911         this.updateNavArrows();
18912     },
18913     
18914     updateNavArrows: function() 
18915     {
18916         if(this.singleMode){
18917             return;
18918         }
18919         
18920         var d = new Date(this.viewDate),
18921         year = d.getUTCFullYear(),
18922         month = d.getUTCMonth();
18923         
18924         Roo.each(this.picker().select('.prev', true).elements, function(v){
18925             v.show();
18926             switch (this.viewMode) {
18927                 case 0:
18928
18929                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18930                         v.hide();
18931                     }
18932                     break;
18933                 case 1:
18934                 case 2:
18935                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18936                         v.hide();
18937                     }
18938                     break;
18939             }
18940         });
18941         
18942         Roo.each(this.picker().select('.next', true).elements, function(v){
18943             v.show();
18944             switch (this.viewMode) {
18945                 case 0:
18946
18947                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18948                         v.hide();
18949                     }
18950                     break;
18951                 case 1:
18952                 case 2:
18953                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18954                         v.hide();
18955                     }
18956                     break;
18957             }
18958         })
18959     },
18960     
18961     moveMonth: function(date, dir)
18962     {
18963         if (!dir) {
18964             return date;
18965         }
18966         var new_date = new Date(date.valueOf()),
18967         day = new_date.getUTCDate(),
18968         month = new_date.getUTCMonth(),
18969         mag = Math.abs(dir),
18970         new_month, test;
18971         dir = dir > 0 ? 1 : -1;
18972         if (mag == 1){
18973             test = dir == -1
18974             // If going back one month, make sure month is not current month
18975             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18976             ? function(){
18977                 return new_date.getUTCMonth() == month;
18978             }
18979             // If going forward one month, make sure month is as expected
18980             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18981             : function(){
18982                 return new_date.getUTCMonth() != new_month;
18983             };
18984             new_month = month + dir;
18985             new_date.setUTCMonth(new_month);
18986             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18987             if (new_month < 0 || new_month > 11) {
18988                 new_month = (new_month + 12) % 12;
18989             }
18990         } else {
18991             // For magnitudes >1, move one month at a time...
18992             for (var i=0; i<mag; i++) {
18993                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18994                 new_date = this.moveMonth(new_date, dir);
18995             }
18996             // ...then reset the day, keeping it in the new month
18997             new_month = new_date.getUTCMonth();
18998             new_date.setUTCDate(day);
18999             test = function(){
19000                 return new_month != new_date.getUTCMonth();
19001             };
19002         }
19003         // Common date-resetting loop -- if date is beyond end of month, make it
19004         // end of month
19005         while (test()){
19006             new_date.setUTCDate(--day);
19007             new_date.setUTCMonth(new_month);
19008         }
19009         return new_date;
19010     },
19011
19012     moveYear: function(date, dir)
19013     {
19014         return this.moveMonth(date, dir*12);
19015     },
19016
19017     dateWithinRange: function(date)
19018     {
19019         return date >= this.startDate && date <= this.endDate;
19020     },
19021
19022     
19023     remove: function() 
19024     {
19025         this.picker().remove();
19026     },
19027     
19028     validateValue : function(value)
19029     {
19030         if(value.length < 1)  {
19031             if(this.allowBlank){
19032                 return true;
19033             }
19034             return false;
19035         }
19036         
19037         if(value.length < this.minLength){
19038             return false;
19039         }
19040         if(value.length > this.maxLength){
19041             return false;
19042         }
19043         if(this.vtype){
19044             var vt = Roo.form.VTypes;
19045             if(!vt[this.vtype](value, this)){
19046                 return false;
19047             }
19048         }
19049         if(typeof this.validator == "function"){
19050             var msg = this.validator(value);
19051             if(msg !== true){
19052                 return false;
19053             }
19054         }
19055         
19056         if(this.regex && !this.regex.test(value)){
19057             return false;
19058         }
19059         
19060         if(typeof(this.parseDate(value)) == 'undefined'){
19061             return false;
19062         }
19063         
19064         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19065             return false;
19066         }      
19067         
19068         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19069             return false;
19070         } 
19071         
19072         
19073         return true;
19074     }
19075    
19076 });
19077
19078 Roo.apply(Roo.bootstrap.DateField,  {
19079     
19080     head : {
19081         tag: 'thead',
19082         cn: [
19083         {
19084             tag: 'tr',
19085             cn: [
19086             {
19087                 tag: 'th',
19088                 cls: 'prev',
19089                 html: '<i class="fa fa-arrow-left"/>'
19090             },
19091             {
19092                 tag: 'th',
19093                 cls: 'switch',
19094                 colspan: '5'
19095             },
19096             {
19097                 tag: 'th',
19098                 cls: 'next',
19099                 html: '<i class="fa fa-arrow-right"/>'
19100             }
19101
19102             ]
19103         }
19104         ]
19105     },
19106     
19107     content : {
19108         tag: 'tbody',
19109         cn: [
19110         {
19111             tag: 'tr',
19112             cn: [
19113             {
19114                 tag: 'td',
19115                 colspan: '7'
19116             }
19117             ]
19118         }
19119         ]
19120     },
19121     
19122     footer : {
19123         tag: 'tfoot',
19124         cn: [
19125         {
19126             tag: 'tr',
19127             cn: [
19128             {
19129                 tag: 'th',
19130                 colspan: '7',
19131                 cls: 'today'
19132             }
19133                     
19134             ]
19135         }
19136         ]
19137     },
19138     
19139     dates:{
19140         en: {
19141             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19142             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19143             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19144             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19145             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19146             today: "Today"
19147         }
19148     },
19149     
19150     modes: [
19151     {
19152         clsName: 'days',
19153         navFnc: 'Month',
19154         navStep: 1
19155     },
19156     {
19157         clsName: 'months',
19158         navFnc: 'FullYear',
19159         navStep: 1
19160     },
19161     {
19162         clsName: 'years',
19163         navFnc: 'FullYear',
19164         navStep: 10
19165     }]
19166 });
19167
19168 Roo.apply(Roo.bootstrap.DateField,  {
19169   
19170     template : {
19171         tag: 'div',
19172         cls: 'datepicker dropdown-menu roo-dynamic',
19173         cn: [
19174         {
19175             tag: 'div',
19176             cls: 'datepicker-days',
19177             cn: [
19178             {
19179                 tag: 'table',
19180                 cls: 'table-condensed',
19181                 cn:[
19182                 Roo.bootstrap.DateField.head,
19183                 {
19184                     tag: 'tbody'
19185                 },
19186                 Roo.bootstrap.DateField.footer
19187                 ]
19188             }
19189             ]
19190         },
19191         {
19192             tag: 'div',
19193             cls: 'datepicker-months',
19194             cn: [
19195             {
19196                 tag: 'table',
19197                 cls: 'table-condensed',
19198                 cn:[
19199                 Roo.bootstrap.DateField.head,
19200                 Roo.bootstrap.DateField.content,
19201                 Roo.bootstrap.DateField.footer
19202                 ]
19203             }
19204             ]
19205         },
19206         {
19207             tag: 'div',
19208             cls: 'datepicker-years',
19209             cn: [
19210             {
19211                 tag: 'table',
19212                 cls: 'table-condensed',
19213                 cn:[
19214                 Roo.bootstrap.DateField.head,
19215                 Roo.bootstrap.DateField.content,
19216                 Roo.bootstrap.DateField.footer
19217                 ]
19218             }
19219             ]
19220         }
19221         ]
19222     }
19223 });
19224
19225  
19226
19227  /*
19228  * - LGPL
19229  *
19230  * TimeField
19231  * 
19232  */
19233
19234 /**
19235  * @class Roo.bootstrap.TimeField
19236  * @extends Roo.bootstrap.Input
19237  * Bootstrap DateField class
19238  * 
19239  * 
19240  * @constructor
19241  * Create a new TimeField
19242  * @param {Object} config The config object
19243  */
19244
19245 Roo.bootstrap.TimeField = function(config){
19246     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19247     this.addEvents({
19248             /**
19249              * @event show
19250              * Fires when this field show.
19251              * @param {Roo.bootstrap.DateField} thisthis
19252              * @param {Mixed} date The date value
19253              */
19254             show : true,
19255             /**
19256              * @event show
19257              * Fires when this field hide.
19258              * @param {Roo.bootstrap.DateField} this
19259              * @param {Mixed} date The date value
19260              */
19261             hide : true,
19262             /**
19263              * @event select
19264              * Fires when select a date.
19265              * @param {Roo.bootstrap.DateField} this
19266              * @param {Mixed} date The date value
19267              */
19268             select : true
19269         });
19270 };
19271
19272 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19273     
19274     /**
19275      * @cfg {String} format
19276      * The default time format string which can be overriden for localization support.  The format must be
19277      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19278      */
19279     format : "H:i",
19280        
19281     onRender: function(ct, position)
19282     {
19283         
19284         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19285                 
19286         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19287         
19288         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19289         
19290         this.pop = this.picker().select('>.datepicker-time',true).first();
19291         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19292         
19293         this.picker().on('mousedown', this.onMousedown, this);
19294         this.picker().on('click', this.onClick, this);
19295         
19296         this.picker().addClass('datepicker-dropdown');
19297     
19298         this.fillTime();
19299         this.update();
19300             
19301         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19302         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19303         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19304         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19305         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19306         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19307
19308     },
19309     
19310     fireKey: function(e){
19311         if (!this.picker().isVisible()){
19312             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19313                 this.show();
19314             }
19315             return;
19316         }
19317
19318         e.preventDefault();
19319         
19320         switch(e.keyCode){
19321             case 27: // escape
19322                 this.hide();
19323                 break;
19324             case 37: // left
19325             case 39: // right
19326                 this.onTogglePeriod();
19327                 break;
19328             case 38: // up
19329                 this.onIncrementMinutes();
19330                 break;
19331             case 40: // down
19332                 this.onDecrementMinutes();
19333                 break;
19334             case 13: // enter
19335             case 9: // tab
19336                 this.setTime();
19337                 break;
19338         }
19339     },
19340     
19341     onClick: function(e) {
19342         e.stopPropagation();
19343         e.preventDefault();
19344     },
19345     
19346     picker : function()
19347     {
19348         return this.el.select('.datepicker', true).first();
19349     },
19350     
19351     fillTime: function()
19352     {    
19353         var time = this.pop.select('tbody', true).first();
19354         
19355         time.dom.innerHTML = '';
19356         
19357         time.createChild({
19358             tag: 'tr',
19359             cn: [
19360                 {
19361                     tag: 'td',
19362                     cn: [
19363                         {
19364                             tag: 'a',
19365                             href: '#',
19366                             cls: 'btn',
19367                             cn: [
19368                                 {
19369                                     tag: 'span',
19370                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19371                                 }
19372                             ]
19373                         } 
19374                     ]
19375                 },
19376                 {
19377                     tag: 'td',
19378                     cls: 'separator'
19379                 },
19380                 {
19381                     tag: 'td',
19382                     cn: [
19383                         {
19384                             tag: 'a',
19385                             href: '#',
19386                             cls: 'btn',
19387                             cn: [
19388                                 {
19389                                     tag: 'span',
19390                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19391                                 }
19392                             ]
19393                         }
19394                     ]
19395                 },
19396                 {
19397                     tag: 'td',
19398                     cls: 'separator'
19399                 }
19400             ]
19401         });
19402         
19403         time.createChild({
19404             tag: 'tr',
19405             cn: [
19406                 {
19407                     tag: 'td',
19408                     cn: [
19409                         {
19410                             tag: 'span',
19411                             cls: 'timepicker-hour',
19412                             html: '00'
19413                         }  
19414                     ]
19415                 },
19416                 {
19417                     tag: 'td',
19418                     cls: 'separator',
19419                     html: ':'
19420                 },
19421                 {
19422                     tag: 'td',
19423                     cn: [
19424                         {
19425                             tag: 'span',
19426                             cls: 'timepicker-minute',
19427                             html: '00'
19428                         }  
19429                     ]
19430                 },
19431                 {
19432                     tag: 'td',
19433                     cls: 'separator'
19434                 },
19435                 {
19436                     tag: 'td',
19437                     cn: [
19438                         {
19439                             tag: 'button',
19440                             type: 'button',
19441                             cls: 'btn btn-primary period',
19442                             html: 'AM'
19443                             
19444                         }
19445                     ]
19446                 }
19447             ]
19448         });
19449         
19450         time.createChild({
19451             tag: 'tr',
19452             cn: [
19453                 {
19454                     tag: 'td',
19455                     cn: [
19456                         {
19457                             tag: 'a',
19458                             href: '#',
19459                             cls: 'btn',
19460                             cn: [
19461                                 {
19462                                     tag: 'span',
19463                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19464                                 }
19465                             ]
19466                         }
19467                     ]
19468                 },
19469                 {
19470                     tag: 'td',
19471                     cls: 'separator'
19472                 },
19473                 {
19474                     tag: 'td',
19475                     cn: [
19476                         {
19477                             tag: 'a',
19478                             href: '#',
19479                             cls: 'btn',
19480                             cn: [
19481                                 {
19482                                     tag: 'span',
19483                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19484                                 }
19485                             ]
19486                         }
19487                     ]
19488                 },
19489                 {
19490                     tag: 'td',
19491                     cls: 'separator'
19492                 }
19493             ]
19494         });
19495         
19496     },
19497     
19498     update: function()
19499     {
19500         
19501         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19502         
19503         this.fill();
19504     },
19505     
19506     fill: function() 
19507     {
19508         var hours = this.time.getHours();
19509         var minutes = this.time.getMinutes();
19510         var period = 'AM';
19511         
19512         if(hours > 11){
19513             period = 'PM';
19514         }
19515         
19516         if(hours == 0){
19517             hours = 12;
19518         }
19519         
19520         
19521         if(hours > 12){
19522             hours = hours - 12;
19523         }
19524         
19525         if(hours < 10){
19526             hours = '0' + hours;
19527         }
19528         
19529         if(minutes < 10){
19530             minutes = '0' + minutes;
19531         }
19532         
19533         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19534         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19535         this.pop.select('button', true).first().dom.innerHTML = period;
19536         
19537     },
19538     
19539     place: function()
19540     {   
19541         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19542         
19543         var cls = ['bottom'];
19544         
19545         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19546             cls.pop();
19547             cls.push('top');
19548         }
19549         
19550         cls.push('right');
19551         
19552         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19553             cls.pop();
19554             cls.push('left');
19555         }
19556         
19557         this.picker().addClass(cls.join('-'));
19558         
19559         var _this = this;
19560         
19561         Roo.each(cls, function(c){
19562             if(c == 'bottom'){
19563                 _this.picker().setTop(_this.inputEl().getHeight());
19564                 return;
19565             }
19566             if(c == 'top'){
19567                 _this.picker().setTop(0 - _this.picker().getHeight());
19568                 return;
19569             }
19570             
19571             if(c == 'left'){
19572                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19573                 return;
19574             }
19575             if(c == 'right'){
19576                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19577                 return;
19578             }
19579         });
19580         
19581     },
19582   
19583     onFocus : function()
19584     {
19585         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19586         this.show();
19587     },
19588     
19589     onBlur : function()
19590     {
19591         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19592         this.hide();
19593     },
19594     
19595     show : function()
19596     {
19597         this.picker().show();
19598         this.pop.show();
19599         this.update();
19600         this.place();
19601         
19602         this.fireEvent('show', this, this.date);
19603     },
19604     
19605     hide : function()
19606     {
19607         this.picker().hide();
19608         this.pop.hide();
19609         
19610         this.fireEvent('hide', this, this.date);
19611     },
19612     
19613     setTime : function()
19614     {
19615         this.hide();
19616         this.setValue(this.time.format(this.format));
19617         
19618         this.fireEvent('select', this, this.date);
19619         
19620         
19621     },
19622     
19623     onMousedown: function(e){
19624         e.stopPropagation();
19625         e.preventDefault();
19626     },
19627     
19628     onIncrementHours: function()
19629     {
19630         Roo.log('onIncrementHours');
19631         this.time = this.time.add(Date.HOUR, 1);
19632         this.update();
19633         
19634     },
19635     
19636     onDecrementHours: function()
19637     {
19638         Roo.log('onDecrementHours');
19639         this.time = this.time.add(Date.HOUR, -1);
19640         this.update();
19641     },
19642     
19643     onIncrementMinutes: function()
19644     {
19645         Roo.log('onIncrementMinutes');
19646         this.time = this.time.add(Date.MINUTE, 1);
19647         this.update();
19648     },
19649     
19650     onDecrementMinutes: function()
19651     {
19652         Roo.log('onDecrementMinutes');
19653         this.time = this.time.add(Date.MINUTE, -1);
19654         this.update();
19655     },
19656     
19657     onTogglePeriod: function()
19658     {
19659         Roo.log('onTogglePeriod');
19660         this.time = this.time.add(Date.HOUR, 12);
19661         this.update();
19662     }
19663     
19664    
19665 });
19666
19667 Roo.apply(Roo.bootstrap.TimeField,  {
19668     
19669     content : {
19670         tag: 'tbody',
19671         cn: [
19672             {
19673                 tag: 'tr',
19674                 cn: [
19675                 {
19676                     tag: 'td',
19677                     colspan: '7'
19678                 }
19679                 ]
19680             }
19681         ]
19682     },
19683     
19684     footer : {
19685         tag: 'tfoot',
19686         cn: [
19687             {
19688                 tag: 'tr',
19689                 cn: [
19690                 {
19691                     tag: 'th',
19692                     colspan: '7',
19693                     cls: '',
19694                     cn: [
19695                         {
19696                             tag: 'button',
19697                             cls: 'btn btn-info ok',
19698                             html: 'OK'
19699                         }
19700                     ]
19701                 }
19702
19703                 ]
19704             }
19705         ]
19706     }
19707 });
19708
19709 Roo.apply(Roo.bootstrap.TimeField,  {
19710   
19711     template : {
19712         tag: 'div',
19713         cls: 'datepicker dropdown-menu',
19714         cn: [
19715             {
19716                 tag: 'div',
19717                 cls: 'datepicker-time',
19718                 cn: [
19719                 {
19720                     tag: 'table',
19721                     cls: 'table-condensed',
19722                     cn:[
19723                     Roo.bootstrap.TimeField.content,
19724                     Roo.bootstrap.TimeField.footer
19725                     ]
19726                 }
19727                 ]
19728             }
19729         ]
19730     }
19731 });
19732
19733  
19734
19735  /*
19736  * - LGPL
19737  *
19738  * MonthField
19739  * 
19740  */
19741
19742 /**
19743  * @class Roo.bootstrap.MonthField
19744  * @extends Roo.bootstrap.Input
19745  * Bootstrap MonthField class
19746  * 
19747  * @cfg {String} language default en
19748  * 
19749  * @constructor
19750  * Create a new MonthField
19751  * @param {Object} config The config object
19752  */
19753
19754 Roo.bootstrap.MonthField = function(config){
19755     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19756     
19757     this.addEvents({
19758         /**
19759          * @event show
19760          * Fires when this field show.
19761          * @param {Roo.bootstrap.MonthField} this
19762          * @param {Mixed} date The date value
19763          */
19764         show : true,
19765         /**
19766          * @event show
19767          * Fires when this field hide.
19768          * @param {Roo.bootstrap.MonthField} this
19769          * @param {Mixed} date The date value
19770          */
19771         hide : true,
19772         /**
19773          * @event select
19774          * Fires when select a date.
19775          * @param {Roo.bootstrap.MonthField} this
19776          * @param {String} oldvalue The old value
19777          * @param {String} newvalue The new value
19778          */
19779         select : true
19780     });
19781 };
19782
19783 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19784     
19785     onRender: function(ct, position)
19786     {
19787         
19788         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19789         
19790         this.language = this.language || 'en';
19791         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19792         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19793         
19794         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19795         this.isInline = false;
19796         this.isInput = true;
19797         this.component = this.el.select('.add-on', true).first() || false;
19798         this.component = (this.component && this.component.length === 0) ? false : this.component;
19799         this.hasInput = this.component && this.inputEL().length;
19800         
19801         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19802         
19803         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19804         
19805         this.picker().on('mousedown', this.onMousedown, this);
19806         this.picker().on('click', this.onClick, this);
19807         
19808         this.picker().addClass('datepicker-dropdown');
19809         
19810         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19811             v.setStyle('width', '189px');
19812         });
19813         
19814         this.fillMonths();
19815         
19816         this.update();
19817         
19818         if(this.isInline) {
19819             this.show();
19820         }
19821         
19822     },
19823     
19824     setValue: function(v, suppressEvent)
19825     {   
19826         var o = this.getValue();
19827         
19828         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19829         
19830         this.update();
19831
19832         if(suppressEvent !== true){
19833             this.fireEvent('select', this, o, v);
19834         }
19835         
19836     },
19837     
19838     getValue: function()
19839     {
19840         return this.value;
19841     },
19842     
19843     onClick: function(e) 
19844     {
19845         e.stopPropagation();
19846         e.preventDefault();
19847         
19848         var target = e.getTarget();
19849         
19850         if(target.nodeName.toLowerCase() === 'i'){
19851             target = Roo.get(target).dom.parentNode;
19852         }
19853         
19854         var nodeName = target.nodeName;
19855         var className = target.className;
19856         var html = target.innerHTML;
19857         
19858         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19859             return;
19860         }
19861         
19862         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19863         
19864         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19865         
19866         this.hide();
19867                         
19868     },
19869     
19870     picker : function()
19871     {
19872         return this.pickerEl;
19873     },
19874     
19875     fillMonths: function()
19876     {    
19877         var i = 0;
19878         var months = this.picker().select('>.datepicker-months td', true).first();
19879         
19880         months.dom.innerHTML = '';
19881         
19882         while (i < 12) {
19883             var month = {
19884                 tag: 'span',
19885                 cls: 'month',
19886                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19887             };
19888             
19889             months.createChild(month);
19890         }
19891         
19892     },
19893     
19894     update: function()
19895     {
19896         var _this = this;
19897         
19898         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19899             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19900         }
19901         
19902         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19903             e.removeClass('active');
19904             
19905             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19906                 e.addClass('active');
19907             }
19908         })
19909     },
19910     
19911     place: function()
19912     {
19913         if(this.isInline) {
19914             return;
19915         }
19916         
19917         this.picker().removeClass(['bottom', 'top']);
19918         
19919         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19920             /*
19921              * place to the top of element!
19922              *
19923              */
19924             
19925             this.picker().addClass('top');
19926             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19927             
19928             return;
19929         }
19930         
19931         this.picker().addClass('bottom');
19932         
19933         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19934     },
19935     
19936     onFocus : function()
19937     {
19938         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19939         this.show();
19940     },
19941     
19942     onBlur : function()
19943     {
19944         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19945         
19946         var d = this.inputEl().getValue();
19947         
19948         this.setValue(d);
19949                 
19950         this.hide();
19951     },
19952     
19953     show : function()
19954     {
19955         this.picker().show();
19956         this.picker().select('>.datepicker-months', true).first().show();
19957         this.update();
19958         this.place();
19959         
19960         this.fireEvent('show', this, this.date);
19961     },
19962     
19963     hide : function()
19964     {
19965         if(this.isInline) {
19966             return;
19967         }
19968         this.picker().hide();
19969         this.fireEvent('hide', this, this.date);
19970         
19971     },
19972     
19973     onMousedown: function(e)
19974     {
19975         e.stopPropagation();
19976         e.preventDefault();
19977     },
19978     
19979     keyup: function(e)
19980     {
19981         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19982         this.update();
19983     },
19984
19985     fireKey: function(e)
19986     {
19987         if (!this.picker().isVisible()){
19988             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19989                 this.show();
19990             }
19991             return;
19992         }
19993         
19994         var dir;
19995         
19996         switch(e.keyCode){
19997             case 27: // escape
19998                 this.hide();
19999                 e.preventDefault();
20000                 break;
20001             case 37: // left
20002             case 39: // right
20003                 dir = e.keyCode == 37 ? -1 : 1;
20004                 
20005                 this.vIndex = this.vIndex + dir;
20006                 
20007                 if(this.vIndex < 0){
20008                     this.vIndex = 0;
20009                 }
20010                 
20011                 if(this.vIndex > 11){
20012                     this.vIndex = 11;
20013                 }
20014                 
20015                 if(isNaN(this.vIndex)){
20016                     this.vIndex = 0;
20017                 }
20018                 
20019                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20020                 
20021                 break;
20022             case 38: // up
20023             case 40: // down
20024                 
20025                 dir = e.keyCode == 38 ? -1 : 1;
20026                 
20027                 this.vIndex = this.vIndex + dir * 4;
20028                 
20029                 if(this.vIndex < 0){
20030                     this.vIndex = 0;
20031                 }
20032                 
20033                 if(this.vIndex > 11){
20034                     this.vIndex = 11;
20035                 }
20036                 
20037                 if(isNaN(this.vIndex)){
20038                     this.vIndex = 0;
20039                 }
20040                 
20041                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20042                 break;
20043                 
20044             case 13: // enter
20045                 
20046                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20047                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20048                 }
20049                 
20050                 this.hide();
20051                 e.preventDefault();
20052                 break;
20053             case 9: // tab
20054                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20055                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20056                 }
20057                 this.hide();
20058                 break;
20059             case 16: // shift
20060             case 17: // ctrl
20061             case 18: // alt
20062                 break;
20063             default :
20064                 this.hide();
20065                 
20066         }
20067     },
20068     
20069     remove: function() 
20070     {
20071         this.picker().remove();
20072     }
20073    
20074 });
20075
20076 Roo.apply(Roo.bootstrap.MonthField,  {
20077     
20078     content : {
20079         tag: 'tbody',
20080         cn: [
20081         {
20082             tag: 'tr',
20083             cn: [
20084             {
20085                 tag: 'td',
20086                 colspan: '7'
20087             }
20088             ]
20089         }
20090         ]
20091     },
20092     
20093     dates:{
20094         en: {
20095             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20096             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20097         }
20098     }
20099 });
20100
20101 Roo.apply(Roo.bootstrap.MonthField,  {
20102   
20103     template : {
20104         tag: 'div',
20105         cls: 'datepicker dropdown-menu roo-dynamic',
20106         cn: [
20107             {
20108                 tag: 'div',
20109                 cls: 'datepicker-months',
20110                 cn: [
20111                 {
20112                     tag: 'table',
20113                     cls: 'table-condensed',
20114                     cn:[
20115                         Roo.bootstrap.DateField.content
20116                     ]
20117                 }
20118                 ]
20119             }
20120         ]
20121     }
20122 });
20123
20124  
20125
20126  
20127  /*
20128  * - LGPL
20129  *
20130  * CheckBox
20131  * 
20132  */
20133
20134 /**
20135  * @class Roo.bootstrap.CheckBox
20136  * @extends Roo.bootstrap.Input
20137  * Bootstrap CheckBox class
20138  * 
20139  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20140  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20141  * @cfg {String} boxLabel The text that appears beside the checkbox
20142  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20143  * @cfg {Boolean} checked initnal the element
20144  * @cfg {Boolean} inline inline the element (default false)
20145  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20146  * @cfg {String} tooltip label tooltip
20147  * 
20148  * @constructor
20149  * Create a new CheckBox
20150  * @param {Object} config The config object
20151  */
20152
20153 Roo.bootstrap.CheckBox = function(config){
20154     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20155    
20156     this.addEvents({
20157         /**
20158         * @event check
20159         * Fires when the element is checked or unchecked.
20160         * @param {Roo.bootstrap.CheckBox} this This input
20161         * @param {Boolean} checked The new checked value
20162         */
20163        check : true,
20164        /**
20165         * @event click
20166         * Fires when the element is click.
20167         * @param {Roo.bootstrap.CheckBox} this This input
20168         */
20169        click : true
20170     });
20171     
20172 };
20173
20174 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20175   
20176     inputType: 'checkbox',
20177     inputValue: 1,
20178     valueOff: 0,
20179     boxLabel: false,
20180     checked: false,
20181     weight : false,
20182     inline: false,
20183     tooltip : '',
20184     
20185     getAutoCreate : function()
20186     {
20187         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20188         
20189         var id = Roo.id();
20190         
20191         var cfg = {};
20192         
20193         cfg.cls = 'form-group ' + this.inputType; //input-group
20194         
20195         if(this.inline){
20196             cfg.cls += ' ' + this.inputType + '-inline';
20197         }
20198         
20199         var input =  {
20200             tag: 'input',
20201             id : id,
20202             type : this.inputType,
20203             value : this.inputValue,
20204             cls : 'roo-' + this.inputType, //'form-box',
20205             placeholder : this.placeholder || ''
20206             
20207         };
20208         
20209         if(this.inputType != 'radio'){
20210             var hidden =  {
20211                 tag: 'input',
20212                 type : 'hidden',
20213                 cls : 'roo-hidden-value',
20214                 value : this.checked ? this.inputValue : this.valueOff
20215             };
20216         }
20217         
20218             
20219         if (this.weight) { // Validity check?
20220             cfg.cls += " " + this.inputType + "-" + this.weight;
20221         }
20222         
20223         if (this.disabled) {
20224             input.disabled=true;
20225         }
20226         
20227         if(this.checked){
20228             input.checked = this.checked;
20229         }
20230         
20231         if (this.name) {
20232             
20233             input.name = this.name;
20234             
20235             if(this.inputType != 'radio'){
20236                 hidden.name = this.name;
20237                 input.name = '_hidden_' + this.name;
20238             }
20239         }
20240         
20241         if (this.size) {
20242             input.cls += ' input-' + this.size;
20243         }
20244         
20245         var settings=this;
20246         
20247         ['xs','sm','md','lg'].map(function(size){
20248             if (settings[size]) {
20249                 cfg.cls += ' col-' + size + '-' + settings[size];
20250             }
20251         });
20252         
20253         var inputblock = input;
20254          
20255         if (this.before || this.after) {
20256             
20257             inputblock = {
20258                 cls : 'input-group',
20259                 cn :  [] 
20260             };
20261             
20262             if (this.before) {
20263                 inputblock.cn.push({
20264                     tag :'span',
20265                     cls : 'input-group-addon',
20266                     html : this.before
20267                 });
20268             }
20269             
20270             inputblock.cn.push(input);
20271             
20272             if(this.inputType != 'radio'){
20273                 inputblock.cn.push(hidden);
20274             }
20275             
20276             if (this.after) {
20277                 inputblock.cn.push({
20278                     tag :'span',
20279                     cls : 'input-group-addon',
20280                     html : this.after
20281                 });
20282             }
20283             
20284         }
20285         
20286         if (align ==='left' && this.fieldLabel.length) {
20287 //                Roo.log("left and has label");
20288             cfg.cn = [
20289                 {
20290                     tag: 'label',
20291                     'for' :  id,
20292                     cls : 'control-label',
20293                     html : this.fieldLabel
20294                 },
20295                 {
20296                     cls : "", 
20297                     cn: [
20298                         inputblock
20299                     ]
20300                 }
20301             ];
20302             
20303             if(this.labelWidth > 12){
20304                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20305             }
20306             
20307             if(this.labelWidth < 13 && this.labelmd == 0){
20308                 this.labelmd = this.labelWidth;
20309             }
20310             
20311             if(this.labellg > 0){
20312                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20313                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20314             }
20315             
20316             if(this.labelmd > 0){
20317                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20318                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20319             }
20320             
20321             if(this.labelsm > 0){
20322                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20323                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20324             }
20325             
20326             if(this.labelxs > 0){
20327                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20328                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20329             }
20330             
20331         } else if ( this.fieldLabel.length) {
20332 //                Roo.log(" label");
20333                 cfg.cn = [
20334                    
20335                     {
20336                         tag: this.boxLabel ? 'span' : 'label',
20337                         'for': id,
20338                         cls: 'control-label box-input-label',
20339                         //cls : 'input-group-addon',
20340                         html : this.fieldLabel
20341                     },
20342                     
20343                     inputblock
20344                     
20345                 ];
20346
20347         } else {
20348             
20349 //                Roo.log(" no label && no align");
20350                 cfg.cn = [  inputblock ] ;
20351                 
20352                 
20353         }
20354         
20355         if(this.boxLabel){
20356              var boxLabelCfg = {
20357                 tag: 'label',
20358                 //'for': id, // box label is handled by onclick - so no for...
20359                 cls: 'box-label',
20360                 html: this.boxLabel
20361             };
20362             
20363             if(this.tooltip){
20364                 boxLabelCfg.tooltip = this.tooltip;
20365             }
20366              
20367             cfg.cn.push(boxLabelCfg);
20368         }
20369         
20370         if(this.inputType != 'radio'){
20371             cfg.cn.push(hidden);
20372         }
20373         
20374         return cfg;
20375         
20376     },
20377     
20378     /**
20379      * return the real input element.
20380      */
20381     inputEl: function ()
20382     {
20383         return this.el.select('input.roo-' + this.inputType,true).first();
20384     },
20385     hiddenEl: function ()
20386     {
20387         return this.el.select('input.roo-hidden-value',true).first();
20388     },
20389     
20390     labelEl: function()
20391     {
20392         return this.el.select('label.control-label',true).first();
20393     },
20394     /* depricated... */
20395     
20396     label: function()
20397     {
20398         return this.labelEl();
20399     },
20400     
20401     boxLabelEl: function()
20402     {
20403         return this.el.select('label.box-label',true).first();
20404     },
20405     
20406     initEvents : function()
20407     {
20408 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20409         
20410         this.inputEl().on('click', this.onClick,  this);
20411         
20412         if (this.boxLabel) { 
20413             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20414         }
20415         
20416         this.startValue = this.getValue();
20417         
20418         if(this.groupId){
20419             Roo.bootstrap.CheckBox.register(this);
20420         }
20421     },
20422     
20423     onClick : function(e)
20424     {   
20425         if(this.fireEvent('click', this, e) !== false){
20426             this.setChecked(!this.checked);
20427         }
20428         
20429     },
20430     
20431     setChecked : function(state,suppressEvent)
20432     {
20433         this.startValue = this.getValue();
20434
20435         if(this.inputType == 'radio'){
20436             
20437             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20438                 e.dom.checked = false;
20439             });
20440             
20441             this.inputEl().dom.checked = true;
20442             
20443             this.inputEl().dom.value = this.inputValue;
20444             
20445             if(suppressEvent !== true){
20446                 this.fireEvent('check', this, true);
20447             }
20448             
20449             this.validate();
20450             
20451             return;
20452         }
20453         
20454         this.checked = state;
20455         
20456         this.inputEl().dom.checked = state;
20457         
20458         
20459         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20460         
20461         if(suppressEvent !== true){
20462             this.fireEvent('check', this, state);
20463         }
20464         
20465         this.validate();
20466     },
20467     
20468     getValue : function()
20469     {
20470         if(this.inputType == 'radio'){
20471             return this.getGroupValue();
20472         }
20473         
20474         return this.hiddenEl().dom.value;
20475         
20476     },
20477     
20478     getGroupValue : function()
20479     {
20480         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20481             return '';
20482         }
20483         
20484         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20485     },
20486     
20487     setValue : function(v,suppressEvent)
20488     {
20489         if(this.inputType == 'radio'){
20490             this.setGroupValue(v, suppressEvent);
20491             return;
20492         }
20493         
20494         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20495         
20496         this.validate();
20497     },
20498     
20499     setGroupValue : function(v, suppressEvent)
20500     {
20501         this.startValue = this.getValue();
20502         
20503         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20504             e.dom.checked = false;
20505             
20506             if(e.dom.value == v){
20507                 e.dom.checked = true;
20508             }
20509         });
20510         
20511         if(suppressEvent !== true){
20512             this.fireEvent('check', this, true);
20513         }
20514
20515         this.validate();
20516         
20517         return;
20518     },
20519     
20520     validate : function()
20521     {
20522         if(
20523                 this.disabled || 
20524                 (this.inputType == 'radio' && this.validateRadio()) ||
20525                 (this.inputType == 'checkbox' && this.validateCheckbox())
20526         ){
20527             this.markValid();
20528             return true;
20529         }
20530         
20531         this.markInvalid();
20532         return false;
20533     },
20534     
20535     validateRadio : function()
20536     {
20537         if(this.allowBlank){
20538             return true;
20539         }
20540         
20541         var valid = false;
20542         
20543         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20544             if(!e.dom.checked){
20545                 return;
20546             }
20547             
20548             valid = true;
20549             
20550             return false;
20551         });
20552         
20553         return valid;
20554     },
20555     
20556     validateCheckbox : function()
20557     {
20558         if(!this.groupId){
20559             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20560             //return (this.getValue() == this.inputValue) ? true : false;
20561         }
20562         
20563         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20564         
20565         if(!group){
20566             return false;
20567         }
20568         
20569         var r = false;
20570         
20571         for(var i in group){
20572             if(group[i].el.isVisible(true)){
20573                 r = false;
20574                 break;
20575             }
20576             
20577             r = true;
20578         }
20579         
20580         for(var i in group){
20581             if(r){
20582                 break;
20583             }
20584             
20585             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20586         }
20587         
20588         return r;
20589     },
20590     
20591     /**
20592      * Mark this field as valid
20593      */
20594     markValid : function()
20595     {
20596         var _this = this;
20597         
20598         this.fireEvent('valid', this);
20599         
20600         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20601         
20602         if(this.groupId){
20603             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20604         }
20605         
20606         if(label){
20607             label.markValid();
20608         }
20609
20610         if(this.inputType == 'radio'){
20611             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20612                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20613                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20614             });
20615             
20616             return;
20617         }
20618
20619         if(!this.groupId){
20620             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20621             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20622             return;
20623         }
20624         
20625         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20626         
20627         if(!group){
20628             return;
20629         }
20630         
20631         for(var i in group){
20632             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20633             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20634         }
20635     },
20636     
20637      /**
20638      * Mark this field as invalid
20639      * @param {String} msg The validation message
20640      */
20641     markInvalid : function(msg)
20642     {
20643         if(this.allowBlank){
20644             return;
20645         }
20646         
20647         var _this = this;
20648         
20649         this.fireEvent('invalid', this, msg);
20650         
20651         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20652         
20653         if(this.groupId){
20654             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20655         }
20656         
20657         if(label){
20658             label.markInvalid();
20659         }
20660             
20661         if(this.inputType == 'radio'){
20662             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20663                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20664                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20665             });
20666             
20667             return;
20668         }
20669         
20670         if(!this.groupId){
20671             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20672             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20673             return;
20674         }
20675         
20676         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20677         
20678         if(!group){
20679             return;
20680         }
20681         
20682         for(var i in group){
20683             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20684             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20685         }
20686         
20687     },
20688     
20689     clearInvalid : function()
20690     {
20691         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20692         
20693         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20694         
20695         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20696         
20697         if (label && label.iconEl) {
20698             label.iconEl.removeClass(label.validClass);
20699             label.iconEl.removeClass(label.invalidClass);
20700         }
20701     },
20702     
20703     disable : function()
20704     {
20705         if(this.inputType != 'radio'){
20706             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20707             return;
20708         }
20709         
20710         var _this = this;
20711         
20712         if(this.rendered){
20713             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20714                 _this.getActionEl().addClass(this.disabledClass);
20715                 e.dom.disabled = true;
20716             });
20717         }
20718         
20719         this.disabled = true;
20720         this.fireEvent("disable", this);
20721         return this;
20722     },
20723
20724     enable : function()
20725     {
20726         if(this.inputType != 'radio'){
20727             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20728             return;
20729         }
20730         
20731         var _this = this;
20732         
20733         if(this.rendered){
20734             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20735                 _this.getActionEl().removeClass(this.disabledClass);
20736                 e.dom.disabled = false;
20737             });
20738         }
20739         
20740         this.disabled = false;
20741         this.fireEvent("enable", this);
20742         return this;
20743     }
20744
20745 });
20746
20747 Roo.apply(Roo.bootstrap.CheckBox, {
20748     
20749     groups: {},
20750     
20751      /**
20752     * register a CheckBox Group
20753     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20754     */
20755     register : function(checkbox)
20756     {
20757         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20758             this.groups[checkbox.groupId] = {};
20759         }
20760         
20761         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20762             return;
20763         }
20764         
20765         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20766         
20767     },
20768     /**
20769     * fetch a CheckBox Group based on the group ID
20770     * @param {string} the group ID
20771     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20772     */
20773     get: function(groupId) {
20774         if (typeof(this.groups[groupId]) == 'undefined') {
20775             return false;
20776         }
20777         
20778         return this.groups[groupId] ;
20779     }
20780     
20781     
20782 });
20783 /*
20784  * - LGPL
20785  *
20786  * RadioItem
20787  * 
20788  */
20789
20790 /**
20791  * @class Roo.bootstrap.Radio
20792  * @extends Roo.bootstrap.Component
20793  * Bootstrap Radio class
20794  * @cfg {String} boxLabel - the label associated
20795  * @cfg {String} value - the value of radio
20796  * 
20797  * @constructor
20798  * Create a new Radio
20799  * @param {Object} config The config object
20800  */
20801 Roo.bootstrap.Radio = function(config){
20802     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20803     
20804 };
20805
20806 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20807     
20808     boxLabel : '',
20809     
20810     value : '',
20811     
20812     getAutoCreate : function()
20813     {
20814         var cfg = {
20815             tag : 'div',
20816             cls : 'form-group radio',
20817             cn : [
20818                 {
20819                     tag : 'label',
20820                     cls : 'box-label',
20821                     html : this.boxLabel
20822                 }
20823             ]
20824         };
20825         
20826         return cfg;
20827     },
20828     
20829     initEvents : function() 
20830     {
20831         this.parent().register(this);
20832         
20833         this.el.on('click', this.onClick, this);
20834         
20835     },
20836     
20837     onClick : function()
20838     {
20839         this.setChecked(true);
20840     },
20841     
20842     setChecked : function(state, suppressEvent)
20843     {
20844         this.parent().setValue(this.value, suppressEvent);
20845         
20846     },
20847     
20848     setBoxLabel : function(v)
20849     {
20850         this.boxLabel = v;
20851         
20852         if(this.rendered){
20853             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20854         }
20855     }
20856     
20857 });
20858  
20859
20860  /*
20861  * - LGPL
20862  *
20863  * Input
20864  * 
20865  */
20866
20867 /**
20868  * @class Roo.bootstrap.SecurePass
20869  * @extends Roo.bootstrap.Input
20870  * Bootstrap SecurePass class
20871  *
20872  * 
20873  * @constructor
20874  * Create a new SecurePass
20875  * @param {Object} config The config object
20876  */
20877  
20878 Roo.bootstrap.SecurePass = function (config) {
20879     // these go here, so the translation tool can replace them..
20880     this.errors = {
20881         PwdEmpty: "Please type a password, and then retype it to confirm.",
20882         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20883         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20884         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20885         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20886         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20887         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20888         TooWeak: "Your password is Too Weak."
20889     },
20890     this.meterLabel = "Password strength:";
20891     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20892     this.meterClass = [
20893         "roo-password-meter-tooweak", 
20894         "roo-password-meter-weak", 
20895         "roo-password-meter-medium", 
20896         "roo-password-meter-strong", 
20897         "roo-password-meter-grey"
20898     ];
20899     
20900     this.errors = {};
20901     
20902     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20903 }
20904
20905 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20906     /**
20907      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20908      * {
20909      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20910      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20911      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20912      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20913      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20914      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20915      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20916      * })
20917      */
20918     // private
20919     
20920     meterWidth: 300,
20921     errorMsg :'',    
20922     errors: false,
20923     imageRoot: '/',
20924     /**
20925      * @cfg {String/Object} Label for the strength meter (defaults to
20926      * 'Password strength:')
20927      */
20928     // private
20929     meterLabel: '',
20930     /**
20931      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20932      * ['Weak', 'Medium', 'Strong'])
20933      */
20934     // private    
20935     pwdStrengths: false,    
20936     // private
20937     strength: 0,
20938     // private
20939     _lastPwd: null,
20940     // private
20941     kCapitalLetter: 0,
20942     kSmallLetter: 1,
20943     kDigit: 2,
20944     kPunctuation: 3,
20945     
20946     insecure: false,
20947     // private
20948     initEvents: function ()
20949     {
20950         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20951
20952         if (this.el.is('input[type=password]') && Roo.isSafari) {
20953             this.el.on('keydown', this.SafariOnKeyDown, this);
20954         }
20955
20956         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20957     },
20958     // private
20959     onRender: function (ct, position)
20960     {
20961         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20962         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20963         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20964
20965         this.trigger.createChild({
20966                    cn: [
20967                     {
20968                     //id: 'PwdMeter',
20969                     tag: 'div',
20970                     cls: 'roo-password-meter-grey col-xs-12',
20971                     style: {
20972                         //width: 0,
20973                         //width: this.meterWidth + 'px'                                                
20974                         }
20975                     },
20976                     {                            
20977                          cls: 'roo-password-meter-text'                          
20978                     }
20979                 ]            
20980         });
20981
20982          
20983         if (this.hideTrigger) {
20984             this.trigger.setDisplayed(false);
20985         }
20986         this.setSize(this.width || '', this.height || '');
20987     },
20988     // private
20989     onDestroy: function ()
20990     {
20991         if (this.trigger) {
20992             this.trigger.removeAllListeners();
20993             this.trigger.remove();
20994         }
20995         if (this.wrap) {
20996             this.wrap.remove();
20997         }
20998         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20999     },
21000     // private
21001     checkStrength: function ()
21002     {
21003         var pwd = this.inputEl().getValue();
21004         if (pwd == this._lastPwd) {
21005             return;
21006         }
21007
21008         var strength;
21009         if (this.ClientSideStrongPassword(pwd)) {
21010             strength = 3;
21011         } else if (this.ClientSideMediumPassword(pwd)) {
21012             strength = 2;
21013         } else if (this.ClientSideWeakPassword(pwd)) {
21014             strength = 1;
21015         } else {
21016             strength = 0;
21017         }
21018         
21019         Roo.log('strength1: ' + strength);
21020         
21021         //var pm = this.trigger.child('div/div/div').dom;
21022         var pm = this.trigger.child('div/div');
21023         pm.removeClass(this.meterClass);
21024         pm.addClass(this.meterClass[strength]);
21025                 
21026         
21027         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21028                 
21029         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21030         
21031         this._lastPwd = pwd;
21032     },
21033     reset: function ()
21034     {
21035         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21036         
21037         this._lastPwd = '';
21038         
21039         var pm = this.trigger.child('div/div');
21040         pm.removeClass(this.meterClass);
21041         pm.addClass('roo-password-meter-grey');        
21042         
21043         
21044         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21045         
21046         pt.innerHTML = '';
21047         this.inputEl().dom.type='password';
21048     },
21049     // private
21050     validateValue: function (value)
21051     {
21052         
21053         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21054             return false;
21055         }
21056         if (value.length == 0) {
21057             if (this.allowBlank) {
21058                 this.clearInvalid();
21059                 return true;
21060             }
21061
21062             this.markInvalid(this.errors.PwdEmpty);
21063             this.errorMsg = this.errors.PwdEmpty;
21064             return false;
21065         }
21066         
21067         if(this.insecure){
21068             return true;
21069         }
21070         
21071         if ('[\x21-\x7e]*'.match(value)) {
21072             this.markInvalid(this.errors.PwdBadChar);
21073             this.errorMsg = this.errors.PwdBadChar;
21074             return false;
21075         }
21076         if (value.length < 6) {
21077             this.markInvalid(this.errors.PwdShort);
21078             this.errorMsg = this.errors.PwdShort;
21079             return false;
21080         }
21081         if (value.length > 16) {
21082             this.markInvalid(this.errors.PwdLong);
21083             this.errorMsg = this.errors.PwdLong;
21084             return false;
21085         }
21086         var strength;
21087         if (this.ClientSideStrongPassword(value)) {
21088             strength = 3;
21089         } else if (this.ClientSideMediumPassword(value)) {
21090             strength = 2;
21091         } else if (this.ClientSideWeakPassword(value)) {
21092             strength = 1;
21093         } else {
21094             strength = 0;
21095         }
21096
21097         
21098         if (strength < 2) {
21099             //this.markInvalid(this.errors.TooWeak);
21100             this.errorMsg = this.errors.TooWeak;
21101             //return false;
21102         }
21103         
21104         
21105         console.log('strength2: ' + strength);
21106         
21107         //var pm = this.trigger.child('div/div/div').dom;
21108         
21109         var pm = this.trigger.child('div/div');
21110         pm.removeClass(this.meterClass);
21111         pm.addClass(this.meterClass[strength]);
21112                 
21113         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21114                 
21115         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21116         
21117         this.errorMsg = ''; 
21118         return true;
21119     },
21120     // private
21121     CharacterSetChecks: function (type)
21122     {
21123         this.type = type;
21124         this.fResult = false;
21125     },
21126     // private
21127     isctype: function (character, type)
21128     {
21129         switch (type) {  
21130             case this.kCapitalLetter:
21131                 if (character >= 'A' && character <= 'Z') {
21132                     return true;
21133                 }
21134                 break;
21135             
21136             case this.kSmallLetter:
21137                 if (character >= 'a' && character <= 'z') {
21138                     return true;
21139                 }
21140                 break;
21141             
21142             case this.kDigit:
21143                 if (character >= '0' && character <= '9') {
21144                     return true;
21145                 }
21146                 break;
21147             
21148             case this.kPunctuation:
21149                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21150                     return true;
21151                 }
21152                 break;
21153             
21154             default:
21155                 return false;
21156         }
21157
21158     },
21159     // private
21160     IsLongEnough: function (pwd, size)
21161     {
21162         return !(pwd == null || isNaN(size) || pwd.length < size);
21163     },
21164     // private
21165     SpansEnoughCharacterSets: function (word, nb)
21166     {
21167         if (!this.IsLongEnough(word, nb))
21168         {
21169             return false;
21170         }
21171
21172         var characterSetChecks = new Array(
21173             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21174             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21175         );
21176         
21177         for (var index = 0; index < word.length; ++index) {
21178             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21179                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21180                     characterSetChecks[nCharSet].fResult = true;
21181                     break;
21182                 }
21183             }
21184         }
21185
21186         var nCharSets = 0;
21187         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21188             if (characterSetChecks[nCharSet].fResult) {
21189                 ++nCharSets;
21190             }
21191         }
21192
21193         if (nCharSets < nb) {
21194             return false;
21195         }
21196         return true;
21197     },
21198     // private
21199     ClientSideStrongPassword: function (pwd)
21200     {
21201         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21202     },
21203     // private
21204     ClientSideMediumPassword: function (pwd)
21205     {
21206         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21207     },
21208     // private
21209     ClientSideWeakPassword: function (pwd)
21210     {
21211         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21212     }
21213           
21214 })//<script type="text/javascript">
21215
21216 /*
21217  * Based  Ext JS Library 1.1.1
21218  * Copyright(c) 2006-2007, Ext JS, LLC.
21219  * LGPL
21220  *
21221  */
21222  
21223 /**
21224  * @class Roo.HtmlEditorCore
21225  * @extends Roo.Component
21226  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21227  *
21228  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21229  */
21230
21231 Roo.HtmlEditorCore = function(config){
21232     
21233     
21234     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21235     
21236     
21237     this.addEvents({
21238         /**
21239          * @event initialize
21240          * Fires when the editor is fully initialized (including the iframe)
21241          * @param {Roo.HtmlEditorCore} this
21242          */
21243         initialize: true,
21244         /**
21245          * @event activate
21246          * Fires when the editor is first receives the focus. Any insertion must wait
21247          * until after this event.
21248          * @param {Roo.HtmlEditorCore} this
21249          */
21250         activate: true,
21251          /**
21252          * @event beforesync
21253          * Fires before the textarea is updated with content from the editor iframe. Return false
21254          * to cancel the sync.
21255          * @param {Roo.HtmlEditorCore} this
21256          * @param {String} html
21257          */
21258         beforesync: true,
21259          /**
21260          * @event beforepush
21261          * Fires before the iframe editor is updated with content from the textarea. Return false
21262          * to cancel the push.
21263          * @param {Roo.HtmlEditorCore} this
21264          * @param {String} html
21265          */
21266         beforepush: true,
21267          /**
21268          * @event sync
21269          * Fires when the textarea is updated with content from the editor iframe.
21270          * @param {Roo.HtmlEditorCore} this
21271          * @param {String} html
21272          */
21273         sync: true,
21274          /**
21275          * @event push
21276          * Fires when the iframe editor is updated with content from the textarea.
21277          * @param {Roo.HtmlEditorCore} this
21278          * @param {String} html
21279          */
21280         push: true,
21281         
21282         /**
21283          * @event editorevent
21284          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21285          * @param {Roo.HtmlEditorCore} this
21286          */
21287         editorevent: true
21288         
21289     });
21290     
21291     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21292     
21293     // defaults : white / black...
21294     this.applyBlacklists();
21295     
21296     
21297     
21298 };
21299
21300
21301 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21302
21303
21304      /**
21305      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21306      */
21307     
21308     owner : false,
21309     
21310      /**
21311      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21312      *                        Roo.resizable.
21313      */
21314     resizable : false,
21315      /**
21316      * @cfg {Number} height (in pixels)
21317      */   
21318     height: 300,
21319    /**
21320      * @cfg {Number} width (in pixels)
21321      */   
21322     width: 500,
21323     
21324     /**
21325      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21326      * 
21327      */
21328     stylesheets: false,
21329     
21330     // id of frame..
21331     frameId: false,
21332     
21333     // private properties
21334     validationEvent : false,
21335     deferHeight: true,
21336     initialized : false,
21337     activated : false,
21338     sourceEditMode : false,
21339     onFocus : Roo.emptyFn,
21340     iframePad:3,
21341     hideMode:'offsets',
21342     
21343     clearUp: true,
21344     
21345     // blacklist + whitelisted elements..
21346     black: false,
21347     white: false,
21348      
21349     bodyCls : '',
21350
21351     /**
21352      * Protected method that will not generally be called directly. It
21353      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21354      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21355      */
21356     getDocMarkup : function(){
21357         // body styles..
21358         var st = '';
21359         
21360         // inherit styels from page...?? 
21361         if (this.stylesheets === false) {
21362             
21363             Roo.get(document.head).select('style').each(function(node) {
21364                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21365             });
21366             
21367             Roo.get(document.head).select('link').each(function(node) { 
21368                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21369             });
21370             
21371         } else if (!this.stylesheets.length) {
21372                 // simple..
21373                 st = '<style type="text/css">' +
21374                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21375                    '</style>';
21376         } else { 
21377             st = '<style type="text/css">' +
21378                     this.stylesheets +
21379                 '</style>';
21380         }
21381         
21382         st +=  '<style type="text/css">' +
21383             'IMG { cursor: pointer } ' +
21384         '</style>';
21385
21386         var cls = 'roo-htmleditor-body';
21387         
21388         if(this.bodyCls.length){
21389             cls += ' ' + this.bodyCls;
21390         }
21391         
21392         return '<html><head>' + st  +
21393             //<style type="text/css">' +
21394             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21395             //'</style>' +
21396             ' </head><body class="' +  cls + '"></body></html>';
21397     },
21398
21399     // private
21400     onRender : function(ct, position)
21401     {
21402         var _t = this;
21403         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21404         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21405         
21406         
21407         this.el.dom.style.border = '0 none';
21408         this.el.dom.setAttribute('tabIndex', -1);
21409         this.el.addClass('x-hidden hide');
21410         
21411         
21412         
21413         if(Roo.isIE){ // fix IE 1px bogus margin
21414             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21415         }
21416        
21417         
21418         this.frameId = Roo.id();
21419         
21420          
21421         
21422         var iframe = this.owner.wrap.createChild({
21423             tag: 'iframe',
21424             cls: 'form-control', // bootstrap..
21425             id: this.frameId,
21426             name: this.frameId,
21427             frameBorder : 'no',
21428             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21429         }, this.el
21430         );
21431         
21432         
21433         this.iframe = iframe.dom;
21434
21435          this.assignDocWin();
21436         
21437         this.doc.designMode = 'on';
21438        
21439         this.doc.open();
21440         this.doc.write(this.getDocMarkup());
21441         this.doc.close();
21442
21443         
21444         var task = { // must defer to wait for browser to be ready
21445             run : function(){
21446                 //console.log("run task?" + this.doc.readyState);
21447                 this.assignDocWin();
21448                 if(this.doc.body || this.doc.readyState == 'complete'){
21449                     try {
21450                         this.doc.designMode="on";
21451                     } catch (e) {
21452                         return;
21453                     }
21454                     Roo.TaskMgr.stop(task);
21455                     this.initEditor.defer(10, this);
21456                 }
21457             },
21458             interval : 10,
21459             duration: 10000,
21460             scope: this
21461         };
21462         Roo.TaskMgr.start(task);
21463
21464     },
21465
21466     // private
21467     onResize : function(w, h)
21468     {
21469          Roo.log('resize: ' +w + ',' + h );
21470         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21471         if(!this.iframe){
21472             return;
21473         }
21474         if(typeof w == 'number'){
21475             
21476             this.iframe.style.width = w + 'px';
21477         }
21478         if(typeof h == 'number'){
21479             
21480             this.iframe.style.height = h + 'px';
21481             if(this.doc){
21482                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21483             }
21484         }
21485         
21486     },
21487
21488     /**
21489      * Toggles the editor between standard and source edit mode.
21490      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21491      */
21492     toggleSourceEdit : function(sourceEditMode){
21493         
21494         this.sourceEditMode = sourceEditMode === true;
21495         
21496         if(this.sourceEditMode){
21497  
21498             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21499             
21500         }else{
21501             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21502             //this.iframe.className = '';
21503             this.deferFocus();
21504         }
21505         //this.setSize(this.owner.wrap.getSize());
21506         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21507     },
21508
21509     
21510   
21511
21512     /**
21513      * Protected method that will not generally be called directly. If you need/want
21514      * custom HTML cleanup, this is the method you should override.
21515      * @param {String} html The HTML to be cleaned
21516      * return {String} The cleaned HTML
21517      */
21518     cleanHtml : function(html){
21519         html = String(html);
21520         if(html.length > 5){
21521             if(Roo.isSafari){ // strip safari nonsense
21522                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21523             }
21524         }
21525         if(html == '&nbsp;'){
21526             html = '';
21527         }
21528         return html;
21529     },
21530
21531     /**
21532      * HTML Editor -> Textarea
21533      * Protected method that will not generally be called directly. Syncs the contents
21534      * of the editor iframe with the textarea.
21535      */
21536     syncValue : function(){
21537         if(this.initialized){
21538             var bd = (this.doc.body || this.doc.documentElement);
21539             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21540             var html = bd.innerHTML;
21541             if(Roo.isSafari){
21542                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21543                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21544                 if(m && m[1]){
21545                     html = '<div style="'+m[0]+'">' + html + '</div>';
21546                 }
21547             }
21548             html = this.cleanHtml(html);
21549             // fix up the special chars.. normaly like back quotes in word...
21550             // however we do not want to do this with chinese..
21551             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21552                 var cc = b.charCodeAt();
21553                 if (
21554                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21555                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21556                     (cc >= 0xf900 && cc < 0xfb00 )
21557                 ) {
21558                         return b;
21559                 }
21560                 return "&#"+cc+";" 
21561             });
21562             if(this.owner.fireEvent('beforesync', this, html) !== false){
21563                 this.el.dom.value = html;
21564                 this.owner.fireEvent('sync', this, html);
21565             }
21566         }
21567     },
21568
21569     /**
21570      * Protected method that will not generally be called directly. Pushes the value of the textarea
21571      * into the iframe editor.
21572      */
21573     pushValue : function(){
21574         if(this.initialized){
21575             var v = this.el.dom.value.trim();
21576             
21577 //            if(v.length < 1){
21578 //                v = '&#160;';
21579 //            }
21580             
21581             if(this.owner.fireEvent('beforepush', this, v) !== false){
21582                 var d = (this.doc.body || this.doc.documentElement);
21583                 d.innerHTML = v;
21584                 this.cleanUpPaste();
21585                 this.el.dom.value = d.innerHTML;
21586                 this.owner.fireEvent('push', this, v);
21587             }
21588         }
21589     },
21590
21591     // private
21592     deferFocus : function(){
21593         this.focus.defer(10, this);
21594     },
21595
21596     // doc'ed in Field
21597     focus : function(){
21598         if(this.win && !this.sourceEditMode){
21599             this.win.focus();
21600         }else{
21601             this.el.focus();
21602         }
21603     },
21604     
21605     assignDocWin: function()
21606     {
21607         var iframe = this.iframe;
21608         
21609          if(Roo.isIE){
21610             this.doc = iframe.contentWindow.document;
21611             this.win = iframe.contentWindow;
21612         } else {
21613 //            if (!Roo.get(this.frameId)) {
21614 //                return;
21615 //            }
21616 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21617 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21618             
21619             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21620                 return;
21621             }
21622             
21623             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21624             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21625         }
21626     },
21627     
21628     // private
21629     initEditor : function(){
21630         //console.log("INIT EDITOR");
21631         this.assignDocWin();
21632         
21633         
21634         
21635         this.doc.designMode="on";
21636         this.doc.open();
21637         this.doc.write(this.getDocMarkup());
21638         this.doc.close();
21639         
21640         var dbody = (this.doc.body || this.doc.documentElement);
21641         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21642         // this copies styles from the containing element into thsi one..
21643         // not sure why we need all of this..
21644         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21645         
21646         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21647         //ss['background-attachment'] = 'fixed'; // w3c
21648         dbody.bgProperties = 'fixed'; // ie
21649         //Roo.DomHelper.applyStyles(dbody, ss);
21650         Roo.EventManager.on(this.doc, {
21651             //'mousedown': this.onEditorEvent,
21652             'mouseup': this.onEditorEvent,
21653             'dblclick': this.onEditorEvent,
21654             'click': this.onEditorEvent,
21655             'keyup': this.onEditorEvent,
21656             buffer:100,
21657             scope: this
21658         });
21659         if(Roo.isGecko){
21660             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21661         }
21662         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21663             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21664         }
21665         this.initialized = true;
21666
21667         this.owner.fireEvent('initialize', this);
21668         this.pushValue();
21669     },
21670
21671     // private
21672     onDestroy : function(){
21673         
21674         
21675         
21676         if(this.rendered){
21677             
21678             //for (var i =0; i < this.toolbars.length;i++) {
21679             //    // fixme - ask toolbars for heights?
21680             //    this.toolbars[i].onDestroy();
21681            // }
21682             
21683             //this.wrap.dom.innerHTML = '';
21684             //this.wrap.remove();
21685         }
21686     },
21687
21688     // private
21689     onFirstFocus : function(){
21690         
21691         this.assignDocWin();
21692         
21693         
21694         this.activated = true;
21695          
21696     
21697         if(Roo.isGecko){ // prevent silly gecko errors
21698             this.win.focus();
21699             var s = this.win.getSelection();
21700             if(!s.focusNode || s.focusNode.nodeType != 3){
21701                 var r = s.getRangeAt(0);
21702                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21703                 r.collapse(true);
21704                 this.deferFocus();
21705             }
21706             try{
21707                 this.execCmd('useCSS', true);
21708                 this.execCmd('styleWithCSS', false);
21709             }catch(e){}
21710         }
21711         this.owner.fireEvent('activate', this);
21712     },
21713
21714     // private
21715     adjustFont: function(btn){
21716         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21717         //if(Roo.isSafari){ // safari
21718         //    adjust *= 2;
21719        // }
21720         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21721         if(Roo.isSafari){ // safari
21722             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21723             v =  (v < 10) ? 10 : v;
21724             v =  (v > 48) ? 48 : v;
21725             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21726             
21727         }
21728         
21729         
21730         v = Math.max(1, v+adjust);
21731         
21732         this.execCmd('FontSize', v  );
21733     },
21734
21735     onEditorEvent : function(e)
21736     {
21737         this.owner.fireEvent('editorevent', this, e);
21738       //  this.updateToolbar();
21739         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21740     },
21741
21742     insertTag : function(tg)
21743     {
21744         // could be a bit smarter... -> wrap the current selected tRoo..
21745         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21746             
21747             range = this.createRange(this.getSelection());
21748             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21749             wrappingNode.appendChild(range.extractContents());
21750             range.insertNode(wrappingNode);
21751
21752             return;
21753             
21754             
21755             
21756         }
21757         this.execCmd("formatblock",   tg);
21758         
21759     },
21760     
21761     insertText : function(txt)
21762     {
21763         
21764         
21765         var range = this.createRange();
21766         range.deleteContents();
21767                //alert(Sender.getAttribute('label'));
21768                
21769         range.insertNode(this.doc.createTextNode(txt));
21770     } ,
21771     
21772      
21773
21774     /**
21775      * Executes a Midas editor command on the editor document and performs necessary focus and
21776      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21777      * @param {String} cmd The Midas command
21778      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21779      */
21780     relayCmd : function(cmd, value){
21781         this.win.focus();
21782         this.execCmd(cmd, value);
21783         this.owner.fireEvent('editorevent', this);
21784         //this.updateToolbar();
21785         this.owner.deferFocus();
21786     },
21787
21788     /**
21789      * Executes a Midas editor command directly on the editor document.
21790      * For visual commands, you should use {@link #relayCmd} instead.
21791      * <b>This should only be called after the editor is initialized.</b>
21792      * @param {String} cmd The Midas command
21793      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21794      */
21795     execCmd : function(cmd, value){
21796         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21797         this.syncValue();
21798     },
21799  
21800  
21801    
21802     /**
21803      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21804      * to insert tRoo.
21805      * @param {String} text | dom node.. 
21806      */
21807     insertAtCursor : function(text)
21808     {
21809         
21810         if(!this.activated){
21811             return;
21812         }
21813         /*
21814         if(Roo.isIE){
21815             this.win.focus();
21816             var r = this.doc.selection.createRange();
21817             if(r){
21818                 r.collapse(true);
21819                 r.pasteHTML(text);
21820                 this.syncValue();
21821                 this.deferFocus();
21822             
21823             }
21824             return;
21825         }
21826         */
21827         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21828             this.win.focus();
21829             
21830             
21831             // from jquery ui (MIT licenced)
21832             var range, node;
21833             var win = this.win;
21834             
21835             if (win.getSelection && win.getSelection().getRangeAt) {
21836                 range = win.getSelection().getRangeAt(0);
21837                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21838                 range.insertNode(node);
21839             } else if (win.document.selection && win.document.selection.createRange) {
21840                 // no firefox support
21841                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21842                 win.document.selection.createRange().pasteHTML(txt);
21843             } else {
21844                 // no firefox support
21845                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21846                 this.execCmd('InsertHTML', txt);
21847             } 
21848             
21849             this.syncValue();
21850             
21851             this.deferFocus();
21852         }
21853     },
21854  // private
21855     mozKeyPress : function(e){
21856         if(e.ctrlKey){
21857             var c = e.getCharCode(), cmd;
21858           
21859             if(c > 0){
21860                 c = String.fromCharCode(c).toLowerCase();
21861                 switch(c){
21862                     case 'b':
21863                         cmd = 'bold';
21864                         break;
21865                     case 'i':
21866                         cmd = 'italic';
21867                         break;
21868                     
21869                     case 'u':
21870                         cmd = 'underline';
21871                         break;
21872                     
21873                     case 'v':
21874                         this.cleanUpPaste.defer(100, this);
21875                         return;
21876                         
21877                 }
21878                 if(cmd){
21879                     this.win.focus();
21880                     this.execCmd(cmd);
21881                     this.deferFocus();
21882                     e.preventDefault();
21883                 }
21884                 
21885             }
21886         }
21887     },
21888
21889     // private
21890     fixKeys : function(){ // load time branching for fastest keydown performance
21891         if(Roo.isIE){
21892             return function(e){
21893                 var k = e.getKey(), r;
21894                 if(k == e.TAB){
21895                     e.stopEvent();
21896                     r = this.doc.selection.createRange();
21897                     if(r){
21898                         r.collapse(true);
21899                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21900                         this.deferFocus();
21901                     }
21902                     return;
21903                 }
21904                 
21905                 if(k == e.ENTER){
21906                     r = this.doc.selection.createRange();
21907                     if(r){
21908                         var target = r.parentElement();
21909                         if(!target || target.tagName.toLowerCase() != 'li'){
21910                             e.stopEvent();
21911                             r.pasteHTML('<br />');
21912                             r.collapse(false);
21913                             r.select();
21914                         }
21915                     }
21916                 }
21917                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21918                     this.cleanUpPaste.defer(100, this);
21919                     return;
21920                 }
21921                 
21922                 
21923             };
21924         }else if(Roo.isOpera){
21925             return function(e){
21926                 var k = e.getKey();
21927                 if(k == e.TAB){
21928                     e.stopEvent();
21929                     this.win.focus();
21930                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21931                     this.deferFocus();
21932                 }
21933                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21934                     this.cleanUpPaste.defer(100, this);
21935                     return;
21936                 }
21937                 
21938             };
21939         }else if(Roo.isSafari){
21940             return function(e){
21941                 var k = e.getKey();
21942                 
21943                 if(k == e.TAB){
21944                     e.stopEvent();
21945                     this.execCmd('InsertText','\t');
21946                     this.deferFocus();
21947                     return;
21948                 }
21949                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21950                     this.cleanUpPaste.defer(100, this);
21951                     return;
21952                 }
21953                 
21954              };
21955         }
21956     }(),
21957     
21958     getAllAncestors: function()
21959     {
21960         var p = this.getSelectedNode();
21961         var a = [];
21962         if (!p) {
21963             a.push(p); // push blank onto stack..
21964             p = this.getParentElement();
21965         }
21966         
21967         
21968         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21969             a.push(p);
21970             p = p.parentNode;
21971         }
21972         a.push(this.doc.body);
21973         return a;
21974     },
21975     lastSel : false,
21976     lastSelNode : false,
21977     
21978     
21979     getSelection : function() 
21980     {
21981         this.assignDocWin();
21982         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21983     },
21984     
21985     getSelectedNode: function() 
21986     {
21987         // this may only work on Gecko!!!
21988         
21989         // should we cache this!!!!
21990         
21991         
21992         
21993          
21994         var range = this.createRange(this.getSelection()).cloneRange();
21995         
21996         if (Roo.isIE) {
21997             var parent = range.parentElement();
21998             while (true) {
21999                 var testRange = range.duplicate();
22000                 testRange.moveToElementText(parent);
22001                 if (testRange.inRange(range)) {
22002                     break;
22003                 }
22004                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22005                     break;
22006                 }
22007                 parent = parent.parentElement;
22008             }
22009             return parent;
22010         }
22011         
22012         // is ancestor a text element.
22013         var ac =  range.commonAncestorContainer;
22014         if (ac.nodeType == 3) {
22015             ac = ac.parentNode;
22016         }
22017         
22018         var ar = ac.childNodes;
22019          
22020         var nodes = [];
22021         var other_nodes = [];
22022         var has_other_nodes = false;
22023         for (var i=0;i<ar.length;i++) {
22024             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22025                 continue;
22026             }
22027             // fullly contained node.
22028             
22029             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22030                 nodes.push(ar[i]);
22031                 continue;
22032             }
22033             
22034             // probably selected..
22035             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22036                 other_nodes.push(ar[i]);
22037                 continue;
22038             }
22039             // outer..
22040             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22041                 continue;
22042             }
22043             
22044             
22045             has_other_nodes = true;
22046         }
22047         if (!nodes.length && other_nodes.length) {
22048             nodes= other_nodes;
22049         }
22050         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22051             return false;
22052         }
22053         
22054         return nodes[0];
22055     },
22056     createRange: function(sel)
22057     {
22058         // this has strange effects when using with 
22059         // top toolbar - not sure if it's a great idea.
22060         //this.editor.contentWindow.focus();
22061         if (typeof sel != "undefined") {
22062             try {
22063                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22064             } catch(e) {
22065                 return this.doc.createRange();
22066             }
22067         } else {
22068             return this.doc.createRange();
22069         }
22070     },
22071     getParentElement: function()
22072     {
22073         
22074         this.assignDocWin();
22075         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22076         
22077         var range = this.createRange(sel);
22078          
22079         try {
22080             var p = range.commonAncestorContainer;
22081             while (p.nodeType == 3) { // text node
22082                 p = p.parentNode;
22083             }
22084             return p;
22085         } catch (e) {
22086             return null;
22087         }
22088     
22089     },
22090     /***
22091      *
22092      * Range intersection.. the hard stuff...
22093      *  '-1' = before
22094      *  '0' = hits..
22095      *  '1' = after.
22096      *         [ -- selected range --- ]
22097      *   [fail]                        [fail]
22098      *
22099      *    basically..
22100      *      if end is before start or  hits it. fail.
22101      *      if start is after end or hits it fail.
22102      *
22103      *   if either hits (but other is outside. - then it's not 
22104      *   
22105      *    
22106      **/
22107     
22108     
22109     // @see http://www.thismuchiknow.co.uk/?p=64.
22110     rangeIntersectsNode : function(range, node)
22111     {
22112         var nodeRange = node.ownerDocument.createRange();
22113         try {
22114             nodeRange.selectNode(node);
22115         } catch (e) {
22116             nodeRange.selectNodeContents(node);
22117         }
22118     
22119         var rangeStartRange = range.cloneRange();
22120         rangeStartRange.collapse(true);
22121     
22122         var rangeEndRange = range.cloneRange();
22123         rangeEndRange.collapse(false);
22124     
22125         var nodeStartRange = nodeRange.cloneRange();
22126         nodeStartRange.collapse(true);
22127     
22128         var nodeEndRange = nodeRange.cloneRange();
22129         nodeEndRange.collapse(false);
22130     
22131         return rangeStartRange.compareBoundaryPoints(
22132                  Range.START_TO_START, nodeEndRange) == -1 &&
22133                rangeEndRange.compareBoundaryPoints(
22134                  Range.START_TO_START, nodeStartRange) == 1;
22135         
22136          
22137     },
22138     rangeCompareNode : function(range, node)
22139     {
22140         var nodeRange = node.ownerDocument.createRange();
22141         try {
22142             nodeRange.selectNode(node);
22143         } catch (e) {
22144             nodeRange.selectNodeContents(node);
22145         }
22146         
22147         
22148         range.collapse(true);
22149     
22150         nodeRange.collapse(true);
22151      
22152         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22153         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22154          
22155         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22156         
22157         var nodeIsBefore   =  ss == 1;
22158         var nodeIsAfter    = ee == -1;
22159         
22160         if (nodeIsBefore && nodeIsAfter) {
22161             return 0; // outer
22162         }
22163         if (!nodeIsBefore && nodeIsAfter) {
22164             return 1; //right trailed.
22165         }
22166         
22167         if (nodeIsBefore && !nodeIsAfter) {
22168             return 2;  // left trailed.
22169         }
22170         // fully contined.
22171         return 3;
22172     },
22173
22174     // private? - in a new class?
22175     cleanUpPaste :  function()
22176     {
22177         // cleans up the whole document..
22178         Roo.log('cleanuppaste');
22179         
22180         this.cleanUpChildren(this.doc.body);
22181         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22182         if (clean != this.doc.body.innerHTML) {
22183             this.doc.body.innerHTML = clean;
22184         }
22185         
22186     },
22187     
22188     cleanWordChars : function(input) {// change the chars to hex code
22189         var he = Roo.HtmlEditorCore;
22190         
22191         var output = input;
22192         Roo.each(he.swapCodes, function(sw) { 
22193             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22194             
22195             output = output.replace(swapper, sw[1]);
22196         });
22197         
22198         return output;
22199     },
22200     
22201     
22202     cleanUpChildren : function (n)
22203     {
22204         if (!n.childNodes.length) {
22205             return;
22206         }
22207         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22208            this.cleanUpChild(n.childNodes[i]);
22209         }
22210     },
22211     
22212     
22213         
22214     
22215     cleanUpChild : function (node)
22216     {
22217         var ed = this;
22218         //console.log(node);
22219         if (node.nodeName == "#text") {
22220             // clean up silly Windows -- stuff?
22221             return; 
22222         }
22223         if (node.nodeName == "#comment") {
22224             node.parentNode.removeChild(node);
22225             // clean up silly Windows -- stuff?
22226             return; 
22227         }
22228         var lcname = node.tagName.toLowerCase();
22229         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22230         // whitelist of tags..
22231         
22232         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22233             // remove node.
22234             node.parentNode.removeChild(node);
22235             return;
22236             
22237         }
22238         
22239         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22240         
22241         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22242         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22243         
22244         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22245         //    remove_keep_children = true;
22246         //}
22247         
22248         if (remove_keep_children) {
22249             this.cleanUpChildren(node);
22250             // inserts everything just before this node...
22251             while (node.childNodes.length) {
22252                 var cn = node.childNodes[0];
22253                 node.removeChild(cn);
22254                 node.parentNode.insertBefore(cn, node);
22255             }
22256             node.parentNode.removeChild(node);
22257             return;
22258         }
22259         
22260         if (!node.attributes || !node.attributes.length) {
22261             this.cleanUpChildren(node);
22262             return;
22263         }
22264         
22265         function cleanAttr(n,v)
22266         {
22267             
22268             if (v.match(/^\./) || v.match(/^\//)) {
22269                 return;
22270             }
22271             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22272                 return;
22273             }
22274             if (v.match(/^#/)) {
22275                 return;
22276             }
22277 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22278             node.removeAttribute(n);
22279             
22280         }
22281         
22282         var cwhite = this.cwhite;
22283         var cblack = this.cblack;
22284             
22285         function cleanStyle(n,v)
22286         {
22287             if (v.match(/expression/)) { //XSS?? should we even bother..
22288                 node.removeAttribute(n);
22289                 return;
22290             }
22291             
22292             var parts = v.split(/;/);
22293             var clean = [];
22294             
22295             Roo.each(parts, function(p) {
22296                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22297                 if (!p.length) {
22298                     return true;
22299                 }
22300                 var l = p.split(':').shift().replace(/\s+/g,'');
22301                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22302                 
22303                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22304 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22305                     //node.removeAttribute(n);
22306                     return true;
22307                 }
22308                 //Roo.log()
22309                 // only allow 'c whitelisted system attributes'
22310                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22311 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22312                     //node.removeAttribute(n);
22313                     return true;
22314                 }
22315                 
22316                 
22317                  
22318                 
22319                 clean.push(p);
22320                 return true;
22321             });
22322             if (clean.length) { 
22323                 node.setAttribute(n, clean.join(';'));
22324             } else {
22325                 node.removeAttribute(n);
22326             }
22327             
22328         }
22329         
22330         
22331         for (var i = node.attributes.length-1; i > -1 ; i--) {
22332             var a = node.attributes[i];
22333             //console.log(a);
22334             
22335             if (a.name.toLowerCase().substr(0,2)=='on')  {
22336                 node.removeAttribute(a.name);
22337                 continue;
22338             }
22339             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22340                 node.removeAttribute(a.name);
22341                 continue;
22342             }
22343             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22344                 cleanAttr(a.name,a.value); // fixme..
22345                 continue;
22346             }
22347             if (a.name == 'style') {
22348                 cleanStyle(a.name,a.value);
22349                 continue;
22350             }
22351             /// clean up MS crap..
22352             // tecnically this should be a list of valid class'es..
22353             
22354             
22355             if (a.name == 'class') {
22356                 if (a.value.match(/^Mso/)) {
22357                     node.className = '';
22358                 }
22359                 
22360                 if (a.value.match(/^body$/)) {
22361                     node.className = '';
22362                 }
22363                 continue;
22364             }
22365             
22366             // style cleanup!?
22367             // class cleanup?
22368             
22369         }
22370         
22371         
22372         this.cleanUpChildren(node);
22373         
22374         
22375     },
22376     
22377     /**
22378      * Clean up MS wordisms...
22379      */
22380     cleanWord : function(node)
22381     {
22382         
22383         
22384         if (!node) {
22385             this.cleanWord(this.doc.body);
22386             return;
22387         }
22388         if (node.nodeName == "#text") {
22389             // clean up silly Windows -- stuff?
22390             return; 
22391         }
22392         if (node.nodeName == "#comment") {
22393             node.parentNode.removeChild(node);
22394             // clean up silly Windows -- stuff?
22395             return; 
22396         }
22397         
22398         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22399             node.parentNode.removeChild(node);
22400             return;
22401         }
22402         
22403         // remove - but keep children..
22404         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22405             while (node.childNodes.length) {
22406                 var cn = node.childNodes[0];
22407                 node.removeChild(cn);
22408                 node.parentNode.insertBefore(cn, node);
22409             }
22410             node.parentNode.removeChild(node);
22411             this.iterateChildren(node, this.cleanWord);
22412             return;
22413         }
22414         // clean styles
22415         if (node.className.length) {
22416             
22417             var cn = node.className.split(/\W+/);
22418             var cna = [];
22419             Roo.each(cn, function(cls) {
22420                 if (cls.match(/Mso[a-zA-Z]+/)) {
22421                     return;
22422                 }
22423                 cna.push(cls);
22424             });
22425             node.className = cna.length ? cna.join(' ') : '';
22426             if (!cna.length) {
22427                 node.removeAttribute("class");
22428             }
22429         }
22430         
22431         if (node.hasAttribute("lang")) {
22432             node.removeAttribute("lang");
22433         }
22434         
22435         if (node.hasAttribute("style")) {
22436             
22437             var styles = node.getAttribute("style").split(";");
22438             var nstyle = [];
22439             Roo.each(styles, function(s) {
22440                 if (!s.match(/:/)) {
22441                     return;
22442                 }
22443                 var kv = s.split(":");
22444                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22445                     return;
22446                 }
22447                 // what ever is left... we allow.
22448                 nstyle.push(s);
22449             });
22450             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22451             if (!nstyle.length) {
22452                 node.removeAttribute('style');
22453             }
22454         }
22455         this.iterateChildren(node, this.cleanWord);
22456         
22457         
22458         
22459     },
22460     /**
22461      * iterateChildren of a Node, calling fn each time, using this as the scole..
22462      * @param {DomNode} node node to iterate children of.
22463      * @param {Function} fn method of this class to call on each item.
22464      */
22465     iterateChildren : function(node, fn)
22466     {
22467         if (!node.childNodes.length) {
22468                 return;
22469         }
22470         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22471            fn.call(this, node.childNodes[i])
22472         }
22473     },
22474     
22475     
22476     /**
22477      * cleanTableWidths.
22478      *
22479      * Quite often pasting from word etc.. results in tables with column and widths.
22480      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22481      *
22482      */
22483     cleanTableWidths : function(node)
22484     {
22485          
22486          
22487         if (!node) {
22488             this.cleanTableWidths(this.doc.body);
22489             return;
22490         }
22491         
22492         // ignore list...
22493         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22494             return; 
22495         }
22496         Roo.log(node.tagName);
22497         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22498             this.iterateChildren(node, this.cleanTableWidths);
22499             return;
22500         }
22501         if (node.hasAttribute('width')) {
22502             node.removeAttribute('width');
22503         }
22504         
22505          
22506         if (node.hasAttribute("style")) {
22507             // pretty basic...
22508             
22509             var styles = node.getAttribute("style").split(";");
22510             var nstyle = [];
22511             Roo.each(styles, function(s) {
22512                 if (!s.match(/:/)) {
22513                     return;
22514                 }
22515                 var kv = s.split(":");
22516                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22517                     return;
22518                 }
22519                 // what ever is left... we allow.
22520                 nstyle.push(s);
22521             });
22522             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22523             if (!nstyle.length) {
22524                 node.removeAttribute('style');
22525             }
22526         }
22527         
22528         this.iterateChildren(node, this.cleanTableWidths);
22529         
22530         
22531     },
22532     
22533     
22534     
22535     
22536     domToHTML : function(currentElement, depth, nopadtext) {
22537         
22538         depth = depth || 0;
22539         nopadtext = nopadtext || false;
22540     
22541         if (!currentElement) {
22542             return this.domToHTML(this.doc.body);
22543         }
22544         
22545         //Roo.log(currentElement);
22546         var j;
22547         var allText = false;
22548         var nodeName = currentElement.nodeName;
22549         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22550         
22551         if  (nodeName == '#text') {
22552             
22553             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22554         }
22555         
22556         
22557         var ret = '';
22558         if (nodeName != 'BODY') {
22559              
22560             var i = 0;
22561             // Prints the node tagName, such as <A>, <IMG>, etc
22562             if (tagName) {
22563                 var attr = [];
22564                 for(i = 0; i < currentElement.attributes.length;i++) {
22565                     // quoting?
22566                     var aname = currentElement.attributes.item(i).name;
22567                     if (!currentElement.attributes.item(i).value.length) {
22568                         continue;
22569                     }
22570                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22571                 }
22572                 
22573                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22574             } 
22575             else {
22576                 
22577                 // eack
22578             }
22579         } else {
22580             tagName = false;
22581         }
22582         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22583             return ret;
22584         }
22585         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22586             nopadtext = true;
22587         }
22588         
22589         
22590         // Traverse the tree
22591         i = 0;
22592         var currentElementChild = currentElement.childNodes.item(i);
22593         var allText = true;
22594         var innerHTML  = '';
22595         lastnode = '';
22596         while (currentElementChild) {
22597             // Formatting code (indent the tree so it looks nice on the screen)
22598             var nopad = nopadtext;
22599             if (lastnode == 'SPAN') {
22600                 nopad  = true;
22601             }
22602             // text
22603             if  (currentElementChild.nodeName == '#text') {
22604                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22605                 toadd = nopadtext ? toadd : toadd.trim();
22606                 if (!nopad && toadd.length > 80) {
22607                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22608                 }
22609                 innerHTML  += toadd;
22610                 
22611                 i++;
22612                 currentElementChild = currentElement.childNodes.item(i);
22613                 lastNode = '';
22614                 continue;
22615             }
22616             allText = false;
22617             
22618             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22619                 
22620             // Recursively traverse the tree structure of the child node
22621             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22622             lastnode = currentElementChild.nodeName;
22623             i++;
22624             currentElementChild=currentElement.childNodes.item(i);
22625         }
22626         
22627         ret += innerHTML;
22628         
22629         if (!allText) {
22630                 // The remaining code is mostly for formatting the tree
22631             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22632         }
22633         
22634         
22635         if (tagName) {
22636             ret+= "</"+tagName+">";
22637         }
22638         return ret;
22639         
22640     },
22641         
22642     applyBlacklists : function()
22643     {
22644         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22645         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22646         
22647         this.white = [];
22648         this.black = [];
22649         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22650             if (b.indexOf(tag) > -1) {
22651                 return;
22652             }
22653             this.white.push(tag);
22654             
22655         }, this);
22656         
22657         Roo.each(w, function(tag) {
22658             if (b.indexOf(tag) > -1) {
22659                 return;
22660             }
22661             if (this.white.indexOf(tag) > -1) {
22662                 return;
22663             }
22664             this.white.push(tag);
22665             
22666         }, this);
22667         
22668         
22669         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22670             if (w.indexOf(tag) > -1) {
22671                 return;
22672             }
22673             this.black.push(tag);
22674             
22675         }, this);
22676         
22677         Roo.each(b, function(tag) {
22678             if (w.indexOf(tag) > -1) {
22679                 return;
22680             }
22681             if (this.black.indexOf(tag) > -1) {
22682                 return;
22683             }
22684             this.black.push(tag);
22685             
22686         }, this);
22687         
22688         
22689         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22690         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22691         
22692         this.cwhite = [];
22693         this.cblack = [];
22694         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22695             if (b.indexOf(tag) > -1) {
22696                 return;
22697             }
22698             this.cwhite.push(tag);
22699             
22700         }, this);
22701         
22702         Roo.each(w, function(tag) {
22703             if (b.indexOf(tag) > -1) {
22704                 return;
22705             }
22706             if (this.cwhite.indexOf(tag) > -1) {
22707                 return;
22708             }
22709             this.cwhite.push(tag);
22710             
22711         }, this);
22712         
22713         
22714         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22715             if (w.indexOf(tag) > -1) {
22716                 return;
22717             }
22718             this.cblack.push(tag);
22719             
22720         }, this);
22721         
22722         Roo.each(b, function(tag) {
22723             if (w.indexOf(tag) > -1) {
22724                 return;
22725             }
22726             if (this.cblack.indexOf(tag) > -1) {
22727                 return;
22728             }
22729             this.cblack.push(tag);
22730             
22731         }, this);
22732     },
22733     
22734     setStylesheets : function(stylesheets)
22735     {
22736         if(typeof(stylesheets) == 'string'){
22737             Roo.get(this.iframe.contentDocument.head).createChild({
22738                 tag : 'link',
22739                 rel : 'stylesheet',
22740                 type : 'text/css',
22741                 href : stylesheets
22742             });
22743             
22744             return;
22745         }
22746         var _this = this;
22747      
22748         Roo.each(stylesheets, function(s) {
22749             if(!s.length){
22750                 return;
22751             }
22752             
22753             Roo.get(_this.iframe.contentDocument.head).createChild({
22754                 tag : 'link',
22755                 rel : 'stylesheet',
22756                 type : 'text/css',
22757                 href : s
22758             });
22759         });
22760
22761         
22762     },
22763     
22764     removeStylesheets : function()
22765     {
22766         var _this = this;
22767         
22768         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22769             s.remove();
22770         });
22771     },
22772     
22773     setStyle : function(style)
22774     {
22775         Roo.get(this.iframe.contentDocument.head).createChild({
22776             tag : 'style',
22777             type : 'text/css',
22778             html : style
22779         });
22780
22781         return;
22782     }
22783     
22784     // hide stuff that is not compatible
22785     /**
22786      * @event blur
22787      * @hide
22788      */
22789     /**
22790      * @event change
22791      * @hide
22792      */
22793     /**
22794      * @event focus
22795      * @hide
22796      */
22797     /**
22798      * @event specialkey
22799      * @hide
22800      */
22801     /**
22802      * @cfg {String} fieldClass @hide
22803      */
22804     /**
22805      * @cfg {String} focusClass @hide
22806      */
22807     /**
22808      * @cfg {String} autoCreate @hide
22809      */
22810     /**
22811      * @cfg {String} inputType @hide
22812      */
22813     /**
22814      * @cfg {String} invalidClass @hide
22815      */
22816     /**
22817      * @cfg {String} invalidText @hide
22818      */
22819     /**
22820      * @cfg {String} msgFx @hide
22821      */
22822     /**
22823      * @cfg {String} validateOnBlur @hide
22824      */
22825 });
22826
22827 Roo.HtmlEditorCore.white = [
22828         'area', 'br', 'img', 'input', 'hr', 'wbr',
22829         
22830        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22831        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22832        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22833        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22834        'table',   'ul',         'xmp', 
22835        
22836        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22837       'thead',   'tr', 
22838      
22839       'dir', 'menu', 'ol', 'ul', 'dl',
22840        
22841       'embed',  'object'
22842 ];
22843
22844
22845 Roo.HtmlEditorCore.black = [
22846     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22847         'applet', // 
22848         'base',   'basefont', 'bgsound', 'blink',  'body', 
22849         'frame',  'frameset', 'head',    'html',   'ilayer', 
22850         'iframe', 'layer',  'link',     'meta',    'object',   
22851         'script', 'style' ,'title',  'xml' // clean later..
22852 ];
22853 Roo.HtmlEditorCore.clean = [
22854     'script', 'style', 'title', 'xml'
22855 ];
22856 Roo.HtmlEditorCore.remove = [
22857     'font'
22858 ];
22859 // attributes..
22860
22861 Roo.HtmlEditorCore.ablack = [
22862     'on'
22863 ];
22864     
22865 Roo.HtmlEditorCore.aclean = [ 
22866     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22867 ];
22868
22869 // protocols..
22870 Roo.HtmlEditorCore.pwhite= [
22871         'http',  'https',  'mailto'
22872 ];
22873
22874 // white listed style attributes.
22875 Roo.HtmlEditorCore.cwhite= [
22876       //  'text-align', /// default is to allow most things..
22877       
22878          
22879 //        'font-size'//??
22880 ];
22881
22882 // black listed style attributes.
22883 Roo.HtmlEditorCore.cblack= [
22884       //  'font-size' -- this can be set by the project 
22885 ];
22886
22887
22888 Roo.HtmlEditorCore.swapCodes   =[ 
22889     [    8211, "--" ], 
22890     [    8212, "--" ], 
22891     [    8216,  "'" ],  
22892     [    8217, "'" ],  
22893     [    8220, '"' ],  
22894     [    8221, '"' ],  
22895     [    8226, "*" ],  
22896     [    8230, "..." ]
22897 ]; 
22898
22899     /*
22900  * - LGPL
22901  *
22902  * HtmlEditor
22903  * 
22904  */
22905
22906 /**
22907  * @class Roo.bootstrap.HtmlEditor
22908  * @extends Roo.bootstrap.TextArea
22909  * Bootstrap HtmlEditor class
22910
22911  * @constructor
22912  * Create a new HtmlEditor
22913  * @param {Object} config The config object
22914  */
22915
22916 Roo.bootstrap.HtmlEditor = function(config){
22917     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22918     if (!this.toolbars) {
22919         this.toolbars = [];
22920     }
22921     
22922     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22923     this.addEvents({
22924             /**
22925              * @event initialize
22926              * Fires when the editor is fully initialized (including the iframe)
22927              * @param {HtmlEditor} this
22928              */
22929             initialize: true,
22930             /**
22931              * @event activate
22932              * Fires when the editor is first receives the focus. Any insertion must wait
22933              * until after this event.
22934              * @param {HtmlEditor} this
22935              */
22936             activate: true,
22937              /**
22938              * @event beforesync
22939              * Fires before the textarea is updated with content from the editor iframe. Return false
22940              * to cancel the sync.
22941              * @param {HtmlEditor} this
22942              * @param {String} html
22943              */
22944             beforesync: true,
22945              /**
22946              * @event beforepush
22947              * Fires before the iframe editor is updated with content from the textarea. Return false
22948              * to cancel the push.
22949              * @param {HtmlEditor} this
22950              * @param {String} html
22951              */
22952             beforepush: true,
22953              /**
22954              * @event sync
22955              * Fires when the textarea is updated with content from the editor iframe.
22956              * @param {HtmlEditor} this
22957              * @param {String} html
22958              */
22959             sync: true,
22960              /**
22961              * @event push
22962              * Fires when the iframe editor is updated with content from the textarea.
22963              * @param {HtmlEditor} this
22964              * @param {String} html
22965              */
22966             push: true,
22967              /**
22968              * @event editmodechange
22969              * Fires when the editor switches edit modes
22970              * @param {HtmlEditor} this
22971              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22972              */
22973             editmodechange: true,
22974             /**
22975              * @event editorevent
22976              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22977              * @param {HtmlEditor} this
22978              */
22979             editorevent: true,
22980             /**
22981              * @event firstfocus
22982              * Fires when on first focus - needed by toolbars..
22983              * @param {HtmlEditor} this
22984              */
22985             firstfocus: true,
22986             /**
22987              * @event autosave
22988              * Auto save the htmlEditor value as a file into Events
22989              * @param {HtmlEditor} this
22990              */
22991             autosave: true,
22992             /**
22993              * @event savedpreview
22994              * preview the saved version of htmlEditor
22995              * @param {HtmlEditor} this
22996              */
22997             savedpreview: true
22998         });
22999 };
23000
23001
23002 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23003     
23004     
23005       /**
23006      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23007      */
23008     toolbars : false,
23009     
23010      /**
23011     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23012     */
23013     btns : [],
23014    
23015      /**
23016      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23017      *                        Roo.resizable.
23018      */
23019     resizable : false,
23020      /**
23021      * @cfg {Number} height (in pixels)
23022      */   
23023     height: 300,
23024    /**
23025      * @cfg {Number} width (in pixels)
23026      */   
23027     width: false,
23028     
23029     /**
23030      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23031      * 
23032      */
23033     stylesheets: false,
23034     
23035     // id of frame..
23036     frameId: false,
23037     
23038     // private properties
23039     validationEvent : false,
23040     deferHeight: true,
23041     initialized : false,
23042     activated : false,
23043     
23044     onFocus : Roo.emptyFn,
23045     iframePad:3,
23046     hideMode:'offsets',
23047     
23048     tbContainer : false,
23049     
23050     bodyCls : '',
23051     
23052     toolbarContainer :function() {
23053         return this.wrap.select('.x-html-editor-tb',true).first();
23054     },
23055
23056     /**
23057      * Protected method that will not generally be called directly. It
23058      * is called when the editor creates its toolbar. Override this method if you need to
23059      * add custom toolbar buttons.
23060      * @param {HtmlEditor} editor
23061      */
23062     createToolbar : function(){
23063         Roo.log('renewing');
23064         Roo.log("create toolbars");
23065         
23066         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23067         this.toolbars[0].render(this.toolbarContainer());
23068         
23069         return;
23070         
23071 //        if (!editor.toolbars || !editor.toolbars.length) {
23072 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23073 //        }
23074 //        
23075 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23076 //            editor.toolbars[i] = Roo.factory(
23077 //                    typeof(editor.toolbars[i]) == 'string' ?
23078 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23079 //                Roo.bootstrap.HtmlEditor);
23080 //            editor.toolbars[i].init(editor);
23081 //        }
23082     },
23083
23084      
23085     // private
23086     onRender : function(ct, position)
23087     {
23088        // Roo.log("Call onRender: " + this.xtype);
23089         var _t = this;
23090         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23091       
23092         this.wrap = this.inputEl().wrap({
23093             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23094         });
23095         
23096         this.editorcore.onRender(ct, position);
23097          
23098         if (this.resizable) {
23099             this.resizeEl = new Roo.Resizable(this.wrap, {
23100                 pinned : true,
23101                 wrap: true,
23102                 dynamic : true,
23103                 minHeight : this.height,
23104                 height: this.height,
23105                 handles : this.resizable,
23106                 width: this.width,
23107                 listeners : {
23108                     resize : function(r, w, h) {
23109                         _t.onResize(w,h); // -something
23110                     }
23111                 }
23112             });
23113             
23114         }
23115         this.createToolbar(this);
23116        
23117         
23118         if(!this.width && this.resizable){
23119             this.setSize(this.wrap.getSize());
23120         }
23121         if (this.resizeEl) {
23122             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23123             // should trigger onReize..
23124         }
23125         
23126     },
23127
23128     // private
23129     onResize : function(w, h)
23130     {
23131         Roo.log('resize: ' +w + ',' + h );
23132         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23133         var ew = false;
23134         var eh = false;
23135         
23136         if(this.inputEl() ){
23137             if(typeof w == 'number'){
23138                 var aw = w - this.wrap.getFrameWidth('lr');
23139                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23140                 ew = aw;
23141             }
23142             if(typeof h == 'number'){
23143                  var tbh = -11;  // fixme it needs to tool bar size!
23144                 for (var i =0; i < this.toolbars.length;i++) {
23145                     // fixme - ask toolbars for heights?
23146                     tbh += this.toolbars[i].el.getHeight();
23147                     //if (this.toolbars[i].footer) {
23148                     //    tbh += this.toolbars[i].footer.el.getHeight();
23149                     //}
23150                 }
23151               
23152                 
23153                 
23154                 
23155                 
23156                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23157                 ah -= 5; // knock a few pixes off for look..
23158                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23159                 var eh = ah;
23160             }
23161         }
23162         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23163         this.editorcore.onResize(ew,eh);
23164         
23165     },
23166
23167     /**
23168      * Toggles the editor between standard and source edit mode.
23169      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23170      */
23171     toggleSourceEdit : function(sourceEditMode)
23172     {
23173         this.editorcore.toggleSourceEdit(sourceEditMode);
23174         
23175         if(this.editorcore.sourceEditMode){
23176             Roo.log('editor - showing textarea');
23177             
23178 //            Roo.log('in');
23179 //            Roo.log(this.syncValue());
23180             this.syncValue();
23181             this.inputEl().removeClass(['hide', 'x-hidden']);
23182             this.inputEl().dom.removeAttribute('tabIndex');
23183             this.inputEl().focus();
23184         }else{
23185             Roo.log('editor - hiding textarea');
23186 //            Roo.log('out')
23187 //            Roo.log(this.pushValue()); 
23188             this.pushValue();
23189             
23190             this.inputEl().addClass(['hide', 'x-hidden']);
23191             this.inputEl().dom.setAttribute('tabIndex', -1);
23192             //this.deferFocus();
23193         }
23194          
23195         if(this.resizable){
23196             this.setSize(this.wrap.getSize());
23197         }
23198         
23199         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23200     },
23201  
23202     // private (for BoxComponent)
23203     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23204
23205     // private (for BoxComponent)
23206     getResizeEl : function(){
23207         return this.wrap;
23208     },
23209
23210     // private (for BoxComponent)
23211     getPositionEl : function(){
23212         return this.wrap;
23213     },
23214
23215     // private
23216     initEvents : function(){
23217         this.originalValue = this.getValue();
23218     },
23219
23220 //    /**
23221 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23222 //     * @method
23223 //     */
23224 //    markInvalid : Roo.emptyFn,
23225 //    /**
23226 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23227 //     * @method
23228 //     */
23229 //    clearInvalid : Roo.emptyFn,
23230
23231     setValue : function(v){
23232         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23233         this.editorcore.pushValue();
23234     },
23235
23236      
23237     // private
23238     deferFocus : function(){
23239         this.focus.defer(10, this);
23240     },
23241
23242     // doc'ed in Field
23243     focus : function(){
23244         this.editorcore.focus();
23245         
23246     },
23247       
23248
23249     // private
23250     onDestroy : function(){
23251         
23252         
23253         
23254         if(this.rendered){
23255             
23256             for (var i =0; i < this.toolbars.length;i++) {
23257                 // fixme - ask toolbars for heights?
23258                 this.toolbars[i].onDestroy();
23259             }
23260             
23261             this.wrap.dom.innerHTML = '';
23262             this.wrap.remove();
23263         }
23264     },
23265
23266     // private
23267     onFirstFocus : function(){
23268         //Roo.log("onFirstFocus");
23269         this.editorcore.onFirstFocus();
23270          for (var i =0; i < this.toolbars.length;i++) {
23271             this.toolbars[i].onFirstFocus();
23272         }
23273         
23274     },
23275     
23276     // private
23277     syncValue : function()
23278     {   
23279         this.editorcore.syncValue();
23280     },
23281     
23282     pushValue : function()
23283     {   
23284         this.editorcore.pushValue();
23285     }
23286      
23287     
23288     // hide stuff that is not compatible
23289     /**
23290      * @event blur
23291      * @hide
23292      */
23293     /**
23294      * @event change
23295      * @hide
23296      */
23297     /**
23298      * @event focus
23299      * @hide
23300      */
23301     /**
23302      * @event specialkey
23303      * @hide
23304      */
23305     /**
23306      * @cfg {String} fieldClass @hide
23307      */
23308     /**
23309      * @cfg {String} focusClass @hide
23310      */
23311     /**
23312      * @cfg {String} autoCreate @hide
23313      */
23314     /**
23315      * @cfg {String} inputType @hide
23316      */
23317     /**
23318      * @cfg {String} invalidClass @hide
23319      */
23320     /**
23321      * @cfg {String} invalidText @hide
23322      */
23323     /**
23324      * @cfg {String} msgFx @hide
23325      */
23326     /**
23327      * @cfg {String} validateOnBlur @hide
23328      */
23329 });
23330  
23331     
23332    
23333    
23334    
23335       
23336 Roo.namespace('Roo.bootstrap.htmleditor');
23337 /**
23338  * @class Roo.bootstrap.HtmlEditorToolbar1
23339  * Basic Toolbar
23340  * 
23341  * Usage:
23342  *
23343  new Roo.bootstrap.HtmlEditor({
23344     ....
23345     toolbars : [
23346         new Roo.bootstrap.HtmlEditorToolbar1({
23347             disable : { fonts: 1 , format: 1, ..., ... , ...],
23348             btns : [ .... ]
23349         })
23350     }
23351      
23352  * 
23353  * @cfg {Object} disable List of elements to disable..
23354  * @cfg {Array} btns List of additional buttons.
23355  * 
23356  * 
23357  * NEEDS Extra CSS? 
23358  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23359  */
23360  
23361 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23362 {
23363     
23364     Roo.apply(this, config);
23365     
23366     // default disabled, based on 'good practice'..
23367     this.disable = this.disable || {};
23368     Roo.applyIf(this.disable, {
23369         fontSize : true,
23370         colors : true,
23371         specialElements : true
23372     });
23373     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23374     
23375     this.editor = config.editor;
23376     this.editorcore = config.editor.editorcore;
23377     
23378     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23379     
23380     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23381     // dont call parent... till later.
23382 }
23383 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23384      
23385     bar : true,
23386     
23387     editor : false,
23388     editorcore : false,
23389     
23390     
23391     formats : [
23392         "p" ,  
23393         "h1","h2","h3","h4","h5","h6", 
23394         "pre", "code", 
23395         "abbr", "acronym", "address", "cite", "samp", "var",
23396         'div','span'
23397     ],
23398     
23399     onRender : function(ct, position)
23400     {
23401        // Roo.log("Call onRender: " + this.xtype);
23402         
23403        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23404        Roo.log(this.el);
23405        this.el.dom.style.marginBottom = '0';
23406        var _this = this;
23407        var editorcore = this.editorcore;
23408        var editor= this.editor;
23409        
23410        var children = [];
23411        var btn = function(id,cmd , toggle, handler, html){
23412        
23413             var  event = toggle ? 'toggle' : 'click';
23414        
23415             var a = {
23416                 size : 'sm',
23417                 xtype: 'Button',
23418                 xns: Roo.bootstrap,
23419                 glyphicon : id,
23420                 cmd : id || cmd,
23421                 enableToggle:toggle !== false,
23422                 html : html || '',
23423                 pressed : toggle ? false : null,
23424                 listeners : {}
23425             };
23426             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23427                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23428             };
23429             children.push(a);
23430             return a;
23431        }
23432        
23433     //    var cb_box = function...
23434         
23435         var style = {
23436                 xtype: 'Button',
23437                 size : 'sm',
23438                 xns: Roo.bootstrap,
23439                 glyphicon : 'font',
23440                 //html : 'submit'
23441                 menu : {
23442                     xtype: 'Menu',
23443                     xns: Roo.bootstrap,
23444                     items:  []
23445                 }
23446         };
23447         Roo.each(this.formats, function(f) {
23448             style.menu.items.push({
23449                 xtype :'MenuItem',
23450                 xns: Roo.bootstrap,
23451                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23452                 tagname : f,
23453                 listeners : {
23454                     click : function()
23455                     {
23456                         editorcore.insertTag(this.tagname);
23457                         editor.focus();
23458                     }
23459                 }
23460                 
23461             });
23462         });
23463         children.push(style);   
23464         
23465         btn('bold',false,true);
23466         btn('italic',false,true);
23467         btn('align-left', 'justifyleft',true);
23468         btn('align-center', 'justifycenter',true);
23469         btn('align-right' , 'justifyright',true);
23470         btn('link', false, false, function(btn) {
23471             //Roo.log("create link?");
23472             var url = prompt(this.createLinkText, this.defaultLinkValue);
23473             if(url && url != 'http:/'+'/'){
23474                 this.editorcore.relayCmd('createlink', url);
23475             }
23476         }),
23477         btn('list','insertunorderedlist',true);
23478         btn('pencil', false,true, function(btn){
23479                 Roo.log(this);
23480                 this.toggleSourceEdit(btn.pressed);
23481         });
23482         
23483         if (this.editor.btns.length > 0) {
23484             for (var i = 0; i<this.editor.btns.length; i++) {
23485                 children.push(this.editor.btns[i]);
23486             }
23487         }
23488         
23489         /*
23490         var cog = {
23491                 xtype: 'Button',
23492                 size : 'sm',
23493                 xns: Roo.bootstrap,
23494                 glyphicon : 'cog',
23495                 //html : 'submit'
23496                 menu : {
23497                     xtype: 'Menu',
23498                     xns: Roo.bootstrap,
23499                     items:  []
23500                 }
23501         };
23502         
23503         cog.menu.items.push({
23504             xtype :'MenuItem',
23505             xns: Roo.bootstrap,
23506             html : Clean styles,
23507             tagname : f,
23508             listeners : {
23509                 click : function()
23510                 {
23511                     editorcore.insertTag(this.tagname);
23512                     editor.focus();
23513                 }
23514             }
23515             
23516         });
23517        */
23518         
23519          
23520        this.xtype = 'NavSimplebar';
23521         
23522         for(var i=0;i< children.length;i++) {
23523             
23524             this.buttons.add(this.addxtypeChild(children[i]));
23525             
23526         }
23527         
23528         editor.on('editorevent', this.updateToolbar, this);
23529     },
23530     onBtnClick : function(id)
23531     {
23532        this.editorcore.relayCmd(id);
23533        this.editorcore.focus();
23534     },
23535     
23536     /**
23537      * Protected method that will not generally be called directly. It triggers
23538      * a toolbar update by reading the markup state of the current selection in the editor.
23539      */
23540     updateToolbar: function(){
23541
23542         if(!this.editorcore.activated){
23543             this.editor.onFirstFocus(); // is this neeed?
23544             return;
23545         }
23546
23547         var btns = this.buttons; 
23548         var doc = this.editorcore.doc;
23549         btns.get('bold').setActive(doc.queryCommandState('bold'));
23550         btns.get('italic').setActive(doc.queryCommandState('italic'));
23551         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23552         
23553         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23554         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23555         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23556         
23557         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23558         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23559          /*
23560         
23561         var ans = this.editorcore.getAllAncestors();
23562         if (this.formatCombo) {
23563             
23564             
23565             var store = this.formatCombo.store;
23566             this.formatCombo.setValue("");
23567             for (var i =0; i < ans.length;i++) {
23568                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23569                     // select it..
23570                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23571                     break;
23572                 }
23573             }
23574         }
23575         
23576         
23577         
23578         // hides menus... - so this cant be on a menu...
23579         Roo.bootstrap.MenuMgr.hideAll();
23580         */
23581         Roo.bootstrap.MenuMgr.hideAll();
23582         //this.editorsyncValue();
23583     },
23584     onFirstFocus: function() {
23585         this.buttons.each(function(item){
23586            item.enable();
23587         });
23588     },
23589     toggleSourceEdit : function(sourceEditMode){
23590         
23591           
23592         if(sourceEditMode){
23593             Roo.log("disabling buttons");
23594            this.buttons.each( function(item){
23595                 if(item.cmd != 'pencil'){
23596                     item.disable();
23597                 }
23598             });
23599           
23600         }else{
23601             Roo.log("enabling buttons");
23602             if(this.editorcore.initialized){
23603                 this.buttons.each( function(item){
23604                     item.enable();
23605                 });
23606             }
23607             
23608         }
23609         Roo.log("calling toggole on editor");
23610         // tell the editor that it's been pressed..
23611         this.editor.toggleSourceEdit(sourceEditMode);
23612        
23613     }
23614 });
23615
23616
23617
23618
23619
23620 /**
23621  * @class Roo.bootstrap.Table.AbstractSelectionModel
23622  * @extends Roo.util.Observable
23623  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23624  * implemented by descendant classes.  This class should not be directly instantiated.
23625  * @constructor
23626  */
23627 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23628     this.locked = false;
23629     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23630 };
23631
23632
23633 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23634     /** @ignore Called by the grid automatically. Do not call directly. */
23635     init : function(grid){
23636         this.grid = grid;
23637         this.initEvents();
23638     },
23639
23640     /**
23641      * Locks the selections.
23642      */
23643     lock : function(){
23644         this.locked = true;
23645     },
23646
23647     /**
23648      * Unlocks the selections.
23649      */
23650     unlock : function(){
23651         this.locked = false;
23652     },
23653
23654     /**
23655      * Returns true if the selections are locked.
23656      * @return {Boolean}
23657      */
23658     isLocked : function(){
23659         return this.locked;
23660     }
23661 });
23662 /**
23663  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23664  * @class Roo.bootstrap.Table.RowSelectionModel
23665  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23666  * It supports multiple selections and keyboard selection/navigation. 
23667  * @constructor
23668  * @param {Object} config
23669  */
23670
23671 Roo.bootstrap.Table.RowSelectionModel = function(config){
23672     Roo.apply(this, config);
23673     this.selections = new Roo.util.MixedCollection(false, function(o){
23674         return o.id;
23675     });
23676
23677     this.last = false;
23678     this.lastActive = false;
23679
23680     this.addEvents({
23681         /**
23682              * @event selectionchange
23683              * Fires when the selection changes
23684              * @param {SelectionModel} this
23685              */
23686             "selectionchange" : true,
23687         /**
23688              * @event afterselectionchange
23689              * Fires after the selection changes (eg. by key press or clicking)
23690              * @param {SelectionModel} this
23691              */
23692             "afterselectionchange" : true,
23693         /**
23694              * @event beforerowselect
23695              * Fires when a row is selected being selected, return false to cancel.
23696              * @param {SelectionModel} this
23697              * @param {Number} rowIndex The selected index
23698              * @param {Boolean} keepExisting False if other selections will be cleared
23699              */
23700             "beforerowselect" : true,
23701         /**
23702              * @event rowselect
23703              * Fires when a row is selected.
23704              * @param {SelectionModel} this
23705              * @param {Number} rowIndex The selected index
23706              * @param {Roo.data.Record} r The record
23707              */
23708             "rowselect" : true,
23709         /**
23710              * @event rowdeselect
23711              * Fires when a row is deselected.
23712              * @param {SelectionModel} this
23713              * @param {Number} rowIndex The selected index
23714              */
23715         "rowdeselect" : true
23716     });
23717     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23718     this.locked = false;
23719  };
23720
23721 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23722     /**
23723      * @cfg {Boolean} singleSelect
23724      * True to allow selection of only one row at a time (defaults to false)
23725      */
23726     singleSelect : false,
23727
23728     // private
23729     initEvents : function()
23730     {
23731
23732         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23733         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23734         //}else{ // allow click to work like normal
23735          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23736         //}
23737         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23738         this.grid.on("rowclick", this.handleMouseDown, this);
23739         
23740         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23741             "up" : function(e){
23742                 if(!e.shiftKey){
23743                     this.selectPrevious(e.shiftKey);
23744                 }else if(this.last !== false && this.lastActive !== false){
23745                     var last = this.last;
23746                     this.selectRange(this.last,  this.lastActive-1);
23747                     this.grid.getView().focusRow(this.lastActive);
23748                     if(last !== false){
23749                         this.last = last;
23750                     }
23751                 }else{
23752                     this.selectFirstRow();
23753                 }
23754                 this.fireEvent("afterselectionchange", this);
23755             },
23756             "down" : function(e){
23757                 if(!e.shiftKey){
23758                     this.selectNext(e.shiftKey);
23759                 }else if(this.last !== false && this.lastActive !== false){
23760                     var last = this.last;
23761                     this.selectRange(this.last,  this.lastActive+1);
23762                     this.grid.getView().focusRow(this.lastActive);
23763                     if(last !== false){
23764                         this.last = last;
23765                     }
23766                 }else{
23767                     this.selectFirstRow();
23768                 }
23769                 this.fireEvent("afterselectionchange", this);
23770             },
23771             scope: this
23772         });
23773         this.grid.store.on('load', function(){
23774             this.selections.clear();
23775         },this);
23776         /*
23777         var view = this.grid.view;
23778         view.on("refresh", this.onRefresh, this);
23779         view.on("rowupdated", this.onRowUpdated, this);
23780         view.on("rowremoved", this.onRemove, this);
23781         */
23782     },
23783
23784     // private
23785     onRefresh : function()
23786     {
23787         var ds = this.grid.store, i, v = this.grid.view;
23788         var s = this.selections;
23789         s.each(function(r){
23790             if((i = ds.indexOfId(r.id)) != -1){
23791                 v.onRowSelect(i);
23792             }else{
23793                 s.remove(r);
23794             }
23795         });
23796     },
23797
23798     // private
23799     onRemove : function(v, index, r){
23800         this.selections.remove(r);
23801     },
23802
23803     // private
23804     onRowUpdated : function(v, index, r){
23805         if(this.isSelected(r)){
23806             v.onRowSelect(index);
23807         }
23808     },
23809
23810     /**
23811      * Select records.
23812      * @param {Array} records The records to select
23813      * @param {Boolean} keepExisting (optional) True to keep existing selections
23814      */
23815     selectRecords : function(records, keepExisting)
23816     {
23817         if(!keepExisting){
23818             this.clearSelections();
23819         }
23820             var ds = this.grid.store;
23821         for(var i = 0, len = records.length; i < len; i++){
23822             this.selectRow(ds.indexOf(records[i]), true);
23823         }
23824     },
23825
23826     /**
23827      * Gets the number of selected rows.
23828      * @return {Number}
23829      */
23830     getCount : function(){
23831         return this.selections.length;
23832     },
23833
23834     /**
23835      * Selects the first row in the grid.
23836      */
23837     selectFirstRow : function(){
23838         this.selectRow(0);
23839     },
23840
23841     /**
23842      * Select the last row.
23843      * @param {Boolean} keepExisting (optional) True to keep existing selections
23844      */
23845     selectLastRow : function(keepExisting){
23846         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23847         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23848     },
23849
23850     /**
23851      * Selects the row immediately following the last selected row.
23852      * @param {Boolean} keepExisting (optional) True to keep existing selections
23853      */
23854     selectNext : function(keepExisting)
23855     {
23856             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23857             this.selectRow(this.last+1, keepExisting);
23858             this.grid.getView().focusRow(this.last);
23859         }
23860     },
23861
23862     /**
23863      * Selects the row that precedes the last selected row.
23864      * @param {Boolean} keepExisting (optional) True to keep existing selections
23865      */
23866     selectPrevious : function(keepExisting){
23867         if(this.last){
23868             this.selectRow(this.last-1, keepExisting);
23869             this.grid.getView().focusRow(this.last);
23870         }
23871     },
23872
23873     /**
23874      * Returns the selected records
23875      * @return {Array} Array of selected records
23876      */
23877     getSelections : function(){
23878         return [].concat(this.selections.items);
23879     },
23880
23881     /**
23882      * Returns the first selected record.
23883      * @return {Record}
23884      */
23885     getSelected : function(){
23886         return this.selections.itemAt(0);
23887     },
23888
23889
23890     /**
23891      * Clears all selections.
23892      */
23893     clearSelections : function(fast)
23894     {
23895         if(this.locked) {
23896             return;
23897         }
23898         if(fast !== true){
23899                 var ds = this.grid.store;
23900             var s = this.selections;
23901             s.each(function(r){
23902                 this.deselectRow(ds.indexOfId(r.id));
23903             }, this);
23904             s.clear();
23905         }else{
23906             this.selections.clear();
23907         }
23908         this.last = false;
23909     },
23910
23911
23912     /**
23913      * Selects all rows.
23914      */
23915     selectAll : function(){
23916         if(this.locked) {
23917             return;
23918         }
23919         this.selections.clear();
23920         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23921             this.selectRow(i, true);
23922         }
23923     },
23924
23925     /**
23926      * Returns True if there is a selection.
23927      * @return {Boolean}
23928      */
23929     hasSelection : function(){
23930         return this.selections.length > 0;
23931     },
23932
23933     /**
23934      * Returns True if the specified row is selected.
23935      * @param {Number/Record} record The record or index of the record to check
23936      * @return {Boolean}
23937      */
23938     isSelected : function(index){
23939             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23940         return (r && this.selections.key(r.id) ? true : false);
23941     },
23942
23943     /**
23944      * Returns True if the specified record id is selected.
23945      * @param {String} id The id of record to check
23946      * @return {Boolean}
23947      */
23948     isIdSelected : function(id){
23949         return (this.selections.key(id) ? true : false);
23950     },
23951
23952
23953     // private
23954     handleMouseDBClick : function(e, t){
23955         
23956     },
23957     // private
23958     handleMouseDown : function(e, t)
23959     {
23960             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23961         if(this.isLocked() || rowIndex < 0 ){
23962             return;
23963         };
23964         if(e.shiftKey && this.last !== false){
23965             var last = this.last;
23966             this.selectRange(last, rowIndex, e.ctrlKey);
23967             this.last = last; // reset the last
23968             t.focus();
23969     
23970         }else{
23971             var isSelected = this.isSelected(rowIndex);
23972             //Roo.log("select row:" + rowIndex);
23973             if(isSelected){
23974                 this.deselectRow(rowIndex);
23975             } else {
23976                         this.selectRow(rowIndex, true);
23977             }
23978     
23979             /*
23980                 if(e.button !== 0 && isSelected){
23981                 alert('rowIndex 2: ' + rowIndex);
23982                     view.focusRow(rowIndex);
23983                 }else if(e.ctrlKey && isSelected){
23984                     this.deselectRow(rowIndex);
23985                 }else if(!isSelected){
23986                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23987                     view.focusRow(rowIndex);
23988                 }
23989             */
23990         }
23991         this.fireEvent("afterselectionchange", this);
23992     },
23993     // private
23994     handleDragableRowClick :  function(grid, rowIndex, e) 
23995     {
23996         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23997             this.selectRow(rowIndex, false);
23998             grid.view.focusRow(rowIndex);
23999              this.fireEvent("afterselectionchange", this);
24000         }
24001     },
24002     
24003     /**
24004      * Selects multiple rows.
24005      * @param {Array} rows Array of the indexes of the row to select
24006      * @param {Boolean} keepExisting (optional) True to keep existing selections
24007      */
24008     selectRows : function(rows, keepExisting){
24009         if(!keepExisting){
24010             this.clearSelections();
24011         }
24012         for(var i = 0, len = rows.length; i < len; i++){
24013             this.selectRow(rows[i], true);
24014         }
24015     },
24016
24017     /**
24018      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24019      * @param {Number} startRow The index of the first row in the range
24020      * @param {Number} endRow The index of the last row in the range
24021      * @param {Boolean} keepExisting (optional) True to retain existing selections
24022      */
24023     selectRange : function(startRow, endRow, keepExisting){
24024         if(this.locked) {
24025             return;
24026         }
24027         if(!keepExisting){
24028             this.clearSelections();
24029         }
24030         if(startRow <= endRow){
24031             for(var i = startRow; i <= endRow; i++){
24032                 this.selectRow(i, true);
24033             }
24034         }else{
24035             for(var i = startRow; i >= endRow; i--){
24036                 this.selectRow(i, true);
24037             }
24038         }
24039     },
24040
24041     /**
24042      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24043      * @param {Number} startRow The index of the first row in the range
24044      * @param {Number} endRow The index of the last row in the range
24045      */
24046     deselectRange : function(startRow, endRow, preventViewNotify){
24047         if(this.locked) {
24048             return;
24049         }
24050         for(var i = startRow; i <= endRow; i++){
24051             this.deselectRow(i, preventViewNotify);
24052         }
24053     },
24054
24055     /**
24056      * Selects a row.
24057      * @param {Number} row The index of the row to select
24058      * @param {Boolean} keepExisting (optional) True to keep existing selections
24059      */
24060     selectRow : function(index, keepExisting, preventViewNotify)
24061     {
24062             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24063             return;
24064         }
24065         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24066             if(!keepExisting || this.singleSelect){
24067                 this.clearSelections();
24068             }
24069             
24070             var r = this.grid.store.getAt(index);
24071             //console.log('selectRow - record id :' + r.id);
24072             
24073             this.selections.add(r);
24074             this.last = this.lastActive = index;
24075             if(!preventViewNotify){
24076                 var proxy = new Roo.Element(
24077                                 this.grid.getRowDom(index)
24078                 );
24079                 proxy.addClass('bg-info info');
24080             }
24081             this.fireEvent("rowselect", this, index, r);
24082             this.fireEvent("selectionchange", this);
24083         }
24084     },
24085
24086     /**
24087      * Deselects a row.
24088      * @param {Number} row The index of the row to deselect
24089      */
24090     deselectRow : function(index, preventViewNotify)
24091     {
24092         if(this.locked) {
24093             return;
24094         }
24095         if(this.last == index){
24096             this.last = false;
24097         }
24098         if(this.lastActive == index){
24099             this.lastActive = false;
24100         }
24101         
24102         var r = this.grid.store.getAt(index);
24103         if (!r) {
24104             return;
24105         }
24106         
24107         this.selections.remove(r);
24108         //.console.log('deselectRow - record id :' + r.id);
24109         if(!preventViewNotify){
24110         
24111             var proxy = new Roo.Element(
24112                 this.grid.getRowDom(index)
24113             );
24114             proxy.removeClass('bg-info info');
24115         }
24116         this.fireEvent("rowdeselect", this, index);
24117         this.fireEvent("selectionchange", this);
24118     },
24119
24120     // private
24121     restoreLast : function(){
24122         if(this._last){
24123             this.last = this._last;
24124         }
24125     },
24126
24127     // private
24128     acceptsNav : function(row, col, cm){
24129         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24130     },
24131
24132     // private
24133     onEditorKey : function(field, e){
24134         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24135         if(k == e.TAB){
24136             e.stopEvent();
24137             ed.completeEdit();
24138             if(e.shiftKey){
24139                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24140             }else{
24141                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24142             }
24143         }else if(k == e.ENTER && !e.ctrlKey){
24144             e.stopEvent();
24145             ed.completeEdit();
24146             if(e.shiftKey){
24147                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24148             }else{
24149                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24150             }
24151         }else if(k == e.ESC){
24152             ed.cancelEdit();
24153         }
24154         if(newCell){
24155             g.startEditing(newCell[0], newCell[1]);
24156         }
24157     }
24158 });
24159 /*
24160  * Based on:
24161  * Ext JS Library 1.1.1
24162  * Copyright(c) 2006-2007, Ext JS, LLC.
24163  *
24164  * Originally Released Under LGPL - original licence link has changed is not relivant.
24165  *
24166  * Fork - LGPL
24167  * <script type="text/javascript">
24168  */
24169  
24170 /**
24171  * @class Roo.bootstrap.PagingToolbar
24172  * @extends Roo.bootstrap.NavSimplebar
24173  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24174  * @constructor
24175  * Create a new PagingToolbar
24176  * @param {Object} config The config object
24177  * @param {Roo.data.Store} store
24178  */
24179 Roo.bootstrap.PagingToolbar = function(config)
24180 {
24181     // old args format still supported... - xtype is prefered..
24182         // created from xtype...
24183     
24184     this.ds = config.dataSource;
24185     
24186     if (config.store && !this.ds) {
24187         this.store= Roo.factory(config.store, Roo.data);
24188         this.ds = this.store;
24189         this.ds.xmodule = this.xmodule || false;
24190     }
24191     
24192     this.toolbarItems = [];
24193     if (config.items) {
24194         this.toolbarItems = config.items;
24195     }
24196     
24197     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24198     
24199     this.cursor = 0;
24200     
24201     if (this.ds) { 
24202         this.bind(this.ds);
24203     }
24204     
24205     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24206     
24207 };
24208
24209 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24210     /**
24211      * @cfg {Roo.data.Store} dataSource
24212      * The underlying data store providing the paged data
24213      */
24214     /**
24215      * @cfg {String/HTMLElement/Element} container
24216      * container The id or element that will contain the toolbar
24217      */
24218     /**
24219      * @cfg {Boolean} displayInfo
24220      * True to display the displayMsg (defaults to false)
24221      */
24222     /**
24223      * @cfg {Number} pageSize
24224      * The number of records to display per page (defaults to 20)
24225      */
24226     pageSize: 20,
24227     /**
24228      * @cfg {String} displayMsg
24229      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24230      */
24231     displayMsg : 'Displaying {0} - {1} of {2}',
24232     /**
24233      * @cfg {String} emptyMsg
24234      * The message to display when no records are found (defaults to "No data to display")
24235      */
24236     emptyMsg : 'No data to display',
24237     /**
24238      * Customizable piece of the default paging text (defaults to "Page")
24239      * @type String
24240      */
24241     beforePageText : "Page",
24242     /**
24243      * Customizable piece of the default paging text (defaults to "of %0")
24244      * @type String
24245      */
24246     afterPageText : "of {0}",
24247     /**
24248      * Customizable piece of the default paging text (defaults to "First Page")
24249      * @type String
24250      */
24251     firstText : "First Page",
24252     /**
24253      * Customizable piece of the default paging text (defaults to "Previous Page")
24254      * @type String
24255      */
24256     prevText : "Previous Page",
24257     /**
24258      * Customizable piece of the default paging text (defaults to "Next Page")
24259      * @type String
24260      */
24261     nextText : "Next Page",
24262     /**
24263      * Customizable piece of the default paging text (defaults to "Last Page")
24264      * @type String
24265      */
24266     lastText : "Last Page",
24267     /**
24268      * Customizable piece of the default paging text (defaults to "Refresh")
24269      * @type String
24270      */
24271     refreshText : "Refresh",
24272
24273     buttons : false,
24274     // private
24275     onRender : function(ct, position) 
24276     {
24277         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24278         this.navgroup.parentId = this.id;
24279         this.navgroup.onRender(this.el, null);
24280         // add the buttons to the navgroup
24281         
24282         if(this.displayInfo){
24283             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24284             this.displayEl = this.el.select('.x-paging-info', true).first();
24285 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24286 //            this.displayEl = navel.el.select('span',true).first();
24287         }
24288         
24289         var _this = this;
24290         
24291         if(this.buttons){
24292             Roo.each(_this.buttons, function(e){ // this might need to use render????
24293                Roo.factory(e).onRender(_this.el, null);
24294             });
24295         }
24296             
24297         Roo.each(_this.toolbarItems, function(e) {
24298             _this.navgroup.addItem(e);
24299         });
24300         
24301         
24302         this.first = this.navgroup.addItem({
24303             tooltip: this.firstText,
24304             cls: "prev",
24305             icon : 'fa fa-backward',
24306             disabled: true,
24307             preventDefault: true,
24308             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24309         });
24310         
24311         this.prev =  this.navgroup.addItem({
24312             tooltip: this.prevText,
24313             cls: "prev",
24314             icon : 'fa fa-step-backward',
24315             disabled: true,
24316             preventDefault: true,
24317             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24318         });
24319     //this.addSeparator();
24320         
24321         
24322         var field = this.navgroup.addItem( {
24323             tagtype : 'span',
24324             cls : 'x-paging-position',
24325             
24326             html : this.beforePageText  +
24327                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24328                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24329          } ); //?? escaped?
24330         
24331         this.field = field.el.select('input', true).first();
24332         this.field.on("keydown", this.onPagingKeydown, this);
24333         this.field.on("focus", function(){this.dom.select();});
24334     
24335     
24336         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24337         //this.field.setHeight(18);
24338         //this.addSeparator();
24339         this.next = this.navgroup.addItem({
24340             tooltip: this.nextText,
24341             cls: "next",
24342             html : ' <i class="fa fa-step-forward">',
24343             disabled: true,
24344             preventDefault: true,
24345             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24346         });
24347         this.last = this.navgroup.addItem({
24348             tooltip: this.lastText,
24349             icon : 'fa fa-forward',
24350             cls: "next",
24351             disabled: true,
24352             preventDefault: true,
24353             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24354         });
24355     //this.addSeparator();
24356         this.loading = this.navgroup.addItem({
24357             tooltip: this.refreshText,
24358             icon: 'fa fa-refresh',
24359             preventDefault: true,
24360             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24361         });
24362         
24363     },
24364
24365     // private
24366     updateInfo : function(){
24367         if(this.displayEl){
24368             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24369             var msg = count == 0 ?
24370                 this.emptyMsg :
24371                 String.format(
24372                     this.displayMsg,
24373                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24374                 );
24375             this.displayEl.update(msg);
24376         }
24377     },
24378
24379     // private
24380     onLoad : function(ds, r, o)
24381     {
24382         this.cursor = o.params ? o.params.start : 0;
24383         var d = this.getPageData(),
24384             ap = d.activePage,
24385             ps = d.pages;
24386         
24387         
24388         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24389         this.field.dom.value = ap;
24390         this.first.setDisabled(ap == 1);
24391         this.prev.setDisabled(ap == 1);
24392         this.next.setDisabled(ap == ps);
24393         this.last.setDisabled(ap == ps);
24394         this.loading.enable();
24395         this.updateInfo();
24396     },
24397
24398     // private
24399     getPageData : function(){
24400         var total = this.ds.getTotalCount();
24401         return {
24402             total : total,
24403             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24404             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24405         };
24406     },
24407
24408     // private
24409     onLoadError : function(){
24410         this.loading.enable();
24411     },
24412
24413     // private
24414     onPagingKeydown : function(e){
24415         var k = e.getKey();
24416         var d = this.getPageData();
24417         if(k == e.RETURN){
24418             var v = this.field.dom.value, pageNum;
24419             if(!v || isNaN(pageNum = parseInt(v, 10))){
24420                 this.field.dom.value = d.activePage;
24421                 return;
24422             }
24423             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24424             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24425             e.stopEvent();
24426         }
24427         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))
24428         {
24429           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24430           this.field.dom.value = pageNum;
24431           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24432           e.stopEvent();
24433         }
24434         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24435         {
24436           var v = this.field.dom.value, pageNum; 
24437           var increment = (e.shiftKey) ? 10 : 1;
24438           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24439                 increment *= -1;
24440           }
24441           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24442             this.field.dom.value = d.activePage;
24443             return;
24444           }
24445           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24446           {
24447             this.field.dom.value = parseInt(v, 10) + increment;
24448             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24449             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24450           }
24451           e.stopEvent();
24452         }
24453     },
24454
24455     // private
24456     beforeLoad : function(){
24457         if(this.loading){
24458             this.loading.disable();
24459         }
24460     },
24461
24462     // private
24463     onClick : function(which){
24464         
24465         var ds = this.ds;
24466         if (!ds) {
24467             return;
24468         }
24469         
24470         switch(which){
24471             case "first":
24472                 ds.load({params:{start: 0, limit: this.pageSize}});
24473             break;
24474             case "prev":
24475                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24476             break;
24477             case "next":
24478                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24479             break;
24480             case "last":
24481                 var total = ds.getTotalCount();
24482                 var extra = total % this.pageSize;
24483                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24484                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24485             break;
24486             case "refresh":
24487                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24488             break;
24489         }
24490     },
24491
24492     /**
24493      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24494      * @param {Roo.data.Store} store The data store to unbind
24495      */
24496     unbind : function(ds){
24497         ds.un("beforeload", this.beforeLoad, this);
24498         ds.un("load", this.onLoad, this);
24499         ds.un("loadexception", this.onLoadError, this);
24500         ds.un("remove", this.updateInfo, this);
24501         ds.un("add", this.updateInfo, this);
24502         this.ds = undefined;
24503     },
24504
24505     /**
24506      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24507      * @param {Roo.data.Store} store The data store to bind
24508      */
24509     bind : function(ds){
24510         ds.on("beforeload", this.beforeLoad, this);
24511         ds.on("load", this.onLoad, this);
24512         ds.on("loadexception", this.onLoadError, this);
24513         ds.on("remove", this.updateInfo, this);
24514         ds.on("add", this.updateInfo, this);
24515         this.ds = ds;
24516     }
24517 });/*
24518  * - LGPL
24519  *
24520  * element
24521  * 
24522  */
24523
24524 /**
24525  * @class Roo.bootstrap.MessageBar
24526  * @extends Roo.bootstrap.Component
24527  * Bootstrap MessageBar class
24528  * @cfg {String} html contents of the MessageBar
24529  * @cfg {String} weight (info | success | warning | danger) default info
24530  * @cfg {String} beforeClass insert the bar before the given class
24531  * @cfg {Boolean} closable (true | false) default false
24532  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24533  * 
24534  * @constructor
24535  * Create a new Element
24536  * @param {Object} config The config object
24537  */
24538
24539 Roo.bootstrap.MessageBar = function(config){
24540     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24541 };
24542
24543 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24544     
24545     html: '',
24546     weight: 'info',
24547     closable: false,
24548     fixed: false,
24549     beforeClass: 'bootstrap-sticky-wrap',
24550     
24551     getAutoCreate : function(){
24552         
24553         var cfg = {
24554             tag: 'div',
24555             cls: 'alert alert-dismissable alert-' + this.weight,
24556             cn: [
24557                 {
24558                     tag: 'span',
24559                     cls: 'message',
24560                     html: this.html || ''
24561                 }
24562             ]
24563         };
24564         
24565         if(this.fixed){
24566             cfg.cls += ' alert-messages-fixed';
24567         }
24568         
24569         if(this.closable){
24570             cfg.cn.push({
24571                 tag: 'button',
24572                 cls: 'close',
24573                 html: 'x'
24574             });
24575         }
24576         
24577         return cfg;
24578     },
24579     
24580     onRender : function(ct, position)
24581     {
24582         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24583         
24584         if(!this.el){
24585             var cfg = Roo.apply({},  this.getAutoCreate());
24586             cfg.id = Roo.id();
24587             
24588             if (this.cls) {
24589                 cfg.cls += ' ' + this.cls;
24590             }
24591             if (this.style) {
24592                 cfg.style = this.style;
24593             }
24594             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24595             
24596             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24597         }
24598         
24599         this.el.select('>button.close').on('click', this.hide, this);
24600         
24601     },
24602     
24603     show : function()
24604     {
24605         if (!this.rendered) {
24606             this.render();
24607         }
24608         
24609         this.el.show();
24610         
24611         this.fireEvent('show', this);
24612         
24613     },
24614     
24615     hide : function()
24616     {
24617         if (!this.rendered) {
24618             this.render();
24619         }
24620         
24621         this.el.hide();
24622         
24623         this.fireEvent('hide', this);
24624     },
24625     
24626     update : function()
24627     {
24628 //        var e = this.el.dom.firstChild;
24629 //        
24630 //        if(this.closable){
24631 //            e = e.nextSibling;
24632 //        }
24633 //        
24634 //        e.data = this.html || '';
24635
24636         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24637     }
24638    
24639 });
24640
24641  
24642
24643      /*
24644  * - LGPL
24645  *
24646  * Graph
24647  * 
24648  */
24649
24650
24651 /**
24652  * @class Roo.bootstrap.Graph
24653  * @extends Roo.bootstrap.Component
24654  * Bootstrap Graph class
24655 > Prameters
24656  -sm {number} sm 4
24657  -md {number} md 5
24658  @cfg {String} graphtype  bar | vbar | pie
24659  @cfg {number} g_x coodinator | centre x (pie)
24660  @cfg {number} g_y coodinator | centre y (pie)
24661  @cfg {number} g_r radius (pie)
24662  @cfg {number} g_height height of the chart (respected by all elements in the set)
24663  @cfg {number} g_width width of the chart (respected by all elements in the set)
24664  @cfg {Object} title The title of the chart
24665     
24666  -{Array}  values
24667  -opts (object) options for the chart 
24668      o {
24669      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24670      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24671      o vgutter (number)
24672      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.
24673      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24674      o to
24675      o stretch (boolean)
24676      o }
24677  -opts (object) options for the pie
24678      o{
24679      o cut
24680      o startAngle (number)
24681      o endAngle (number)
24682      } 
24683  *
24684  * @constructor
24685  * Create a new Input
24686  * @param {Object} config The config object
24687  */
24688
24689 Roo.bootstrap.Graph = function(config){
24690     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24691     
24692     this.addEvents({
24693         // img events
24694         /**
24695          * @event click
24696          * The img click event for the img.
24697          * @param {Roo.EventObject} e
24698          */
24699         "click" : true
24700     });
24701 };
24702
24703 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24704     
24705     sm: 4,
24706     md: 5,
24707     graphtype: 'bar',
24708     g_height: 250,
24709     g_width: 400,
24710     g_x: 50,
24711     g_y: 50,
24712     g_r: 30,
24713     opts:{
24714         //g_colors: this.colors,
24715         g_type: 'soft',
24716         g_gutter: '20%'
24717
24718     },
24719     title : false,
24720
24721     getAutoCreate : function(){
24722         
24723         var cfg = {
24724             tag: 'div',
24725             html : null
24726         };
24727         
24728         
24729         return  cfg;
24730     },
24731
24732     onRender : function(ct,position){
24733         
24734         
24735         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24736         
24737         if (typeof(Raphael) == 'undefined') {
24738             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24739             return;
24740         }
24741         
24742         this.raphael = Raphael(this.el.dom);
24743         
24744                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24745                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24746                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24747                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24748                 /*
24749                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24750                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24751                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24752                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24753                 
24754                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24755                 r.barchart(330, 10, 300, 220, data1);
24756                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24757                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24758                 */
24759                 
24760                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24761                 // r.barchart(30, 30, 560, 250,  xdata, {
24762                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24763                 //     axis : "0 0 1 1",
24764                 //     axisxlabels :  xdata
24765                 //     //yvalues : cols,
24766                    
24767                 // });
24768 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24769 //        
24770 //        this.load(null,xdata,{
24771 //                axis : "0 0 1 1",
24772 //                axisxlabels :  xdata
24773 //                });
24774
24775     },
24776
24777     load : function(graphtype,xdata,opts)
24778     {
24779         this.raphael.clear();
24780         if(!graphtype) {
24781             graphtype = this.graphtype;
24782         }
24783         if(!opts){
24784             opts = this.opts;
24785         }
24786         var r = this.raphael,
24787             fin = function () {
24788                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24789             },
24790             fout = function () {
24791                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24792             },
24793             pfin = function() {
24794                 this.sector.stop();
24795                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24796
24797                 if (this.label) {
24798                     this.label[0].stop();
24799                     this.label[0].attr({ r: 7.5 });
24800                     this.label[1].attr({ "font-weight": 800 });
24801                 }
24802             },
24803             pfout = function() {
24804                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24805
24806                 if (this.label) {
24807                     this.label[0].animate({ r: 5 }, 500, "bounce");
24808                     this.label[1].attr({ "font-weight": 400 });
24809                 }
24810             };
24811
24812         switch(graphtype){
24813             case 'bar':
24814                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24815                 break;
24816             case 'hbar':
24817                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24818                 break;
24819             case 'pie':
24820 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24821 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24822 //            
24823                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24824                 
24825                 break;
24826
24827         }
24828         
24829         if(this.title){
24830             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24831         }
24832         
24833     },
24834     
24835     setTitle: function(o)
24836     {
24837         this.title = o;
24838     },
24839     
24840     initEvents: function() {
24841         
24842         if(!this.href){
24843             this.el.on('click', this.onClick, this);
24844         }
24845     },
24846     
24847     onClick : function(e)
24848     {
24849         Roo.log('img onclick');
24850         this.fireEvent('click', this, e);
24851     }
24852    
24853 });
24854
24855  
24856 /*
24857  * - LGPL
24858  *
24859  * numberBox
24860  * 
24861  */
24862 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24863
24864 /**
24865  * @class Roo.bootstrap.dash.NumberBox
24866  * @extends Roo.bootstrap.Component
24867  * Bootstrap NumberBox class
24868  * @cfg {String} headline Box headline
24869  * @cfg {String} content Box content
24870  * @cfg {String} icon Box icon
24871  * @cfg {String} footer Footer text
24872  * @cfg {String} fhref Footer href
24873  * 
24874  * @constructor
24875  * Create a new NumberBox
24876  * @param {Object} config The config object
24877  */
24878
24879
24880 Roo.bootstrap.dash.NumberBox = function(config){
24881     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24882     
24883 };
24884
24885 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24886     
24887     headline : '',
24888     content : '',
24889     icon : '',
24890     footer : '',
24891     fhref : '',
24892     ficon : '',
24893     
24894     getAutoCreate : function(){
24895         
24896         var cfg = {
24897             tag : 'div',
24898             cls : 'small-box ',
24899             cn : [
24900                 {
24901                     tag : 'div',
24902                     cls : 'inner',
24903                     cn :[
24904                         {
24905                             tag : 'h3',
24906                             cls : 'roo-headline',
24907                             html : this.headline
24908                         },
24909                         {
24910                             tag : 'p',
24911                             cls : 'roo-content',
24912                             html : this.content
24913                         }
24914                     ]
24915                 }
24916             ]
24917         };
24918         
24919         if(this.icon){
24920             cfg.cn.push({
24921                 tag : 'div',
24922                 cls : 'icon',
24923                 cn :[
24924                     {
24925                         tag : 'i',
24926                         cls : 'ion ' + this.icon
24927                     }
24928                 ]
24929             });
24930         }
24931         
24932         if(this.footer){
24933             var footer = {
24934                 tag : 'a',
24935                 cls : 'small-box-footer',
24936                 href : this.fhref || '#',
24937                 html : this.footer
24938             };
24939             
24940             cfg.cn.push(footer);
24941             
24942         }
24943         
24944         return  cfg;
24945     },
24946
24947     onRender : function(ct,position){
24948         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24949
24950
24951        
24952                 
24953     },
24954
24955     setHeadline: function (value)
24956     {
24957         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24958     },
24959     
24960     setFooter: function (value, href)
24961     {
24962         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24963         
24964         if(href){
24965             this.el.select('a.small-box-footer',true).first().attr('href', href);
24966         }
24967         
24968     },
24969
24970     setContent: function (value)
24971     {
24972         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24973     },
24974
24975     initEvents: function() 
24976     {   
24977         
24978     }
24979     
24980 });
24981
24982  
24983 /*
24984  * - LGPL
24985  *
24986  * TabBox
24987  * 
24988  */
24989 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24990
24991 /**
24992  * @class Roo.bootstrap.dash.TabBox
24993  * @extends Roo.bootstrap.Component
24994  * Bootstrap TabBox class
24995  * @cfg {String} title Title of the TabBox
24996  * @cfg {String} icon Icon of the TabBox
24997  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24998  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24999  * 
25000  * @constructor
25001  * Create a new TabBox
25002  * @param {Object} config The config object
25003  */
25004
25005
25006 Roo.bootstrap.dash.TabBox = function(config){
25007     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25008     this.addEvents({
25009         // raw events
25010         /**
25011          * @event addpane
25012          * When a pane is added
25013          * @param {Roo.bootstrap.dash.TabPane} pane
25014          */
25015         "addpane" : true,
25016         /**
25017          * @event activatepane
25018          * When a pane is activated
25019          * @param {Roo.bootstrap.dash.TabPane} pane
25020          */
25021         "activatepane" : true
25022         
25023          
25024     });
25025     
25026     this.panes = [];
25027 };
25028
25029 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25030
25031     title : '',
25032     icon : false,
25033     showtabs : true,
25034     tabScrollable : false,
25035     
25036     getChildContainer : function()
25037     {
25038         return this.el.select('.tab-content', true).first();
25039     },
25040     
25041     getAutoCreate : function(){
25042         
25043         var header = {
25044             tag: 'li',
25045             cls: 'pull-left header',
25046             html: this.title,
25047             cn : []
25048         };
25049         
25050         if(this.icon){
25051             header.cn.push({
25052                 tag: 'i',
25053                 cls: 'fa ' + this.icon
25054             });
25055         }
25056         
25057         var h = {
25058             tag: 'ul',
25059             cls: 'nav nav-tabs pull-right',
25060             cn: [
25061                 header
25062             ]
25063         };
25064         
25065         if(this.tabScrollable){
25066             h = {
25067                 tag: 'div',
25068                 cls: 'tab-header',
25069                 cn: [
25070                     {
25071                         tag: 'ul',
25072                         cls: 'nav nav-tabs pull-right',
25073                         cn: [
25074                             header
25075                         ]
25076                     }
25077                 ]
25078             };
25079         }
25080         
25081         var cfg = {
25082             tag: 'div',
25083             cls: 'nav-tabs-custom',
25084             cn: [
25085                 h,
25086                 {
25087                     tag: 'div',
25088                     cls: 'tab-content no-padding',
25089                     cn: []
25090                 }
25091             ]
25092         };
25093
25094         return  cfg;
25095     },
25096     initEvents : function()
25097     {
25098         //Roo.log('add add pane handler');
25099         this.on('addpane', this.onAddPane, this);
25100     },
25101      /**
25102      * Updates the box title
25103      * @param {String} html to set the title to.
25104      */
25105     setTitle : function(value)
25106     {
25107         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25108     },
25109     onAddPane : function(pane)
25110     {
25111         this.panes.push(pane);
25112         //Roo.log('addpane');
25113         //Roo.log(pane);
25114         // tabs are rendere left to right..
25115         if(!this.showtabs){
25116             return;
25117         }
25118         
25119         var ctr = this.el.select('.nav-tabs', true).first();
25120          
25121          
25122         var existing = ctr.select('.nav-tab',true);
25123         var qty = existing.getCount();;
25124         
25125         
25126         var tab = ctr.createChild({
25127             tag : 'li',
25128             cls : 'nav-tab' + (qty ? '' : ' active'),
25129             cn : [
25130                 {
25131                     tag : 'a',
25132                     href:'#',
25133                     html : pane.title
25134                 }
25135             ]
25136         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25137         pane.tab = tab;
25138         
25139         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25140         if (!qty) {
25141             pane.el.addClass('active');
25142         }
25143         
25144                 
25145     },
25146     onTabClick : function(ev,un,ob,pane)
25147     {
25148         //Roo.log('tab - prev default');
25149         ev.preventDefault();
25150         
25151         
25152         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25153         pane.tab.addClass('active');
25154         //Roo.log(pane.title);
25155         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25156         // technically we should have a deactivate event.. but maybe add later.
25157         // and it should not de-activate the selected tab...
25158         this.fireEvent('activatepane', pane);
25159         pane.el.addClass('active');
25160         pane.fireEvent('activate');
25161         
25162         
25163     },
25164     
25165     getActivePane : function()
25166     {
25167         var r = false;
25168         Roo.each(this.panes, function(p) {
25169             if(p.el.hasClass('active')){
25170                 r = p;
25171                 return false;
25172             }
25173             
25174             return;
25175         });
25176         
25177         return r;
25178     }
25179     
25180     
25181 });
25182
25183  
25184 /*
25185  * - LGPL
25186  *
25187  * Tab pane
25188  * 
25189  */
25190 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25191 /**
25192  * @class Roo.bootstrap.TabPane
25193  * @extends Roo.bootstrap.Component
25194  * Bootstrap TabPane class
25195  * @cfg {Boolean} active (false | true) Default false
25196  * @cfg {String} title title of panel
25197
25198  * 
25199  * @constructor
25200  * Create a new TabPane
25201  * @param {Object} config The config object
25202  */
25203
25204 Roo.bootstrap.dash.TabPane = function(config){
25205     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25206     
25207     this.addEvents({
25208         // raw events
25209         /**
25210          * @event activate
25211          * When a pane is activated
25212          * @param {Roo.bootstrap.dash.TabPane} pane
25213          */
25214         "activate" : true
25215          
25216     });
25217 };
25218
25219 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25220     
25221     active : false,
25222     title : '',
25223     
25224     // the tabBox that this is attached to.
25225     tab : false,
25226      
25227     getAutoCreate : function() 
25228     {
25229         var cfg = {
25230             tag: 'div',
25231             cls: 'tab-pane'
25232         };
25233         
25234         if(this.active){
25235             cfg.cls += ' active';
25236         }
25237         
25238         return cfg;
25239     },
25240     initEvents  : function()
25241     {
25242         //Roo.log('trigger add pane handler');
25243         this.parent().fireEvent('addpane', this)
25244     },
25245     
25246      /**
25247      * Updates the tab title 
25248      * @param {String} html to set the title to.
25249      */
25250     setTitle: function(str)
25251     {
25252         if (!this.tab) {
25253             return;
25254         }
25255         this.title = str;
25256         this.tab.select('a', true).first().dom.innerHTML = str;
25257         
25258     }
25259     
25260     
25261     
25262 });
25263
25264  
25265
25266
25267  /*
25268  * - LGPL
25269  *
25270  * menu
25271  * 
25272  */
25273 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25274
25275 /**
25276  * @class Roo.bootstrap.menu.Menu
25277  * @extends Roo.bootstrap.Component
25278  * Bootstrap Menu class - container for Menu
25279  * @cfg {String} html Text of the menu
25280  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25281  * @cfg {String} icon Font awesome icon
25282  * @cfg {String} pos Menu align to (top | bottom) default bottom
25283  * 
25284  * 
25285  * @constructor
25286  * Create a new Menu
25287  * @param {Object} config The config object
25288  */
25289
25290
25291 Roo.bootstrap.menu.Menu = function(config){
25292     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25293     
25294     this.addEvents({
25295         /**
25296          * @event beforeshow
25297          * Fires before this menu is displayed
25298          * @param {Roo.bootstrap.menu.Menu} this
25299          */
25300         beforeshow : true,
25301         /**
25302          * @event beforehide
25303          * Fires before this menu is hidden
25304          * @param {Roo.bootstrap.menu.Menu} this
25305          */
25306         beforehide : true,
25307         /**
25308          * @event show
25309          * Fires after this menu is displayed
25310          * @param {Roo.bootstrap.menu.Menu} this
25311          */
25312         show : true,
25313         /**
25314          * @event hide
25315          * Fires after this menu is hidden
25316          * @param {Roo.bootstrap.menu.Menu} this
25317          */
25318         hide : true,
25319         /**
25320          * @event click
25321          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25322          * @param {Roo.bootstrap.menu.Menu} this
25323          * @param {Roo.EventObject} e
25324          */
25325         click : true
25326     });
25327     
25328 };
25329
25330 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25331     
25332     submenu : false,
25333     html : '',
25334     weight : 'default',
25335     icon : false,
25336     pos : 'bottom',
25337     
25338     
25339     getChildContainer : function() {
25340         if(this.isSubMenu){
25341             return this.el;
25342         }
25343         
25344         return this.el.select('ul.dropdown-menu', true).first();  
25345     },
25346     
25347     getAutoCreate : function()
25348     {
25349         var text = [
25350             {
25351                 tag : 'span',
25352                 cls : 'roo-menu-text',
25353                 html : this.html
25354             }
25355         ];
25356         
25357         if(this.icon){
25358             text.unshift({
25359                 tag : 'i',
25360                 cls : 'fa ' + this.icon
25361             })
25362         }
25363         
25364         
25365         var cfg = {
25366             tag : 'div',
25367             cls : 'btn-group',
25368             cn : [
25369                 {
25370                     tag : 'button',
25371                     cls : 'dropdown-button btn btn-' + this.weight,
25372                     cn : text
25373                 },
25374                 {
25375                     tag : 'button',
25376                     cls : 'dropdown-toggle btn btn-' + this.weight,
25377                     cn : [
25378                         {
25379                             tag : 'span',
25380                             cls : 'caret'
25381                         }
25382                     ]
25383                 },
25384                 {
25385                     tag : 'ul',
25386                     cls : 'dropdown-menu'
25387                 }
25388             ]
25389             
25390         };
25391         
25392         if(this.pos == 'top'){
25393             cfg.cls += ' dropup';
25394         }
25395         
25396         if(this.isSubMenu){
25397             cfg = {
25398                 tag : 'ul',
25399                 cls : 'dropdown-menu'
25400             }
25401         }
25402         
25403         return cfg;
25404     },
25405     
25406     onRender : function(ct, position)
25407     {
25408         this.isSubMenu = ct.hasClass('dropdown-submenu');
25409         
25410         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25411     },
25412     
25413     initEvents : function() 
25414     {
25415         if(this.isSubMenu){
25416             return;
25417         }
25418         
25419         this.hidden = true;
25420         
25421         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25422         this.triggerEl.on('click', this.onTriggerPress, this);
25423         
25424         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25425         this.buttonEl.on('click', this.onClick, this);
25426         
25427     },
25428     
25429     list : function()
25430     {
25431         if(this.isSubMenu){
25432             return this.el;
25433         }
25434         
25435         return this.el.select('ul.dropdown-menu', true).first();
25436     },
25437     
25438     onClick : function(e)
25439     {
25440         this.fireEvent("click", this, e);
25441     },
25442     
25443     onTriggerPress  : function(e)
25444     {   
25445         if (this.isVisible()) {
25446             this.hide();
25447         } else {
25448             this.show();
25449         }
25450     },
25451     
25452     isVisible : function(){
25453         return !this.hidden;
25454     },
25455     
25456     show : function()
25457     {
25458         this.fireEvent("beforeshow", this);
25459         
25460         this.hidden = false;
25461         this.el.addClass('open');
25462         
25463         Roo.get(document).on("mouseup", this.onMouseUp, this);
25464         
25465         this.fireEvent("show", this);
25466         
25467         
25468     },
25469     
25470     hide : function()
25471     {
25472         this.fireEvent("beforehide", this);
25473         
25474         this.hidden = true;
25475         this.el.removeClass('open');
25476         
25477         Roo.get(document).un("mouseup", this.onMouseUp);
25478         
25479         this.fireEvent("hide", this);
25480     },
25481     
25482     onMouseUp : function()
25483     {
25484         this.hide();
25485     }
25486     
25487 });
25488
25489  
25490  /*
25491  * - LGPL
25492  *
25493  * menu item
25494  * 
25495  */
25496 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25497
25498 /**
25499  * @class Roo.bootstrap.menu.Item
25500  * @extends Roo.bootstrap.Component
25501  * Bootstrap MenuItem class
25502  * @cfg {Boolean} submenu (true | false) default false
25503  * @cfg {String} html text of the item
25504  * @cfg {String} href the link
25505  * @cfg {Boolean} disable (true | false) default false
25506  * @cfg {Boolean} preventDefault (true | false) default true
25507  * @cfg {String} icon Font awesome icon
25508  * @cfg {String} pos Submenu align to (left | right) default right 
25509  * 
25510  * 
25511  * @constructor
25512  * Create a new Item
25513  * @param {Object} config The config object
25514  */
25515
25516
25517 Roo.bootstrap.menu.Item = function(config){
25518     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25519     this.addEvents({
25520         /**
25521          * @event mouseover
25522          * Fires when the mouse is hovering over this menu
25523          * @param {Roo.bootstrap.menu.Item} this
25524          * @param {Roo.EventObject} e
25525          */
25526         mouseover : true,
25527         /**
25528          * @event mouseout
25529          * Fires when the mouse exits this menu
25530          * @param {Roo.bootstrap.menu.Item} this
25531          * @param {Roo.EventObject} e
25532          */
25533         mouseout : true,
25534         // raw events
25535         /**
25536          * @event click
25537          * The raw click event for the entire grid.
25538          * @param {Roo.EventObject} e
25539          */
25540         click : true
25541     });
25542 };
25543
25544 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25545     
25546     submenu : false,
25547     href : '',
25548     html : '',
25549     preventDefault: true,
25550     disable : false,
25551     icon : false,
25552     pos : 'right',
25553     
25554     getAutoCreate : function()
25555     {
25556         var text = [
25557             {
25558                 tag : 'span',
25559                 cls : 'roo-menu-item-text',
25560                 html : this.html
25561             }
25562         ];
25563         
25564         if(this.icon){
25565             text.unshift({
25566                 tag : 'i',
25567                 cls : 'fa ' + this.icon
25568             })
25569         }
25570         
25571         var cfg = {
25572             tag : 'li',
25573             cn : [
25574                 {
25575                     tag : 'a',
25576                     href : this.href || '#',
25577                     cn : text
25578                 }
25579             ]
25580         };
25581         
25582         if(this.disable){
25583             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25584         }
25585         
25586         if(this.submenu){
25587             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25588             
25589             if(this.pos == 'left'){
25590                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25591             }
25592         }
25593         
25594         return cfg;
25595     },
25596     
25597     initEvents : function() 
25598     {
25599         this.el.on('mouseover', this.onMouseOver, this);
25600         this.el.on('mouseout', this.onMouseOut, this);
25601         
25602         this.el.select('a', true).first().on('click', this.onClick, this);
25603         
25604     },
25605     
25606     onClick : function(e)
25607     {
25608         if(this.preventDefault){
25609             e.preventDefault();
25610         }
25611         
25612         this.fireEvent("click", this, e);
25613     },
25614     
25615     onMouseOver : function(e)
25616     {
25617         if(this.submenu && this.pos == 'left'){
25618             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25619         }
25620         
25621         this.fireEvent("mouseover", this, e);
25622     },
25623     
25624     onMouseOut : function(e)
25625     {
25626         this.fireEvent("mouseout", this, e);
25627     }
25628 });
25629
25630  
25631
25632  /*
25633  * - LGPL
25634  *
25635  * menu separator
25636  * 
25637  */
25638 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25639
25640 /**
25641  * @class Roo.bootstrap.menu.Separator
25642  * @extends Roo.bootstrap.Component
25643  * Bootstrap Separator class
25644  * 
25645  * @constructor
25646  * Create a new Separator
25647  * @param {Object} config The config object
25648  */
25649
25650
25651 Roo.bootstrap.menu.Separator = function(config){
25652     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25653 };
25654
25655 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25656     
25657     getAutoCreate : function(){
25658         var cfg = {
25659             tag : 'li',
25660             cls: 'divider'
25661         };
25662         
25663         return cfg;
25664     }
25665    
25666 });
25667
25668  
25669
25670  /*
25671  * - LGPL
25672  *
25673  * Tooltip
25674  * 
25675  */
25676
25677 /**
25678  * @class Roo.bootstrap.Tooltip
25679  * Bootstrap Tooltip class
25680  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25681  * to determine which dom element triggers the tooltip.
25682  * 
25683  * It needs to add support for additional attributes like tooltip-position
25684  * 
25685  * @constructor
25686  * Create a new Toolti
25687  * @param {Object} config The config object
25688  */
25689
25690 Roo.bootstrap.Tooltip = function(config){
25691     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25692     
25693     this.alignment = Roo.bootstrap.Tooltip.alignment;
25694     
25695     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25696         this.alignment = config.alignment;
25697     }
25698     
25699 };
25700
25701 Roo.apply(Roo.bootstrap.Tooltip, {
25702     /**
25703      * @function init initialize tooltip monitoring.
25704      * @static
25705      */
25706     currentEl : false,
25707     currentTip : false,
25708     currentRegion : false,
25709     
25710     //  init : delay?
25711     
25712     init : function()
25713     {
25714         Roo.get(document).on('mouseover', this.enter ,this);
25715         Roo.get(document).on('mouseout', this.leave, this);
25716          
25717         
25718         this.currentTip = new Roo.bootstrap.Tooltip();
25719     },
25720     
25721     enter : function(ev)
25722     {
25723         var dom = ev.getTarget();
25724         
25725         //Roo.log(['enter',dom]);
25726         var el = Roo.fly(dom);
25727         if (this.currentEl) {
25728             //Roo.log(dom);
25729             //Roo.log(this.currentEl);
25730             //Roo.log(this.currentEl.contains(dom));
25731             if (this.currentEl == el) {
25732                 return;
25733             }
25734             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25735                 return;
25736             }
25737
25738         }
25739         
25740         if (this.currentTip.el) {
25741             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25742         }    
25743         //Roo.log(ev);
25744         
25745         if(!el || el.dom == document){
25746             return;
25747         }
25748         
25749         var bindEl = el;
25750         
25751         // you can not look for children, as if el is the body.. then everythign is the child..
25752         if (!el.attr('tooltip')) { //
25753             if (!el.select("[tooltip]").elements.length) {
25754                 return;
25755             }
25756             // is the mouse over this child...?
25757             bindEl = el.select("[tooltip]").first();
25758             var xy = ev.getXY();
25759             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25760                 //Roo.log("not in region.");
25761                 return;
25762             }
25763             //Roo.log("child element over..");
25764             
25765         }
25766         this.currentEl = bindEl;
25767         this.currentTip.bind(bindEl);
25768         this.currentRegion = Roo.lib.Region.getRegion(dom);
25769         this.currentTip.enter();
25770         
25771     },
25772     leave : function(ev)
25773     {
25774         var dom = ev.getTarget();
25775         //Roo.log(['leave',dom]);
25776         if (!this.currentEl) {
25777             return;
25778         }
25779         
25780         
25781         if (dom != this.currentEl.dom) {
25782             return;
25783         }
25784         var xy = ev.getXY();
25785         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25786             return;
25787         }
25788         // only activate leave if mouse cursor is outside... bounding box..
25789         
25790         
25791         
25792         
25793         if (this.currentTip) {
25794             this.currentTip.leave();
25795         }
25796         //Roo.log('clear currentEl');
25797         this.currentEl = false;
25798         
25799         
25800     },
25801     alignment : {
25802         'left' : ['r-l', [-2,0], 'right'],
25803         'right' : ['l-r', [2,0], 'left'],
25804         'bottom' : ['t-b', [0,2], 'top'],
25805         'top' : [ 'b-t', [0,-2], 'bottom']
25806     }
25807     
25808 });
25809
25810
25811 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25812     
25813     
25814     bindEl : false,
25815     
25816     delay : null, // can be { show : 300 , hide: 500}
25817     
25818     timeout : null,
25819     
25820     hoverState : null, //???
25821     
25822     placement : 'bottom', 
25823     
25824     alignment : false,
25825     
25826     getAutoCreate : function(){
25827     
25828         var cfg = {
25829            cls : 'tooltip',
25830            role : 'tooltip',
25831            cn : [
25832                 {
25833                     cls : 'tooltip-arrow'
25834                 },
25835                 {
25836                     cls : 'tooltip-inner'
25837                 }
25838            ]
25839         };
25840         
25841         return cfg;
25842     },
25843     bind : function(el)
25844     {
25845         this.bindEl = el;
25846     },
25847       
25848     
25849     enter : function () {
25850        
25851         if (this.timeout != null) {
25852             clearTimeout(this.timeout);
25853         }
25854         
25855         this.hoverState = 'in';
25856          //Roo.log("enter - show");
25857         if (!this.delay || !this.delay.show) {
25858             this.show();
25859             return;
25860         }
25861         var _t = this;
25862         this.timeout = setTimeout(function () {
25863             if (_t.hoverState == 'in') {
25864                 _t.show();
25865             }
25866         }, this.delay.show);
25867     },
25868     leave : function()
25869     {
25870         clearTimeout(this.timeout);
25871     
25872         this.hoverState = 'out';
25873          if (!this.delay || !this.delay.hide) {
25874             this.hide();
25875             return;
25876         }
25877        
25878         var _t = this;
25879         this.timeout = setTimeout(function () {
25880             //Roo.log("leave - timeout");
25881             
25882             if (_t.hoverState == 'out') {
25883                 _t.hide();
25884                 Roo.bootstrap.Tooltip.currentEl = false;
25885             }
25886         }, delay);
25887     },
25888     
25889     show : function (msg)
25890     {
25891         if (!this.el) {
25892             this.render(document.body);
25893         }
25894         // set content.
25895         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25896         
25897         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25898         
25899         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25900         
25901         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25902         
25903         var placement = typeof this.placement == 'function' ?
25904             this.placement.call(this, this.el, on_el) :
25905             this.placement;
25906             
25907         var autoToken = /\s?auto?\s?/i;
25908         var autoPlace = autoToken.test(placement);
25909         if (autoPlace) {
25910             placement = placement.replace(autoToken, '') || 'top';
25911         }
25912         
25913         //this.el.detach()
25914         //this.el.setXY([0,0]);
25915         this.el.show();
25916         //this.el.dom.style.display='block';
25917         
25918         //this.el.appendTo(on_el);
25919         
25920         var p = this.getPosition();
25921         var box = this.el.getBox();
25922         
25923         if (autoPlace) {
25924             // fixme..
25925         }
25926         
25927         var align = this.alignment[placement];
25928         
25929         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25930         
25931         if(placement == 'top' || placement == 'bottom'){
25932             if(xy[0] < 0){
25933                 placement = 'right';
25934             }
25935             
25936             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25937                 placement = 'left';
25938             }
25939             
25940             var scroll = Roo.select('body', true).first().getScroll();
25941             
25942             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25943                 placement = 'top';
25944             }
25945             
25946         }
25947         
25948         this.el.alignTo(this.bindEl, align[0],align[1]);
25949         //var arrow = this.el.select('.arrow',true).first();
25950         //arrow.set(align[2], 
25951         
25952         this.el.addClass(placement);
25953         
25954         this.el.addClass('in fade');
25955         
25956         this.hoverState = null;
25957         
25958         if (this.el.hasClass('fade')) {
25959             // fade it?
25960         }
25961         
25962     },
25963     hide : function()
25964     {
25965          
25966         if (!this.el) {
25967             return;
25968         }
25969         //this.el.setXY([0,0]);
25970         this.el.removeClass('in');
25971         //this.el.hide();
25972         
25973     }
25974     
25975 });
25976  
25977
25978  /*
25979  * - LGPL
25980  *
25981  * Location Picker
25982  * 
25983  */
25984
25985 /**
25986  * @class Roo.bootstrap.LocationPicker
25987  * @extends Roo.bootstrap.Component
25988  * Bootstrap LocationPicker class
25989  * @cfg {Number} latitude Position when init default 0
25990  * @cfg {Number} longitude Position when init default 0
25991  * @cfg {Number} zoom default 15
25992  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25993  * @cfg {Boolean} mapTypeControl default false
25994  * @cfg {Boolean} disableDoubleClickZoom default false
25995  * @cfg {Boolean} scrollwheel default true
25996  * @cfg {Boolean} streetViewControl default false
25997  * @cfg {Number} radius default 0
25998  * @cfg {String} locationName
25999  * @cfg {Boolean} draggable default true
26000  * @cfg {Boolean} enableAutocomplete default false
26001  * @cfg {Boolean} enableReverseGeocode default true
26002  * @cfg {String} markerTitle
26003  * 
26004  * @constructor
26005  * Create a new LocationPicker
26006  * @param {Object} config The config object
26007  */
26008
26009
26010 Roo.bootstrap.LocationPicker = function(config){
26011     
26012     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26013     
26014     this.addEvents({
26015         /**
26016          * @event initial
26017          * Fires when the picker initialized.
26018          * @param {Roo.bootstrap.LocationPicker} this
26019          * @param {Google Location} location
26020          */
26021         initial : true,
26022         /**
26023          * @event positionchanged
26024          * Fires when the picker position changed.
26025          * @param {Roo.bootstrap.LocationPicker} this
26026          * @param {Google Location} location
26027          */
26028         positionchanged : true,
26029         /**
26030          * @event resize
26031          * Fires when the map resize.
26032          * @param {Roo.bootstrap.LocationPicker} this
26033          */
26034         resize : true,
26035         /**
26036          * @event show
26037          * Fires when the map show.
26038          * @param {Roo.bootstrap.LocationPicker} this
26039          */
26040         show : true,
26041         /**
26042          * @event hide
26043          * Fires when the map hide.
26044          * @param {Roo.bootstrap.LocationPicker} this
26045          */
26046         hide : true,
26047         /**
26048          * @event mapClick
26049          * Fires when click the map.
26050          * @param {Roo.bootstrap.LocationPicker} this
26051          * @param {Map event} e
26052          */
26053         mapClick : true,
26054         /**
26055          * @event mapRightClick
26056          * Fires when right click the map.
26057          * @param {Roo.bootstrap.LocationPicker} this
26058          * @param {Map event} e
26059          */
26060         mapRightClick : true,
26061         /**
26062          * @event markerClick
26063          * Fires when click the marker.
26064          * @param {Roo.bootstrap.LocationPicker} this
26065          * @param {Map event} e
26066          */
26067         markerClick : true,
26068         /**
26069          * @event markerRightClick
26070          * Fires when right click the marker.
26071          * @param {Roo.bootstrap.LocationPicker} this
26072          * @param {Map event} e
26073          */
26074         markerRightClick : true,
26075         /**
26076          * @event OverlayViewDraw
26077          * Fires when OverlayView Draw
26078          * @param {Roo.bootstrap.LocationPicker} this
26079          */
26080         OverlayViewDraw : true,
26081         /**
26082          * @event OverlayViewOnAdd
26083          * Fires when OverlayView Draw
26084          * @param {Roo.bootstrap.LocationPicker} this
26085          */
26086         OverlayViewOnAdd : true,
26087         /**
26088          * @event OverlayViewOnRemove
26089          * Fires when OverlayView Draw
26090          * @param {Roo.bootstrap.LocationPicker} this
26091          */
26092         OverlayViewOnRemove : true,
26093         /**
26094          * @event OverlayViewShow
26095          * Fires when OverlayView Draw
26096          * @param {Roo.bootstrap.LocationPicker} this
26097          * @param {Pixel} cpx
26098          */
26099         OverlayViewShow : true,
26100         /**
26101          * @event OverlayViewHide
26102          * Fires when OverlayView Draw
26103          * @param {Roo.bootstrap.LocationPicker} this
26104          */
26105         OverlayViewHide : true,
26106         /**
26107          * @event loadexception
26108          * Fires when load google lib failed.
26109          * @param {Roo.bootstrap.LocationPicker} this
26110          */
26111         loadexception : true
26112     });
26113         
26114 };
26115
26116 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26117     
26118     gMapContext: false,
26119     
26120     latitude: 0,
26121     longitude: 0,
26122     zoom: 15,
26123     mapTypeId: false,
26124     mapTypeControl: false,
26125     disableDoubleClickZoom: false,
26126     scrollwheel: true,
26127     streetViewControl: false,
26128     radius: 0,
26129     locationName: '',
26130     draggable: true,
26131     enableAutocomplete: false,
26132     enableReverseGeocode: true,
26133     markerTitle: '',
26134     
26135     getAutoCreate: function()
26136     {
26137
26138         var cfg = {
26139             tag: 'div',
26140             cls: 'roo-location-picker'
26141         };
26142         
26143         return cfg
26144     },
26145     
26146     initEvents: function(ct, position)
26147     {       
26148         if(!this.el.getWidth() || this.isApplied()){
26149             return;
26150         }
26151         
26152         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26153         
26154         this.initial();
26155     },
26156     
26157     initial: function()
26158     {
26159         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26160             this.fireEvent('loadexception', this);
26161             return;
26162         }
26163         
26164         if(!this.mapTypeId){
26165             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26166         }
26167         
26168         this.gMapContext = this.GMapContext();
26169         
26170         this.initOverlayView();
26171         
26172         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26173         
26174         var _this = this;
26175                 
26176         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26177             _this.setPosition(_this.gMapContext.marker.position);
26178         });
26179         
26180         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26181             _this.fireEvent('mapClick', this, event);
26182             
26183         });
26184
26185         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26186             _this.fireEvent('mapRightClick', this, event);
26187             
26188         });
26189         
26190         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26191             _this.fireEvent('markerClick', this, event);
26192             
26193         });
26194
26195         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26196             _this.fireEvent('markerRightClick', this, event);
26197             
26198         });
26199         
26200         this.setPosition(this.gMapContext.location);
26201         
26202         this.fireEvent('initial', this, this.gMapContext.location);
26203     },
26204     
26205     initOverlayView: function()
26206     {
26207         var _this = this;
26208         
26209         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26210             
26211             draw: function()
26212             {
26213                 _this.fireEvent('OverlayViewDraw', _this);
26214             },
26215             
26216             onAdd: function()
26217             {
26218                 _this.fireEvent('OverlayViewOnAdd', _this);
26219             },
26220             
26221             onRemove: function()
26222             {
26223                 _this.fireEvent('OverlayViewOnRemove', _this);
26224             },
26225             
26226             show: function(cpx)
26227             {
26228                 _this.fireEvent('OverlayViewShow', _this, cpx);
26229             },
26230             
26231             hide: function()
26232             {
26233                 _this.fireEvent('OverlayViewHide', _this);
26234             }
26235             
26236         });
26237     },
26238     
26239     fromLatLngToContainerPixel: function(event)
26240     {
26241         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26242     },
26243     
26244     isApplied: function() 
26245     {
26246         return this.getGmapContext() == false ? false : true;
26247     },
26248     
26249     getGmapContext: function() 
26250     {
26251         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26252     },
26253     
26254     GMapContext: function() 
26255     {
26256         var position = new google.maps.LatLng(this.latitude, this.longitude);
26257         
26258         var _map = new google.maps.Map(this.el.dom, {
26259             center: position,
26260             zoom: this.zoom,
26261             mapTypeId: this.mapTypeId,
26262             mapTypeControl: this.mapTypeControl,
26263             disableDoubleClickZoom: this.disableDoubleClickZoom,
26264             scrollwheel: this.scrollwheel,
26265             streetViewControl: this.streetViewControl,
26266             locationName: this.locationName,
26267             draggable: this.draggable,
26268             enableAutocomplete: this.enableAutocomplete,
26269             enableReverseGeocode: this.enableReverseGeocode
26270         });
26271         
26272         var _marker = new google.maps.Marker({
26273             position: position,
26274             map: _map,
26275             title: this.markerTitle,
26276             draggable: this.draggable
26277         });
26278         
26279         return {
26280             map: _map,
26281             marker: _marker,
26282             circle: null,
26283             location: position,
26284             radius: this.radius,
26285             locationName: this.locationName,
26286             addressComponents: {
26287                 formatted_address: null,
26288                 addressLine1: null,
26289                 addressLine2: null,
26290                 streetName: null,
26291                 streetNumber: null,
26292                 city: null,
26293                 district: null,
26294                 state: null,
26295                 stateOrProvince: null
26296             },
26297             settings: this,
26298             domContainer: this.el.dom,
26299             geodecoder: new google.maps.Geocoder()
26300         };
26301     },
26302     
26303     drawCircle: function(center, radius, options) 
26304     {
26305         if (this.gMapContext.circle != null) {
26306             this.gMapContext.circle.setMap(null);
26307         }
26308         if (radius > 0) {
26309             radius *= 1;
26310             options = Roo.apply({}, options, {
26311                 strokeColor: "#0000FF",
26312                 strokeOpacity: .35,
26313                 strokeWeight: 2,
26314                 fillColor: "#0000FF",
26315                 fillOpacity: .2
26316             });
26317             
26318             options.map = this.gMapContext.map;
26319             options.radius = radius;
26320             options.center = center;
26321             this.gMapContext.circle = new google.maps.Circle(options);
26322             return this.gMapContext.circle;
26323         }
26324         
26325         return null;
26326     },
26327     
26328     setPosition: function(location) 
26329     {
26330         this.gMapContext.location = location;
26331         this.gMapContext.marker.setPosition(location);
26332         this.gMapContext.map.panTo(location);
26333         this.drawCircle(location, this.gMapContext.radius, {});
26334         
26335         var _this = this;
26336         
26337         if (this.gMapContext.settings.enableReverseGeocode) {
26338             this.gMapContext.geodecoder.geocode({
26339                 latLng: this.gMapContext.location
26340             }, function(results, status) {
26341                 
26342                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26343                     _this.gMapContext.locationName = results[0].formatted_address;
26344                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26345                     
26346                     _this.fireEvent('positionchanged', this, location);
26347                 }
26348             });
26349             
26350             return;
26351         }
26352         
26353         this.fireEvent('positionchanged', this, location);
26354     },
26355     
26356     resize: function()
26357     {
26358         google.maps.event.trigger(this.gMapContext.map, "resize");
26359         
26360         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26361         
26362         this.fireEvent('resize', this);
26363     },
26364     
26365     setPositionByLatLng: function(latitude, longitude)
26366     {
26367         this.setPosition(new google.maps.LatLng(latitude, longitude));
26368     },
26369     
26370     getCurrentPosition: function() 
26371     {
26372         return {
26373             latitude: this.gMapContext.location.lat(),
26374             longitude: this.gMapContext.location.lng()
26375         };
26376     },
26377     
26378     getAddressName: function() 
26379     {
26380         return this.gMapContext.locationName;
26381     },
26382     
26383     getAddressComponents: function() 
26384     {
26385         return this.gMapContext.addressComponents;
26386     },
26387     
26388     address_component_from_google_geocode: function(address_components) 
26389     {
26390         var result = {};
26391         
26392         for (var i = 0; i < address_components.length; i++) {
26393             var component = address_components[i];
26394             if (component.types.indexOf("postal_code") >= 0) {
26395                 result.postalCode = component.short_name;
26396             } else if (component.types.indexOf("street_number") >= 0) {
26397                 result.streetNumber = component.short_name;
26398             } else if (component.types.indexOf("route") >= 0) {
26399                 result.streetName = component.short_name;
26400             } else if (component.types.indexOf("neighborhood") >= 0) {
26401                 result.city = component.short_name;
26402             } else if (component.types.indexOf("locality") >= 0) {
26403                 result.city = component.short_name;
26404             } else if (component.types.indexOf("sublocality") >= 0) {
26405                 result.district = component.short_name;
26406             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26407                 result.stateOrProvince = component.short_name;
26408             } else if (component.types.indexOf("country") >= 0) {
26409                 result.country = component.short_name;
26410             }
26411         }
26412         
26413         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26414         result.addressLine2 = "";
26415         return result;
26416     },
26417     
26418     setZoomLevel: function(zoom)
26419     {
26420         this.gMapContext.map.setZoom(zoom);
26421     },
26422     
26423     show: function()
26424     {
26425         if(!this.el){
26426             return;
26427         }
26428         
26429         this.el.show();
26430         
26431         this.resize();
26432         
26433         this.fireEvent('show', this);
26434     },
26435     
26436     hide: function()
26437     {
26438         if(!this.el){
26439             return;
26440         }
26441         
26442         this.el.hide();
26443         
26444         this.fireEvent('hide', this);
26445     }
26446     
26447 });
26448
26449 Roo.apply(Roo.bootstrap.LocationPicker, {
26450     
26451     OverlayView : function(map, options)
26452     {
26453         options = options || {};
26454         
26455         this.setMap(map);
26456     }
26457     
26458     
26459 });/*
26460  * - LGPL
26461  *
26462  * Alert
26463  * 
26464  */
26465
26466 /**
26467  * @class Roo.bootstrap.Alert
26468  * @extends Roo.bootstrap.Component
26469  * Bootstrap Alert class
26470  * @cfg {String} title The title of alert
26471  * @cfg {String} html The content of alert
26472  * @cfg {String} weight (  success | info | warning | danger )
26473  * @cfg {String} faicon font-awesomeicon
26474  * 
26475  * @constructor
26476  * Create a new alert
26477  * @param {Object} config The config object
26478  */
26479
26480
26481 Roo.bootstrap.Alert = function(config){
26482     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26483     
26484 };
26485
26486 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26487     
26488     title: '',
26489     html: '',
26490     weight: false,
26491     faicon: false,
26492     
26493     getAutoCreate : function()
26494     {
26495         
26496         var cfg = {
26497             tag : 'div',
26498             cls : 'alert',
26499             cn : [
26500                 {
26501                     tag : 'i',
26502                     cls : 'roo-alert-icon'
26503                     
26504                 },
26505                 {
26506                     tag : 'b',
26507                     cls : 'roo-alert-title',
26508                     html : this.title
26509                 },
26510                 {
26511                     tag : 'span',
26512                     cls : 'roo-alert-text',
26513                     html : this.html
26514                 }
26515             ]
26516         };
26517         
26518         if(this.faicon){
26519             cfg.cn[0].cls += ' fa ' + this.faicon;
26520         }
26521         
26522         if(this.weight){
26523             cfg.cls += ' alert-' + this.weight;
26524         }
26525         
26526         return cfg;
26527     },
26528     
26529     initEvents: function() 
26530     {
26531         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26532     },
26533     
26534     setTitle : function(str)
26535     {
26536         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26537     },
26538     
26539     setText : function(str)
26540     {
26541         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26542     },
26543     
26544     setWeight : function(weight)
26545     {
26546         if(this.weight){
26547             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26548         }
26549         
26550         this.weight = weight;
26551         
26552         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26553     },
26554     
26555     setIcon : function(icon)
26556     {
26557         if(this.faicon){
26558             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26559         }
26560         
26561         this.faicon = icon;
26562         
26563         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26564     },
26565     
26566     hide: function() 
26567     {
26568         this.el.hide();   
26569     },
26570     
26571     show: function() 
26572     {  
26573         this.el.show();   
26574     }
26575     
26576 });
26577
26578  
26579 /*
26580 * Licence: LGPL
26581 */
26582
26583 /**
26584  * @class Roo.bootstrap.UploadCropbox
26585  * @extends Roo.bootstrap.Component
26586  * Bootstrap UploadCropbox class
26587  * @cfg {String} emptyText show when image has been loaded
26588  * @cfg {String} rotateNotify show when image too small to rotate
26589  * @cfg {Number} errorTimeout default 3000
26590  * @cfg {Number} minWidth default 300
26591  * @cfg {Number} minHeight default 300
26592  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26593  * @cfg {Boolean} isDocument (true|false) default false
26594  * @cfg {String} url action url
26595  * @cfg {String} paramName default 'imageUpload'
26596  * @cfg {String} method default POST
26597  * @cfg {Boolean} loadMask (true|false) default true
26598  * @cfg {Boolean} loadingText default 'Loading...'
26599  * 
26600  * @constructor
26601  * Create a new UploadCropbox
26602  * @param {Object} config The config object
26603  */
26604
26605 Roo.bootstrap.UploadCropbox = function(config){
26606     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26607     
26608     this.addEvents({
26609         /**
26610          * @event beforeselectfile
26611          * Fire before select file
26612          * @param {Roo.bootstrap.UploadCropbox} this
26613          */
26614         "beforeselectfile" : true,
26615         /**
26616          * @event initial
26617          * Fire after initEvent
26618          * @param {Roo.bootstrap.UploadCropbox} this
26619          */
26620         "initial" : true,
26621         /**
26622          * @event crop
26623          * Fire after initEvent
26624          * @param {Roo.bootstrap.UploadCropbox} this
26625          * @param {String} data
26626          */
26627         "crop" : true,
26628         /**
26629          * @event prepare
26630          * Fire when preparing the file data
26631          * @param {Roo.bootstrap.UploadCropbox} this
26632          * @param {Object} file
26633          */
26634         "prepare" : true,
26635         /**
26636          * @event exception
26637          * Fire when get exception
26638          * @param {Roo.bootstrap.UploadCropbox} this
26639          * @param {XMLHttpRequest} xhr
26640          */
26641         "exception" : true,
26642         /**
26643          * @event beforeloadcanvas
26644          * Fire before load the canvas
26645          * @param {Roo.bootstrap.UploadCropbox} this
26646          * @param {String} src
26647          */
26648         "beforeloadcanvas" : true,
26649         /**
26650          * @event trash
26651          * Fire when trash image
26652          * @param {Roo.bootstrap.UploadCropbox} this
26653          */
26654         "trash" : true,
26655         /**
26656          * @event download
26657          * Fire when download the image
26658          * @param {Roo.bootstrap.UploadCropbox} this
26659          */
26660         "download" : true,
26661         /**
26662          * @event footerbuttonclick
26663          * Fire when footerbuttonclick
26664          * @param {Roo.bootstrap.UploadCropbox} this
26665          * @param {String} type
26666          */
26667         "footerbuttonclick" : true,
26668         /**
26669          * @event resize
26670          * Fire when resize
26671          * @param {Roo.bootstrap.UploadCropbox} this
26672          */
26673         "resize" : true,
26674         /**
26675          * @event rotate
26676          * Fire when rotate the image
26677          * @param {Roo.bootstrap.UploadCropbox} this
26678          * @param {String} pos
26679          */
26680         "rotate" : true,
26681         /**
26682          * @event inspect
26683          * Fire when inspect the file
26684          * @param {Roo.bootstrap.UploadCropbox} this
26685          * @param {Object} file
26686          */
26687         "inspect" : true,
26688         /**
26689          * @event upload
26690          * Fire when xhr upload the file
26691          * @param {Roo.bootstrap.UploadCropbox} this
26692          * @param {Object} data
26693          */
26694         "upload" : true,
26695         /**
26696          * @event arrange
26697          * Fire when arrange the file data
26698          * @param {Roo.bootstrap.UploadCropbox} this
26699          * @param {Object} formData
26700          */
26701         "arrange" : true
26702     });
26703     
26704     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26705 };
26706
26707 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26708     
26709     emptyText : 'Click to upload image',
26710     rotateNotify : 'Image is too small to rotate',
26711     errorTimeout : 3000,
26712     scale : 0,
26713     baseScale : 1,
26714     rotate : 0,
26715     dragable : false,
26716     pinching : false,
26717     mouseX : 0,
26718     mouseY : 0,
26719     cropData : false,
26720     minWidth : 300,
26721     minHeight : 300,
26722     file : false,
26723     exif : {},
26724     baseRotate : 1,
26725     cropType : 'image/jpeg',
26726     buttons : false,
26727     canvasLoaded : false,
26728     isDocument : false,
26729     method : 'POST',
26730     paramName : 'imageUpload',
26731     loadMask : true,
26732     loadingText : 'Loading...',
26733     maskEl : false,
26734     
26735     getAutoCreate : function()
26736     {
26737         var cfg = {
26738             tag : 'div',
26739             cls : 'roo-upload-cropbox',
26740             cn : [
26741                 {
26742                     tag : 'input',
26743                     cls : 'roo-upload-cropbox-selector',
26744                     type : 'file'
26745                 },
26746                 {
26747                     tag : 'div',
26748                     cls : 'roo-upload-cropbox-body',
26749                     style : 'cursor:pointer',
26750                     cn : [
26751                         {
26752                             tag : 'div',
26753                             cls : 'roo-upload-cropbox-preview'
26754                         },
26755                         {
26756                             tag : 'div',
26757                             cls : 'roo-upload-cropbox-thumb'
26758                         },
26759                         {
26760                             tag : 'div',
26761                             cls : 'roo-upload-cropbox-empty-notify',
26762                             html : this.emptyText
26763                         },
26764                         {
26765                             tag : 'div',
26766                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26767                             html : this.rotateNotify
26768                         }
26769                     ]
26770                 },
26771                 {
26772                     tag : 'div',
26773                     cls : 'roo-upload-cropbox-footer',
26774                     cn : {
26775                         tag : 'div',
26776                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26777                         cn : []
26778                     }
26779                 }
26780             ]
26781         };
26782         
26783         return cfg;
26784     },
26785     
26786     onRender : function(ct, position)
26787     {
26788         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26789         
26790         if (this.buttons.length) {
26791             
26792             Roo.each(this.buttons, function(bb) {
26793                 
26794                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26795                 
26796                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26797                 
26798             }, this);
26799         }
26800         
26801         if(this.loadMask){
26802             this.maskEl = this.el;
26803         }
26804     },
26805     
26806     initEvents : function()
26807     {
26808         this.urlAPI = (window.createObjectURL && window) || 
26809                                 (window.URL && URL.revokeObjectURL && URL) || 
26810                                 (window.webkitURL && webkitURL);
26811                         
26812         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26813         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26814         
26815         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26816         this.selectorEl.hide();
26817         
26818         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26819         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26820         
26821         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26822         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26823         this.thumbEl.hide();
26824         
26825         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26826         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26827         
26828         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26829         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26830         this.errorEl.hide();
26831         
26832         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26833         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26834         this.footerEl.hide();
26835         
26836         this.setThumbBoxSize();
26837         
26838         this.bind();
26839         
26840         this.resize();
26841         
26842         this.fireEvent('initial', this);
26843     },
26844
26845     bind : function()
26846     {
26847         var _this = this;
26848         
26849         window.addEventListener("resize", function() { _this.resize(); } );
26850         
26851         this.bodyEl.on('click', this.beforeSelectFile, this);
26852         
26853         if(Roo.isTouch){
26854             this.bodyEl.on('touchstart', this.onTouchStart, this);
26855             this.bodyEl.on('touchmove', this.onTouchMove, this);
26856             this.bodyEl.on('touchend', this.onTouchEnd, this);
26857         }
26858         
26859         if(!Roo.isTouch){
26860             this.bodyEl.on('mousedown', this.onMouseDown, this);
26861             this.bodyEl.on('mousemove', this.onMouseMove, this);
26862             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26863             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26864             Roo.get(document).on('mouseup', this.onMouseUp, this);
26865         }
26866         
26867         this.selectorEl.on('change', this.onFileSelected, this);
26868     },
26869     
26870     reset : function()
26871     {    
26872         this.scale = 0;
26873         this.baseScale = 1;
26874         this.rotate = 0;
26875         this.baseRotate = 1;
26876         this.dragable = false;
26877         this.pinching = false;
26878         this.mouseX = 0;
26879         this.mouseY = 0;
26880         this.cropData = false;
26881         this.notifyEl.dom.innerHTML = this.emptyText;
26882         
26883         this.selectorEl.dom.value = '';
26884         
26885     },
26886     
26887     resize : function()
26888     {
26889         if(this.fireEvent('resize', this) != false){
26890             this.setThumbBoxPosition();
26891             this.setCanvasPosition();
26892         }
26893     },
26894     
26895     onFooterButtonClick : function(e, el, o, type)
26896     {
26897         switch (type) {
26898             case 'rotate-left' :
26899                 this.onRotateLeft(e);
26900                 break;
26901             case 'rotate-right' :
26902                 this.onRotateRight(e);
26903                 break;
26904             case 'picture' :
26905                 this.beforeSelectFile(e);
26906                 break;
26907             case 'trash' :
26908                 this.trash(e);
26909                 break;
26910             case 'crop' :
26911                 this.crop(e);
26912                 break;
26913             case 'download' :
26914                 this.download(e);
26915                 break;
26916             default :
26917                 break;
26918         }
26919         
26920         this.fireEvent('footerbuttonclick', this, type);
26921     },
26922     
26923     beforeSelectFile : function(e)
26924     {
26925         e.preventDefault();
26926         
26927         if(this.fireEvent('beforeselectfile', this) != false){
26928             this.selectorEl.dom.click();
26929         }
26930     },
26931     
26932     onFileSelected : function(e)
26933     {
26934         e.preventDefault();
26935         
26936         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26937             return;
26938         }
26939         
26940         var file = this.selectorEl.dom.files[0];
26941         
26942         if(this.fireEvent('inspect', this, file) != false){
26943             this.prepare(file);
26944         }
26945         
26946     },
26947     
26948     trash : function(e)
26949     {
26950         this.fireEvent('trash', this);
26951     },
26952     
26953     download : function(e)
26954     {
26955         this.fireEvent('download', this);
26956     },
26957     
26958     loadCanvas : function(src)
26959     {   
26960         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26961             
26962             this.reset();
26963             
26964             this.imageEl = document.createElement('img');
26965             
26966             var _this = this;
26967             
26968             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26969             
26970             this.imageEl.src = src;
26971         }
26972     },
26973     
26974     onLoadCanvas : function()
26975     {   
26976         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26977         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26978         
26979         this.bodyEl.un('click', this.beforeSelectFile, this);
26980         
26981         this.notifyEl.hide();
26982         this.thumbEl.show();
26983         this.footerEl.show();
26984         
26985         this.baseRotateLevel();
26986         
26987         if(this.isDocument){
26988             this.setThumbBoxSize();
26989         }
26990         
26991         this.setThumbBoxPosition();
26992         
26993         this.baseScaleLevel();
26994         
26995         this.draw();
26996         
26997         this.resize();
26998         
26999         this.canvasLoaded = true;
27000         
27001         if(this.loadMask){
27002             this.maskEl.unmask();
27003         }
27004         
27005     },
27006     
27007     setCanvasPosition : function()
27008     {   
27009         if(!this.canvasEl){
27010             return;
27011         }
27012         
27013         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27014         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27015         
27016         this.previewEl.setLeft(pw);
27017         this.previewEl.setTop(ph);
27018         
27019     },
27020     
27021     onMouseDown : function(e)
27022     {   
27023         e.stopEvent();
27024         
27025         this.dragable = true;
27026         this.pinching = false;
27027         
27028         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27029             this.dragable = false;
27030             return;
27031         }
27032         
27033         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27034         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27035         
27036     },
27037     
27038     onMouseMove : function(e)
27039     {   
27040         e.stopEvent();
27041         
27042         if(!this.canvasLoaded){
27043             return;
27044         }
27045         
27046         if (!this.dragable){
27047             return;
27048         }
27049         
27050         var minX = Math.ceil(this.thumbEl.getLeft(true));
27051         var minY = Math.ceil(this.thumbEl.getTop(true));
27052         
27053         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27054         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27055         
27056         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27057         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27058         
27059         x = x - this.mouseX;
27060         y = y - this.mouseY;
27061         
27062         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27063         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27064         
27065         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27066         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27067         
27068         this.previewEl.setLeft(bgX);
27069         this.previewEl.setTop(bgY);
27070         
27071         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27072         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27073     },
27074     
27075     onMouseUp : function(e)
27076     {   
27077         e.stopEvent();
27078         
27079         this.dragable = false;
27080     },
27081     
27082     onMouseWheel : function(e)
27083     {   
27084         e.stopEvent();
27085         
27086         this.startScale = this.scale;
27087         
27088         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27089         
27090         if(!this.zoomable()){
27091             this.scale = this.startScale;
27092             return;
27093         }
27094         
27095         this.draw();
27096         
27097         return;
27098     },
27099     
27100     zoomable : function()
27101     {
27102         var minScale = this.thumbEl.getWidth() / this.minWidth;
27103         
27104         if(this.minWidth < this.minHeight){
27105             minScale = this.thumbEl.getHeight() / this.minHeight;
27106         }
27107         
27108         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27109         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27110         
27111         if(
27112                 this.isDocument &&
27113                 (this.rotate == 0 || this.rotate == 180) && 
27114                 (
27115                     width > this.imageEl.OriginWidth || 
27116                     height > this.imageEl.OriginHeight ||
27117                     (width < this.minWidth && height < this.minHeight)
27118                 )
27119         ){
27120             return false;
27121         }
27122         
27123         if(
27124                 this.isDocument &&
27125                 (this.rotate == 90 || this.rotate == 270) && 
27126                 (
27127                     width > this.imageEl.OriginWidth || 
27128                     height > this.imageEl.OriginHeight ||
27129                     (width < this.minHeight && height < this.minWidth)
27130                 )
27131         ){
27132             return false;
27133         }
27134         
27135         if(
27136                 !this.isDocument &&
27137                 (this.rotate == 0 || this.rotate == 180) && 
27138                 (
27139                     width < this.minWidth || 
27140                     width > this.imageEl.OriginWidth || 
27141                     height < this.minHeight || 
27142                     height > this.imageEl.OriginHeight
27143                 )
27144         ){
27145             return false;
27146         }
27147         
27148         if(
27149                 !this.isDocument &&
27150                 (this.rotate == 90 || this.rotate == 270) && 
27151                 (
27152                     width < this.minHeight || 
27153                     width > this.imageEl.OriginWidth || 
27154                     height < this.minWidth || 
27155                     height > this.imageEl.OriginHeight
27156                 )
27157         ){
27158             return false;
27159         }
27160         
27161         return true;
27162         
27163     },
27164     
27165     onRotateLeft : function(e)
27166     {   
27167         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27168             
27169             var minScale = this.thumbEl.getWidth() / this.minWidth;
27170             
27171             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27172             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27173             
27174             this.startScale = this.scale;
27175             
27176             while (this.getScaleLevel() < minScale){
27177             
27178                 this.scale = this.scale + 1;
27179                 
27180                 if(!this.zoomable()){
27181                     break;
27182                 }
27183                 
27184                 if(
27185                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27186                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27187                 ){
27188                     continue;
27189                 }
27190                 
27191                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27192
27193                 this.draw();
27194                 
27195                 return;
27196             }
27197             
27198             this.scale = this.startScale;
27199             
27200             this.onRotateFail();
27201             
27202             return false;
27203         }
27204         
27205         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27206
27207         if(this.isDocument){
27208             this.setThumbBoxSize();
27209             this.setThumbBoxPosition();
27210             this.setCanvasPosition();
27211         }
27212         
27213         this.draw();
27214         
27215         this.fireEvent('rotate', this, 'left');
27216         
27217     },
27218     
27219     onRotateRight : function(e)
27220     {
27221         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27222             
27223             var minScale = this.thumbEl.getWidth() / this.minWidth;
27224         
27225             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27226             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27227             
27228             this.startScale = this.scale;
27229             
27230             while (this.getScaleLevel() < minScale){
27231             
27232                 this.scale = this.scale + 1;
27233                 
27234                 if(!this.zoomable()){
27235                     break;
27236                 }
27237                 
27238                 if(
27239                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27240                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27241                 ){
27242                     continue;
27243                 }
27244                 
27245                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27246
27247                 this.draw();
27248                 
27249                 return;
27250             }
27251             
27252             this.scale = this.startScale;
27253             
27254             this.onRotateFail();
27255             
27256             return false;
27257         }
27258         
27259         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27260
27261         if(this.isDocument){
27262             this.setThumbBoxSize();
27263             this.setThumbBoxPosition();
27264             this.setCanvasPosition();
27265         }
27266         
27267         this.draw();
27268         
27269         this.fireEvent('rotate', this, 'right');
27270     },
27271     
27272     onRotateFail : function()
27273     {
27274         this.errorEl.show(true);
27275         
27276         var _this = this;
27277         
27278         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27279     },
27280     
27281     draw : function()
27282     {
27283         this.previewEl.dom.innerHTML = '';
27284         
27285         var canvasEl = document.createElement("canvas");
27286         
27287         var contextEl = canvasEl.getContext("2d");
27288         
27289         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27290         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27291         var center = this.imageEl.OriginWidth / 2;
27292         
27293         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27294             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27295             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27296             center = this.imageEl.OriginHeight / 2;
27297         }
27298         
27299         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27300         
27301         contextEl.translate(center, center);
27302         contextEl.rotate(this.rotate * Math.PI / 180);
27303
27304         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27305         
27306         this.canvasEl = document.createElement("canvas");
27307         
27308         this.contextEl = this.canvasEl.getContext("2d");
27309         
27310         switch (this.rotate) {
27311             case 0 :
27312                 
27313                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27314                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27315                 
27316                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27317                 
27318                 break;
27319             case 90 : 
27320                 
27321                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27322                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27323                 
27324                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27325                     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);
27326                     break;
27327                 }
27328                 
27329                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27330                 
27331                 break;
27332             case 180 :
27333                 
27334                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27335                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27336                 
27337                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27338                     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);
27339                     break;
27340                 }
27341                 
27342                 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);
27343                 
27344                 break;
27345             case 270 :
27346                 
27347                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27348                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27349         
27350                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27351                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27352                     break;
27353                 }
27354                 
27355                 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);
27356                 
27357                 break;
27358             default : 
27359                 break;
27360         }
27361         
27362         this.previewEl.appendChild(this.canvasEl);
27363         
27364         this.setCanvasPosition();
27365     },
27366     
27367     crop : function()
27368     {
27369         if(!this.canvasLoaded){
27370             return;
27371         }
27372         
27373         var imageCanvas = document.createElement("canvas");
27374         
27375         var imageContext = imageCanvas.getContext("2d");
27376         
27377         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27378         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27379         
27380         var center = imageCanvas.width / 2;
27381         
27382         imageContext.translate(center, center);
27383         
27384         imageContext.rotate(this.rotate * Math.PI / 180);
27385         
27386         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27387         
27388         var canvas = document.createElement("canvas");
27389         
27390         var context = canvas.getContext("2d");
27391                 
27392         canvas.width = this.minWidth;
27393         canvas.height = this.minHeight;
27394
27395         switch (this.rotate) {
27396             case 0 :
27397                 
27398                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27399                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27400                 
27401                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27402                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27403                 
27404                 var targetWidth = this.minWidth - 2 * x;
27405                 var targetHeight = this.minHeight - 2 * y;
27406                 
27407                 var scale = 1;
27408                 
27409                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27410                     scale = targetWidth / width;
27411                 }
27412                 
27413                 if(x > 0 && y == 0){
27414                     scale = targetHeight / height;
27415                 }
27416                 
27417                 if(x > 0 && y > 0){
27418                     scale = targetWidth / width;
27419                     
27420                     if(width < height){
27421                         scale = targetHeight / height;
27422                     }
27423                 }
27424                 
27425                 context.scale(scale, scale);
27426                 
27427                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27428                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27429
27430                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27431                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27432
27433                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27434                 
27435                 break;
27436             case 90 : 
27437                 
27438                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27439                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27440                 
27441                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27442                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27443                 
27444                 var targetWidth = this.minWidth - 2 * x;
27445                 var targetHeight = this.minHeight - 2 * y;
27446                 
27447                 var scale = 1;
27448                 
27449                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27450                     scale = targetWidth / width;
27451                 }
27452                 
27453                 if(x > 0 && y == 0){
27454                     scale = targetHeight / height;
27455                 }
27456                 
27457                 if(x > 0 && y > 0){
27458                     scale = targetWidth / width;
27459                     
27460                     if(width < height){
27461                         scale = targetHeight / height;
27462                     }
27463                 }
27464                 
27465                 context.scale(scale, scale);
27466                 
27467                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27468                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27469
27470                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27471                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27472                 
27473                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27474                 
27475                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27476                 
27477                 break;
27478             case 180 :
27479                 
27480                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27481                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27482                 
27483                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27484                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27485                 
27486                 var targetWidth = this.minWidth - 2 * x;
27487                 var targetHeight = this.minHeight - 2 * y;
27488                 
27489                 var scale = 1;
27490                 
27491                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27492                     scale = targetWidth / width;
27493                 }
27494                 
27495                 if(x > 0 && y == 0){
27496                     scale = targetHeight / height;
27497                 }
27498                 
27499                 if(x > 0 && y > 0){
27500                     scale = targetWidth / width;
27501                     
27502                     if(width < height){
27503                         scale = targetHeight / height;
27504                     }
27505                 }
27506                 
27507                 context.scale(scale, scale);
27508                 
27509                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27510                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27511
27512                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27513                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27514
27515                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27516                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27517                 
27518                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27519                 
27520                 break;
27521             case 270 :
27522                 
27523                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27524                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27525                 
27526                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27527                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27528                 
27529                 var targetWidth = this.minWidth - 2 * x;
27530                 var targetHeight = this.minHeight - 2 * y;
27531                 
27532                 var scale = 1;
27533                 
27534                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27535                     scale = targetWidth / width;
27536                 }
27537                 
27538                 if(x > 0 && y == 0){
27539                     scale = targetHeight / height;
27540                 }
27541                 
27542                 if(x > 0 && y > 0){
27543                     scale = targetWidth / width;
27544                     
27545                     if(width < height){
27546                         scale = targetHeight / height;
27547                     }
27548                 }
27549                 
27550                 context.scale(scale, scale);
27551                 
27552                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27553                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27554
27555                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27556                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27557                 
27558                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27559                 
27560                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27561                 
27562                 break;
27563             default : 
27564                 break;
27565         }
27566         
27567         this.cropData = canvas.toDataURL(this.cropType);
27568         
27569         if(this.fireEvent('crop', this, this.cropData) !== false){
27570             this.process(this.file, this.cropData);
27571         }
27572         
27573         return;
27574         
27575     },
27576     
27577     setThumbBoxSize : function()
27578     {
27579         var width, height;
27580         
27581         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27582             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27583             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27584             
27585             this.minWidth = width;
27586             this.minHeight = height;
27587             
27588             if(this.rotate == 90 || this.rotate == 270){
27589                 this.minWidth = height;
27590                 this.minHeight = width;
27591             }
27592         }
27593         
27594         height = 300;
27595         width = Math.ceil(this.minWidth * height / this.minHeight);
27596         
27597         if(this.minWidth > this.minHeight){
27598             width = 300;
27599             height = Math.ceil(this.minHeight * width / this.minWidth);
27600         }
27601         
27602         this.thumbEl.setStyle({
27603             width : width + 'px',
27604             height : height + 'px'
27605         });
27606
27607         return;
27608             
27609     },
27610     
27611     setThumbBoxPosition : function()
27612     {
27613         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27614         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27615         
27616         this.thumbEl.setLeft(x);
27617         this.thumbEl.setTop(y);
27618         
27619     },
27620     
27621     baseRotateLevel : function()
27622     {
27623         this.baseRotate = 1;
27624         
27625         if(
27626                 typeof(this.exif) != 'undefined' &&
27627                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27628                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27629         ){
27630             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27631         }
27632         
27633         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27634         
27635     },
27636     
27637     baseScaleLevel : function()
27638     {
27639         var width, height;
27640         
27641         if(this.isDocument){
27642             
27643             if(this.baseRotate == 6 || this.baseRotate == 8){
27644             
27645                 height = this.thumbEl.getHeight();
27646                 this.baseScale = height / this.imageEl.OriginWidth;
27647
27648                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27649                     width = this.thumbEl.getWidth();
27650                     this.baseScale = width / this.imageEl.OriginHeight;
27651                 }
27652
27653                 return;
27654             }
27655
27656             height = this.thumbEl.getHeight();
27657             this.baseScale = height / this.imageEl.OriginHeight;
27658
27659             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27660                 width = this.thumbEl.getWidth();
27661                 this.baseScale = width / this.imageEl.OriginWidth;
27662             }
27663
27664             return;
27665         }
27666         
27667         if(this.baseRotate == 6 || this.baseRotate == 8){
27668             
27669             width = this.thumbEl.getHeight();
27670             this.baseScale = width / this.imageEl.OriginHeight;
27671             
27672             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27673                 height = this.thumbEl.getWidth();
27674                 this.baseScale = height / this.imageEl.OriginHeight;
27675             }
27676             
27677             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27678                 height = this.thumbEl.getWidth();
27679                 this.baseScale = height / this.imageEl.OriginHeight;
27680                 
27681                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27682                     width = this.thumbEl.getHeight();
27683                     this.baseScale = width / this.imageEl.OriginWidth;
27684                 }
27685             }
27686             
27687             return;
27688         }
27689         
27690         width = this.thumbEl.getWidth();
27691         this.baseScale = width / this.imageEl.OriginWidth;
27692         
27693         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27694             height = this.thumbEl.getHeight();
27695             this.baseScale = height / this.imageEl.OriginHeight;
27696         }
27697         
27698         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27699             
27700             height = this.thumbEl.getHeight();
27701             this.baseScale = height / this.imageEl.OriginHeight;
27702             
27703             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27704                 width = this.thumbEl.getWidth();
27705                 this.baseScale = width / this.imageEl.OriginWidth;
27706             }
27707             
27708         }
27709         
27710         return;
27711     },
27712     
27713     getScaleLevel : function()
27714     {
27715         return this.baseScale * Math.pow(1.1, this.scale);
27716     },
27717     
27718     onTouchStart : function(e)
27719     {
27720         if(!this.canvasLoaded){
27721             this.beforeSelectFile(e);
27722             return;
27723         }
27724         
27725         var touches = e.browserEvent.touches;
27726         
27727         if(!touches){
27728             return;
27729         }
27730         
27731         if(touches.length == 1){
27732             this.onMouseDown(e);
27733             return;
27734         }
27735         
27736         if(touches.length != 2){
27737             return;
27738         }
27739         
27740         var coords = [];
27741         
27742         for(var i = 0, finger; finger = touches[i]; i++){
27743             coords.push(finger.pageX, finger.pageY);
27744         }
27745         
27746         var x = Math.pow(coords[0] - coords[2], 2);
27747         var y = Math.pow(coords[1] - coords[3], 2);
27748         
27749         this.startDistance = Math.sqrt(x + y);
27750         
27751         this.startScale = this.scale;
27752         
27753         this.pinching = true;
27754         this.dragable = false;
27755         
27756     },
27757     
27758     onTouchMove : function(e)
27759     {
27760         if(!this.pinching && !this.dragable){
27761             return;
27762         }
27763         
27764         var touches = e.browserEvent.touches;
27765         
27766         if(!touches){
27767             return;
27768         }
27769         
27770         if(this.dragable){
27771             this.onMouseMove(e);
27772             return;
27773         }
27774         
27775         var coords = [];
27776         
27777         for(var i = 0, finger; finger = touches[i]; i++){
27778             coords.push(finger.pageX, finger.pageY);
27779         }
27780         
27781         var x = Math.pow(coords[0] - coords[2], 2);
27782         var y = Math.pow(coords[1] - coords[3], 2);
27783         
27784         this.endDistance = Math.sqrt(x + y);
27785         
27786         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27787         
27788         if(!this.zoomable()){
27789             this.scale = this.startScale;
27790             return;
27791         }
27792         
27793         this.draw();
27794         
27795     },
27796     
27797     onTouchEnd : function(e)
27798     {
27799         this.pinching = false;
27800         this.dragable = false;
27801         
27802     },
27803     
27804     process : function(file, crop)
27805     {
27806         if(this.loadMask){
27807             this.maskEl.mask(this.loadingText);
27808         }
27809         
27810         this.xhr = new XMLHttpRequest();
27811         
27812         file.xhr = this.xhr;
27813
27814         this.xhr.open(this.method, this.url, true);
27815         
27816         var headers = {
27817             "Accept": "application/json",
27818             "Cache-Control": "no-cache",
27819             "X-Requested-With": "XMLHttpRequest"
27820         };
27821         
27822         for (var headerName in headers) {
27823             var headerValue = headers[headerName];
27824             if (headerValue) {
27825                 this.xhr.setRequestHeader(headerName, headerValue);
27826             }
27827         }
27828         
27829         var _this = this;
27830         
27831         this.xhr.onload = function()
27832         {
27833             _this.xhrOnLoad(_this.xhr);
27834         }
27835         
27836         this.xhr.onerror = function()
27837         {
27838             _this.xhrOnError(_this.xhr);
27839         }
27840         
27841         var formData = new FormData();
27842
27843         formData.append('returnHTML', 'NO');
27844         
27845         if(crop){
27846             formData.append('crop', crop);
27847         }
27848         
27849         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27850             formData.append(this.paramName, file, file.name);
27851         }
27852         
27853         if(typeof(file.filename) != 'undefined'){
27854             formData.append('filename', file.filename);
27855         }
27856         
27857         if(typeof(file.mimetype) != 'undefined'){
27858             formData.append('mimetype', file.mimetype);
27859         }
27860         
27861         if(this.fireEvent('arrange', this, formData) != false){
27862             this.xhr.send(formData);
27863         };
27864     },
27865     
27866     xhrOnLoad : function(xhr)
27867     {
27868         if(this.loadMask){
27869             this.maskEl.unmask();
27870         }
27871         
27872         if (xhr.readyState !== 4) {
27873             this.fireEvent('exception', this, xhr);
27874             return;
27875         }
27876
27877         var response = Roo.decode(xhr.responseText);
27878         
27879         if(!response.success){
27880             this.fireEvent('exception', this, xhr);
27881             return;
27882         }
27883         
27884         var response = Roo.decode(xhr.responseText);
27885         
27886         this.fireEvent('upload', this, response);
27887         
27888     },
27889     
27890     xhrOnError : function()
27891     {
27892         if(this.loadMask){
27893             this.maskEl.unmask();
27894         }
27895         
27896         Roo.log('xhr on error');
27897         
27898         var response = Roo.decode(xhr.responseText);
27899           
27900         Roo.log(response);
27901         
27902     },
27903     
27904     prepare : function(file)
27905     {   
27906         if(this.loadMask){
27907             this.maskEl.mask(this.loadingText);
27908         }
27909         
27910         this.file = false;
27911         this.exif = {};
27912         
27913         if(typeof(file) === 'string'){
27914             this.loadCanvas(file);
27915             return;
27916         }
27917         
27918         if(!file || !this.urlAPI){
27919             return;
27920         }
27921         
27922         this.file = file;
27923         this.cropType = file.type;
27924         
27925         var _this = this;
27926         
27927         if(this.fireEvent('prepare', this, this.file) != false){
27928             
27929             var reader = new FileReader();
27930             
27931             reader.onload = function (e) {
27932                 if (e.target.error) {
27933                     Roo.log(e.target.error);
27934                     return;
27935                 }
27936                 
27937                 var buffer = e.target.result,
27938                     dataView = new DataView(buffer),
27939                     offset = 2,
27940                     maxOffset = dataView.byteLength - 4,
27941                     markerBytes,
27942                     markerLength;
27943                 
27944                 if (dataView.getUint16(0) === 0xffd8) {
27945                     while (offset < maxOffset) {
27946                         markerBytes = dataView.getUint16(offset);
27947                         
27948                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27949                             markerLength = dataView.getUint16(offset + 2) + 2;
27950                             if (offset + markerLength > dataView.byteLength) {
27951                                 Roo.log('Invalid meta data: Invalid segment size.');
27952                                 break;
27953                             }
27954                             
27955                             if(markerBytes == 0xffe1){
27956                                 _this.parseExifData(
27957                                     dataView,
27958                                     offset,
27959                                     markerLength
27960                                 );
27961                             }
27962                             
27963                             offset += markerLength;
27964                             
27965                             continue;
27966                         }
27967                         
27968                         break;
27969                     }
27970                     
27971                 }
27972                 
27973                 var url = _this.urlAPI.createObjectURL(_this.file);
27974                 
27975                 _this.loadCanvas(url);
27976                 
27977                 return;
27978             }
27979             
27980             reader.readAsArrayBuffer(this.file);
27981             
27982         }
27983         
27984     },
27985     
27986     parseExifData : function(dataView, offset, length)
27987     {
27988         var tiffOffset = offset + 10,
27989             littleEndian,
27990             dirOffset;
27991     
27992         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27993             // No Exif data, might be XMP data instead
27994             return;
27995         }
27996         
27997         // Check for the ASCII code for "Exif" (0x45786966):
27998         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27999             // No Exif data, might be XMP data instead
28000             return;
28001         }
28002         if (tiffOffset + 8 > dataView.byteLength) {
28003             Roo.log('Invalid Exif data: Invalid segment size.');
28004             return;
28005         }
28006         // Check for the two null bytes:
28007         if (dataView.getUint16(offset + 8) !== 0x0000) {
28008             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28009             return;
28010         }
28011         // Check the byte alignment:
28012         switch (dataView.getUint16(tiffOffset)) {
28013         case 0x4949:
28014             littleEndian = true;
28015             break;
28016         case 0x4D4D:
28017             littleEndian = false;
28018             break;
28019         default:
28020             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28021             return;
28022         }
28023         // Check for the TIFF tag marker (0x002A):
28024         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28025             Roo.log('Invalid Exif data: Missing TIFF marker.');
28026             return;
28027         }
28028         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28029         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28030         
28031         this.parseExifTags(
28032             dataView,
28033             tiffOffset,
28034             tiffOffset + dirOffset,
28035             littleEndian
28036         );
28037     },
28038     
28039     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28040     {
28041         var tagsNumber,
28042             dirEndOffset,
28043             i;
28044         if (dirOffset + 6 > dataView.byteLength) {
28045             Roo.log('Invalid Exif data: Invalid directory offset.');
28046             return;
28047         }
28048         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28049         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28050         if (dirEndOffset + 4 > dataView.byteLength) {
28051             Roo.log('Invalid Exif data: Invalid directory size.');
28052             return;
28053         }
28054         for (i = 0; i < tagsNumber; i += 1) {
28055             this.parseExifTag(
28056                 dataView,
28057                 tiffOffset,
28058                 dirOffset + 2 + 12 * i, // tag offset
28059                 littleEndian
28060             );
28061         }
28062         // Return the offset to the next directory:
28063         return dataView.getUint32(dirEndOffset, littleEndian);
28064     },
28065     
28066     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28067     {
28068         var tag = dataView.getUint16(offset, littleEndian);
28069         
28070         this.exif[tag] = this.getExifValue(
28071             dataView,
28072             tiffOffset,
28073             offset,
28074             dataView.getUint16(offset + 2, littleEndian), // tag type
28075             dataView.getUint32(offset + 4, littleEndian), // tag length
28076             littleEndian
28077         );
28078     },
28079     
28080     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28081     {
28082         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28083             tagSize,
28084             dataOffset,
28085             values,
28086             i,
28087             str,
28088             c;
28089     
28090         if (!tagType) {
28091             Roo.log('Invalid Exif data: Invalid tag type.');
28092             return;
28093         }
28094         
28095         tagSize = tagType.size * length;
28096         // Determine if the value is contained in the dataOffset bytes,
28097         // or if the value at the dataOffset is a pointer to the actual data:
28098         dataOffset = tagSize > 4 ?
28099                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28100         if (dataOffset + tagSize > dataView.byteLength) {
28101             Roo.log('Invalid Exif data: Invalid data offset.');
28102             return;
28103         }
28104         if (length === 1) {
28105             return tagType.getValue(dataView, dataOffset, littleEndian);
28106         }
28107         values = [];
28108         for (i = 0; i < length; i += 1) {
28109             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28110         }
28111         
28112         if (tagType.ascii) {
28113             str = '';
28114             // Concatenate the chars:
28115             for (i = 0; i < values.length; i += 1) {
28116                 c = values[i];
28117                 // Ignore the terminating NULL byte(s):
28118                 if (c === '\u0000') {
28119                     break;
28120                 }
28121                 str += c;
28122             }
28123             return str;
28124         }
28125         return values;
28126     }
28127     
28128 });
28129
28130 Roo.apply(Roo.bootstrap.UploadCropbox, {
28131     tags : {
28132         'Orientation': 0x0112
28133     },
28134     
28135     Orientation: {
28136             1: 0, //'top-left',
28137 //            2: 'top-right',
28138             3: 180, //'bottom-right',
28139 //            4: 'bottom-left',
28140 //            5: 'left-top',
28141             6: 90, //'right-top',
28142 //            7: 'right-bottom',
28143             8: 270 //'left-bottom'
28144     },
28145     
28146     exifTagTypes : {
28147         // byte, 8-bit unsigned int:
28148         1: {
28149             getValue: function (dataView, dataOffset) {
28150                 return dataView.getUint8(dataOffset);
28151             },
28152             size: 1
28153         },
28154         // ascii, 8-bit byte:
28155         2: {
28156             getValue: function (dataView, dataOffset) {
28157                 return String.fromCharCode(dataView.getUint8(dataOffset));
28158             },
28159             size: 1,
28160             ascii: true
28161         },
28162         // short, 16 bit int:
28163         3: {
28164             getValue: function (dataView, dataOffset, littleEndian) {
28165                 return dataView.getUint16(dataOffset, littleEndian);
28166             },
28167             size: 2
28168         },
28169         // long, 32 bit int:
28170         4: {
28171             getValue: function (dataView, dataOffset, littleEndian) {
28172                 return dataView.getUint32(dataOffset, littleEndian);
28173             },
28174             size: 4
28175         },
28176         // rational = two long values, first is numerator, second is denominator:
28177         5: {
28178             getValue: function (dataView, dataOffset, littleEndian) {
28179                 return dataView.getUint32(dataOffset, littleEndian) /
28180                     dataView.getUint32(dataOffset + 4, littleEndian);
28181             },
28182             size: 8
28183         },
28184         // slong, 32 bit signed int:
28185         9: {
28186             getValue: function (dataView, dataOffset, littleEndian) {
28187                 return dataView.getInt32(dataOffset, littleEndian);
28188             },
28189             size: 4
28190         },
28191         // srational, two slongs, first is numerator, second is denominator:
28192         10: {
28193             getValue: function (dataView, dataOffset, littleEndian) {
28194                 return dataView.getInt32(dataOffset, littleEndian) /
28195                     dataView.getInt32(dataOffset + 4, littleEndian);
28196             },
28197             size: 8
28198         }
28199     },
28200     
28201     footer : {
28202         STANDARD : [
28203             {
28204                 tag : 'div',
28205                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28206                 action : 'rotate-left',
28207                 cn : [
28208                     {
28209                         tag : 'button',
28210                         cls : 'btn btn-default',
28211                         html : '<i class="fa fa-undo"></i>'
28212                     }
28213                 ]
28214             },
28215             {
28216                 tag : 'div',
28217                 cls : 'btn-group roo-upload-cropbox-picture',
28218                 action : 'picture',
28219                 cn : [
28220                     {
28221                         tag : 'button',
28222                         cls : 'btn btn-default',
28223                         html : '<i class="fa fa-picture-o"></i>'
28224                     }
28225                 ]
28226             },
28227             {
28228                 tag : 'div',
28229                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28230                 action : 'rotate-right',
28231                 cn : [
28232                     {
28233                         tag : 'button',
28234                         cls : 'btn btn-default',
28235                         html : '<i class="fa fa-repeat"></i>'
28236                     }
28237                 ]
28238             }
28239         ],
28240         DOCUMENT : [
28241             {
28242                 tag : 'div',
28243                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28244                 action : 'rotate-left',
28245                 cn : [
28246                     {
28247                         tag : 'button',
28248                         cls : 'btn btn-default',
28249                         html : '<i class="fa fa-undo"></i>'
28250                     }
28251                 ]
28252             },
28253             {
28254                 tag : 'div',
28255                 cls : 'btn-group roo-upload-cropbox-download',
28256                 action : 'download',
28257                 cn : [
28258                     {
28259                         tag : 'button',
28260                         cls : 'btn btn-default',
28261                         html : '<i class="fa fa-download"></i>'
28262                     }
28263                 ]
28264             },
28265             {
28266                 tag : 'div',
28267                 cls : 'btn-group roo-upload-cropbox-crop',
28268                 action : 'crop',
28269                 cn : [
28270                     {
28271                         tag : 'button',
28272                         cls : 'btn btn-default',
28273                         html : '<i class="fa fa-crop"></i>'
28274                     }
28275                 ]
28276             },
28277             {
28278                 tag : 'div',
28279                 cls : 'btn-group roo-upload-cropbox-trash',
28280                 action : 'trash',
28281                 cn : [
28282                     {
28283                         tag : 'button',
28284                         cls : 'btn btn-default',
28285                         html : '<i class="fa fa-trash"></i>'
28286                     }
28287                 ]
28288             },
28289             {
28290                 tag : 'div',
28291                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28292                 action : 'rotate-right',
28293                 cn : [
28294                     {
28295                         tag : 'button',
28296                         cls : 'btn btn-default',
28297                         html : '<i class="fa fa-repeat"></i>'
28298                     }
28299                 ]
28300             }
28301         ],
28302         ROTATOR : [
28303             {
28304                 tag : 'div',
28305                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28306                 action : 'rotate-left',
28307                 cn : [
28308                     {
28309                         tag : 'button',
28310                         cls : 'btn btn-default',
28311                         html : '<i class="fa fa-undo"></i>'
28312                     }
28313                 ]
28314             },
28315             {
28316                 tag : 'div',
28317                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28318                 action : 'rotate-right',
28319                 cn : [
28320                     {
28321                         tag : 'button',
28322                         cls : 'btn btn-default',
28323                         html : '<i class="fa fa-repeat"></i>'
28324                     }
28325                 ]
28326             }
28327         ]
28328     }
28329 });
28330
28331 /*
28332 * Licence: LGPL
28333 */
28334
28335 /**
28336  * @class Roo.bootstrap.DocumentManager
28337  * @extends Roo.bootstrap.Component
28338  * Bootstrap DocumentManager class
28339  * @cfg {String} paramName default 'imageUpload'
28340  * @cfg {String} toolTipName default 'filename'
28341  * @cfg {String} method default POST
28342  * @cfg {String} url action url
28343  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28344  * @cfg {Boolean} multiple multiple upload default true
28345  * @cfg {Number} thumbSize default 300
28346  * @cfg {String} fieldLabel
28347  * @cfg {Number} labelWidth default 4
28348  * @cfg {String} labelAlign (left|top) default left
28349  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28350 * @cfg {Number} labellg set the width of label (1-12)
28351  * @cfg {Number} labelmd set the width of label (1-12)
28352  * @cfg {Number} labelsm set the width of label (1-12)
28353  * @cfg {Number} labelxs set the width of label (1-12)
28354  * 
28355  * @constructor
28356  * Create a new DocumentManager
28357  * @param {Object} config The config object
28358  */
28359
28360 Roo.bootstrap.DocumentManager = function(config){
28361     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28362     
28363     this.files = [];
28364     this.delegates = [];
28365     
28366     this.addEvents({
28367         /**
28368          * @event initial
28369          * Fire when initial the DocumentManager
28370          * @param {Roo.bootstrap.DocumentManager} this
28371          */
28372         "initial" : true,
28373         /**
28374          * @event inspect
28375          * inspect selected file
28376          * @param {Roo.bootstrap.DocumentManager} this
28377          * @param {File} file
28378          */
28379         "inspect" : true,
28380         /**
28381          * @event exception
28382          * Fire when xhr load exception
28383          * @param {Roo.bootstrap.DocumentManager} this
28384          * @param {XMLHttpRequest} xhr
28385          */
28386         "exception" : true,
28387         /**
28388          * @event afterupload
28389          * Fire when xhr load exception
28390          * @param {Roo.bootstrap.DocumentManager} this
28391          * @param {XMLHttpRequest} xhr
28392          */
28393         "afterupload" : true,
28394         /**
28395          * @event prepare
28396          * prepare the form data
28397          * @param {Roo.bootstrap.DocumentManager} this
28398          * @param {Object} formData
28399          */
28400         "prepare" : true,
28401         /**
28402          * @event remove
28403          * Fire when remove the file
28404          * @param {Roo.bootstrap.DocumentManager} this
28405          * @param {Object} file
28406          */
28407         "remove" : true,
28408         /**
28409          * @event refresh
28410          * Fire after refresh the file
28411          * @param {Roo.bootstrap.DocumentManager} this
28412          */
28413         "refresh" : true,
28414         /**
28415          * @event click
28416          * Fire after click the image
28417          * @param {Roo.bootstrap.DocumentManager} this
28418          * @param {Object} file
28419          */
28420         "click" : true,
28421         /**
28422          * @event edit
28423          * Fire when upload a image and editable set to true
28424          * @param {Roo.bootstrap.DocumentManager} this
28425          * @param {Object} file
28426          */
28427         "edit" : true,
28428         /**
28429          * @event beforeselectfile
28430          * Fire before select file
28431          * @param {Roo.bootstrap.DocumentManager} this
28432          */
28433         "beforeselectfile" : true,
28434         /**
28435          * @event process
28436          * Fire before process file
28437          * @param {Roo.bootstrap.DocumentManager} this
28438          * @param {Object} file
28439          */
28440         "process" : true,
28441         /**
28442          * @event previewrendered
28443          * Fire when preview rendered
28444          * @param {Roo.bootstrap.DocumentManager} this
28445          * @param {Object} file
28446          */
28447         "previewrendered" : true
28448         
28449     });
28450 };
28451
28452 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28453     
28454     boxes : 0,
28455     inputName : '',
28456     thumbSize : 300,
28457     multiple : true,
28458     files : false,
28459     method : 'POST',
28460     url : '',
28461     paramName : 'imageUpload',
28462     toolTipName : 'filename',
28463     fieldLabel : '',
28464     labelWidth : 4,
28465     labelAlign : 'left',
28466     editable : true,
28467     delegates : false,
28468     xhr : false, 
28469     
28470     labellg : 0,
28471     labelmd : 0,
28472     labelsm : 0,
28473     labelxs : 0,
28474     
28475     getAutoCreate : function()
28476     {   
28477         var managerWidget = {
28478             tag : 'div',
28479             cls : 'roo-document-manager',
28480             cn : [
28481                 {
28482                     tag : 'input',
28483                     cls : 'roo-document-manager-selector',
28484                     type : 'file'
28485                 },
28486                 {
28487                     tag : 'div',
28488                     cls : 'roo-document-manager-uploader',
28489                     cn : [
28490                         {
28491                             tag : 'div',
28492                             cls : 'roo-document-manager-upload-btn',
28493                             html : '<i class="fa fa-plus"></i>'
28494                         }
28495                     ]
28496                     
28497                 }
28498             ]
28499         };
28500         
28501         var content = [
28502             {
28503                 tag : 'div',
28504                 cls : 'column col-md-12',
28505                 cn : managerWidget
28506             }
28507         ];
28508         
28509         if(this.fieldLabel.length){
28510             
28511             content = [
28512                 {
28513                     tag : 'div',
28514                     cls : 'column col-md-12',
28515                     html : this.fieldLabel
28516                 },
28517                 {
28518                     tag : 'div',
28519                     cls : 'column col-md-12',
28520                     cn : managerWidget
28521                 }
28522             ];
28523
28524             if(this.labelAlign == 'left'){
28525                 content = [
28526                     {
28527                         tag : 'div',
28528                         cls : 'column',
28529                         html : this.fieldLabel
28530                     },
28531                     {
28532                         tag : 'div',
28533                         cls : 'column',
28534                         cn : managerWidget
28535                     }
28536                 ];
28537                 
28538                 if(this.labelWidth > 12){
28539                     content[0].style = "width: " + this.labelWidth + 'px';
28540                 }
28541
28542                 if(this.labelWidth < 13 && this.labelmd == 0){
28543                     this.labelmd = this.labelWidth;
28544                 }
28545
28546                 if(this.labellg > 0){
28547                     content[0].cls += ' col-lg-' + this.labellg;
28548                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28549                 }
28550
28551                 if(this.labelmd > 0){
28552                     content[0].cls += ' col-md-' + this.labelmd;
28553                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28554                 }
28555
28556                 if(this.labelsm > 0){
28557                     content[0].cls += ' col-sm-' + this.labelsm;
28558                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28559                 }
28560
28561                 if(this.labelxs > 0){
28562                     content[0].cls += ' col-xs-' + this.labelxs;
28563                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28564                 }
28565                 
28566             }
28567         }
28568         
28569         var cfg = {
28570             tag : 'div',
28571             cls : 'row clearfix',
28572             cn : content
28573         };
28574         
28575         return cfg;
28576         
28577     },
28578     
28579     initEvents : function()
28580     {
28581         this.managerEl = this.el.select('.roo-document-manager', true).first();
28582         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28583         
28584         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28585         this.selectorEl.hide();
28586         
28587         if(this.multiple){
28588             this.selectorEl.attr('multiple', 'multiple');
28589         }
28590         
28591         this.selectorEl.on('change', this.onFileSelected, this);
28592         
28593         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28594         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28595         
28596         this.uploader.on('click', this.onUploaderClick, this);
28597         
28598         this.renderProgressDialog();
28599         
28600         var _this = this;
28601         
28602         window.addEventListener("resize", function() { _this.refresh(); } );
28603         
28604         this.fireEvent('initial', this);
28605     },
28606     
28607     renderProgressDialog : function()
28608     {
28609         var _this = this;
28610         
28611         this.progressDialog = new Roo.bootstrap.Modal({
28612             cls : 'roo-document-manager-progress-dialog',
28613             allow_close : false,
28614             title : '',
28615             buttons : [
28616                 {
28617                     name  :'cancel',
28618                     weight : 'danger',
28619                     html : 'Cancel'
28620                 }
28621             ], 
28622             listeners : { 
28623                 btnclick : function() {
28624                     _this.uploadCancel();
28625                     this.hide();
28626                 }
28627             }
28628         });
28629          
28630         this.progressDialog.render(Roo.get(document.body));
28631          
28632         this.progress = new Roo.bootstrap.Progress({
28633             cls : 'roo-document-manager-progress',
28634             active : true,
28635             striped : true
28636         });
28637         
28638         this.progress.render(this.progressDialog.getChildContainer());
28639         
28640         this.progressBar = new Roo.bootstrap.ProgressBar({
28641             cls : 'roo-document-manager-progress-bar',
28642             aria_valuenow : 0,
28643             aria_valuemin : 0,
28644             aria_valuemax : 12,
28645             panel : 'success'
28646         });
28647         
28648         this.progressBar.render(this.progress.getChildContainer());
28649     },
28650     
28651     onUploaderClick : function(e)
28652     {
28653         e.preventDefault();
28654      
28655         if(this.fireEvent('beforeselectfile', this) != false){
28656             this.selectorEl.dom.click();
28657         }
28658         
28659     },
28660     
28661     onFileSelected : function(e)
28662     {
28663         e.preventDefault();
28664         
28665         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28666             return;
28667         }
28668         
28669         Roo.each(this.selectorEl.dom.files, function(file){
28670             if(this.fireEvent('inspect', this, file) != false){
28671                 this.files.push(file);
28672             }
28673         }, this);
28674         
28675         this.queue();
28676         
28677     },
28678     
28679     queue : function()
28680     {
28681         this.selectorEl.dom.value = '';
28682         
28683         if(!this.files || !this.files.length){
28684             return;
28685         }
28686         
28687         if(this.boxes > 0 && this.files.length > this.boxes){
28688             this.files = this.files.slice(0, this.boxes);
28689         }
28690         
28691         this.uploader.show();
28692         
28693         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28694             this.uploader.hide();
28695         }
28696         
28697         var _this = this;
28698         
28699         var files = [];
28700         
28701         var docs = [];
28702         
28703         Roo.each(this.files, function(file){
28704             
28705             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28706                 var f = this.renderPreview(file);
28707                 files.push(f);
28708                 return;
28709             }
28710             
28711             if(file.type.indexOf('image') != -1){
28712                 this.delegates.push(
28713                     (function(){
28714                         _this.process(file);
28715                     }).createDelegate(this)
28716                 );
28717         
28718                 return;
28719             }
28720             
28721             docs.push(
28722                 (function(){
28723                     _this.process(file);
28724                 }).createDelegate(this)
28725             );
28726             
28727         }, this);
28728         
28729         this.files = files;
28730         
28731         this.delegates = this.delegates.concat(docs);
28732         
28733         if(!this.delegates.length){
28734             this.refresh();
28735             return;
28736         }
28737         
28738         this.progressBar.aria_valuemax = this.delegates.length;
28739         
28740         this.arrange();
28741         
28742         return;
28743     },
28744     
28745     arrange : function()
28746     {
28747         if(!this.delegates.length){
28748             this.progressDialog.hide();
28749             this.refresh();
28750             return;
28751         }
28752         
28753         var delegate = this.delegates.shift();
28754         
28755         this.progressDialog.show();
28756         
28757         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28758         
28759         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28760         
28761         delegate();
28762     },
28763     
28764     refresh : function()
28765     {
28766         this.uploader.show();
28767         
28768         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28769             this.uploader.hide();
28770         }
28771         
28772         Roo.isTouch ? this.closable(false) : this.closable(true);
28773         
28774         this.fireEvent('refresh', this);
28775     },
28776     
28777     onRemove : function(e, el, o)
28778     {
28779         e.preventDefault();
28780         
28781         this.fireEvent('remove', this, o);
28782         
28783     },
28784     
28785     remove : function(o)
28786     {
28787         var files = [];
28788         
28789         Roo.each(this.files, function(file){
28790             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28791                 files.push(file);
28792                 return;
28793             }
28794
28795             o.target.remove();
28796
28797         }, this);
28798         
28799         this.files = files;
28800         
28801         this.refresh();
28802     },
28803     
28804     clear : function()
28805     {
28806         Roo.each(this.files, function(file){
28807             if(!file.target){
28808                 return;
28809             }
28810             
28811             file.target.remove();
28812
28813         }, this);
28814         
28815         this.files = [];
28816         
28817         this.refresh();
28818     },
28819     
28820     onClick : function(e, el, o)
28821     {
28822         e.preventDefault();
28823         
28824         this.fireEvent('click', this, o);
28825         
28826     },
28827     
28828     closable : function(closable)
28829     {
28830         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28831             
28832             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28833             
28834             if(closable){
28835                 el.show();
28836                 return;
28837             }
28838             
28839             el.hide();
28840             
28841         }, this);
28842     },
28843     
28844     xhrOnLoad : function(xhr)
28845     {
28846         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28847             el.remove();
28848         }, this);
28849         
28850         if (xhr.readyState !== 4) {
28851             this.arrange();
28852             this.fireEvent('exception', this, xhr);
28853             return;
28854         }
28855
28856         var response = Roo.decode(xhr.responseText);
28857         
28858         if(!response.success){
28859             this.arrange();
28860             this.fireEvent('exception', this, xhr);
28861             return;
28862         }
28863         
28864         var file = this.renderPreview(response.data);
28865         
28866         this.files.push(file);
28867         
28868         this.arrange();
28869         
28870         this.fireEvent('afterupload', this, xhr);
28871         
28872     },
28873     
28874     xhrOnError : function(xhr)
28875     {
28876         Roo.log('xhr on error');
28877         
28878         var response = Roo.decode(xhr.responseText);
28879           
28880         Roo.log(response);
28881         
28882         this.arrange();
28883     },
28884     
28885     process : function(file)
28886     {
28887         if(this.fireEvent('process', this, file) !== false){
28888             if(this.editable && file.type.indexOf('image') != -1){
28889                 this.fireEvent('edit', this, file);
28890                 return;
28891             }
28892
28893             this.uploadStart(file, false);
28894
28895             return;
28896         }
28897         
28898     },
28899     
28900     uploadStart : function(file, crop)
28901     {
28902         this.xhr = new XMLHttpRequest();
28903         
28904         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28905             this.arrange();
28906             return;
28907         }
28908         
28909         file.xhr = this.xhr;
28910             
28911         this.managerEl.createChild({
28912             tag : 'div',
28913             cls : 'roo-document-manager-loading',
28914             cn : [
28915                 {
28916                     tag : 'div',
28917                     tooltip : file.name,
28918                     cls : 'roo-document-manager-thumb',
28919                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28920                 }
28921             ]
28922
28923         });
28924
28925         this.xhr.open(this.method, this.url, true);
28926         
28927         var headers = {
28928             "Accept": "application/json",
28929             "Cache-Control": "no-cache",
28930             "X-Requested-With": "XMLHttpRequest"
28931         };
28932         
28933         for (var headerName in headers) {
28934             var headerValue = headers[headerName];
28935             if (headerValue) {
28936                 this.xhr.setRequestHeader(headerName, headerValue);
28937             }
28938         }
28939         
28940         var _this = this;
28941         
28942         this.xhr.onload = function()
28943         {
28944             _this.xhrOnLoad(_this.xhr);
28945         }
28946         
28947         this.xhr.onerror = function()
28948         {
28949             _this.xhrOnError(_this.xhr);
28950         }
28951         
28952         var formData = new FormData();
28953
28954         formData.append('returnHTML', 'NO');
28955         
28956         if(crop){
28957             formData.append('crop', crop);
28958         }
28959         
28960         formData.append(this.paramName, file, file.name);
28961         
28962         var options = {
28963             file : file, 
28964             manually : false
28965         };
28966         
28967         if(this.fireEvent('prepare', this, formData, options) != false){
28968             
28969             if(options.manually){
28970                 return;
28971             }
28972             
28973             this.xhr.send(formData);
28974             return;
28975         };
28976         
28977         this.uploadCancel();
28978     },
28979     
28980     uploadCancel : function()
28981     {
28982         if (this.xhr) {
28983             this.xhr.abort();
28984         }
28985         
28986         this.delegates = [];
28987         
28988         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28989             el.remove();
28990         }, this);
28991         
28992         this.arrange();
28993     },
28994     
28995     renderPreview : function(file)
28996     {
28997         if(typeof(file.target) != 'undefined' && file.target){
28998             return file;
28999         }
29000         
29001         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29002         
29003         var previewEl = this.managerEl.createChild({
29004             tag : 'div',
29005             cls : 'roo-document-manager-preview',
29006             cn : [
29007                 {
29008                     tag : 'div',
29009                     tooltip : file[this.toolTipName],
29010                     cls : 'roo-document-manager-thumb',
29011                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29012                 },
29013                 {
29014                     tag : 'button',
29015                     cls : 'close',
29016                     html : '<i class="fa fa-times-circle"></i>'
29017                 }
29018             ]
29019         });
29020
29021         var close = previewEl.select('button.close', true).first();
29022
29023         close.on('click', this.onRemove, this, file);
29024
29025         file.target = previewEl;
29026
29027         var image = previewEl.select('img', true).first();
29028         
29029         var _this = this;
29030         
29031         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29032         
29033         image.on('click', this.onClick, this, file);
29034         
29035         this.fireEvent('previewrendered', this, file);
29036         
29037         return file;
29038         
29039     },
29040     
29041     onPreviewLoad : function(file, image)
29042     {
29043         if(typeof(file.target) == 'undefined' || !file.target){
29044             return;
29045         }
29046         
29047         var width = image.dom.naturalWidth || image.dom.width;
29048         var height = image.dom.naturalHeight || image.dom.height;
29049         
29050         if(width > height){
29051             file.target.addClass('wide');
29052             return;
29053         }
29054         
29055         file.target.addClass('tall');
29056         return;
29057         
29058     },
29059     
29060     uploadFromSource : function(file, crop)
29061     {
29062         this.xhr = new XMLHttpRequest();
29063         
29064         this.managerEl.createChild({
29065             tag : 'div',
29066             cls : 'roo-document-manager-loading',
29067             cn : [
29068                 {
29069                     tag : 'div',
29070                     tooltip : file.name,
29071                     cls : 'roo-document-manager-thumb',
29072                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29073                 }
29074             ]
29075
29076         });
29077
29078         this.xhr.open(this.method, this.url, true);
29079         
29080         var headers = {
29081             "Accept": "application/json",
29082             "Cache-Control": "no-cache",
29083             "X-Requested-With": "XMLHttpRequest"
29084         };
29085         
29086         for (var headerName in headers) {
29087             var headerValue = headers[headerName];
29088             if (headerValue) {
29089                 this.xhr.setRequestHeader(headerName, headerValue);
29090             }
29091         }
29092         
29093         var _this = this;
29094         
29095         this.xhr.onload = function()
29096         {
29097             _this.xhrOnLoad(_this.xhr);
29098         }
29099         
29100         this.xhr.onerror = function()
29101         {
29102             _this.xhrOnError(_this.xhr);
29103         }
29104         
29105         var formData = new FormData();
29106
29107         formData.append('returnHTML', 'NO');
29108         
29109         formData.append('crop', crop);
29110         
29111         if(typeof(file.filename) != 'undefined'){
29112             formData.append('filename', file.filename);
29113         }
29114         
29115         if(typeof(file.mimetype) != 'undefined'){
29116             formData.append('mimetype', file.mimetype);
29117         }
29118         
29119         Roo.log(formData);
29120         
29121         if(this.fireEvent('prepare', this, formData) != false){
29122             this.xhr.send(formData);
29123         };
29124     }
29125 });
29126
29127 /*
29128 * Licence: LGPL
29129 */
29130
29131 /**
29132  * @class Roo.bootstrap.DocumentViewer
29133  * @extends Roo.bootstrap.Component
29134  * Bootstrap DocumentViewer class
29135  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29136  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29137  * 
29138  * @constructor
29139  * Create a new DocumentViewer
29140  * @param {Object} config The config object
29141  */
29142
29143 Roo.bootstrap.DocumentViewer = function(config){
29144     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29145     
29146     this.addEvents({
29147         /**
29148          * @event initial
29149          * Fire after initEvent
29150          * @param {Roo.bootstrap.DocumentViewer} this
29151          */
29152         "initial" : true,
29153         /**
29154          * @event click
29155          * Fire after click
29156          * @param {Roo.bootstrap.DocumentViewer} this
29157          */
29158         "click" : true,
29159         /**
29160          * @event download
29161          * Fire after download button
29162          * @param {Roo.bootstrap.DocumentViewer} this
29163          */
29164         "download" : true,
29165         /**
29166          * @event trash
29167          * Fire after trash button
29168          * @param {Roo.bootstrap.DocumentViewer} this
29169          */
29170         "trash" : true
29171         
29172     });
29173 };
29174
29175 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29176     
29177     showDownload : true,
29178     
29179     showTrash : true,
29180     
29181     getAutoCreate : function()
29182     {
29183         var cfg = {
29184             tag : 'div',
29185             cls : 'roo-document-viewer',
29186             cn : [
29187                 {
29188                     tag : 'div',
29189                     cls : 'roo-document-viewer-body',
29190                     cn : [
29191                         {
29192                             tag : 'div',
29193                             cls : 'roo-document-viewer-thumb',
29194                             cn : [
29195                                 {
29196                                     tag : 'img',
29197                                     cls : 'roo-document-viewer-image'
29198                                 }
29199                             ]
29200                         }
29201                     ]
29202                 },
29203                 {
29204                     tag : 'div',
29205                     cls : 'roo-document-viewer-footer',
29206                     cn : {
29207                         tag : 'div',
29208                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29209                         cn : [
29210                             {
29211                                 tag : 'div',
29212                                 cls : 'btn-group roo-document-viewer-download',
29213                                 cn : [
29214                                     {
29215                                         tag : 'button',
29216                                         cls : 'btn btn-default',
29217                                         html : '<i class="fa fa-download"></i>'
29218                                     }
29219                                 ]
29220                             },
29221                             {
29222                                 tag : 'div',
29223                                 cls : 'btn-group roo-document-viewer-trash',
29224                                 cn : [
29225                                     {
29226                                         tag : 'button',
29227                                         cls : 'btn btn-default',
29228                                         html : '<i class="fa fa-trash"></i>'
29229                                     }
29230                                 ]
29231                             }
29232                         ]
29233                     }
29234                 }
29235             ]
29236         };
29237         
29238         return cfg;
29239     },
29240     
29241     initEvents : function()
29242     {
29243         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29244         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29245         
29246         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29247         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29248         
29249         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29250         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29251         
29252         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29253         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29254         
29255         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29256         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29257         
29258         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29259         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29260         
29261         this.bodyEl.on('click', this.onClick, this);
29262         this.downloadBtn.on('click', this.onDownload, this);
29263         this.trashBtn.on('click', this.onTrash, this);
29264         
29265         this.downloadBtn.hide();
29266         this.trashBtn.hide();
29267         
29268         if(this.showDownload){
29269             this.downloadBtn.show();
29270         }
29271         
29272         if(this.showTrash){
29273             this.trashBtn.show();
29274         }
29275         
29276         if(!this.showDownload && !this.showTrash) {
29277             this.footerEl.hide();
29278         }
29279         
29280     },
29281     
29282     initial : function()
29283     {
29284         this.fireEvent('initial', this);
29285         
29286     },
29287     
29288     onClick : function(e)
29289     {
29290         e.preventDefault();
29291         
29292         this.fireEvent('click', this);
29293     },
29294     
29295     onDownload : function(e)
29296     {
29297         e.preventDefault();
29298         
29299         this.fireEvent('download', this);
29300     },
29301     
29302     onTrash : function(e)
29303     {
29304         e.preventDefault();
29305         
29306         this.fireEvent('trash', this);
29307     }
29308     
29309 });
29310 /*
29311  * - LGPL
29312  *
29313  * nav progress bar
29314  * 
29315  */
29316
29317 /**
29318  * @class Roo.bootstrap.NavProgressBar
29319  * @extends Roo.bootstrap.Component
29320  * Bootstrap NavProgressBar class
29321  * 
29322  * @constructor
29323  * Create a new nav progress bar
29324  * @param {Object} config The config object
29325  */
29326
29327 Roo.bootstrap.NavProgressBar = function(config){
29328     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29329
29330     this.bullets = this.bullets || [];
29331    
29332 //    Roo.bootstrap.NavProgressBar.register(this);
29333      this.addEvents({
29334         /**
29335              * @event changed
29336              * Fires when the active item changes
29337              * @param {Roo.bootstrap.NavProgressBar} this
29338              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29339              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29340          */
29341         'changed': true
29342      });
29343     
29344 };
29345
29346 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29347     
29348     bullets : [],
29349     barItems : [],
29350     
29351     getAutoCreate : function()
29352     {
29353         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29354         
29355         cfg = {
29356             tag : 'div',
29357             cls : 'roo-navigation-bar-group',
29358             cn : [
29359                 {
29360                     tag : 'div',
29361                     cls : 'roo-navigation-top-bar'
29362                 },
29363                 {
29364                     tag : 'div',
29365                     cls : 'roo-navigation-bullets-bar',
29366                     cn : [
29367                         {
29368                             tag : 'ul',
29369                             cls : 'roo-navigation-bar'
29370                         }
29371                     ]
29372                 },
29373                 
29374                 {
29375                     tag : 'div',
29376                     cls : 'roo-navigation-bottom-bar'
29377                 }
29378             ]
29379             
29380         };
29381         
29382         return cfg;
29383         
29384     },
29385     
29386     initEvents: function() 
29387     {
29388         
29389     },
29390     
29391     onRender : function(ct, position) 
29392     {
29393         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29394         
29395         if(this.bullets.length){
29396             Roo.each(this.bullets, function(b){
29397                this.addItem(b);
29398             }, this);
29399         }
29400         
29401         this.format();
29402         
29403     },
29404     
29405     addItem : function(cfg)
29406     {
29407         var item = new Roo.bootstrap.NavProgressItem(cfg);
29408         
29409         item.parentId = this.id;
29410         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29411         
29412         if(cfg.html){
29413             var top = new Roo.bootstrap.Element({
29414                 tag : 'div',
29415                 cls : 'roo-navigation-bar-text'
29416             });
29417             
29418             var bottom = new Roo.bootstrap.Element({
29419                 tag : 'div',
29420                 cls : 'roo-navigation-bar-text'
29421             });
29422             
29423             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29424             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29425             
29426             var topText = new Roo.bootstrap.Element({
29427                 tag : 'span',
29428                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29429             });
29430             
29431             var bottomText = new Roo.bootstrap.Element({
29432                 tag : 'span',
29433                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29434             });
29435             
29436             topText.onRender(top.el, null);
29437             bottomText.onRender(bottom.el, null);
29438             
29439             item.topEl = top;
29440             item.bottomEl = bottom;
29441         }
29442         
29443         this.barItems.push(item);
29444         
29445         return item;
29446     },
29447     
29448     getActive : function()
29449     {
29450         var active = false;
29451         
29452         Roo.each(this.barItems, function(v){
29453             
29454             if (!v.isActive()) {
29455                 return;
29456             }
29457             
29458             active = v;
29459             return false;
29460             
29461         });
29462         
29463         return active;
29464     },
29465     
29466     setActiveItem : function(item)
29467     {
29468         var prev = false;
29469         
29470         Roo.each(this.barItems, function(v){
29471             if (v.rid == item.rid) {
29472                 return ;
29473             }
29474             
29475             if (v.isActive()) {
29476                 v.setActive(false);
29477                 prev = v;
29478             }
29479         });
29480
29481         item.setActive(true);
29482         
29483         this.fireEvent('changed', this, item, prev);
29484     },
29485     
29486     getBarItem: function(rid)
29487     {
29488         var ret = false;
29489         
29490         Roo.each(this.barItems, function(e) {
29491             if (e.rid != rid) {
29492                 return;
29493             }
29494             
29495             ret =  e;
29496             return false;
29497         });
29498         
29499         return ret;
29500     },
29501     
29502     indexOfItem : function(item)
29503     {
29504         var index = false;
29505         
29506         Roo.each(this.barItems, function(v, i){
29507             
29508             if (v.rid != item.rid) {
29509                 return;
29510             }
29511             
29512             index = i;
29513             return false
29514         });
29515         
29516         return index;
29517     },
29518     
29519     setActiveNext : function()
29520     {
29521         var i = this.indexOfItem(this.getActive());
29522         
29523         if (i > this.barItems.length) {
29524             return;
29525         }
29526         
29527         this.setActiveItem(this.barItems[i+1]);
29528     },
29529     
29530     setActivePrev : function()
29531     {
29532         var i = this.indexOfItem(this.getActive());
29533         
29534         if (i  < 1) {
29535             return;
29536         }
29537         
29538         this.setActiveItem(this.barItems[i-1]);
29539     },
29540     
29541     format : function()
29542     {
29543         if(!this.barItems.length){
29544             return;
29545         }
29546      
29547         var width = 100 / this.barItems.length;
29548         
29549         Roo.each(this.barItems, function(i){
29550             i.el.setStyle('width', width + '%');
29551             i.topEl.el.setStyle('width', width + '%');
29552             i.bottomEl.el.setStyle('width', width + '%');
29553         }, this);
29554         
29555     }
29556     
29557 });
29558 /*
29559  * - LGPL
29560  *
29561  * Nav Progress Item
29562  * 
29563  */
29564
29565 /**
29566  * @class Roo.bootstrap.NavProgressItem
29567  * @extends Roo.bootstrap.Component
29568  * Bootstrap NavProgressItem class
29569  * @cfg {String} rid the reference id
29570  * @cfg {Boolean} active (true|false) Is item active default false
29571  * @cfg {Boolean} disabled (true|false) Is item active default false
29572  * @cfg {String} html
29573  * @cfg {String} position (top|bottom) text position default bottom
29574  * @cfg {String} icon show icon instead of number
29575  * 
29576  * @constructor
29577  * Create a new NavProgressItem
29578  * @param {Object} config The config object
29579  */
29580 Roo.bootstrap.NavProgressItem = function(config){
29581     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29582     this.addEvents({
29583         // raw events
29584         /**
29585          * @event click
29586          * The raw click event for the entire grid.
29587          * @param {Roo.bootstrap.NavProgressItem} this
29588          * @param {Roo.EventObject} e
29589          */
29590         "click" : true
29591     });
29592    
29593 };
29594
29595 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29596     
29597     rid : '',
29598     active : false,
29599     disabled : false,
29600     html : '',
29601     position : 'bottom',
29602     icon : false,
29603     
29604     getAutoCreate : function()
29605     {
29606         var iconCls = 'roo-navigation-bar-item-icon';
29607         
29608         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29609         
29610         var cfg = {
29611             tag: 'li',
29612             cls: 'roo-navigation-bar-item',
29613             cn : [
29614                 {
29615                     tag : 'i',
29616                     cls : iconCls
29617                 }
29618             ]
29619         };
29620         
29621         if(this.active){
29622             cfg.cls += ' active';
29623         }
29624         if(this.disabled){
29625             cfg.cls += ' disabled';
29626         }
29627         
29628         return cfg;
29629     },
29630     
29631     disable : function()
29632     {
29633         this.setDisabled(true);
29634     },
29635     
29636     enable : function()
29637     {
29638         this.setDisabled(false);
29639     },
29640     
29641     initEvents: function() 
29642     {
29643         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29644         
29645         this.iconEl.on('click', this.onClick, this);
29646     },
29647     
29648     onClick : function(e)
29649     {
29650         e.preventDefault();
29651         
29652         if(this.disabled){
29653             return;
29654         }
29655         
29656         if(this.fireEvent('click', this, e) === false){
29657             return;
29658         };
29659         
29660         this.parent().setActiveItem(this);
29661     },
29662     
29663     isActive: function () 
29664     {
29665         return this.active;
29666     },
29667     
29668     setActive : function(state)
29669     {
29670         if(this.active == state){
29671             return;
29672         }
29673         
29674         this.active = state;
29675         
29676         if (state) {
29677             this.el.addClass('active');
29678             return;
29679         }
29680         
29681         this.el.removeClass('active');
29682         
29683         return;
29684     },
29685     
29686     setDisabled : function(state)
29687     {
29688         if(this.disabled == state){
29689             return;
29690         }
29691         
29692         this.disabled = state;
29693         
29694         if (state) {
29695             this.el.addClass('disabled');
29696             return;
29697         }
29698         
29699         this.el.removeClass('disabled');
29700     },
29701     
29702     tooltipEl : function()
29703     {
29704         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29705     }
29706 });
29707  
29708
29709  /*
29710  * - LGPL
29711  *
29712  * FieldLabel
29713  * 
29714  */
29715
29716 /**
29717  * @class Roo.bootstrap.FieldLabel
29718  * @extends Roo.bootstrap.Component
29719  * Bootstrap FieldLabel class
29720  * @cfg {String} html contents of the element
29721  * @cfg {String} tag tag of the element default label
29722  * @cfg {String} cls class of the element
29723  * @cfg {String} target label target 
29724  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29725  * @cfg {String} invalidClass default "text-warning"
29726  * @cfg {String} validClass default "text-success"
29727  * @cfg {String} iconTooltip default "This field is required"
29728  * @cfg {String} indicatorpos (left|right) default left
29729  * 
29730  * @constructor
29731  * Create a new FieldLabel
29732  * @param {Object} config The config object
29733  */
29734
29735 Roo.bootstrap.FieldLabel = function(config){
29736     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29737     
29738     this.addEvents({
29739             /**
29740              * @event invalid
29741              * Fires after the field has been marked as invalid.
29742              * @param {Roo.form.FieldLabel} this
29743              * @param {String} msg The validation message
29744              */
29745             invalid : true,
29746             /**
29747              * @event valid
29748              * Fires after the field has been validated with no errors.
29749              * @param {Roo.form.FieldLabel} this
29750              */
29751             valid : true
29752         });
29753 };
29754
29755 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29756     
29757     tag: 'label',
29758     cls: '',
29759     html: '',
29760     target: '',
29761     allowBlank : true,
29762     invalidClass : 'has-warning',
29763     validClass : 'has-success',
29764     iconTooltip : 'This field is required',
29765     indicatorpos : 'left',
29766     
29767     getAutoCreate : function(){
29768         
29769         var cfg = {
29770             tag : this.tag,
29771             cls : 'roo-bootstrap-field-label ' + this.cls,
29772             for : this.target,
29773             cn : [
29774                 {
29775                     tag : 'i',
29776                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29777                     tooltip : this.iconTooltip
29778                 },
29779                 {
29780                     tag : 'span',
29781                     html : this.html
29782                 }
29783             ] 
29784         };
29785         
29786         if(this.indicatorpos == 'right'){
29787             var cfg = {
29788                 tag : this.tag,
29789                 cls : 'roo-bootstrap-field-label ' + this.cls,
29790                 for : this.target,
29791                 cn : [
29792                     {
29793                         tag : 'span',
29794                         html : this.html
29795                     },
29796                     {
29797                         tag : 'i',
29798                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29799                         tooltip : this.iconTooltip
29800                     }
29801                 ] 
29802             };
29803         }
29804         
29805         return cfg;
29806     },
29807     
29808     initEvents: function() 
29809     {
29810         Roo.bootstrap.Element.superclass.initEvents.call(this);
29811         
29812         this.indicator = this.indicatorEl();
29813         
29814         if(this.indicator){
29815             this.indicator.removeClass('visible');
29816             this.indicator.addClass('invisible');
29817         }
29818         
29819         Roo.bootstrap.FieldLabel.register(this);
29820     },
29821     
29822     indicatorEl : function()
29823     {
29824         var indicator = this.el.select('i.roo-required-indicator',true).first();
29825         
29826         if(!indicator){
29827             return false;
29828         }
29829         
29830         return indicator;
29831         
29832     },
29833     
29834     /**
29835      * Mark this field as valid
29836      */
29837     markValid : function()
29838     {
29839         if(this.indicator){
29840             this.indicator.removeClass('visible');
29841             this.indicator.addClass('invisible');
29842         }
29843         
29844         this.el.removeClass(this.invalidClass);
29845         
29846         this.el.addClass(this.validClass);
29847         
29848         this.fireEvent('valid', this);
29849     },
29850     
29851     /**
29852      * Mark this field as invalid
29853      * @param {String} msg The validation message
29854      */
29855     markInvalid : function(msg)
29856     {
29857         if(this.indicator){
29858             this.indicator.removeClass('invisible');
29859             this.indicator.addClass('visible');
29860         }
29861         
29862         this.el.removeClass(this.validClass);
29863         
29864         this.el.addClass(this.invalidClass);
29865         
29866         this.fireEvent('invalid', this, msg);
29867     }
29868     
29869    
29870 });
29871
29872 Roo.apply(Roo.bootstrap.FieldLabel, {
29873     
29874     groups: {},
29875     
29876      /**
29877     * register a FieldLabel Group
29878     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29879     */
29880     register : function(label)
29881     {
29882         if(this.groups.hasOwnProperty(label.target)){
29883             return;
29884         }
29885      
29886         this.groups[label.target] = label;
29887         
29888     },
29889     /**
29890     * fetch a FieldLabel Group based on the target
29891     * @param {string} target
29892     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29893     */
29894     get: function(target) {
29895         if (typeof(this.groups[target]) == 'undefined') {
29896             return false;
29897         }
29898         
29899         return this.groups[target] ;
29900     }
29901 });
29902
29903  
29904
29905  /*
29906  * - LGPL
29907  *
29908  * page DateSplitField.
29909  * 
29910  */
29911
29912
29913 /**
29914  * @class Roo.bootstrap.DateSplitField
29915  * @extends Roo.bootstrap.Component
29916  * Bootstrap DateSplitField class
29917  * @cfg {string} fieldLabel - the label associated
29918  * @cfg {Number} labelWidth set the width of label (0-12)
29919  * @cfg {String} labelAlign (top|left)
29920  * @cfg {Boolean} dayAllowBlank (true|false) default false
29921  * @cfg {Boolean} monthAllowBlank (true|false) default false
29922  * @cfg {Boolean} yearAllowBlank (true|false) default false
29923  * @cfg {string} dayPlaceholder 
29924  * @cfg {string} monthPlaceholder
29925  * @cfg {string} yearPlaceholder
29926  * @cfg {string} dayFormat default 'd'
29927  * @cfg {string} monthFormat default 'm'
29928  * @cfg {string} yearFormat default 'Y'
29929  * @cfg {Number} labellg set the width of label (1-12)
29930  * @cfg {Number} labelmd set the width of label (1-12)
29931  * @cfg {Number} labelsm set the width of label (1-12)
29932  * @cfg {Number} labelxs set the width of label (1-12)
29933
29934  *     
29935  * @constructor
29936  * Create a new DateSplitField
29937  * @param {Object} config The config object
29938  */
29939
29940 Roo.bootstrap.DateSplitField = function(config){
29941     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29942     
29943     this.addEvents({
29944         // raw events
29945          /**
29946          * @event years
29947          * getting the data of years
29948          * @param {Roo.bootstrap.DateSplitField} this
29949          * @param {Object} years
29950          */
29951         "years" : true,
29952         /**
29953          * @event days
29954          * getting the data of days
29955          * @param {Roo.bootstrap.DateSplitField} this
29956          * @param {Object} days
29957          */
29958         "days" : true,
29959         /**
29960          * @event invalid
29961          * Fires after the field has been marked as invalid.
29962          * @param {Roo.form.Field} this
29963          * @param {String} msg The validation message
29964          */
29965         invalid : true,
29966        /**
29967          * @event valid
29968          * Fires after the field has been validated with no errors.
29969          * @param {Roo.form.Field} this
29970          */
29971         valid : true
29972     });
29973 };
29974
29975 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29976     
29977     fieldLabel : '',
29978     labelAlign : 'top',
29979     labelWidth : 3,
29980     dayAllowBlank : false,
29981     monthAllowBlank : false,
29982     yearAllowBlank : false,
29983     dayPlaceholder : '',
29984     monthPlaceholder : '',
29985     yearPlaceholder : '',
29986     dayFormat : 'd',
29987     monthFormat : 'm',
29988     yearFormat : 'Y',
29989     isFormField : true,
29990     labellg : 0,
29991     labelmd : 0,
29992     labelsm : 0,
29993     labelxs : 0,
29994     
29995     getAutoCreate : function()
29996     {
29997         var cfg = {
29998             tag : 'div',
29999             cls : 'row roo-date-split-field-group',
30000             cn : [
30001                 {
30002                     tag : 'input',
30003                     type : 'hidden',
30004                     cls : 'form-hidden-field roo-date-split-field-group-value',
30005                     name : this.name
30006                 }
30007             ]
30008         };
30009         
30010         var labelCls = 'col-md-12';
30011         var contentCls = 'col-md-4';
30012         
30013         if(this.fieldLabel){
30014             
30015             var label = {
30016                 tag : 'div',
30017                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30018                 cn : [
30019                     {
30020                         tag : 'label',
30021                         html : this.fieldLabel
30022                     }
30023                 ]
30024             };
30025             
30026             if(this.labelAlign == 'left'){
30027             
30028                 if(this.labelWidth > 12){
30029                     label.style = "width: " + this.labelWidth + 'px';
30030                 }
30031
30032                 if(this.labelWidth < 13 && this.labelmd == 0){
30033                     this.labelmd = this.labelWidth;
30034                 }
30035
30036                 if(this.labellg > 0){
30037                     labelCls = ' col-lg-' + this.labellg;
30038                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30039                 }
30040
30041                 if(this.labelmd > 0){
30042                     labelCls = ' col-md-' + this.labelmd;
30043                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30044                 }
30045
30046                 if(this.labelsm > 0){
30047                     labelCls = ' col-sm-' + this.labelsm;
30048                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30049                 }
30050
30051                 if(this.labelxs > 0){
30052                     labelCls = ' col-xs-' + this.labelxs;
30053                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30054                 }
30055             }
30056             
30057             label.cls += ' ' + labelCls;
30058             
30059             cfg.cn.push(label);
30060         }
30061         
30062         Roo.each(['day', 'month', 'year'], function(t){
30063             cfg.cn.push({
30064                 tag : 'div',
30065                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30066             });
30067         }, this);
30068         
30069         return cfg;
30070     },
30071     
30072     inputEl: function ()
30073     {
30074         return this.el.select('.roo-date-split-field-group-value', true).first();
30075     },
30076     
30077     onRender : function(ct, position) 
30078     {
30079         var _this = this;
30080         
30081         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30082         
30083         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30084         
30085         this.dayField = new Roo.bootstrap.ComboBox({
30086             allowBlank : this.dayAllowBlank,
30087             alwaysQuery : true,
30088             displayField : 'value',
30089             editable : false,
30090             fieldLabel : '',
30091             forceSelection : true,
30092             mode : 'local',
30093             placeholder : this.dayPlaceholder,
30094             selectOnFocus : true,
30095             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30096             triggerAction : 'all',
30097             typeAhead : true,
30098             valueField : 'value',
30099             store : new Roo.data.SimpleStore({
30100                 data : (function() {    
30101                     var days = [];
30102                     _this.fireEvent('days', _this, days);
30103                     return days;
30104                 })(),
30105                 fields : [ 'value' ]
30106             }),
30107             listeners : {
30108                 select : function (_self, record, index)
30109                 {
30110                     _this.setValue(_this.getValue());
30111                 }
30112             }
30113         });
30114
30115         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30116         
30117         this.monthField = new Roo.bootstrap.MonthField({
30118             after : '<i class=\"fa fa-calendar\"></i>',
30119             allowBlank : this.monthAllowBlank,
30120             placeholder : this.monthPlaceholder,
30121             readOnly : true,
30122             listeners : {
30123                 render : function (_self)
30124                 {
30125                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30126                         e.preventDefault();
30127                         _self.focus();
30128                     });
30129                 },
30130                 select : function (_self, oldvalue, newvalue)
30131                 {
30132                     _this.setValue(_this.getValue());
30133                 }
30134             }
30135         });
30136         
30137         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30138         
30139         this.yearField = new Roo.bootstrap.ComboBox({
30140             allowBlank : this.yearAllowBlank,
30141             alwaysQuery : true,
30142             displayField : 'value',
30143             editable : false,
30144             fieldLabel : '',
30145             forceSelection : true,
30146             mode : 'local',
30147             placeholder : this.yearPlaceholder,
30148             selectOnFocus : true,
30149             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30150             triggerAction : 'all',
30151             typeAhead : true,
30152             valueField : 'value',
30153             store : new Roo.data.SimpleStore({
30154                 data : (function() {
30155                     var years = [];
30156                     _this.fireEvent('years', _this, years);
30157                     return years;
30158                 })(),
30159                 fields : [ 'value' ]
30160             }),
30161             listeners : {
30162                 select : function (_self, record, index)
30163                 {
30164                     _this.setValue(_this.getValue());
30165                 }
30166             }
30167         });
30168
30169         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30170     },
30171     
30172     setValue : function(v, format)
30173     {
30174         this.inputEl.dom.value = v;
30175         
30176         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30177         
30178         var d = Date.parseDate(v, f);
30179         
30180         if(!d){
30181             this.validate();
30182             return;
30183         }
30184         
30185         this.setDay(d.format(this.dayFormat));
30186         this.setMonth(d.format(this.monthFormat));
30187         this.setYear(d.format(this.yearFormat));
30188         
30189         this.validate();
30190         
30191         return;
30192     },
30193     
30194     setDay : function(v)
30195     {
30196         this.dayField.setValue(v);
30197         this.inputEl.dom.value = this.getValue();
30198         this.validate();
30199         return;
30200     },
30201     
30202     setMonth : function(v)
30203     {
30204         this.monthField.setValue(v, true);
30205         this.inputEl.dom.value = this.getValue();
30206         this.validate();
30207         return;
30208     },
30209     
30210     setYear : function(v)
30211     {
30212         this.yearField.setValue(v);
30213         this.inputEl.dom.value = this.getValue();
30214         this.validate();
30215         return;
30216     },
30217     
30218     getDay : function()
30219     {
30220         return this.dayField.getValue();
30221     },
30222     
30223     getMonth : function()
30224     {
30225         return this.monthField.getValue();
30226     },
30227     
30228     getYear : function()
30229     {
30230         return this.yearField.getValue();
30231     },
30232     
30233     getValue : function()
30234     {
30235         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30236         
30237         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30238         
30239         return date;
30240     },
30241     
30242     reset : function()
30243     {
30244         this.setDay('');
30245         this.setMonth('');
30246         this.setYear('');
30247         this.inputEl.dom.value = '';
30248         this.validate();
30249         return;
30250     },
30251     
30252     validate : function()
30253     {
30254         var d = this.dayField.validate();
30255         var m = this.monthField.validate();
30256         var y = this.yearField.validate();
30257         
30258         var valid = true;
30259         
30260         if(
30261                 (!this.dayAllowBlank && !d) ||
30262                 (!this.monthAllowBlank && !m) ||
30263                 (!this.yearAllowBlank && !y)
30264         ){
30265             valid = false;
30266         }
30267         
30268         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30269             return valid;
30270         }
30271         
30272         if(valid){
30273             this.markValid();
30274             return valid;
30275         }
30276         
30277         this.markInvalid();
30278         
30279         return valid;
30280     },
30281     
30282     markValid : function()
30283     {
30284         
30285         var label = this.el.select('label', true).first();
30286         var icon = this.el.select('i.fa-star', true).first();
30287
30288         if(label && icon){
30289             icon.remove();
30290         }
30291         
30292         this.fireEvent('valid', this);
30293     },
30294     
30295      /**
30296      * Mark this field as invalid
30297      * @param {String} msg The validation message
30298      */
30299     markInvalid : function(msg)
30300     {
30301         
30302         var label = this.el.select('label', true).first();
30303         var icon = this.el.select('i.fa-star', true).first();
30304
30305         if(label && !icon){
30306             this.el.select('.roo-date-split-field-label', true).createChild({
30307                 tag : 'i',
30308                 cls : 'text-danger fa fa-lg fa-star',
30309                 tooltip : 'This field is required',
30310                 style : 'margin-right:5px;'
30311             }, label, true);
30312         }
30313         
30314         this.fireEvent('invalid', this, msg);
30315     },
30316     
30317     clearInvalid : function()
30318     {
30319         var label = this.el.select('label', true).first();
30320         var icon = this.el.select('i.fa-star', true).first();
30321
30322         if(label && icon){
30323             icon.remove();
30324         }
30325         
30326         this.fireEvent('valid', this);
30327     },
30328     
30329     getName: function()
30330     {
30331         return this.name;
30332     }
30333     
30334 });
30335
30336  /**
30337  *
30338  * This is based on 
30339  * http://masonry.desandro.com
30340  *
30341  * The idea is to render all the bricks based on vertical width...
30342  *
30343  * The original code extends 'outlayer' - we might need to use that....
30344  * 
30345  */
30346
30347
30348 /**
30349  * @class Roo.bootstrap.LayoutMasonry
30350  * @extends Roo.bootstrap.Component
30351  * Bootstrap Layout Masonry class
30352  * 
30353  * @constructor
30354  * Create a new Element
30355  * @param {Object} config The config object
30356  */
30357
30358 Roo.bootstrap.LayoutMasonry = function(config){
30359     
30360     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30361     
30362     this.bricks = [];
30363     
30364     Roo.bootstrap.LayoutMasonry.register(this);
30365     
30366     this.addEvents({
30367         // raw events
30368         /**
30369          * @event layout
30370          * Fire after layout the items
30371          * @param {Roo.bootstrap.LayoutMasonry} this
30372          * @param {Roo.EventObject} e
30373          */
30374         "layout" : true
30375     });
30376     
30377 };
30378
30379 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30380     
30381     /**
30382      * @cfg {Boolean} isLayoutInstant = no animation?
30383      */   
30384     isLayoutInstant : false, // needed?
30385    
30386     /**
30387      * @cfg {Number} boxWidth  width of the columns
30388      */   
30389     boxWidth : 450,
30390     
30391       /**
30392      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30393      */   
30394     boxHeight : 0,
30395     
30396     /**
30397      * @cfg {Number} padWidth padding below box..
30398      */   
30399     padWidth : 10, 
30400     
30401     /**
30402      * @cfg {Number} gutter gutter width..
30403      */   
30404     gutter : 10,
30405     
30406      /**
30407      * @cfg {Number} maxCols maximum number of columns
30408      */   
30409     
30410     maxCols: 0,
30411     
30412     /**
30413      * @cfg {Boolean} isAutoInitial defalut true
30414      */   
30415     isAutoInitial : true, 
30416     
30417     containerWidth: 0,
30418     
30419     /**
30420      * @cfg {Boolean} isHorizontal defalut false
30421      */   
30422     isHorizontal : false, 
30423
30424     currentSize : null,
30425     
30426     tag: 'div',
30427     
30428     cls: '',
30429     
30430     bricks: null, //CompositeElement
30431     
30432     cols : 1,
30433     
30434     _isLayoutInited : false,
30435     
30436 //    isAlternative : false, // only use for vertical layout...
30437     
30438     /**
30439      * @cfg {Number} alternativePadWidth padding below box..
30440      */   
30441     alternativePadWidth : 50,
30442     
30443     selectedBrick : [],
30444     
30445     getAutoCreate : function(){
30446         
30447         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30448         
30449         var cfg = {
30450             tag: this.tag,
30451             cls: 'blog-masonary-wrapper ' + this.cls,
30452             cn : {
30453                 cls : 'mas-boxes masonary'
30454             }
30455         };
30456         
30457         return cfg;
30458     },
30459     
30460     getChildContainer: function( )
30461     {
30462         if (this.boxesEl) {
30463             return this.boxesEl;
30464         }
30465         
30466         this.boxesEl = this.el.select('.mas-boxes').first();
30467         
30468         return this.boxesEl;
30469     },
30470     
30471     
30472     initEvents : function()
30473     {
30474         var _this = this;
30475         
30476         if(this.isAutoInitial){
30477             Roo.log('hook children rendered');
30478             this.on('childrenrendered', function() {
30479                 Roo.log('children rendered');
30480                 _this.initial();
30481             } ,this);
30482         }
30483     },
30484     
30485     initial : function()
30486     {
30487         this.selectedBrick = [];
30488         
30489         this.currentSize = this.el.getBox(true);
30490         
30491         Roo.EventManager.onWindowResize(this.resize, this); 
30492
30493         if(!this.isAutoInitial){
30494             this.layout();
30495             return;
30496         }
30497         
30498         this.layout();
30499         
30500         return;
30501         //this.layout.defer(500,this);
30502         
30503     },
30504     
30505     resize : function()
30506     {
30507         var cs = this.el.getBox(true);
30508         
30509         if (
30510                 this.currentSize.width == cs.width && 
30511                 this.currentSize.x == cs.x && 
30512                 this.currentSize.height == cs.height && 
30513                 this.currentSize.y == cs.y 
30514         ) {
30515             Roo.log("no change in with or X or Y");
30516             return;
30517         }
30518         
30519         this.currentSize = cs;
30520         
30521         this.layout();
30522         
30523     },
30524     
30525     layout : function()
30526     {   
30527         this._resetLayout();
30528         
30529         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30530         
30531         this.layoutItems( isInstant );
30532       
30533         this._isLayoutInited = true;
30534         
30535         this.fireEvent('layout', this);
30536         
30537     },
30538     
30539     _resetLayout : function()
30540     {
30541         if(this.isHorizontal){
30542             this.horizontalMeasureColumns();
30543             return;
30544         }
30545         
30546         this.verticalMeasureColumns();
30547         
30548     },
30549     
30550     verticalMeasureColumns : function()
30551     {
30552         this.getContainerWidth();
30553         
30554 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30555 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30556 //            return;
30557 //        }
30558         
30559         var boxWidth = this.boxWidth + this.padWidth;
30560         
30561         if(this.containerWidth < this.boxWidth){
30562             boxWidth = this.containerWidth
30563         }
30564         
30565         var containerWidth = this.containerWidth;
30566         
30567         var cols = Math.floor(containerWidth / boxWidth);
30568         
30569         this.cols = Math.max( cols, 1 );
30570         
30571         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30572         
30573         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30574         
30575         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30576         
30577         this.colWidth = boxWidth + avail - this.padWidth;
30578         
30579         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30580         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30581     },
30582     
30583     horizontalMeasureColumns : function()
30584     {
30585         this.getContainerWidth();
30586         
30587         var boxWidth = this.boxWidth;
30588         
30589         if(this.containerWidth < boxWidth){
30590             boxWidth = this.containerWidth;
30591         }
30592         
30593         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30594         
30595         this.el.setHeight(boxWidth);
30596         
30597     },
30598     
30599     getContainerWidth : function()
30600     {
30601         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30602     },
30603     
30604     layoutItems : function( isInstant )
30605     {
30606         Roo.log(this.bricks);
30607         
30608         var items = Roo.apply([], this.bricks);
30609         
30610         if(this.isHorizontal){
30611             this._horizontalLayoutItems( items , isInstant );
30612             return;
30613         }
30614         
30615 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30616 //            this._verticalAlternativeLayoutItems( items , isInstant );
30617 //            return;
30618 //        }
30619         
30620         this._verticalLayoutItems( items , isInstant );
30621         
30622     },
30623     
30624     _verticalLayoutItems : function ( items , isInstant)
30625     {
30626         if ( !items || !items.length ) {
30627             return;
30628         }
30629         
30630         var standard = [
30631             ['xs', 'xs', 'xs', 'tall'],
30632             ['xs', 'xs', 'tall'],
30633             ['xs', 'xs', 'sm'],
30634             ['xs', 'xs', 'xs'],
30635             ['xs', 'tall'],
30636             ['xs', 'sm'],
30637             ['xs', 'xs'],
30638             ['xs'],
30639             
30640             ['sm', 'xs', 'xs'],
30641             ['sm', 'xs'],
30642             ['sm'],
30643             
30644             ['tall', 'xs', 'xs', 'xs'],
30645             ['tall', 'xs', 'xs'],
30646             ['tall', 'xs'],
30647             ['tall']
30648             
30649         ];
30650         
30651         var queue = [];
30652         
30653         var boxes = [];
30654         
30655         var box = [];
30656         
30657         Roo.each(items, function(item, k){
30658             
30659             switch (item.size) {
30660                 // these layouts take up a full box,
30661                 case 'md' :
30662                 case 'md-left' :
30663                 case 'md-right' :
30664                 case 'wide' :
30665                     
30666                     if(box.length){
30667                         boxes.push(box);
30668                         box = [];
30669                     }
30670                     
30671                     boxes.push([item]);
30672                     
30673                     break;
30674                     
30675                 case 'xs' :
30676                 case 'sm' :
30677                 case 'tall' :
30678                     
30679                     box.push(item);
30680                     
30681                     break;
30682                 default :
30683                     break;
30684                     
30685             }
30686             
30687         }, this);
30688         
30689         if(box.length){
30690             boxes.push(box);
30691             box = [];
30692         }
30693         
30694         var filterPattern = function(box, length)
30695         {
30696             if(!box.length){
30697                 return;
30698             }
30699             
30700             var match = false;
30701             
30702             var pattern = box.slice(0, length);
30703             
30704             var format = [];
30705             
30706             Roo.each(pattern, function(i){
30707                 format.push(i.size);
30708             }, this);
30709             
30710             Roo.each(standard, function(s){
30711                 
30712                 if(String(s) != String(format)){
30713                     return;
30714                 }
30715                 
30716                 match = true;
30717                 return false;
30718                 
30719             }, this);
30720             
30721             if(!match && length == 1){
30722                 return;
30723             }
30724             
30725             if(!match){
30726                 filterPattern(box, length - 1);
30727                 return;
30728             }
30729                 
30730             queue.push(pattern);
30731
30732             box = box.slice(length, box.length);
30733
30734             filterPattern(box, 4);
30735
30736             return;
30737             
30738         }
30739         
30740         Roo.each(boxes, function(box, k){
30741             
30742             if(!box.length){
30743                 return;
30744             }
30745             
30746             if(box.length == 1){
30747                 queue.push(box);
30748                 return;
30749             }
30750             
30751             filterPattern(box, 4);
30752             
30753         }, this);
30754         
30755         this._processVerticalLayoutQueue( queue, isInstant );
30756         
30757     },
30758     
30759 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30760 //    {
30761 //        if ( !items || !items.length ) {
30762 //            return;
30763 //        }
30764 //
30765 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30766 //        
30767 //    },
30768     
30769     _horizontalLayoutItems : function ( items , isInstant)
30770     {
30771         if ( !items || !items.length || items.length < 3) {
30772             return;
30773         }
30774         
30775         items.reverse();
30776         
30777         var eItems = items.slice(0, 3);
30778         
30779         items = items.slice(3, items.length);
30780         
30781         var standard = [
30782             ['xs', 'xs', 'xs', 'wide'],
30783             ['xs', 'xs', 'wide'],
30784             ['xs', 'xs', 'sm'],
30785             ['xs', 'xs', 'xs'],
30786             ['xs', 'wide'],
30787             ['xs', 'sm'],
30788             ['xs', 'xs'],
30789             ['xs'],
30790             
30791             ['sm', 'xs', 'xs'],
30792             ['sm', 'xs'],
30793             ['sm'],
30794             
30795             ['wide', 'xs', 'xs', 'xs'],
30796             ['wide', 'xs', 'xs'],
30797             ['wide', 'xs'],
30798             ['wide'],
30799             
30800             ['wide-thin']
30801         ];
30802         
30803         var queue = [];
30804         
30805         var boxes = [];
30806         
30807         var box = [];
30808         
30809         Roo.each(items, function(item, k){
30810             
30811             switch (item.size) {
30812                 case 'md' :
30813                 case 'md-left' :
30814                 case 'md-right' :
30815                 case 'tall' :
30816                     
30817                     if(box.length){
30818                         boxes.push(box);
30819                         box = [];
30820                     }
30821                     
30822                     boxes.push([item]);
30823                     
30824                     break;
30825                     
30826                 case 'xs' :
30827                 case 'sm' :
30828                 case 'wide' :
30829                 case 'wide-thin' :
30830                     
30831                     box.push(item);
30832                     
30833                     break;
30834                 default :
30835                     break;
30836                     
30837             }
30838             
30839         }, this);
30840         
30841         if(box.length){
30842             boxes.push(box);
30843             box = [];
30844         }
30845         
30846         var filterPattern = function(box, length)
30847         {
30848             if(!box.length){
30849                 return;
30850             }
30851             
30852             var match = false;
30853             
30854             var pattern = box.slice(0, length);
30855             
30856             var format = [];
30857             
30858             Roo.each(pattern, function(i){
30859                 format.push(i.size);
30860             }, this);
30861             
30862             Roo.each(standard, function(s){
30863                 
30864                 if(String(s) != String(format)){
30865                     return;
30866                 }
30867                 
30868                 match = true;
30869                 return false;
30870                 
30871             }, this);
30872             
30873             if(!match && length == 1){
30874                 return;
30875             }
30876             
30877             if(!match){
30878                 filterPattern(box, length - 1);
30879                 return;
30880             }
30881                 
30882             queue.push(pattern);
30883
30884             box = box.slice(length, box.length);
30885
30886             filterPattern(box, 4);
30887
30888             return;
30889             
30890         }
30891         
30892         Roo.each(boxes, function(box, k){
30893             
30894             if(!box.length){
30895                 return;
30896             }
30897             
30898             if(box.length == 1){
30899                 queue.push(box);
30900                 return;
30901             }
30902             
30903             filterPattern(box, 4);
30904             
30905         }, this);
30906         
30907         
30908         var prune = [];
30909         
30910         var pos = this.el.getBox(true);
30911         
30912         var minX = pos.x;
30913         
30914         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30915         
30916         var hit_end = false;
30917         
30918         Roo.each(queue, function(box){
30919             
30920             if(hit_end){
30921                 
30922                 Roo.each(box, function(b){
30923                 
30924                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30925                     b.el.hide();
30926
30927                 }, this);
30928
30929                 return;
30930             }
30931             
30932             var mx = 0;
30933             
30934             Roo.each(box, function(b){
30935                 
30936                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30937                 b.el.show();
30938
30939                 mx = Math.max(mx, b.x);
30940                 
30941             }, this);
30942             
30943             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30944             
30945             if(maxX < minX){
30946                 
30947                 Roo.each(box, function(b){
30948                 
30949                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30950                     b.el.hide();
30951                     
30952                 }, this);
30953                 
30954                 hit_end = true;
30955                 
30956                 return;
30957             }
30958             
30959             prune.push(box);
30960             
30961         }, this);
30962         
30963         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30964     },
30965     
30966     /** Sets position of item in DOM
30967     * @param {Element} item
30968     * @param {Number} x - horizontal position
30969     * @param {Number} y - vertical position
30970     * @param {Boolean} isInstant - disables transitions
30971     */
30972     _processVerticalLayoutQueue : function( queue, isInstant )
30973     {
30974         var pos = this.el.getBox(true);
30975         var x = pos.x;
30976         var y = pos.y;
30977         var maxY = [];
30978         
30979         for (var i = 0; i < this.cols; i++){
30980             maxY[i] = pos.y;
30981         }
30982         
30983         Roo.each(queue, function(box, k){
30984             
30985             var col = k % this.cols;
30986             
30987             Roo.each(box, function(b,kk){
30988                 
30989                 b.el.position('absolute');
30990                 
30991                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30992                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30993                 
30994                 if(b.size == 'md-left' || b.size == 'md-right'){
30995                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30996                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30997                 }
30998                 
30999                 b.el.setWidth(width);
31000                 b.el.setHeight(height);
31001                 // iframe?
31002                 b.el.select('iframe',true).setSize(width,height);
31003                 
31004             }, this);
31005             
31006             for (var i = 0; i < this.cols; i++){
31007                 
31008                 if(maxY[i] < maxY[col]){
31009                     col = i;
31010                     continue;
31011                 }
31012                 
31013                 col = Math.min(col, i);
31014                 
31015             }
31016             
31017             x = pos.x + col * (this.colWidth + this.padWidth);
31018             
31019             y = maxY[col];
31020             
31021             var positions = [];
31022             
31023             switch (box.length){
31024                 case 1 :
31025                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31026                     break;
31027                 case 2 :
31028                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31029                     break;
31030                 case 3 :
31031                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31032                     break;
31033                 case 4 :
31034                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31035                     break;
31036                 default :
31037                     break;
31038             }
31039             
31040             Roo.each(box, function(b,kk){
31041                 
31042                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31043                 
31044                 var sz = b.el.getSize();
31045                 
31046                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31047                 
31048             }, this);
31049             
31050         }, this);
31051         
31052         var mY = 0;
31053         
31054         for (var i = 0; i < this.cols; i++){
31055             mY = Math.max(mY, maxY[i]);
31056         }
31057         
31058         this.el.setHeight(mY - pos.y);
31059         
31060     },
31061     
31062 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31063 //    {
31064 //        var pos = this.el.getBox(true);
31065 //        var x = pos.x;
31066 //        var y = pos.y;
31067 //        var maxX = pos.right;
31068 //        
31069 //        var maxHeight = 0;
31070 //        
31071 //        Roo.each(items, function(item, k){
31072 //            
31073 //            var c = k % 2;
31074 //            
31075 //            item.el.position('absolute');
31076 //                
31077 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31078 //
31079 //            item.el.setWidth(width);
31080 //
31081 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31082 //
31083 //            item.el.setHeight(height);
31084 //            
31085 //            if(c == 0){
31086 //                item.el.setXY([x, y], isInstant ? false : true);
31087 //            } else {
31088 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31089 //            }
31090 //            
31091 //            y = y + height + this.alternativePadWidth;
31092 //            
31093 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31094 //            
31095 //        }, this);
31096 //        
31097 //        this.el.setHeight(maxHeight);
31098 //        
31099 //    },
31100     
31101     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31102     {
31103         var pos = this.el.getBox(true);
31104         
31105         var minX = pos.x;
31106         var minY = pos.y;
31107         
31108         var maxX = pos.right;
31109         
31110         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31111         
31112         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31113         
31114         Roo.each(queue, function(box, k){
31115             
31116             Roo.each(box, function(b, kk){
31117                 
31118                 b.el.position('absolute');
31119                 
31120                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31121                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31122                 
31123                 if(b.size == 'md-left' || b.size == 'md-right'){
31124                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31125                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31126                 }
31127                 
31128                 b.el.setWidth(width);
31129                 b.el.setHeight(height);
31130                 
31131             }, this);
31132             
31133             if(!box.length){
31134                 return;
31135             }
31136             
31137             var positions = [];
31138             
31139             switch (box.length){
31140                 case 1 :
31141                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31142                     break;
31143                 case 2 :
31144                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31145                     break;
31146                 case 3 :
31147                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31148                     break;
31149                 case 4 :
31150                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31151                     break;
31152                 default :
31153                     break;
31154             }
31155             
31156             Roo.each(box, function(b,kk){
31157                 
31158                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31159                 
31160                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31161                 
31162             }, this);
31163             
31164         }, this);
31165         
31166     },
31167     
31168     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31169     {
31170         Roo.each(eItems, function(b,k){
31171             
31172             b.size = (k == 0) ? 'sm' : 'xs';
31173             b.x = (k == 0) ? 2 : 1;
31174             b.y = (k == 0) ? 2 : 1;
31175             
31176             b.el.position('absolute');
31177             
31178             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31179                 
31180             b.el.setWidth(width);
31181             
31182             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31183             
31184             b.el.setHeight(height);
31185             
31186         }, this);
31187
31188         var positions = [];
31189         
31190         positions.push({
31191             x : maxX - this.unitWidth * 2 - this.gutter,
31192             y : minY
31193         });
31194         
31195         positions.push({
31196             x : maxX - this.unitWidth,
31197             y : minY + (this.unitWidth + this.gutter) * 2
31198         });
31199         
31200         positions.push({
31201             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31202             y : minY
31203         });
31204         
31205         Roo.each(eItems, function(b,k){
31206             
31207             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31208
31209         }, this);
31210         
31211     },
31212     
31213     getVerticalOneBoxColPositions : function(x, y, box)
31214     {
31215         var pos = [];
31216         
31217         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31218         
31219         if(box[0].size == 'md-left'){
31220             rand = 0;
31221         }
31222         
31223         if(box[0].size == 'md-right'){
31224             rand = 1;
31225         }
31226         
31227         pos.push({
31228             x : x + (this.unitWidth + this.gutter) * rand,
31229             y : y
31230         });
31231         
31232         return pos;
31233     },
31234     
31235     getVerticalTwoBoxColPositions : function(x, y, box)
31236     {
31237         var pos = [];
31238         
31239         if(box[0].size == 'xs'){
31240             
31241             pos.push({
31242                 x : x,
31243                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31244             });
31245
31246             pos.push({
31247                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31248                 y : y
31249             });
31250             
31251             return pos;
31252             
31253         }
31254         
31255         pos.push({
31256             x : x,
31257             y : y
31258         });
31259
31260         pos.push({
31261             x : x + (this.unitWidth + this.gutter) * 2,
31262             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31263         });
31264         
31265         return pos;
31266         
31267     },
31268     
31269     getVerticalThreeBoxColPositions : function(x, y, box)
31270     {
31271         var pos = [];
31272         
31273         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31274             
31275             pos.push({
31276                 x : x,
31277                 y : y
31278             });
31279
31280             pos.push({
31281                 x : x + (this.unitWidth + this.gutter) * 1,
31282                 y : y
31283             });
31284             
31285             pos.push({
31286                 x : x + (this.unitWidth + this.gutter) * 2,
31287                 y : y
31288             });
31289             
31290             return pos;
31291             
31292         }
31293         
31294         if(box[0].size == 'xs' && box[1].size == 'xs'){
31295             
31296             pos.push({
31297                 x : x,
31298                 y : y
31299             });
31300
31301             pos.push({
31302                 x : x,
31303                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31304             });
31305             
31306             pos.push({
31307                 x : x + (this.unitWidth + this.gutter) * 1,
31308                 y : y
31309             });
31310             
31311             return pos;
31312             
31313         }
31314         
31315         pos.push({
31316             x : x,
31317             y : y
31318         });
31319
31320         pos.push({
31321             x : x + (this.unitWidth + this.gutter) * 2,
31322             y : y
31323         });
31324
31325         pos.push({
31326             x : x + (this.unitWidth + this.gutter) * 2,
31327             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31328         });
31329             
31330         return pos;
31331         
31332     },
31333     
31334     getVerticalFourBoxColPositions : function(x, y, box)
31335     {
31336         var pos = [];
31337         
31338         if(box[0].size == 'xs'){
31339             
31340             pos.push({
31341                 x : x,
31342                 y : y
31343             });
31344
31345             pos.push({
31346                 x : x,
31347                 y : y + (this.unitHeight + this.gutter) * 1
31348             });
31349             
31350             pos.push({
31351                 x : x,
31352                 y : y + (this.unitHeight + this.gutter) * 2
31353             });
31354             
31355             pos.push({
31356                 x : x + (this.unitWidth + this.gutter) * 1,
31357                 y : y
31358             });
31359             
31360             return pos;
31361             
31362         }
31363         
31364         pos.push({
31365             x : x,
31366             y : y
31367         });
31368
31369         pos.push({
31370             x : x + (this.unitWidth + this.gutter) * 2,
31371             y : y
31372         });
31373
31374         pos.push({
31375             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31376             y : y + (this.unitHeight + this.gutter) * 1
31377         });
31378
31379         pos.push({
31380             x : x + (this.unitWidth + this.gutter) * 2,
31381             y : y + (this.unitWidth + this.gutter) * 2
31382         });
31383
31384         return pos;
31385         
31386     },
31387     
31388     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31389     {
31390         var pos = [];
31391         
31392         if(box[0].size == 'md-left'){
31393             pos.push({
31394                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31395                 y : minY
31396             });
31397             
31398             return pos;
31399         }
31400         
31401         if(box[0].size == 'md-right'){
31402             pos.push({
31403                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31404                 y : minY + (this.unitWidth + this.gutter) * 1
31405             });
31406             
31407             return pos;
31408         }
31409         
31410         var rand = Math.floor(Math.random() * (4 - box[0].y));
31411         
31412         pos.push({
31413             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31414             y : minY + (this.unitWidth + this.gutter) * rand
31415         });
31416         
31417         return pos;
31418         
31419     },
31420     
31421     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31422     {
31423         var pos = [];
31424         
31425         if(box[0].size == 'xs'){
31426             
31427             pos.push({
31428                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31429                 y : minY
31430             });
31431
31432             pos.push({
31433                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31434                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31435             });
31436             
31437             return pos;
31438             
31439         }
31440         
31441         pos.push({
31442             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31443             y : minY
31444         });
31445
31446         pos.push({
31447             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31448             y : minY + (this.unitWidth + this.gutter) * 2
31449         });
31450         
31451         return pos;
31452         
31453     },
31454     
31455     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31456     {
31457         var pos = [];
31458         
31459         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31460             
31461             pos.push({
31462                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31463                 y : minY
31464             });
31465
31466             pos.push({
31467                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31468                 y : minY + (this.unitWidth + this.gutter) * 1
31469             });
31470             
31471             pos.push({
31472                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31473                 y : minY + (this.unitWidth + this.gutter) * 2
31474             });
31475             
31476             return pos;
31477             
31478         }
31479         
31480         if(box[0].size == 'xs' && box[1].size == 'xs'){
31481             
31482             pos.push({
31483                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31484                 y : minY
31485             });
31486
31487             pos.push({
31488                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31489                 y : minY
31490             });
31491             
31492             pos.push({
31493                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31494                 y : minY + (this.unitWidth + this.gutter) * 1
31495             });
31496             
31497             return pos;
31498             
31499         }
31500         
31501         pos.push({
31502             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31503             y : minY
31504         });
31505
31506         pos.push({
31507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31508             y : minY + (this.unitWidth + this.gutter) * 2
31509         });
31510
31511         pos.push({
31512             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31513             y : minY + (this.unitWidth + this.gutter) * 2
31514         });
31515             
31516         return pos;
31517         
31518     },
31519     
31520     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31521     {
31522         var pos = [];
31523         
31524         if(box[0].size == 'xs'){
31525             
31526             pos.push({
31527                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31528                 y : minY
31529             });
31530
31531             pos.push({
31532                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31533                 y : minY
31534             });
31535             
31536             pos.push({
31537                 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),
31538                 y : minY
31539             });
31540             
31541             pos.push({
31542                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31543                 y : minY + (this.unitWidth + this.gutter) * 1
31544             });
31545             
31546             return pos;
31547             
31548         }
31549         
31550         pos.push({
31551             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31552             y : minY
31553         });
31554         
31555         pos.push({
31556             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31557             y : minY + (this.unitWidth + this.gutter) * 2
31558         });
31559         
31560         pos.push({
31561             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31562             y : minY + (this.unitWidth + this.gutter) * 2
31563         });
31564         
31565         pos.push({
31566             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),
31567             y : minY + (this.unitWidth + this.gutter) * 2
31568         });
31569
31570         return pos;
31571         
31572     },
31573     
31574     /**
31575     * remove a Masonry Brick
31576     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31577     */
31578     removeBrick : function(brick_id)
31579     {
31580         if (!brick_id) {
31581             return;
31582         }
31583         
31584         for (var i = 0; i<this.bricks.length; i++) {
31585             if (this.bricks[i].id == brick_id) {
31586                 this.bricks.splice(i,1);
31587                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31588                 this.initial();
31589             }
31590         }
31591     },
31592     
31593     /**
31594     * adds a Masonry Brick
31595     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31596     */
31597     addBrick : function(cfg)
31598     {
31599         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31600         //this.register(cn);
31601         cn.parentId = this.id;
31602         cn.onRender(this.el, null);
31603         return cn;
31604     },
31605     
31606     /**
31607     * register a Masonry Brick
31608     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31609     */
31610     
31611     register : function(brick)
31612     {
31613         this.bricks.push(brick);
31614         brick.masonryId = this.id;
31615     },
31616     
31617     /**
31618     * clear all the Masonry Brick
31619     */
31620     clearAll : function()
31621     {
31622         this.bricks = [];
31623         //this.getChildContainer().dom.innerHTML = "";
31624         this.el.dom.innerHTML = '';
31625     },
31626     
31627     getSelected : function()
31628     {
31629         if (!this.selectedBrick) {
31630             return false;
31631         }
31632         
31633         return this.selectedBrick;
31634     }
31635 });
31636
31637 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31638     
31639     groups: {},
31640      /**
31641     * register a Masonry Layout
31642     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31643     */
31644     
31645     register : function(layout)
31646     {
31647         this.groups[layout.id] = layout;
31648     },
31649     /**
31650     * fetch a  Masonry Layout based on the masonry layout ID
31651     * @param {string} the masonry layout to add
31652     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31653     */
31654     
31655     get: function(layout_id) {
31656         if (typeof(this.groups[layout_id]) == 'undefined') {
31657             return false;
31658         }
31659         return this.groups[layout_id] ;
31660     }
31661     
31662     
31663     
31664 });
31665
31666  
31667
31668  /**
31669  *
31670  * This is based on 
31671  * http://masonry.desandro.com
31672  *
31673  * The idea is to render all the bricks based on vertical width...
31674  *
31675  * The original code extends 'outlayer' - we might need to use that....
31676  * 
31677  */
31678
31679
31680 /**
31681  * @class Roo.bootstrap.LayoutMasonryAuto
31682  * @extends Roo.bootstrap.Component
31683  * Bootstrap Layout Masonry class
31684  * 
31685  * @constructor
31686  * Create a new Element
31687  * @param {Object} config The config object
31688  */
31689
31690 Roo.bootstrap.LayoutMasonryAuto = function(config){
31691     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31692 };
31693
31694 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31695     
31696       /**
31697      * @cfg {Boolean} isFitWidth  - resize the width..
31698      */   
31699     isFitWidth : false,  // options..
31700     /**
31701      * @cfg {Boolean} isOriginLeft = left align?
31702      */   
31703     isOriginLeft : true,
31704     /**
31705      * @cfg {Boolean} isOriginTop = top align?
31706      */   
31707     isOriginTop : false,
31708     /**
31709      * @cfg {Boolean} isLayoutInstant = no animation?
31710      */   
31711     isLayoutInstant : false, // needed?
31712     /**
31713      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31714      */   
31715     isResizingContainer : true,
31716     /**
31717      * @cfg {Number} columnWidth  width of the columns 
31718      */   
31719     
31720     columnWidth : 0,
31721     
31722     /**
31723      * @cfg {Number} maxCols maximum number of columns
31724      */   
31725     
31726     maxCols: 0,
31727     /**
31728      * @cfg {Number} padHeight padding below box..
31729      */   
31730     
31731     padHeight : 10, 
31732     
31733     /**
31734      * @cfg {Boolean} isAutoInitial defalut true
31735      */   
31736     
31737     isAutoInitial : true, 
31738     
31739     // private?
31740     gutter : 0,
31741     
31742     containerWidth: 0,
31743     initialColumnWidth : 0,
31744     currentSize : null,
31745     
31746     colYs : null, // array.
31747     maxY : 0,
31748     padWidth: 10,
31749     
31750     
31751     tag: 'div',
31752     cls: '',
31753     bricks: null, //CompositeElement
31754     cols : 0, // array?
31755     // element : null, // wrapped now this.el
31756     _isLayoutInited : null, 
31757     
31758     
31759     getAutoCreate : function(){
31760         
31761         var cfg = {
31762             tag: this.tag,
31763             cls: 'blog-masonary-wrapper ' + this.cls,
31764             cn : {
31765                 cls : 'mas-boxes masonary'
31766             }
31767         };
31768         
31769         return cfg;
31770     },
31771     
31772     getChildContainer: function( )
31773     {
31774         if (this.boxesEl) {
31775             return this.boxesEl;
31776         }
31777         
31778         this.boxesEl = this.el.select('.mas-boxes').first();
31779         
31780         return this.boxesEl;
31781     },
31782     
31783     
31784     initEvents : function()
31785     {
31786         var _this = this;
31787         
31788         if(this.isAutoInitial){
31789             Roo.log('hook children rendered');
31790             this.on('childrenrendered', function() {
31791                 Roo.log('children rendered');
31792                 _this.initial();
31793             } ,this);
31794         }
31795         
31796     },
31797     
31798     initial : function()
31799     {
31800         this.reloadItems();
31801
31802         this.currentSize = this.el.getBox(true);
31803
31804         /// was window resize... - let's see if this works..
31805         Roo.EventManager.onWindowResize(this.resize, this); 
31806
31807         if(!this.isAutoInitial){
31808             this.layout();
31809             return;
31810         }
31811         
31812         this.layout.defer(500,this);
31813     },
31814     
31815     reloadItems: function()
31816     {
31817         this.bricks = this.el.select('.masonry-brick', true);
31818         
31819         this.bricks.each(function(b) {
31820             //Roo.log(b.getSize());
31821             if (!b.attr('originalwidth')) {
31822                 b.attr('originalwidth',  b.getSize().width);
31823             }
31824             
31825         });
31826         
31827         Roo.log(this.bricks.elements.length);
31828     },
31829     
31830     resize : function()
31831     {
31832         Roo.log('resize');
31833         var cs = this.el.getBox(true);
31834         
31835         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31836             Roo.log("no change in with or X");
31837             return;
31838         }
31839         this.currentSize = cs;
31840         this.layout();
31841     },
31842     
31843     layout : function()
31844     {
31845          Roo.log('layout');
31846         this._resetLayout();
31847         //this._manageStamps();
31848       
31849         // don't animate first layout
31850         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31851         this.layoutItems( isInstant );
31852       
31853         // flag for initalized
31854         this._isLayoutInited = true;
31855     },
31856     
31857     layoutItems : function( isInstant )
31858     {
31859         //var items = this._getItemsForLayout( this.items );
31860         // original code supports filtering layout items.. we just ignore it..
31861         
31862         this._layoutItems( this.bricks , isInstant );
31863       
31864         this._postLayout();
31865     },
31866     _layoutItems : function ( items , isInstant)
31867     {
31868        //this.fireEvent( 'layout', this, items );
31869     
31870
31871         if ( !items || !items.elements.length ) {
31872           // no items, emit event with empty array
31873             return;
31874         }
31875
31876         var queue = [];
31877         items.each(function(item) {
31878             Roo.log("layout item");
31879             Roo.log(item);
31880             // get x/y object from method
31881             var position = this._getItemLayoutPosition( item );
31882             // enqueue
31883             position.item = item;
31884             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31885             queue.push( position );
31886         }, this);
31887       
31888         this._processLayoutQueue( queue );
31889     },
31890     /** Sets position of item in DOM
31891     * @param {Element} item
31892     * @param {Number} x - horizontal position
31893     * @param {Number} y - vertical position
31894     * @param {Boolean} isInstant - disables transitions
31895     */
31896     _processLayoutQueue : function( queue )
31897     {
31898         for ( var i=0, len = queue.length; i < len; i++ ) {
31899             var obj = queue[i];
31900             obj.item.position('absolute');
31901             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31902         }
31903     },
31904       
31905     
31906     /**
31907     * Any logic you want to do after each layout,
31908     * i.e. size the container
31909     */
31910     _postLayout : function()
31911     {
31912         this.resizeContainer();
31913     },
31914     
31915     resizeContainer : function()
31916     {
31917         if ( !this.isResizingContainer ) {
31918             return;
31919         }
31920         var size = this._getContainerSize();
31921         if ( size ) {
31922             this.el.setSize(size.width,size.height);
31923             this.boxesEl.setSize(size.width,size.height);
31924         }
31925     },
31926     
31927     
31928     
31929     _resetLayout : function()
31930     {
31931         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31932         this.colWidth = this.el.getWidth();
31933         //this.gutter = this.el.getWidth(); 
31934         
31935         this.measureColumns();
31936
31937         // reset column Y
31938         var i = this.cols;
31939         this.colYs = [];
31940         while (i--) {
31941             this.colYs.push( 0 );
31942         }
31943     
31944         this.maxY = 0;
31945     },
31946
31947     measureColumns : function()
31948     {
31949         this.getContainerWidth();
31950       // if columnWidth is 0, default to outerWidth of first item
31951         if ( !this.columnWidth ) {
31952             var firstItem = this.bricks.first();
31953             Roo.log(firstItem);
31954             this.columnWidth  = this.containerWidth;
31955             if (firstItem && firstItem.attr('originalwidth') ) {
31956                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31957             }
31958             // columnWidth fall back to item of first element
31959             Roo.log("set column width?");
31960                         this.initialColumnWidth = this.columnWidth  ;
31961
31962             // if first elem has no width, default to size of container
31963             
31964         }
31965         
31966         
31967         if (this.initialColumnWidth) {
31968             this.columnWidth = this.initialColumnWidth;
31969         }
31970         
31971         
31972             
31973         // column width is fixed at the top - however if container width get's smaller we should
31974         // reduce it...
31975         
31976         // this bit calcs how man columns..
31977             
31978         var columnWidth = this.columnWidth += this.gutter;
31979       
31980         // calculate columns
31981         var containerWidth = this.containerWidth + this.gutter;
31982         
31983         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31984         // fix rounding errors, typically with gutters
31985         var excess = columnWidth - containerWidth % columnWidth;
31986         
31987         
31988         // if overshoot is less than a pixel, round up, otherwise floor it
31989         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31990         cols = Math[ mathMethod ]( cols );
31991         this.cols = Math.max( cols, 1 );
31992         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31993         
31994          // padding positioning..
31995         var totalColWidth = this.cols * this.columnWidth;
31996         var padavail = this.containerWidth - totalColWidth;
31997         // so for 2 columns - we need 3 'pads'
31998         
31999         var padNeeded = (1+this.cols) * this.padWidth;
32000         
32001         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32002         
32003         this.columnWidth += padExtra
32004         //this.padWidth = Math.floor(padavail /  ( this.cols));
32005         
32006         // adjust colum width so that padding is fixed??
32007         
32008         // we have 3 columns ... total = width * 3
32009         // we have X left over... that should be used by 
32010         
32011         //if (this.expandC) {
32012             
32013         //}
32014         
32015         
32016         
32017     },
32018     
32019     getContainerWidth : function()
32020     {
32021        /* // container is parent if fit width
32022         var container = this.isFitWidth ? this.element.parentNode : this.element;
32023         // check that this.size and size are there
32024         // IE8 triggers resize on body size change, so they might not be
32025         
32026         var size = getSize( container );  //FIXME
32027         this.containerWidth = size && size.innerWidth; //FIXME
32028         */
32029          
32030         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32031         
32032     },
32033     
32034     _getItemLayoutPosition : function( item )  // what is item?
32035     {
32036         // we resize the item to our columnWidth..
32037       
32038         item.setWidth(this.columnWidth);
32039         item.autoBoxAdjust  = false;
32040         
32041         var sz = item.getSize();
32042  
32043         // how many columns does this brick span
32044         var remainder = this.containerWidth % this.columnWidth;
32045         
32046         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32047         // round if off by 1 pixel, otherwise use ceil
32048         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32049         colSpan = Math.min( colSpan, this.cols );
32050         
32051         // normally this should be '1' as we dont' currently allow multi width columns..
32052         
32053         var colGroup = this._getColGroup( colSpan );
32054         // get the minimum Y value from the columns
32055         var minimumY = Math.min.apply( Math, colGroup );
32056         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32057         
32058         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32059          
32060         // position the brick
32061         var position = {
32062             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32063             y: this.currentSize.y + minimumY + this.padHeight
32064         };
32065         
32066         Roo.log(position);
32067         // apply setHeight to necessary columns
32068         var setHeight = minimumY + sz.height + this.padHeight;
32069         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32070         
32071         var setSpan = this.cols + 1 - colGroup.length;
32072         for ( var i = 0; i < setSpan; i++ ) {
32073           this.colYs[ shortColIndex + i ] = setHeight ;
32074         }
32075       
32076         return position;
32077     },
32078     
32079     /**
32080      * @param {Number} colSpan - number of columns the element spans
32081      * @returns {Array} colGroup
32082      */
32083     _getColGroup : function( colSpan )
32084     {
32085         if ( colSpan < 2 ) {
32086           // if brick spans only one column, use all the column Ys
32087           return this.colYs;
32088         }
32089       
32090         var colGroup = [];
32091         // how many different places could this brick fit horizontally
32092         var groupCount = this.cols + 1 - colSpan;
32093         // for each group potential horizontal position
32094         for ( var i = 0; i < groupCount; i++ ) {
32095           // make an array of colY values for that one group
32096           var groupColYs = this.colYs.slice( i, i + colSpan );
32097           // and get the max value of the array
32098           colGroup[i] = Math.max.apply( Math, groupColYs );
32099         }
32100         return colGroup;
32101     },
32102     /*
32103     _manageStamp : function( stamp )
32104     {
32105         var stampSize =  stamp.getSize();
32106         var offset = stamp.getBox();
32107         // get the columns that this stamp affects
32108         var firstX = this.isOriginLeft ? offset.x : offset.right;
32109         var lastX = firstX + stampSize.width;
32110         var firstCol = Math.floor( firstX / this.columnWidth );
32111         firstCol = Math.max( 0, firstCol );
32112         
32113         var lastCol = Math.floor( lastX / this.columnWidth );
32114         // lastCol should not go over if multiple of columnWidth #425
32115         lastCol -= lastX % this.columnWidth ? 0 : 1;
32116         lastCol = Math.min( this.cols - 1, lastCol );
32117         
32118         // set colYs to bottom of the stamp
32119         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32120             stampSize.height;
32121             
32122         for ( var i = firstCol; i <= lastCol; i++ ) {
32123           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32124         }
32125     },
32126     */
32127     
32128     _getContainerSize : function()
32129     {
32130         this.maxY = Math.max.apply( Math, this.colYs );
32131         var size = {
32132             height: this.maxY
32133         };
32134       
32135         if ( this.isFitWidth ) {
32136             size.width = this._getContainerFitWidth();
32137         }
32138       
32139         return size;
32140     },
32141     
32142     _getContainerFitWidth : function()
32143     {
32144         var unusedCols = 0;
32145         // count unused columns
32146         var i = this.cols;
32147         while ( --i ) {
32148           if ( this.colYs[i] !== 0 ) {
32149             break;
32150           }
32151           unusedCols++;
32152         }
32153         // fit container to columns that have been used
32154         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32155     },
32156     
32157     needsResizeLayout : function()
32158     {
32159         var previousWidth = this.containerWidth;
32160         this.getContainerWidth();
32161         return previousWidth !== this.containerWidth;
32162     }
32163  
32164 });
32165
32166  
32167
32168  /*
32169  * - LGPL
32170  *
32171  * element
32172  * 
32173  */
32174
32175 /**
32176  * @class Roo.bootstrap.MasonryBrick
32177  * @extends Roo.bootstrap.Component
32178  * Bootstrap MasonryBrick class
32179  * 
32180  * @constructor
32181  * Create a new MasonryBrick
32182  * @param {Object} config The config object
32183  */
32184
32185 Roo.bootstrap.MasonryBrick = function(config){
32186     
32187     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32188     
32189     Roo.bootstrap.MasonryBrick.register(this);
32190     
32191     this.addEvents({
32192         // raw events
32193         /**
32194          * @event click
32195          * When a MasonryBrick is clcik
32196          * @param {Roo.bootstrap.MasonryBrick} this
32197          * @param {Roo.EventObject} e
32198          */
32199         "click" : true
32200     });
32201 };
32202
32203 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32204     
32205     /**
32206      * @cfg {String} title
32207      */   
32208     title : '',
32209     /**
32210      * @cfg {String} html
32211      */   
32212     html : '',
32213     /**
32214      * @cfg {String} bgimage
32215      */   
32216     bgimage : '',
32217     /**
32218      * @cfg {String} videourl
32219      */   
32220     videourl : '',
32221     /**
32222      * @cfg {String} cls
32223      */   
32224     cls : '',
32225     /**
32226      * @cfg {String} href
32227      */   
32228     href : '',
32229     /**
32230      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32231      */   
32232     size : 'xs',
32233     
32234     /**
32235      * @cfg {String} placetitle (center|bottom)
32236      */   
32237     placetitle : '',
32238     
32239     /**
32240      * @cfg {Boolean} isFitContainer defalut true
32241      */   
32242     isFitContainer : true, 
32243     
32244     /**
32245      * @cfg {Boolean} preventDefault defalut false
32246      */   
32247     preventDefault : false, 
32248     
32249     /**
32250      * @cfg {Boolean} inverse defalut false
32251      */   
32252     maskInverse : false, 
32253     
32254     getAutoCreate : function()
32255     {
32256         if(!this.isFitContainer){
32257             return this.getSplitAutoCreate();
32258         }
32259         
32260         var cls = 'masonry-brick masonry-brick-full';
32261         
32262         if(this.href.length){
32263             cls += ' masonry-brick-link';
32264         }
32265         
32266         if(this.bgimage.length){
32267             cls += ' masonry-brick-image';
32268         }
32269         
32270         if(this.maskInverse){
32271             cls += ' mask-inverse';
32272         }
32273         
32274         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32275             cls += ' enable-mask';
32276         }
32277         
32278         if(this.size){
32279             cls += ' masonry-' + this.size + '-brick';
32280         }
32281         
32282         if(this.placetitle.length){
32283             
32284             switch (this.placetitle) {
32285                 case 'center' :
32286                     cls += ' masonry-center-title';
32287                     break;
32288                 case 'bottom' :
32289                     cls += ' masonry-bottom-title';
32290                     break;
32291                 default:
32292                     break;
32293             }
32294             
32295         } else {
32296             if(!this.html.length && !this.bgimage.length){
32297                 cls += ' masonry-center-title';
32298             }
32299
32300             if(!this.html.length && this.bgimage.length){
32301                 cls += ' masonry-bottom-title';
32302             }
32303         }
32304         
32305         if(this.cls){
32306             cls += ' ' + this.cls;
32307         }
32308         
32309         var cfg = {
32310             tag: (this.href.length) ? 'a' : 'div',
32311             cls: cls,
32312             cn: [
32313                 {
32314                     tag: 'div',
32315                     cls: 'masonry-brick-mask'
32316                 },
32317                 {
32318                     tag: 'div',
32319                     cls: 'masonry-brick-paragraph',
32320                     cn: []
32321                 }
32322             ]
32323         };
32324         
32325         if(this.href.length){
32326             cfg.href = this.href;
32327         }
32328         
32329         var cn = cfg.cn[1].cn;
32330         
32331         if(this.title.length){
32332             cn.push({
32333                 tag: 'h4',
32334                 cls: 'masonry-brick-title',
32335                 html: this.title
32336             });
32337         }
32338         
32339         if(this.html.length){
32340             cn.push({
32341                 tag: 'p',
32342                 cls: 'masonry-brick-text',
32343                 html: this.html
32344             });
32345         }
32346         
32347         if (!this.title.length && !this.html.length) {
32348             cfg.cn[1].cls += ' hide';
32349         }
32350         
32351         if(this.bgimage.length){
32352             cfg.cn.push({
32353                 tag: 'img',
32354                 cls: 'masonry-brick-image-view',
32355                 src: this.bgimage
32356             });
32357         }
32358         
32359         if(this.videourl.length){
32360             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32361             // youtube support only?
32362             cfg.cn.push({
32363                 tag: 'iframe',
32364                 cls: 'masonry-brick-image-view',
32365                 src: vurl,
32366                 frameborder : 0,
32367                 allowfullscreen : true
32368             });
32369         }
32370         
32371         return cfg;
32372         
32373     },
32374     
32375     getSplitAutoCreate : function()
32376     {
32377         var cls = 'masonry-brick masonry-brick-split';
32378         
32379         if(this.href.length){
32380             cls += ' masonry-brick-link';
32381         }
32382         
32383         if(this.bgimage.length){
32384             cls += ' masonry-brick-image';
32385         }
32386         
32387         if(this.size){
32388             cls += ' masonry-' + this.size + '-brick';
32389         }
32390         
32391         switch (this.placetitle) {
32392             case 'center' :
32393                 cls += ' masonry-center-title';
32394                 break;
32395             case 'bottom' :
32396                 cls += ' masonry-bottom-title';
32397                 break;
32398             default:
32399                 if(!this.bgimage.length){
32400                     cls += ' masonry-center-title';
32401                 }
32402
32403                 if(this.bgimage.length){
32404                     cls += ' masonry-bottom-title';
32405                 }
32406                 break;
32407         }
32408         
32409         if(this.cls){
32410             cls += ' ' + this.cls;
32411         }
32412         
32413         var cfg = {
32414             tag: (this.href.length) ? 'a' : 'div',
32415             cls: cls,
32416             cn: [
32417                 {
32418                     tag: 'div',
32419                     cls: 'masonry-brick-split-head',
32420                     cn: [
32421                         {
32422                             tag: 'div',
32423                             cls: 'masonry-brick-paragraph',
32424                             cn: []
32425                         }
32426                     ]
32427                 },
32428                 {
32429                     tag: 'div',
32430                     cls: 'masonry-brick-split-body',
32431                     cn: []
32432                 }
32433             ]
32434         };
32435         
32436         if(this.href.length){
32437             cfg.href = this.href;
32438         }
32439         
32440         if(this.title.length){
32441             cfg.cn[0].cn[0].cn.push({
32442                 tag: 'h4',
32443                 cls: 'masonry-brick-title',
32444                 html: this.title
32445             });
32446         }
32447         
32448         if(this.html.length){
32449             cfg.cn[1].cn.push({
32450                 tag: 'p',
32451                 cls: 'masonry-brick-text',
32452                 html: this.html
32453             });
32454         }
32455
32456         if(this.bgimage.length){
32457             cfg.cn[0].cn.push({
32458                 tag: 'img',
32459                 cls: 'masonry-brick-image-view',
32460                 src: this.bgimage
32461             });
32462         }
32463         
32464         if(this.videourl.length){
32465             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32466             // youtube support only?
32467             cfg.cn[0].cn.cn.push({
32468                 tag: 'iframe',
32469                 cls: 'masonry-brick-image-view',
32470                 src: vurl,
32471                 frameborder : 0,
32472                 allowfullscreen : true
32473             });
32474         }
32475         
32476         return cfg;
32477     },
32478     
32479     initEvents: function() 
32480     {
32481         switch (this.size) {
32482             case 'xs' :
32483                 this.x = 1;
32484                 this.y = 1;
32485                 break;
32486             case 'sm' :
32487                 this.x = 2;
32488                 this.y = 2;
32489                 break;
32490             case 'md' :
32491             case 'md-left' :
32492             case 'md-right' :
32493                 this.x = 3;
32494                 this.y = 3;
32495                 break;
32496             case 'tall' :
32497                 this.x = 2;
32498                 this.y = 3;
32499                 break;
32500             case 'wide' :
32501                 this.x = 3;
32502                 this.y = 2;
32503                 break;
32504             case 'wide-thin' :
32505                 this.x = 3;
32506                 this.y = 1;
32507                 break;
32508                         
32509             default :
32510                 break;
32511         }
32512         
32513         if(Roo.isTouch){
32514             this.el.on('touchstart', this.onTouchStart, this);
32515             this.el.on('touchmove', this.onTouchMove, this);
32516             this.el.on('touchend', this.onTouchEnd, this);
32517             this.el.on('contextmenu', this.onContextMenu, this);
32518         } else {
32519             this.el.on('mouseenter'  ,this.enter, this);
32520             this.el.on('mouseleave', this.leave, this);
32521             this.el.on('click', this.onClick, this);
32522         }
32523         
32524         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32525             this.parent().bricks.push(this);   
32526         }
32527         
32528     },
32529     
32530     onClick: function(e, el)
32531     {
32532         var time = this.endTimer - this.startTimer;
32533         // Roo.log(e.preventDefault());
32534         if(Roo.isTouch){
32535             if(time > 1000){
32536                 e.preventDefault();
32537                 return;
32538             }
32539         }
32540         
32541         if(!this.preventDefault){
32542             return;
32543         }
32544         
32545         e.preventDefault();
32546         
32547         if (this.activcClass != '') {
32548             this.selectBrick();
32549         }
32550         
32551         this.fireEvent('click', this);
32552     },
32553     
32554     enter: 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.9, true);
32564         }
32565     },
32566     
32567     leave: function(e, el)
32568     {
32569         e.preventDefault();
32570         
32571         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32572             return;
32573         }
32574         
32575         if(this.bgimage.length && this.html.length){
32576             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32577         }
32578     },
32579     
32580     onTouchStart: function(e, el)
32581     {
32582 //        e.preventDefault();
32583         
32584         this.touchmoved = false;
32585         
32586         if(!this.isFitContainer){
32587             return;
32588         }
32589         
32590         if(!this.bgimage.length || !this.html.length){
32591             return;
32592         }
32593         
32594         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32595         
32596         this.timer = new Date().getTime();
32597         
32598     },
32599     
32600     onTouchMove: function(e, el)
32601     {
32602         this.touchmoved = true;
32603     },
32604     
32605     onContextMenu : function(e,el)
32606     {
32607         e.preventDefault();
32608         e.stopPropagation();
32609         return false;
32610     },
32611     
32612     onTouchEnd: function(e, el)
32613     {
32614 //        e.preventDefault();
32615         
32616         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32617         
32618             this.leave(e,el);
32619             
32620             return;
32621         }
32622         
32623         if(!this.bgimage.length || !this.html.length){
32624             
32625             if(this.href.length){
32626                 window.location.href = this.href;
32627             }
32628             
32629             return;
32630         }
32631         
32632         if(!this.isFitContainer){
32633             return;
32634         }
32635         
32636         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32637         
32638         window.location.href = this.href;
32639     },
32640     
32641     //selection on single brick only
32642     selectBrick : function() {
32643         
32644         if (!this.parentId) {
32645             return;
32646         }
32647         
32648         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32649         var index = m.selectedBrick.indexOf(this.id);
32650         
32651         if ( index > -1) {
32652             m.selectedBrick.splice(index,1);
32653             this.el.removeClass(this.activeClass);
32654             return;
32655         }
32656         
32657         for(var i = 0; i < m.selectedBrick.length; i++) {
32658             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32659             b.el.removeClass(b.activeClass);
32660         }
32661         
32662         m.selectedBrick = [];
32663         
32664         m.selectedBrick.push(this.id);
32665         this.el.addClass(this.activeClass);
32666         return;
32667     }
32668     
32669 });
32670
32671 Roo.apply(Roo.bootstrap.MasonryBrick, {
32672     
32673     //groups: {},
32674     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32675      /**
32676     * register a Masonry Brick
32677     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32678     */
32679     
32680     register : function(brick)
32681     {
32682         //this.groups[brick.id] = brick;
32683         this.groups.add(brick.id, brick);
32684     },
32685     /**
32686     * fetch a  masonry brick based on the masonry brick ID
32687     * @param {string} the masonry brick to add
32688     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32689     */
32690     
32691     get: function(brick_id) 
32692     {
32693         // if (typeof(this.groups[brick_id]) == 'undefined') {
32694         //     return false;
32695         // }
32696         // return this.groups[brick_id] ;
32697         
32698         if(this.groups.key(brick_id)) {
32699             return this.groups.key(brick_id);
32700         }
32701         
32702         return false;
32703     }
32704     
32705     
32706     
32707 });
32708
32709  /*
32710  * - LGPL
32711  *
32712  * element
32713  * 
32714  */
32715
32716 /**
32717  * @class Roo.bootstrap.Brick
32718  * @extends Roo.bootstrap.Component
32719  * Bootstrap Brick class
32720  * 
32721  * @constructor
32722  * Create a new Brick
32723  * @param {Object} config The config object
32724  */
32725
32726 Roo.bootstrap.Brick = function(config){
32727     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32728     
32729     this.addEvents({
32730         // raw events
32731         /**
32732          * @event click
32733          * When a Brick is click
32734          * @param {Roo.bootstrap.Brick} this
32735          * @param {Roo.EventObject} e
32736          */
32737         "click" : true
32738     });
32739 };
32740
32741 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32742     
32743     /**
32744      * @cfg {String} title
32745      */   
32746     title : '',
32747     /**
32748      * @cfg {String} html
32749      */   
32750     html : '',
32751     /**
32752      * @cfg {String} bgimage
32753      */   
32754     bgimage : '',
32755     /**
32756      * @cfg {String} cls
32757      */   
32758     cls : '',
32759     /**
32760      * @cfg {String} href
32761      */   
32762     href : '',
32763     /**
32764      * @cfg {String} video
32765      */   
32766     video : '',
32767     /**
32768      * @cfg {Boolean} square
32769      */   
32770     square : true,
32771     
32772     getAutoCreate : function()
32773     {
32774         var cls = 'roo-brick';
32775         
32776         if(this.href.length){
32777             cls += ' roo-brick-link';
32778         }
32779         
32780         if(this.bgimage.length){
32781             cls += ' roo-brick-image';
32782         }
32783         
32784         if(!this.html.length && !this.bgimage.length){
32785             cls += ' roo-brick-center-title';
32786         }
32787         
32788         if(!this.html.length && this.bgimage.length){
32789             cls += ' roo-brick-bottom-title';
32790         }
32791         
32792         if(this.cls){
32793             cls += ' ' + this.cls;
32794         }
32795         
32796         var cfg = {
32797             tag: (this.href.length) ? 'a' : 'div',
32798             cls: cls,
32799             cn: [
32800                 {
32801                     tag: 'div',
32802                     cls: 'roo-brick-paragraph',
32803                     cn: []
32804                 }
32805             ]
32806         };
32807         
32808         if(this.href.length){
32809             cfg.href = this.href;
32810         }
32811         
32812         var cn = cfg.cn[0].cn;
32813         
32814         if(this.title.length){
32815             cn.push({
32816                 tag: 'h4',
32817                 cls: 'roo-brick-title',
32818                 html: this.title
32819             });
32820         }
32821         
32822         if(this.html.length){
32823             cn.push({
32824                 tag: 'p',
32825                 cls: 'roo-brick-text',
32826                 html: this.html
32827             });
32828         } else {
32829             cn.cls += ' hide';
32830         }
32831         
32832         if(this.bgimage.length){
32833             cfg.cn.push({
32834                 tag: 'img',
32835                 cls: 'roo-brick-image-view',
32836                 src: this.bgimage
32837             });
32838         }
32839         
32840         return cfg;
32841     },
32842     
32843     initEvents: function() 
32844     {
32845         if(this.title.length || this.html.length){
32846             this.el.on('mouseenter'  ,this.enter, this);
32847             this.el.on('mouseleave', this.leave, this);
32848         }
32849         
32850         Roo.EventManager.onWindowResize(this.resize, this); 
32851         
32852         if(this.bgimage.length){
32853             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32854             this.imageEl.on('load', this.onImageLoad, this);
32855             return;
32856         }
32857         
32858         this.resize();
32859     },
32860     
32861     onImageLoad : function()
32862     {
32863         this.resize();
32864     },
32865     
32866     resize : function()
32867     {
32868         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32869         
32870         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32871         
32872         if(this.bgimage.length){
32873             var image = this.el.select('.roo-brick-image-view', true).first();
32874             
32875             image.setWidth(paragraph.getWidth());
32876             
32877             if(this.square){
32878                 image.setHeight(paragraph.getWidth());
32879             }
32880             
32881             this.el.setHeight(image.getHeight());
32882             paragraph.setHeight(image.getHeight());
32883             
32884         }
32885         
32886     },
32887     
32888     enter: function(e, el)
32889     {
32890         e.preventDefault();
32891         
32892         if(this.bgimage.length){
32893             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32894             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32895         }
32896     },
32897     
32898     leave: function(e, el)
32899     {
32900         e.preventDefault();
32901         
32902         if(this.bgimage.length){
32903             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32904             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32905         }
32906     }
32907     
32908 });
32909
32910  
32911
32912  /*
32913  * - LGPL
32914  *
32915  * Input
32916  * 
32917  */
32918
32919 /**
32920  * @class Roo.bootstrap.NumberField
32921  * @extends Roo.bootstrap.Input
32922  * Bootstrap NumberField class
32923  * 
32924  * 
32925  * 
32926  * 
32927  * @constructor
32928  * Create a new NumberField
32929  * @param {Object} config The config object
32930  */
32931
32932 Roo.bootstrap.NumberField = function(config){
32933     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32934 };
32935
32936 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32937     
32938     /**
32939      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32940      */
32941     allowDecimals : true,
32942     /**
32943      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32944      */
32945     decimalSeparator : ".",
32946     /**
32947      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32948      */
32949     decimalPrecision : 2,
32950     /**
32951      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32952      */
32953     allowNegative : true,
32954     /**
32955      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32956      */
32957     minValue : Number.NEGATIVE_INFINITY,
32958     /**
32959      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32960      */
32961     maxValue : Number.MAX_VALUE,
32962     /**
32963      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32964      */
32965     minText : "The minimum value for this field is {0}",
32966     /**
32967      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32968      */
32969     maxText : "The maximum value for this field is {0}",
32970     /**
32971      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32972      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32973      */
32974     nanText : "{0} is not a valid number",
32975     /**
32976      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32977      */
32978     castInt : true,
32979
32980     // private
32981     initEvents : function()
32982     {   
32983         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32984         
32985         var allowed = "0123456789";
32986         
32987         if(this.allowDecimals){
32988             allowed += this.decimalSeparator;
32989         }
32990         
32991         if(this.allowNegative){
32992             allowed += "-";
32993         }
32994         
32995         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32996         
32997         var keyPress = function(e){
32998             
32999             var k = e.getKey();
33000             
33001             var c = e.getCharCode();
33002             
33003             if(
33004                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33005                     allowed.indexOf(String.fromCharCode(c)) === -1
33006             ){
33007                 e.stopEvent();
33008                 return;
33009             }
33010             
33011             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33012                 return;
33013             }
33014             
33015             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33016                 e.stopEvent();
33017             }
33018         };
33019         
33020         this.el.on("keypress", keyPress, this);
33021     },
33022     
33023     validateValue : function(value)
33024     {
33025         
33026         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33027             return false;
33028         }
33029         
33030         var num = this.parseValue(value);
33031         
33032         if(isNaN(num)){
33033             this.markInvalid(String.format(this.nanText, value));
33034             return false;
33035         }
33036         
33037         if(num < this.minValue){
33038             this.markInvalid(String.format(this.minText, this.minValue));
33039             return false;
33040         }
33041         
33042         if(num > this.maxValue){
33043             this.markInvalid(String.format(this.maxText, this.maxValue));
33044             return false;
33045         }
33046         
33047         return true;
33048     },
33049
33050     getValue : function()
33051     {
33052         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33053     },
33054
33055     parseValue : function(value)
33056     {
33057         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33058         return isNaN(value) ? '' : value;
33059     },
33060
33061     fixPrecision : function(value)
33062     {
33063         var nan = isNaN(value);
33064         
33065         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33066             return nan ? '' : value;
33067         }
33068         return parseFloat(value).toFixed(this.decimalPrecision);
33069     },
33070
33071     setValue : function(v)
33072     {
33073         v = this.fixPrecision(v);
33074         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33075     },
33076
33077     decimalPrecisionFcn : function(v)
33078     {
33079         return Math.floor(v);
33080     },
33081
33082     beforeBlur : function()
33083     {
33084         if(!this.castInt){
33085             return;
33086         }
33087         
33088         var v = this.parseValue(this.getRawValue());
33089         if(v){
33090             this.setValue(v);
33091         }
33092     }
33093     
33094 });
33095
33096  
33097
33098 /*
33099 * Licence: LGPL
33100 */
33101
33102 /**
33103  * @class Roo.bootstrap.DocumentSlider
33104  * @extends Roo.bootstrap.Component
33105  * Bootstrap DocumentSlider class
33106  * 
33107  * @constructor
33108  * Create a new DocumentViewer
33109  * @param {Object} config The config object
33110  */
33111
33112 Roo.bootstrap.DocumentSlider = function(config){
33113     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33114     
33115     this.files = [];
33116     
33117     this.addEvents({
33118         /**
33119          * @event initial
33120          * Fire after initEvent
33121          * @param {Roo.bootstrap.DocumentSlider} this
33122          */
33123         "initial" : true,
33124         /**
33125          * @event update
33126          * Fire after update
33127          * @param {Roo.bootstrap.DocumentSlider} this
33128          */
33129         "update" : true,
33130         /**
33131          * @event click
33132          * Fire after click
33133          * @param {Roo.bootstrap.DocumentSlider} this
33134          */
33135         "click" : true
33136     });
33137 };
33138
33139 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33140     
33141     files : false,
33142     
33143     indicator : 0,
33144     
33145     getAutoCreate : function()
33146     {
33147         var cfg = {
33148             tag : 'div',
33149             cls : 'roo-document-slider',
33150             cn : [
33151                 {
33152                     tag : 'div',
33153                     cls : 'roo-document-slider-header',
33154                     cn : [
33155                         {
33156                             tag : 'div',
33157                             cls : 'roo-document-slider-header-title'
33158                         }
33159                     ]
33160                 },
33161                 {
33162                     tag : 'div',
33163                     cls : 'roo-document-slider-body',
33164                     cn : [
33165                         {
33166                             tag : 'div',
33167                             cls : 'roo-document-slider-prev',
33168                             cn : [
33169                                 {
33170                                     tag : 'i',
33171                                     cls : 'fa fa-chevron-left'
33172                                 }
33173                             ]
33174                         },
33175                         {
33176                             tag : 'div',
33177                             cls : 'roo-document-slider-thumb',
33178                             cn : [
33179                                 {
33180                                     tag : 'img',
33181                                     cls : 'roo-document-slider-image'
33182                                 }
33183                             ]
33184                         },
33185                         {
33186                             tag : 'div',
33187                             cls : 'roo-document-slider-next',
33188                             cn : [
33189                                 {
33190                                     tag : 'i',
33191                                     cls : 'fa fa-chevron-right'
33192                                 }
33193                             ]
33194                         }
33195                     ]
33196                 }
33197             ]
33198         };
33199         
33200         return cfg;
33201     },
33202     
33203     initEvents : function()
33204     {
33205         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33206         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33207         
33208         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33209         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33210         
33211         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33212         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33213         
33214         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33215         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33216         
33217         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33218         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33219         
33220         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33221         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33222         
33223         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33224         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33225         
33226         this.thumbEl.on('click', this.onClick, this);
33227         
33228         this.prevIndicator.on('click', this.prev, this);
33229         
33230         this.nextIndicator.on('click', this.next, this);
33231         
33232     },
33233     
33234     initial : function()
33235     {
33236         if(this.files.length){
33237             this.indicator = 1;
33238             this.update()
33239         }
33240         
33241         this.fireEvent('initial', this);
33242     },
33243     
33244     update : function()
33245     {
33246         this.imageEl.attr('src', this.files[this.indicator - 1]);
33247         
33248         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33249         
33250         this.prevIndicator.show();
33251         
33252         if(this.indicator == 1){
33253             this.prevIndicator.hide();
33254         }
33255         
33256         this.nextIndicator.show();
33257         
33258         if(this.indicator == this.files.length){
33259             this.nextIndicator.hide();
33260         }
33261         
33262         this.thumbEl.scrollTo('top');
33263         
33264         this.fireEvent('update', this);
33265     },
33266     
33267     onClick : function(e)
33268     {
33269         e.preventDefault();
33270         
33271         this.fireEvent('click', this);
33272     },
33273     
33274     prev : function(e)
33275     {
33276         e.preventDefault();
33277         
33278         this.indicator = Math.max(1, this.indicator - 1);
33279         
33280         this.update();
33281     },
33282     
33283     next : function(e)
33284     {
33285         e.preventDefault();
33286         
33287         this.indicator = Math.min(this.files.length, this.indicator + 1);
33288         
33289         this.update();
33290     }
33291 });
33292 /*
33293  * - LGPL
33294  *
33295  * RadioSet
33296  *
33297  *
33298  */
33299
33300 /**
33301  * @class Roo.bootstrap.RadioSet
33302  * @extends Roo.bootstrap.Input
33303  * Bootstrap RadioSet class
33304  * @cfg {String} indicatorpos (left|right) default left
33305  * @cfg {Boolean} inline (true|false) inline the element (default true)
33306  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33307  * @constructor
33308  * Create a new RadioSet
33309  * @param {Object} config The config object
33310  */
33311
33312 Roo.bootstrap.RadioSet = function(config){
33313     
33314     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33315     
33316     this.radioes = [];
33317     
33318     Roo.bootstrap.RadioSet.register(this);
33319     
33320     this.addEvents({
33321         /**
33322         * @event check
33323         * Fires when the element is checked or unchecked.
33324         * @param {Roo.bootstrap.RadioSet} this This radio
33325         * @param {Roo.bootstrap.Radio} item The checked item
33326         */
33327        check : true
33328     });
33329     
33330 };
33331
33332 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33333
33334     radioes : false,
33335     
33336     inline : true,
33337     
33338     weight : '',
33339     
33340     indicatorpos : 'left',
33341     
33342     getAutoCreate : function()
33343     {
33344         var label = {
33345             tag : 'label',
33346             cls : 'roo-radio-set-label',
33347             cn : [
33348                 {
33349                     tag : 'span',
33350                     html : this.fieldLabel
33351                 }
33352             ]
33353         };
33354         
33355         if(this.indicatorpos == 'left'){
33356             label.cn.unshift({
33357                 tag : 'i',
33358                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33359                 tooltip : 'This field is required'
33360             });
33361         } else {
33362             label.cn.push({
33363                 tag : 'i',
33364                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33365                 tooltip : 'This field is required'
33366             });
33367         }
33368         
33369         var items = {
33370             tag : 'div',
33371             cls : 'roo-radio-set-items'
33372         };
33373         
33374         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33375         
33376         if (align === 'left' && this.fieldLabel.length) {
33377             
33378             items = {
33379                 cls : "roo-radio-set-right", 
33380                 cn: [
33381                     items
33382                 ]
33383             };
33384             
33385             if(this.labelWidth > 12){
33386                 label.style = "width: " + this.labelWidth + 'px';
33387             }
33388             
33389             if(this.labelWidth < 13 && this.labelmd == 0){
33390                 this.labelmd = this.labelWidth;
33391             }
33392             
33393             if(this.labellg > 0){
33394                 label.cls += ' col-lg-' + this.labellg;
33395                 items.cls += ' col-lg-' + (12 - this.labellg);
33396             }
33397             
33398             if(this.labelmd > 0){
33399                 label.cls += ' col-md-' + this.labelmd;
33400                 items.cls += ' col-md-' + (12 - this.labelmd);
33401             }
33402             
33403             if(this.labelsm > 0){
33404                 label.cls += ' col-sm-' + this.labelsm;
33405                 items.cls += ' col-sm-' + (12 - this.labelsm);
33406             }
33407             
33408             if(this.labelxs > 0){
33409                 label.cls += ' col-xs-' + this.labelxs;
33410                 items.cls += ' col-xs-' + (12 - this.labelxs);
33411             }
33412         }
33413         
33414         var cfg = {
33415             tag : 'div',
33416             cls : 'roo-radio-set',
33417             cn : [
33418                 {
33419                     tag : 'input',
33420                     cls : 'roo-radio-set-input',
33421                     type : 'hidden',
33422                     name : this.name,
33423                     value : this.value ? this.value :  ''
33424                 },
33425                 label,
33426                 items
33427             ]
33428         };
33429         
33430         if(this.weight.length){
33431             cfg.cls += ' roo-radio-' + this.weight;
33432         }
33433         
33434         if(this.inline) {
33435             cfg.cls += ' roo-radio-set-inline';
33436         }
33437         
33438         var settings=this;
33439         ['xs','sm','md','lg'].map(function(size){
33440             if (settings[size]) {
33441                 cfg.cls += ' col-' + size + '-' + settings[size];
33442             }
33443         });
33444         
33445         return cfg;
33446         
33447     },
33448
33449     initEvents : function()
33450     {
33451         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33452         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33453         
33454         if(!this.fieldLabel.length){
33455             this.labelEl.hide();
33456         }
33457         
33458         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33459         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33460         
33461         this.indicatorEl().addClass('invisible');
33462         
33463         this.originalValue = this.getValue();
33464         
33465     },
33466     
33467     inputEl: function ()
33468     {
33469         return this.el.select('.roo-radio-set-input', true).first();
33470     },
33471     
33472     getChildContainer : function()
33473     {
33474         return this.itemsEl;
33475     },
33476     
33477     register : function(item)
33478     {
33479         this.radioes.push(item);
33480         
33481     },
33482     
33483     validate : function()
33484     {   
33485         var valid = false;
33486         
33487         Roo.each(this.radioes, function(i){
33488             if(!i.checked){
33489                 return;
33490             }
33491             
33492             valid = true;
33493             return false;
33494         });
33495         
33496         if(this.allowBlank) {
33497             return true;
33498         }
33499         
33500         if(this.disabled || valid){
33501             this.markValid();
33502             return true;
33503         }
33504         
33505         this.markInvalid();
33506         return false;
33507         
33508     },
33509     
33510     markValid : function()
33511     {
33512         if(this.labelEl.isVisible(true)){
33513             this.indicatorEl().removeClass('visible');
33514             this.indicatorEl().addClass('invisible');
33515         }
33516         
33517         this.el.removeClass([this.invalidClass, this.validClass]);
33518         this.el.addClass(this.validClass);
33519         
33520         this.fireEvent('valid', this);
33521     },
33522     
33523     markInvalid : function(msg)
33524     {
33525         if(this.allowBlank || this.disabled){
33526             return;
33527         }
33528         
33529         if(this.labelEl.isVisible(true)){
33530             this.indicatorEl().removeClass('invisible');
33531             this.indicatorEl().addClass('visible');
33532         }
33533         
33534         this.el.removeClass([this.invalidClass, this.validClass]);
33535         this.el.addClass(this.invalidClass);
33536         
33537         this.fireEvent('invalid', this, msg);
33538         
33539     },
33540     
33541     setValue : function(v, suppressEvent)
33542     {   
33543         if(this.value === v){
33544             return;
33545         }
33546         
33547         this.value = v;
33548         
33549         if(this.rendered){
33550             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33551         }
33552         
33553         Roo.each(this.radioes, function(i){
33554             
33555             i.checked = false;
33556             i.el.removeClass('checked');
33557             
33558             if(i.value === v || i.value.toString() === v.toString()){
33559                 i.checked = true;
33560                 i.el.addClass('checked');
33561                 
33562                 if(suppressEvent !== true){
33563                     this.fireEvent('check', this, i);
33564                 }
33565             }
33566             
33567         }, this);
33568         
33569         this.validate();
33570     },
33571     
33572     clearInvalid : function(){
33573         
33574         if(!this.el || this.preventMark){
33575             return;
33576         }
33577         
33578         this.el.removeClass([this.invalidClass]);
33579         
33580         this.fireEvent('valid', this);
33581     }
33582     
33583 });
33584
33585 Roo.apply(Roo.bootstrap.RadioSet, {
33586     
33587     groups: {},
33588     
33589     register : function(set)
33590     {
33591         this.groups[set.name] = set;
33592     },
33593     
33594     get: function(name) 
33595     {
33596         if (typeof(this.groups[name]) == 'undefined') {
33597             return false;
33598         }
33599         
33600         return this.groups[name] ;
33601     }
33602     
33603 });
33604 /*
33605  * Based on:
33606  * Ext JS Library 1.1.1
33607  * Copyright(c) 2006-2007, Ext JS, LLC.
33608  *
33609  * Originally Released Under LGPL - original licence link has changed is not relivant.
33610  *
33611  * Fork - LGPL
33612  * <script type="text/javascript">
33613  */
33614
33615
33616 /**
33617  * @class Roo.bootstrap.SplitBar
33618  * @extends Roo.util.Observable
33619  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33620  * <br><br>
33621  * Usage:
33622  * <pre><code>
33623 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33624                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33625 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33626 split.minSize = 100;
33627 split.maxSize = 600;
33628 split.animate = true;
33629 split.on('moved', splitterMoved);
33630 </code></pre>
33631  * @constructor
33632  * Create a new SplitBar
33633  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33634  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33635  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33636  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33637                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33638                         position of the SplitBar).
33639  */
33640 Roo.bootstrap.SplitBar = function(cfg){
33641     
33642     /** @private */
33643     
33644     //{
33645     //  dragElement : elm
33646     //  resizingElement: el,
33647         // optional..
33648     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33649     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33650         // existingProxy ???
33651     //}
33652     
33653     this.el = Roo.get(cfg.dragElement, true);
33654     this.el.dom.unselectable = "on";
33655     /** @private */
33656     this.resizingEl = Roo.get(cfg.resizingElement, true);
33657
33658     /**
33659      * @private
33660      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33661      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33662      * @type Number
33663      */
33664     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33665     
33666     /**
33667      * The minimum size of the resizing element. (Defaults to 0)
33668      * @type Number
33669      */
33670     this.minSize = 0;
33671     
33672     /**
33673      * The maximum size of the resizing element. (Defaults to 2000)
33674      * @type Number
33675      */
33676     this.maxSize = 2000;
33677     
33678     /**
33679      * Whether to animate the transition to the new size
33680      * @type Boolean
33681      */
33682     this.animate = false;
33683     
33684     /**
33685      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33686      * @type Boolean
33687      */
33688     this.useShim = false;
33689     
33690     /** @private */
33691     this.shim = null;
33692     
33693     if(!cfg.existingProxy){
33694         /** @private */
33695         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33696     }else{
33697         this.proxy = Roo.get(cfg.existingProxy).dom;
33698     }
33699     /** @private */
33700     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33701     
33702     /** @private */
33703     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33704     
33705     /** @private */
33706     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33707     
33708     /** @private */
33709     this.dragSpecs = {};
33710     
33711     /**
33712      * @private The adapter to use to positon and resize elements
33713      */
33714     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33715     this.adapter.init(this);
33716     
33717     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33718         /** @private */
33719         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33720         this.el.addClass("roo-splitbar-h");
33721     }else{
33722         /** @private */
33723         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33724         this.el.addClass("roo-splitbar-v");
33725     }
33726     
33727     this.addEvents({
33728         /**
33729          * @event resize
33730          * Fires when the splitter is moved (alias for {@link #event-moved})
33731          * @param {Roo.bootstrap.SplitBar} this
33732          * @param {Number} newSize the new width or height
33733          */
33734         "resize" : true,
33735         /**
33736          * @event moved
33737          * Fires when the splitter is moved
33738          * @param {Roo.bootstrap.SplitBar} this
33739          * @param {Number} newSize the new width or height
33740          */
33741         "moved" : true,
33742         /**
33743          * @event beforeresize
33744          * Fires before the splitter is dragged
33745          * @param {Roo.bootstrap.SplitBar} this
33746          */
33747         "beforeresize" : true,
33748
33749         "beforeapply" : true
33750     });
33751
33752     Roo.util.Observable.call(this);
33753 };
33754
33755 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33756     onStartProxyDrag : function(x, y){
33757         this.fireEvent("beforeresize", this);
33758         if(!this.overlay){
33759             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33760             o.unselectable();
33761             o.enableDisplayMode("block");
33762             // all splitbars share the same overlay
33763             Roo.bootstrap.SplitBar.prototype.overlay = o;
33764         }
33765         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33766         this.overlay.show();
33767         Roo.get(this.proxy).setDisplayed("block");
33768         var size = this.adapter.getElementSize(this);
33769         this.activeMinSize = this.getMinimumSize();;
33770         this.activeMaxSize = this.getMaximumSize();;
33771         var c1 = size - this.activeMinSize;
33772         var c2 = Math.max(this.activeMaxSize - size, 0);
33773         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33774             this.dd.resetConstraints();
33775             this.dd.setXConstraint(
33776                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33777                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33778             );
33779             this.dd.setYConstraint(0, 0);
33780         }else{
33781             this.dd.resetConstraints();
33782             this.dd.setXConstraint(0, 0);
33783             this.dd.setYConstraint(
33784                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33785                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33786             );
33787          }
33788         this.dragSpecs.startSize = size;
33789         this.dragSpecs.startPoint = [x, y];
33790         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33791     },
33792     
33793     /** 
33794      * @private Called after the drag operation by the DDProxy
33795      */
33796     onEndProxyDrag : function(e){
33797         Roo.get(this.proxy).setDisplayed(false);
33798         var endPoint = Roo.lib.Event.getXY(e);
33799         if(this.overlay){
33800             this.overlay.hide();
33801         }
33802         var newSize;
33803         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33804             newSize = this.dragSpecs.startSize + 
33805                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33806                     endPoint[0] - this.dragSpecs.startPoint[0] :
33807                     this.dragSpecs.startPoint[0] - endPoint[0]
33808                 );
33809         }else{
33810             newSize = this.dragSpecs.startSize + 
33811                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33812                     endPoint[1] - this.dragSpecs.startPoint[1] :
33813                     this.dragSpecs.startPoint[1] - endPoint[1]
33814                 );
33815         }
33816         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33817         if(newSize != this.dragSpecs.startSize){
33818             if(this.fireEvent('beforeapply', this, newSize) !== false){
33819                 this.adapter.setElementSize(this, newSize);
33820                 this.fireEvent("moved", this, newSize);
33821                 this.fireEvent("resize", this, newSize);
33822             }
33823         }
33824     },
33825     
33826     /**
33827      * Get the adapter this SplitBar uses
33828      * @return The adapter object
33829      */
33830     getAdapter : function(){
33831         return this.adapter;
33832     },
33833     
33834     /**
33835      * Set the adapter this SplitBar uses
33836      * @param {Object} adapter A SplitBar adapter object
33837      */
33838     setAdapter : function(adapter){
33839         this.adapter = adapter;
33840         this.adapter.init(this);
33841     },
33842     
33843     /**
33844      * Gets the minimum size for the resizing element
33845      * @return {Number} The minimum size
33846      */
33847     getMinimumSize : function(){
33848         return this.minSize;
33849     },
33850     
33851     /**
33852      * Sets the minimum size for the resizing element
33853      * @param {Number} minSize The minimum size
33854      */
33855     setMinimumSize : function(minSize){
33856         this.minSize = minSize;
33857     },
33858     
33859     /**
33860      * Gets the maximum size for the resizing element
33861      * @return {Number} The maximum size
33862      */
33863     getMaximumSize : function(){
33864         return this.maxSize;
33865     },
33866     
33867     /**
33868      * Sets the maximum size for the resizing element
33869      * @param {Number} maxSize The maximum size
33870      */
33871     setMaximumSize : function(maxSize){
33872         this.maxSize = maxSize;
33873     },
33874     
33875     /**
33876      * Sets the initialize size for the resizing element
33877      * @param {Number} size The initial size
33878      */
33879     setCurrentSize : function(size){
33880         var oldAnimate = this.animate;
33881         this.animate = false;
33882         this.adapter.setElementSize(this, size);
33883         this.animate = oldAnimate;
33884     },
33885     
33886     /**
33887      * Destroy this splitbar. 
33888      * @param {Boolean} removeEl True to remove the element
33889      */
33890     destroy : function(removeEl){
33891         if(this.shim){
33892             this.shim.remove();
33893         }
33894         this.dd.unreg();
33895         this.proxy.parentNode.removeChild(this.proxy);
33896         if(removeEl){
33897             this.el.remove();
33898         }
33899     }
33900 });
33901
33902 /**
33903  * @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.
33904  */
33905 Roo.bootstrap.SplitBar.createProxy = function(dir){
33906     var proxy = new Roo.Element(document.createElement("div"));
33907     proxy.unselectable();
33908     var cls = 'roo-splitbar-proxy';
33909     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33910     document.body.appendChild(proxy.dom);
33911     return proxy.dom;
33912 };
33913
33914 /** 
33915  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33916  * Default Adapter. It assumes the splitter and resizing element are not positioned
33917  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33918  */
33919 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33920 };
33921
33922 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33923     // do nothing for now
33924     init : function(s){
33925     
33926     },
33927     /**
33928      * Called before drag operations to get the current size of the resizing element. 
33929      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33930      */
33931      getElementSize : function(s){
33932         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33933             return s.resizingEl.getWidth();
33934         }else{
33935             return s.resizingEl.getHeight();
33936         }
33937     },
33938     
33939     /**
33940      * Called after drag operations to set the size of the resizing element.
33941      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33942      * @param {Number} newSize The new size to set
33943      * @param {Function} onComplete A function to be invoked when resizing is complete
33944      */
33945     setElementSize : function(s, newSize, onComplete){
33946         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33947             if(!s.animate){
33948                 s.resizingEl.setWidth(newSize);
33949                 if(onComplete){
33950                     onComplete(s, newSize);
33951                 }
33952             }else{
33953                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33954             }
33955         }else{
33956             
33957             if(!s.animate){
33958                 s.resizingEl.setHeight(newSize);
33959                 if(onComplete){
33960                     onComplete(s, newSize);
33961                 }
33962             }else{
33963                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33964             }
33965         }
33966     }
33967 };
33968
33969 /** 
33970  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33971  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33972  * Adapter that  moves the splitter element to align with the resized sizing element. 
33973  * Used with an absolute positioned SplitBar.
33974  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33975  * document.body, make sure you assign an id to the body element.
33976  */
33977 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33978     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33979     this.container = Roo.get(container);
33980 };
33981
33982 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33983     init : function(s){
33984         this.basic.init(s);
33985     },
33986     
33987     getElementSize : function(s){
33988         return this.basic.getElementSize(s);
33989     },
33990     
33991     setElementSize : function(s, newSize, onComplete){
33992         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33993     },
33994     
33995     moveSplitter : function(s){
33996         var yes = Roo.bootstrap.SplitBar;
33997         switch(s.placement){
33998             case yes.LEFT:
33999                 s.el.setX(s.resizingEl.getRight());
34000                 break;
34001             case yes.RIGHT:
34002                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34003                 break;
34004             case yes.TOP:
34005                 s.el.setY(s.resizingEl.getBottom());
34006                 break;
34007             case yes.BOTTOM:
34008                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34009                 break;
34010         }
34011     }
34012 };
34013
34014 /**
34015  * Orientation constant - Create a vertical SplitBar
34016  * @static
34017  * @type Number
34018  */
34019 Roo.bootstrap.SplitBar.VERTICAL = 1;
34020
34021 /**
34022  * Orientation constant - Create a horizontal SplitBar
34023  * @static
34024  * @type Number
34025  */
34026 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34027
34028 /**
34029  * Placement constant - The resizing element is to the left of the splitter element
34030  * @static
34031  * @type Number
34032  */
34033 Roo.bootstrap.SplitBar.LEFT = 1;
34034
34035 /**
34036  * Placement constant - The resizing element is to the right of the splitter element
34037  * @static
34038  * @type Number
34039  */
34040 Roo.bootstrap.SplitBar.RIGHT = 2;
34041
34042 /**
34043  * Placement constant - The resizing element is positioned above the splitter element
34044  * @static
34045  * @type Number
34046  */
34047 Roo.bootstrap.SplitBar.TOP = 3;
34048
34049 /**
34050  * Placement constant - The resizing element is positioned under splitter element
34051  * @static
34052  * @type Number
34053  */
34054 Roo.bootstrap.SplitBar.BOTTOM = 4;
34055 Roo.namespace("Roo.bootstrap.layout");/*
34056  * Based on:
34057  * Ext JS Library 1.1.1
34058  * Copyright(c) 2006-2007, Ext JS, LLC.
34059  *
34060  * Originally Released Under LGPL - original licence link has changed is not relivant.
34061  *
34062  * Fork - LGPL
34063  * <script type="text/javascript">
34064  */
34065
34066 /**
34067  * @class Roo.bootstrap.layout.Manager
34068  * @extends Roo.bootstrap.Component
34069  * Base class for layout managers.
34070  */
34071 Roo.bootstrap.layout.Manager = function(config)
34072 {
34073     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34074
34075
34076
34077
34078
34079     /** false to disable window resize monitoring @type Boolean */
34080     this.monitorWindowResize = true;
34081     this.regions = {};
34082     this.addEvents({
34083         /**
34084          * @event layout
34085          * Fires when a layout is performed.
34086          * @param {Roo.LayoutManager} this
34087          */
34088         "layout" : true,
34089         /**
34090          * @event regionresized
34091          * Fires when the user resizes a region.
34092          * @param {Roo.LayoutRegion} region The resized region
34093          * @param {Number} newSize The new size (width for east/west, height for north/south)
34094          */
34095         "regionresized" : true,
34096         /**
34097          * @event regioncollapsed
34098          * Fires when a region is collapsed.
34099          * @param {Roo.LayoutRegion} region The collapsed region
34100          */
34101         "regioncollapsed" : true,
34102         /**
34103          * @event regionexpanded
34104          * Fires when a region is expanded.
34105          * @param {Roo.LayoutRegion} region The expanded region
34106          */
34107         "regionexpanded" : true
34108     });
34109     this.updating = false;
34110
34111     if (config.el) {
34112         this.el = Roo.get(config.el);
34113         this.initEvents();
34114     }
34115
34116 };
34117
34118 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34119
34120
34121     regions : null,
34122
34123     monitorWindowResize : true,
34124
34125
34126     updating : false,
34127
34128
34129     onRender : function(ct, position)
34130     {
34131         if(!this.el){
34132             this.el = Roo.get(ct);
34133             this.initEvents();
34134         }
34135         //this.fireEvent('render',this);
34136     },
34137
34138
34139     initEvents: function()
34140     {
34141
34142
34143         // ie scrollbar fix
34144         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34145             document.body.scroll = "no";
34146         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34147             this.el.position('relative');
34148         }
34149         this.id = this.el.id;
34150         this.el.addClass("roo-layout-container");
34151         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34152         if(this.el.dom != document.body ) {
34153             this.el.on('resize', this.layout,this);
34154             this.el.on('show', this.layout,this);
34155         }
34156
34157     },
34158
34159     /**
34160      * Returns true if this layout is currently being updated
34161      * @return {Boolean}
34162      */
34163     isUpdating : function(){
34164         return this.updating;
34165     },
34166
34167     /**
34168      * Suspend the LayoutManager from doing auto-layouts while
34169      * making multiple add or remove calls
34170      */
34171     beginUpdate : function(){
34172         this.updating = true;
34173     },
34174
34175     /**
34176      * Restore auto-layouts and optionally disable the manager from performing a layout
34177      * @param {Boolean} noLayout true to disable a layout update
34178      */
34179     endUpdate : function(noLayout){
34180         this.updating = false;
34181         if(!noLayout){
34182             this.layout();
34183         }
34184     },
34185
34186     layout: function(){
34187         // abstract...
34188     },
34189
34190     onRegionResized : function(region, newSize){
34191         this.fireEvent("regionresized", region, newSize);
34192         this.layout();
34193     },
34194
34195     onRegionCollapsed : function(region){
34196         this.fireEvent("regioncollapsed", region);
34197     },
34198
34199     onRegionExpanded : function(region){
34200         this.fireEvent("regionexpanded", region);
34201     },
34202
34203     /**
34204      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34205      * performs box-model adjustments.
34206      * @return {Object} The size as an object {width: (the width), height: (the height)}
34207      */
34208     getViewSize : function()
34209     {
34210         var size;
34211         if(this.el.dom != document.body){
34212             size = this.el.getSize();
34213         }else{
34214             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34215         }
34216         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34217         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34218         return size;
34219     },
34220
34221     /**
34222      * Returns the Element this layout is bound to.
34223      * @return {Roo.Element}
34224      */
34225     getEl : function(){
34226         return this.el;
34227     },
34228
34229     /**
34230      * Returns the specified region.
34231      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34232      * @return {Roo.LayoutRegion}
34233      */
34234     getRegion : function(target){
34235         return this.regions[target.toLowerCase()];
34236     },
34237
34238     onWindowResize : function(){
34239         if(this.monitorWindowResize){
34240             this.layout();
34241         }
34242     }
34243 });
34244 /*
34245  * Based on:
34246  * Ext JS Library 1.1.1
34247  * Copyright(c) 2006-2007, Ext JS, LLC.
34248  *
34249  * Originally Released Under LGPL - original licence link has changed is not relivant.
34250  *
34251  * Fork - LGPL
34252  * <script type="text/javascript">
34253  */
34254 /**
34255  * @class Roo.bootstrap.layout.Border
34256  * @extends Roo.bootstrap.layout.Manager
34257  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34258  * please see: examples/bootstrap/nested.html<br><br>
34259  
34260 <b>The container the layout is rendered into can be either the body element or any other element.
34261 If it is not the body element, the container needs to either be an absolute positioned element,
34262 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34263 the container size if it is not the body element.</b>
34264
34265 * @constructor
34266 * Create a new Border
34267 * @param {Object} config Configuration options
34268  */
34269 Roo.bootstrap.layout.Border = function(config){
34270     config = config || {};
34271     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34272     
34273     
34274     
34275     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34276         if(config[region]){
34277             config[region].region = region;
34278             this.addRegion(config[region]);
34279         }
34280     },this);
34281     
34282 };
34283
34284 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34285
34286 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34287     /**
34288      * Creates and adds a new region if it doesn't already exist.
34289      * @param {String} target The target region key (north, south, east, west or center).
34290      * @param {Object} config The regions config object
34291      * @return {BorderLayoutRegion} The new region
34292      */
34293     addRegion : function(config)
34294     {
34295         if(!this.regions[config.region]){
34296             var r = this.factory(config);
34297             this.bindRegion(r);
34298         }
34299         return this.regions[config.region];
34300     },
34301
34302     // private (kinda)
34303     bindRegion : function(r){
34304         this.regions[r.config.region] = r;
34305         
34306         r.on("visibilitychange",    this.layout, this);
34307         r.on("paneladded",          this.layout, this);
34308         r.on("panelremoved",        this.layout, this);
34309         r.on("invalidated",         this.layout, this);
34310         r.on("resized",             this.onRegionResized, this);
34311         r.on("collapsed",           this.onRegionCollapsed, this);
34312         r.on("expanded",            this.onRegionExpanded, this);
34313     },
34314
34315     /**
34316      * Performs a layout update.
34317      */
34318     layout : function()
34319     {
34320         if(this.updating) {
34321             return;
34322         }
34323         
34324         // render all the rebions if they have not been done alreayd?
34325         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34326             if(this.regions[region] && !this.regions[region].bodyEl){
34327                 this.regions[region].onRender(this.el)
34328             }
34329         },this);
34330         
34331         var size = this.getViewSize();
34332         var w = size.width;
34333         var h = size.height;
34334         var centerW = w;
34335         var centerH = h;
34336         var centerY = 0;
34337         var centerX = 0;
34338         //var x = 0, y = 0;
34339
34340         var rs = this.regions;
34341         var north = rs["north"];
34342         var south = rs["south"]; 
34343         var west = rs["west"];
34344         var east = rs["east"];
34345         var center = rs["center"];
34346         //if(this.hideOnLayout){ // not supported anymore
34347             //c.el.setStyle("display", "none");
34348         //}
34349         if(north && north.isVisible()){
34350             var b = north.getBox();
34351             var m = north.getMargins();
34352             b.width = w - (m.left+m.right);
34353             b.x = m.left;
34354             b.y = m.top;
34355             centerY = b.height + b.y + m.bottom;
34356             centerH -= centerY;
34357             north.updateBox(this.safeBox(b));
34358         }
34359         if(south && south.isVisible()){
34360             var b = south.getBox();
34361             var m = south.getMargins();
34362             b.width = w - (m.left+m.right);
34363             b.x = m.left;
34364             var totalHeight = (b.height + m.top + m.bottom);
34365             b.y = h - totalHeight + m.top;
34366             centerH -= totalHeight;
34367             south.updateBox(this.safeBox(b));
34368         }
34369         if(west && west.isVisible()){
34370             var b = west.getBox();
34371             var m = west.getMargins();
34372             b.height = centerH - (m.top+m.bottom);
34373             b.x = m.left;
34374             b.y = centerY + m.top;
34375             var totalWidth = (b.width + m.left + m.right);
34376             centerX += totalWidth;
34377             centerW -= totalWidth;
34378             west.updateBox(this.safeBox(b));
34379         }
34380         if(east && east.isVisible()){
34381             var b = east.getBox();
34382             var m = east.getMargins();
34383             b.height = centerH - (m.top+m.bottom);
34384             var totalWidth = (b.width + m.left + m.right);
34385             b.x = w - totalWidth + m.left;
34386             b.y = centerY + m.top;
34387             centerW -= totalWidth;
34388             east.updateBox(this.safeBox(b));
34389         }
34390         if(center){
34391             var m = center.getMargins();
34392             var centerBox = {
34393                 x: centerX + m.left,
34394                 y: centerY + m.top,
34395                 width: centerW - (m.left+m.right),
34396                 height: centerH - (m.top+m.bottom)
34397             };
34398             //if(this.hideOnLayout){
34399                 //center.el.setStyle("display", "block");
34400             //}
34401             center.updateBox(this.safeBox(centerBox));
34402         }
34403         this.el.repaint();
34404         this.fireEvent("layout", this);
34405     },
34406
34407     // private
34408     safeBox : function(box){
34409         box.width = Math.max(0, box.width);
34410         box.height = Math.max(0, box.height);
34411         return box;
34412     },
34413
34414     /**
34415      * Adds a ContentPanel (or subclass) to this layout.
34416      * @param {String} target The target region key (north, south, east, west or center).
34417      * @param {Roo.ContentPanel} panel The panel to add
34418      * @return {Roo.ContentPanel} The added panel
34419      */
34420     add : function(target, panel){
34421          
34422         target = target.toLowerCase();
34423         return this.regions[target].add(panel);
34424     },
34425
34426     /**
34427      * Remove a ContentPanel (or subclass) to this layout.
34428      * @param {String} target The target region key (north, south, east, west or center).
34429      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34430      * @return {Roo.ContentPanel} The removed panel
34431      */
34432     remove : function(target, panel){
34433         target = target.toLowerCase();
34434         return this.regions[target].remove(panel);
34435     },
34436
34437     /**
34438      * Searches all regions for a panel with the specified id
34439      * @param {String} panelId
34440      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34441      */
34442     findPanel : function(panelId){
34443         var rs = this.regions;
34444         for(var target in rs){
34445             if(typeof rs[target] != "function"){
34446                 var p = rs[target].getPanel(panelId);
34447                 if(p){
34448                     return p;
34449                 }
34450             }
34451         }
34452         return null;
34453     },
34454
34455     /**
34456      * Searches all regions for a panel with the specified id and activates (shows) it.
34457      * @param {String/ContentPanel} panelId The panels id or the panel itself
34458      * @return {Roo.ContentPanel} The shown panel or null
34459      */
34460     showPanel : function(panelId) {
34461       var rs = this.regions;
34462       for(var target in rs){
34463          var r = rs[target];
34464          if(typeof r != "function"){
34465             if(r.hasPanel(panelId)){
34466                return r.showPanel(panelId);
34467             }
34468          }
34469       }
34470       return null;
34471    },
34472
34473    /**
34474      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34475      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34476      */
34477    /*
34478     restoreState : function(provider){
34479         if(!provider){
34480             provider = Roo.state.Manager;
34481         }
34482         var sm = new Roo.LayoutStateManager();
34483         sm.init(this, provider);
34484     },
34485 */
34486  
34487  
34488     /**
34489      * Adds a xtype elements to the layout.
34490      * <pre><code>
34491
34492 layout.addxtype({
34493        xtype : 'ContentPanel',
34494        region: 'west',
34495        items: [ .... ]
34496    }
34497 );
34498
34499 layout.addxtype({
34500         xtype : 'NestedLayoutPanel',
34501         region: 'west',
34502         layout: {
34503            center: { },
34504            west: { }   
34505         },
34506         items : [ ... list of content panels or nested layout panels.. ]
34507    }
34508 );
34509 </code></pre>
34510      * @param {Object} cfg Xtype definition of item to add.
34511      */
34512     addxtype : function(cfg)
34513     {
34514         // basically accepts a pannel...
34515         // can accept a layout region..!?!?
34516         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34517         
34518         
34519         // theory?  children can only be panels??
34520         
34521         //if (!cfg.xtype.match(/Panel$/)) {
34522         //    return false;
34523         //}
34524         var ret = false;
34525         
34526         if (typeof(cfg.region) == 'undefined') {
34527             Roo.log("Failed to add Panel, region was not set");
34528             Roo.log(cfg);
34529             return false;
34530         }
34531         var region = cfg.region;
34532         delete cfg.region;
34533         
34534           
34535         var xitems = [];
34536         if (cfg.items) {
34537             xitems = cfg.items;
34538             delete cfg.items;
34539         }
34540         var nb = false;
34541         
34542         switch(cfg.xtype) 
34543         {
34544             case 'Content':  // ContentPanel (el, cfg)
34545             case 'Scroll':  // ContentPanel (el, cfg)
34546             case 'View': 
34547                 cfg.autoCreate = true;
34548                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34549                 //} else {
34550                 //    var el = this.el.createChild();
34551                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34552                 //}
34553                 
34554                 this.add(region, ret);
34555                 break;
34556             
34557             /*
34558             case 'TreePanel': // our new panel!
34559                 cfg.el = this.el.createChild();
34560                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34561                 this.add(region, ret);
34562                 break;
34563             */
34564             
34565             case 'Nest': 
34566                 // create a new Layout (which is  a Border Layout...
34567                 
34568                 var clayout = cfg.layout;
34569                 clayout.el  = this.el.createChild();
34570                 clayout.items   = clayout.items  || [];
34571                 
34572                 delete cfg.layout;
34573                 
34574                 // replace this exitems with the clayout ones..
34575                 xitems = clayout.items;
34576                  
34577                 // force background off if it's in center...
34578                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34579                     cfg.background = false;
34580                 }
34581                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34582                 
34583                 
34584                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34585                 //console.log('adding nested layout panel '  + cfg.toSource());
34586                 this.add(region, ret);
34587                 nb = {}; /// find first...
34588                 break;
34589             
34590             case 'Grid':
34591                 
34592                 // needs grid and region
34593                 
34594                 //var el = this.getRegion(region).el.createChild();
34595                 /*
34596                  *var el = this.el.createChild();
34597                 // create the grid first...
34598                 cfg.grid.container = el;
34599                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34600                 */
34601                 
34602                 if (region == 'center' && this.active ) {
34603                     cfg.background = false;
34604                 }
34605                 
34606                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34607                 
34608                 this.add(region, ret);
34609                 /*
34610                 if (cfg.background) {
34611                     // render grid on panel activation (if panel background)
34612                     ret.on('activate', function(gp) {
34613                         if (!gp.grid.rendered) {
34614                     //        gp.grid.render(el);
34615                         }
34616                     });
34617                 } else {
34618                   //  cfg.grid.render(el);
34619                 }
34620                 */
34621                 break;
34622            
34623            
34624             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34625                 // it was the old xcomponent building that caused this before.
34626                 // espeically if border is the top element in the tree.
34627                 ret = this;
34628                 break; 
34629                 
34630                     
34631                 
34632                 
34633                 
34634             default:
34635                 /*
34636                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34637                     
34638                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34639                     this.add(region, ret);
34640                 } else {
34641                 */
34642                     Roo.log(cfg);
34643                     throw "Can not add '" + cfg.xtype + "' to Border";
34644                     return null;
34645              
34646                                 
34647              
34648         }
34649         this.beginUpdate();
34650         // add children..
34651         var region = '';
34652         var abn = {};
34653         Roo.each(xitems, function(i)  {
34654             region = nb && i.region ? i.region : false;
34655             
34656             var add = ret.addxtype(i);
34657            
34658             if (region) {
34659                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34660                 if (!i.background) {
34661                     abn[region] = nb[region] ;
34662                 }
34663             }
34664             
34665         });
34666         this.endUpdate();
34667
34668         // make the last non-background panel active..
34669         //if (nb) { Roo.log(abn); }
34670         if (nb) {
34671             
34672             for(var r in abn) {
34673                 region = this.getRegion(r);
34674                 if (region) {
34675                     // tried using nb[r], but it does not work..
34676                      
34677                     region.showPanel(abn[r]);
34678                    
34679                 }
34680             }
34681         }
34682         return ret;
34683         
34684     },
34685     
34686     
34687 // private
34688     factory : function(cfg)
34689     {
34690         
34691         var validRegions = Roo.bootstrap.layout.Border.regions;
34692
34693         var target = cfg.region;
34694         cfg.mgr = this;
34695         
34696         var r = Roo.bootstrap.layout;
34697         Roo.log(target);
34698         switch(target){
34699             case "north":
34700                 return new r.North(cfg);
34701             case "south":
34702                 return new r.South(cfg);
34703             case "east":
34704                 return new r.East(cfg);
34705             case "west":
34706                 return new r.West(cfg);
34707             case "center":
34708                 return new r.Center(cfg);
34709         }
34710         throw 'Layout region "'+target+'" not supported.';
34711     }
34712     
34713     
34714 });
34715  /*
34716  * Based on:
34717  * Ext JS Library 1.1.1
34718  * Copyright(c) 2006-2007, Ext JS, LLC.
34719  *
34720  * Originally Released Under LGPL - original licence link has changed is not relivant.
34721  *
34722  * Fork - LGPL
34723  * <script type="text/javascript">
34724  */
34725  
34726 /**
34727  * @class Roo.bootstrap.layout.Basic
34728  * @extends Roo.util.Observable
34729  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34730  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34731  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34732  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34733  * @cfg {string}   region  the region that it inhabits..
34734  * @cfg {bool}   skipConfig skip config?
34735  * 
34736
34737  */
34738 Roo.bootstrap.layout.Basic = function(config){
34739     
34740     this.mgr = config.mgr;
34741     
34742     this.position = config.region;
34743     
34744     var skipConfig = config.skipConfig;
34745     
34746     this.events = {
34747         /**
34748          * @scope Roo.BasicLayoutRegion
34749          */
34750         
34751         /**
34752          * @event beforeremove
34753          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34754          * @param {Roo.LayoutRegion} this
34755          * @param {Roo.ContentPanel} panel The panel
34756          * @param {Object} e The cancel event object
34757          */
34758         "beforeremove" : true,
34759         /**
34760          * @event invalidated
34761          * Fires when the layout for this region is changed.
34762          * @param {Roo.LayoutRegion} this
34763          */
34764         "invalidated" : true,
34765         /**
34766          * @event visibilitychange
34767          * Fires when this region is shown or hidden 
34768          * @param {Roo.LayoutRegion} this
34769          * @param {Boolean} visibility true or false
34770          */
34771         "visibilitychange" : true,
34772         /**
34773          * @event paneladded
34774          * Fires when a panel is added. 
34775          * @param {Roo.LayoutRegion} this
34776          * @param {Roo.ContentPanel} panel The panel
34777          */
34778         "paneladded" : true,
34779         /**
34780          * @event panelremoved
34781          * Fires when a panel is removed. 
34782          * @param {Roo.LayoutRegion} this
34783          * @param {Roo.ContentPanel} panel The panel
34784          */
34785         "panelremoved" : true,
34786         /**
34787          * @event beforecollapse
34788          * Fires when this region before collapse.
34789          * @param {Roo.LayoutRegion} this
34790          */
34791         "beforecollapse" : true,
34792         /**
34793          * @event collapsed
34794          * Fires when this region is collapsed.
34795          * @param {Roo.LayoutRegion} this
34796          */
34797         "collapsed" : true,
34798         /**
34799          * @event expanded
34800          * Fires when this region is expanded.
34801          * @param {Roo.LayoutRegion} this
34802          */
34803         "expanded" : true,
34804         /**
34805          * @event slideshow
34806          * Fires when this region is slid into view.
34807          * @param {Roo.LayoutRegion} this
34808          */
34809         "slideshow" : true,
34810         /**
34811          * @event slidehide
34812          * Fires when this region slides out of view. 
34813          * @param {Roo.LayoutRegion} this
34814          */
34815         "slidehide" : true,
34816         /**
34817          * @event panelactivated
34818          * Fires when a panel is activated. 
34819          * @param {Roo.LayoutRegion} this
34820          * @param {Roo.ContentPanel} panel The activated panel
34821          */
34822         "panelactivated" : true,
34823         /**
34824          * @event resized
34825          * Fires when the user resizes this region. 
34826          * @param {Roo.LayoutRegion} this
34827          * @param {Number} newSize The new size (width for east/west, height for north/south)
34828          */
34829         "resized" : true
34830     };
34831     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34832     this.panels = new Roo.util.MixedCollection();
34833     this.panels.getKey = this.getPanelId.createDelegate(this);
34834     this.box = null;
34835     this.activePanel = null;
34836     // ensure listeners are added...
34837     
34838     if (config.listeners || config.events) {
34839         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34840             listeners : config.listeners || {},
34841             events : config.events || {}
34842         });
34843     }
34844     
34845     if(skipConfig !== true){
34846         this.applyConfig(config);
34847     }
34848 };
34849
34850 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34851 {
34852     getPanelId : function(p){
34853         return p.getId();
34854     },
34855     
34856     applyConfig : function(config){
34857         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34858         this.config = config;
34859         
34860     },
34861     
34862     /**
34863      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34864      * the width, for horizontal (north, south) the height.
34865      * @param {Number} newSize The new width or height
34866      */
34867     resizeTo : function(newSize){
34868         var el = this.el ? this.el :
34869                  (this.activePanel ? this.activePanel.getEl() : null);
34870         if(el){
34871             switch(this.position){
34872                 case "east":
34873                 case "west":
34874                     el.setWidth(newSize);
34875                     this.fireEvent("resized", this, newSize);
34876                 break;
34877                 case "north":
34878                 case "south":
34879                     el.setHeight(newSize);
34880                     this.fireEvent("resized", this, newSize);
34881                 break;                
34882             }
34883         }
34884     },
34885     
34886     getBox : function(){
34887         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34888     },
34889     
34890     getMargins : function(){
34891         return this.margins;
34892     },
34893     
34894     updateBox : function(box){
34895         this.box = box;
34896         var el = this.activePanel.getEl();
34897         el.dom.style.left = box.x + "px";
34898         el.dom.style.top = box.y + "px";
34899         this.activePanel.setSize(box.width, box.height);
34900     },
34901     
34902     /**
34903      * Returns the container element for this region.
34904      * @return {Roo.Element}
34905      */
34906     getEl : function(){
34907         return this.activePanel;
34908     },
34909     
34910     /**
34911      * Returns true if this region is currently visible.
34912      * @return {Boolean}
34913      */
34914     isVisible : function(){
34915         return this.activePanel ? true : false;
34916     },
34917     
34918     setActivePanel : function(panel){
34919         panel = this.getPanel(panel);
34920         if(this.activePanel && this.activePanel != panel){
34921             this.activePanel.setActiveState(false);
34922             this.activePanel.getEl().setLeftTop(-10000,-10000);
34923         }
34924         this.activePanel = panel;
34925         panel.setActiveState(true);
34926         if(this.box){
34927             panel.setSize(this.box.width, this.box.height);
34928         }
34929         this.fireEvent("panelactivated", this, panel);
34930         this.fireEvent("invalidated");
34931     },
34932     
34933     /**
34934      * Show the specified panel.
34935      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34936      * @return {Roo.ContentPanel} The shown panel or null
34937      */
34938     showPanel : function(panel){
34939         panel = this.getPanel(panel);
34940         if(panel){
34941             this.setActivePanel(panel);
34942         }
34943         return panel;
34944     },
34945     
34946     /**
34947      * Get the active panel for this region.
34948      * @return {Roo.ContentPanel} The active panel or null
34949      */
34950     getActivePanel : function(){
34951         return this.activePanel;
34952     },
34953     
34954     /**
34955      * Add the passed ContentPanel(s)
34956      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34957      * @return {Roo.ContentPanel} The panel added (if only one was added)
34958      */
34959     add : function(panel){
34960         if(arguments.length > 1){
34961             for(var i = 0, len = arguments.length; i < len; i++) {
34962                 this.add(arguments[i]);
34963             }
34964             return null;
34965         }
34966         if(this.hasPanel(panel)){
34967             this.showPanel(panel);
34968             return panel;
34969         }
34970         var el = panel.getEl();
34971         if(el.dom.parentNode != this.mgr.el.dom){
34972             this.mgr.el.dom.appendChild(el.dom);
34973         }
34974         if(panel.setRegion){
34975             panel.setRegion(this);
34976         }
34977         this.panels.add(panel);
34978         el.setStyle("position", "absolute");
34979         if(!panel.background){
34980             this.setActivePanel(panel);
34981             if(this.config.initialSize && this.panels.getCount()==1){
34982                 this.resizeTo(this.config.initialSize);
34983             }
34984         }
34985         this.fireEvent("paneladded", this, panel);
34986         return panel;
34987     },
34988     
34989     /**
34990      * Returns true if the panel is in this region.
34991      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34992      * @return {Boolean}
34993      */
34994     hasPanel : function(panel){
34995         if(typeof panel == "object"){ // must be panel obj
34996             panel = panel.getId();
34997         }
34998         return this.getPanel(panel) ? true : false;
34999     },
35000     
35001     /**
35002      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35003      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35004      * @param {Boolean} preservePanel Overrides the config preservePanel option
35005      * @return {Roo.ContentPanel} The panel that was removed
35006      */
35007     remove : function(panel, preservePanel){
35008         panel = this.getPanel(panel);
35009         if(!panel){
35010             return null;
35011         }
35012         var e = {};
35013         this.fireEvent("beforeremove", this, panel, e);
35014         if(e.cancel === true){
35015             return null;
35016         }
35017         var panelId = panel.getId();
35018         this.panels.removeKey(panelId);
35019         return panel;
35020     },
35021     
35022     /**
35023      * Returns the panel specified or null if it's not in this region.
35024      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35025      * @return {Roo.ContentPanel}
35026      */
35027     getPanel : function(id){
35028         if(typeof id == "object"){ // must be panel obj
35029             return id;
35030         }
35031         return this.panels.get(id);
35032     },
35033     
35034     /**
35035      * Returns this regions position (north/south/east/west/center).
35036      * @return {String} 
35037      */
35038     getPosition: function(){
35039         return this.position;    
35040     }
35041 });/*
35042  * Based on:
35043  * Ext JS Library 1.1.1
35044  * Copyright(c) 2006-2007, Ext JS, LLC.
35045  *
35046  * Originally Released Under LGPL - original licence link has changed is not relivant.
35047  *
35048  * Fork - LGPL
35049  * <script type="text/javascript">
35050  */
35051  
35052 /**
35053  * @class Roo.bootstrap.layout.Region
35054  * @extends Roo.bootstrap.layout.Basic
35055  * This class represents a region in a layout manager.
35056  
35057  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35058  * @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})
35059  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35060  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35061  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35062  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35063  * @cfg {String}    title           The title for the region (overrides panel titles)
35064  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35065  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35066  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35067  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35068  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35069  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35070  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35071  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35072  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35073  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35074
35075  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35076  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35077  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35078  * @cfg {Number}    width           For East/West panels
35079  * @cfg {Number}    height          For North/South panels
35080  * @cfg {Boolean}   split           To show the splitter
35081  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35082  * 
35083  * @cfg {string}   cls             Extra CSS classes to add to region
35084  * 
35085  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35086  * @cfg {string}   region  the region that it inhabits..
35087  *
35088
35089  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35090  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35091
35092  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35093  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35094  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35095  */
35096 Roo.bootstrap.layout.Region = function(config)
35097 {
35098     this.applyConfig(config);
35099
35100     var mgr = config.mgr;
35101     var pos = config.region;
35102     config.skipConfig = true;
35103     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35104     
35105     if (mgr.el) {
35106         this.onRender(mgr.el);   
35107     }
35108      
35109     this.visible = true;
35110     this.collapsed = false;
35111     this.unrendered_panels = [];
35112 };
35113
35114 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35115
35116     position: '', // set by wrapper (eg. north/south etc..)
35117     unrendered_panels : null,  // unrendered panels.
35118     createBody : function(){
35119         /** This region's body element 
35120         * @type Roo.Element */
35121         this.bodyEl = this.el.createChild({
35122                 tag: "div",
35123                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35124         });
35125     },
35126
35127     onRender: function(ctr, pos)
35128     {
35129         var dh = Roo.DomHelper;
35130         /** This region's container element 
35131         * @type Roo.Element */
35132         this.el = dh.append(ctr.dom, {
35133                 tag: "div",
35134                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35135             }, true);
35136         /** This region's title element 
35137         * @type Roo.Element */
35138     
35139         this.titleEl = dh.append(this.el.dom,
35140             {
35141                     tag: "div",
35142                     unselectable: "on",
35143                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35144                     children:[
35145                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35146                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35147                     ]}, true);
35148         
35149         this.titleEl.enableDisplayMode();
35150         /** This region's title text element 
35151         * @type HTMLElement */
35152         this.titleTextEl = this.titleEl.dom.firstChild;
35153         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35154         /*
35155         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35156         this.closeBtn.enableDisplayMode();
35157         this.closeBtn.on("click", this.closeClicked, this);
35158         this.closeBtn.hide();
35159     */
35160         this.createBody(this.config);
35161         if(this.config.hideWhenEmpty){
35162             this.hide();
35163             this.on("paneladded", this.validateVisibility, this);
35164             this.on("panelremoved", this.validateVisibility, this);
35165         }
35166         if(this.autoScroll){
35167             this.bodyEl.setStyle("overflow", "auto");
35168         }else{
35169             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35170         }
35171         //if(c.titlebar !== false){
35172             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35173                 this.titleEl.hide();
35174             }else{
35175                 this.titleEl.show();
35176                 if(this.config.title){
35177                     this.titleTextEl.innerHTML = this.config.title;
35178                 }
35179             }
35180         //}
35181         if(this.config.collapsed){
35182             this.collapse(true);
35183         }
35184         if(this.config.hidden){
35185             this.hide();
35186         }
35187         
35188         if (this.unrendered_panels && this.unrendered_panels.length) {
35189             for (var i =0;i< this.unrendered_panels.length; i++) {
35190                 this.add(this.unrendered_panels[i]);
35191             }
35192             this.unrendered_panels = null;
35193             
35194         }
35195         
35196     },
35197     
35198     applyConfig : function(c)
35199     {
35200         /*
35201          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35202             var dh = Roo.DomHelper;
35203             if(c.titlebar !== false){
35204                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35205                 this.collapseBtn.on("click", this.collapse, this);
35206                 this.collapseBtn.enableDisplayMode();
35207                 /*
35208                 if(c.showPin === true || this.showPin){
35209                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35210                     this.stickBtn.enableDisplayMode();
35211                     this.stickBtn.on("click", this.expand, this);
35212                     this.stickBtn.hide();
35213                 }
35214                 
35215             }
35216             */
35217             /** This region's collapsed element
35218             * @type Roo.Element */
35219             /*
35220              *
35221             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35222                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35223             ]}, true);
35224             
35225             if(c.floatable !== false){
35226                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35227                this.collapsedEl.on("click", this.collapseClick, this);
35228             }
35229
35230             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35231                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35232                    id: "message", unselectable: "on", style:{"float":"left"}});
35233                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35234              }
35235             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35236             this.expandBtn.on("click", this.expand, this);
35237             
35238         }
35239         
35240         if(this.collapseBtn){
35241             this.collapseBtn.setVisible(c.collapsible == true);
35242         }
35243         
35244         this.cmargins = c.cmargins || this.cmargins ||
35245                          (this.position == "west" || this.position == "east" ?
35246                              {top: 0, left: 2, right:2, bottom: 0} :
35247                              {top: 2, left: 0, right:0, bottom: 2});
35248         */
35249         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35250         
35251         
35252         this.bottomTabs = c.tabPosition != "top";
35253         
35254         this.autoScroll = c.autoScroll || false;
35255         
35256         
35257        
35258         
35259         this.duration = c.duration || .30;
35260         this.slideDuration = c.slideDuration || .45;
35261         this.config = c;
35262        
35263     },
35264     /**
35265      * Returns true if this region is currently visible.
35266      * @return {Boolean}
35267      */
35268     isVisible : function(){
35269         return this.visible;
35270     },
35271
35272     /**
35273      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35274      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35275      */
35276     //setCollapsedTitle : function(title){
35277     //    title = title || "&#160;";
35278      //   if(this.collapsedTitleTextEl){
35279       //      this.collapsedTitleTextEl.innerHTML = title;
35280        // }
35281     //},
35282
35283     getBox : function(){
35284         var b;
35285       //  if(!this.collapsed){
35286             b = this.el.getBox(false, true);
35287        // }else{
35288           //  b = this.collapsedEl.getBox(false, true);
35289         //}
35290         return b;
35291     },
35292
35293     getMargins : function(){
35294         return this.margins;
35295         //return this.collapsed ? this.cmargins : this.margins;
35296     },
35297 /*
35298     highlight : function(){
35299         this.el.addClass("x-layout-panel-dragover");
35300     },
35301
35302     unhighlight : function(){
35303         this.el.removeClass("x-layout-panel-dragover");
35304     },
35305 */
35306     updateBox : function(box)
35307     {
35308         if (!this.bodyEl) {
35309             return; // not rendered yet..
35310         }
35311         
35312         this.box = box;
35313         if(!this.collapsed){
35314             this.el.dom.style.left = box.x + "px";
35315             this.el.dom.style.top = box.y + "px";
35316             this.updateBody(box.width, box.height);
35317         }else{
35318             this.collapsedEl.dom.style.left = box.x + "px";
35319             this.collapsedEl.dom.style.top = box.y + "px";
35320             this.collapsedEl.setSize(box.width, box.height);
35321         }
35322         if(this.tabs){
35323             this.tabs.autoSizeTabs();
35324         }
35325     },
35326
35327     updateBody : function(w, h)
35328     {
35329         if(w !== null){
35330             this.el.setWidth(w);
35331             w -= this.el.getBorderWidth("rl");
35332             if(this.config.adjustments){
35333                 w += this.config.adjustments[0];
35334             }
35335         }
35336         if(h !== null && h > 0){
35337             this.el.setHeight(h);
35338             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35339             h -= this.el.getBorderWidth("tb");
35340             if(this.config.adjustments){
35341                 h += this.config.adjustments[1];
35342             }
35343             this.bodyEl.setHeight(h);
35344             if(this.tabs){
35345                 h = this.tabs.syncHeight(h);
35346             }
35347         }
35348         if(this.panelSize){
35349             w = w !== null ? w : this.panelSize.width;
35350             h = h !== null ? h : this.panelSize.height;
35351         }
35352         if(this.activePanel){
35353             var el = this.activePanel.getEl();
35354             w = w !== null ? w : el.getWidth();
35355             h = h !== null ? h : el.getHeight();
35356             this.panelSize = {width: w, height: h};
35357             this.activePanel.setSize(w, h);
35358         }
35359         if(Roo.isIE && this.tabs){
35360             this.tabs.el.repaint();
35361         }
35362     },
35363
35364     /**
35365      * Returns the container element for this region.
35366      * @return {Roo.Element}
35367      */
35368     getEl : function(){
35369         return this.el;
35370     },
35371
35372     /**
35373      * Hides this region.
35374      */
35375     hide : function(){
35376         //if(!this.collapsed){
35377             this.el.dom.style.left = "-2000px";
35378             this.el.hide();
35379         //}else{
35380          //   this.collapsedEl.dom.style.left = "-2000px";
35381          //   this.collapsedEl.hide();
35382        // }
35383         this.visible = false;
35384         this.fireEvent("visibilitychange", this, false);
35385     },
35386
35387     /**
35388      * Shows this region if it was previously hidden.
35389      */
35390     show : function(){
35391         //if(!this.collapsed){
35392             this.el.show();
35393         //}else{
35394         //    this.collapsedEl.show();
35395        // }
35396         this.visible = true;
35397         this.fireEvent("visibilitychange", this, true);
35398     },
35399 /*
35400     closeClicked : function(){
35401         if(this.activePanel){
35402             this.remove(this.activePanel);
35403         }
35404     },
35405
35406     collapseClick : function(e){
35407         if(this.isSlid){
35408            e.stopPropagation();
35409            this.slideIn();
35410         }else{
35411            e.stopPropagation();
35412            this.slideOut();
35413         }
35414     },
35415 */
35416     /**
35417      * Collapses this region.
35418      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35419      */
35420     /*
35421     collapse : function(skipAnim, skipCheck = false){
35422         if(this.collapsed) {
35423             return;
35424         }
35425         
35426         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35427             
35428             this.collapsed = true;
35429             if(this.split){
35430                 this.split.el.hide();
35431             }
35432             if(this.config.animate && skipAnim !== true){
35433                 this.fireEvent("invalidated", this);
35434                 this.animateCollapse();
35435             }else{
35436                 this.el.setLocation(-20000,-20000);
35437                 this.el.hide();
35438                 this.collapsedEl.show();
35439                 this.fireEvent("collapsed", this);
35440                 this.fireEvent("invalidated", this);
35441             }
35442         }
35443         
35444     },
35445 */
35446     animateCollapse : function(){
35447         // overridden
35448     },
35449
35450     /**
35451      * Expands this region if it was previously collapsed.
35452      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35453      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35454      */
35455     /*
35456     expand : function(e, skipAnim){
35457         if(e) {
35458             e.stopPropagation();
35459         }
35460         if(!this.collapsed || this.el.hasActiveFx()) {
35461             return;
35462         }
35463         if(this.isSlid){
35464             this.afterSlideIn();
35465             skipAnim = true;
35466         }
35467         this.collapsed = false;
35468         if(this.config.animate && skipAnim !== true){
35469             this.animateExpand();
35470         }else{
35471             this.el.show();
35472             if(this.split){
35473                 this.split.el.show();
35474             }
35475             this.collapsedEl.setLocation(-2000,-2000);
35476             this.collapsedEl.hide();
35477             this.fireEvent("invalidated", this);
35478             this.fireEvent("expanded", this);
35479         }
35480     },
35481 */
35482     animateExpand : function(){
35483         // overridden
35484     },
35485
35486     initTabs : function()
35487     {
35488         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35489         
35490         var ts = new Roo.bootstrap.panel.Tabs({
35491                 el: this.bodyEl.dom,
35492                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35493                 disableTooltips: this.config.disableTabTips,
35494                 toolbar : this.config.toolbar
35495             });
35496         
35497         if(this.config.hideTabs){
35498             ts.stripWrap.setDisplayed(false);
35499         }
35500         this.tabs = ts;
35501         ts.resizeTabs = this.config.resizeTabs === true;
35502         ts.minTabWidth = this.config.minTabWidth || 40;
35503         ts.maxTabWidth = this.config.maxTabWidth || 250;
35504         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35505         ts.monitorResize = false;
35506         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35507         ts.bodyEl.addClass('roo-layout-tabs-body');
35508         this.panels.each(this.initPanelAsTab, this);
35509     },
35510
35511     initPanelAsTab : function(panel){
35512         var ti = this.tabs.addTab(
35513             panel.getEl().id,
35514             panel.getTitle(),
35515             null,
35516             this.config.closeOnTab && panel.isClosable(),
35517             panel.tpl
35518         );
35519         if(panel.tabTip !== undefined){
35520             ti.setTooltip(panel.tabTip);
35521         }
35522         ti.on("activate", function(){
35523               this.setActivePanel(panel);
35524         }, this);
35525         
35526         if(this.config.closeOnTab){
35527             ti.on("beforeclose", function(t, e){
35528                 e.cancel = true;
35529                 this.remove(panel);
35530             }, this);
35531         }
35532         
35533         panel.tabItem = ti;
35534         
35535         return ti;
35536     },
35537
35538     updatePanelTitle : function(panel, title)
35539     {
35540         if(this.activePanel == panel){
35541             this.updateTitle(title);
35542         }
35543         if(this.tabs){
35544             var ti = this.tabs.getTab(panel.getEl().id);
35545             ti.setText(title);
35546             if(panel.tabTip !== undefined){
35547                 ti.setTooltip(panel.tabTip);
35548             }
35549         }
35550     },
35551
35552     updateTitle : function(title){
35553         if(this.titleTextEl && !this.config.title){
35554             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35555         }
35556     },
35557
35558     setActivePanel : function(panel)
35559     {
35560         panel = this.getPanel(panel);
35561         if(this.activePanel && this.activePanel != panel){
35562             if(this.activePanel.setActiveState(false) === false){
35563                 return;
35564             }
35565         }
35566         this.activePanel = panel;
35567         panel.setActiveState(true);
35568         if(this.panelSize){
35569             panel.setSize(this.panelSize.width, this.panelSize.height);
35570         }
35571         if(this.closeBtn){
35572             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35573         }
35574         this.updateTitle(panel.getTitle());
35575         if(this.tabs){
35576             this.fireEvent("invalidated", this);
35577         }
35578         this.fireEvent("panelactivated", this, panel);
35579     },
35580
35581     /**
35582      * Shows the specified panel.
35583      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35584      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35585      */
35586     showPanel : function(panel)
35587     {
35588         panel = this.getPanel(panel);
35589         if(panel){
35590             if(this.tabs){
35591                 var tab = this.tabs.getTab(panel.getEl().id);
35592                 if(tab.isHidden()){
35593                     this.tabs.unhideTab(tab.id);
35594                 }
35595                 tab.activate();
35596             }else{
35597                 this.setActivePanel(panel);
35598             }
35599         }
35600         return panel;
35601     },
35602
35603     /**
35604      * Get the active panel for this region.
35605      * @return {Roo.ContentPanel} The active panel or null
35606      */
35607     getActivePanel : function(){
35608         return this.activePanel;
35609     },
35610
35611     validateVisibility : function(){
35612         if(this.panels.getCount() < 1){
35613             this.updateTitle("&#160;");
35614             this.closeBtn.hide();
35615             this.hide();
35616         }else{
35617             if(!this.isVisible()){
35618                 this.show();
35619             }
35620         }
35621     },
35622
35623     /**
35624      * Adds the passed ContentPanel(s) to this region.
35625      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35626      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35627      */
35628     add : function(panel)
35629     {
35630         if(arguments.length > 1){
35631             for(var i = 0, len = arguments.length; i < len; i++) {
35632                 this.add(arguments[i]);
35633             }
35634             return null;
35635         }
35636         
35637         // if we have not been rendered yet, then we can not really do much of this..
35638         if (!this.bodyEl) {
35639             this.unrendered_panels.push(panel);
35640             return panel;
35641         }
35642         
35643         
35644         
35645         
35646         if(this.hasPanel(panel)){
35647             this.showPanel(panel);
35648             return panel;
35649         }
35650         panel.setRegion(this);
35651         this.panels.add(panel);
35652        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35653             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35654             // and hide them... ???
35655             this.bodyEl.dom.appendChild(panel.getEl().dom);
35656             if(panel.background !== true){
35657                 this.setActivePanel(panel);
35658             }
35659             this.fireEvent("paneladded", this, panel);
35660             return panel;
35661         }
35662         */
35663         if(!this.tabs){
35664             this.initTabs();
35665         }else{
35666             this.initPanelAsTab(panel);
35667         }
35668         
35669         
35670         if(panel.background !== true){
35671             this.tabs.activate(panel.getEl().id);
35672         }
35673         this.fireEvent("paneladded", this, panel);
35674         return panel;
35675     },
35676
35677     /**
35678      * Hides the tab for the specified panel.
35679      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35680      */
35681     hidePanel : function(panel){
35682         if(this.tabs && (panel = this.getPanel(panel))){
35683             this.tabs.hideTab(panel.getEl().id);
35684         }
35685     },
35686
35687     /**
35688      * Unhides the tab for a previously hidden panel.
35689      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35690      */
35691     unhidePanel : function(panel){
35692         if(this.tabs && (panel = this.getPanel(panel))){
35693             this.tabs.unhideTab(panel.getEl().id);
35694         }
35695     },
35696
35697     clearPanels : function(){
35698         while(this.panels.getCount() > 0){
35699              this.remove(this.panels.first());
35700         }
35701     },
35702
35703     /**
35704      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35705      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35706      * @param {Boolean} preservePanel Overrides the config preservePanel option
35707      * @return {Roo.ContentPanel} The panel that was removed
35708      */
35709     remove : function(panel, preservePanel)
35710     {
35711         panel = this.getPanel(panel);
35712         if(!panel){
35713             return null;
35714         }
35715         var e = {};
35716         this.fireEvent("beforeremove", this, panel, e);
35717         if(e.cancel === true){
35718             return null;
35719         }
35720         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35721         var panelId = panel.getId();
35722         this.panels.removeKey(panelId);
35723         if(preservePanel){
35724             document.body.appendChild(panel.getEl().dom);
35725         }
35726         if(this.tabs){
35727             this.tabs.removeTab(panel.getEl().id);
35728         }else if (!preservePanel){
35729             this.bodyEl.dom.removeChild(panel.getEl().dom);
35730         }
35731         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35732             var p = this.panels.first();
35733             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35734             tempEl.appendChild(p.getEl().dom);
35735             this.bodyEl.update("");
35736             this.bodyEl.dom.appendChild(p.getEl().dom);
35737             tempEl = null;
35738             this.updateTitle(p.getTitle());
35739             this.tabs = null;
35740             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35741             this.setActivePanel(p);
35742         }
35743         panel.setRegion(null);
35744         if(this.activePanel == panel){
35745             this.activePanel = null;
35746         }
35747         if(this.config.autoDestroy !== false && preservePanel !== true){
35748             try{panel.destroy();}catch(e){}
35749         }
35750         this.fireEvent("panelremoved", this, panel);
35751         return panel;
35752     },
35753
35754     /**
35755      * Returns the TabPanel component used by this region
35756      * @return {Roo.TabPanel}
35757      */
35758     getTabs : function(){
35759         return this.tabs;
35760     },
35761
35762     createTool : function(parentEl, className){
35763         var btn = Roo.DomHelper.append(parentEl, {
35764             tag: "div",
35765             cls: "x-layout-tools-button",
35766             children: [ {
35767                 tag: "div",
35768                 cls: "roo-layout-tools-button-inner " + className,
35769                 html: "&#160;"
35770             }]
35771         }, true);
35772         btn.addClassOnOver("roo-layout-tools-button-over");
35773         return btn;
35774     }
35775 });/*
35776  * Based on:
35777  * Ext JS Library 1.1.1
35778  * Copyright(c) 2006-2007, Ext JS, LLC.
35779  *
35780  * Originally Released Under LGPL - original licence link has changed is not relivant.
35781  *
35782  * Fork - LGPL
35783  * <script type="text/javascript">
35784  */
35785  
35786
35787
35788 /**
35789  * @class Roo.SplitLayoutRegion
35790  * @extends Roo.LayoutRegion
35791  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35792  */
35793 Roo.bootstrap.layout.Split = function(config){
35794     this.cursor = config.cursor;
35795     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35796 };
35797
35798 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35799 {
35800     splitTip : "Drag to resize.",
35801     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35802     useSplitTips : false,
35803
35804     applyConfig : function(config){
35805         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35806     },
35807     
35808     onRender : function(ctr,pos) {
35809         
35810         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35811         if(!this.config.split){
35812             return;
35813         }
35814         if(!this.split){
35815             
35816             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35817                             tag: "div",
35818                             id: this.el.id + "-split",
35819                             cls: "roo-layout-split roo-layout-split-"+this.position,
35820                             html: "&#160;"
35821             });
35822             /** The SplitBar for this region 
35823             * @type Roo.SplitBar */
35824             // does not exist yet...
35825             Roo.log([this.position, this.orientation]);
35826             
35827             this.split = new Roo.bootstrap.SplitBar({
35828                 dragElement : splitEl,
35829                 resizingElement: this.el,
35830                 orientation : this.orientation
35831             });
35832             
35833             this.split.on("moved", this.onSplitMove, this);
35834             this.split.useShim = this.config.useShim === true;
35835             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35836             if(this.useSplitTips){
35837                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35838             }
35839             //if(config.collapsible){
35840             //    this.split.el.on("dblclick", this.collapse,  this);
35841             //}
35842         }
35843         if(typeof this.config.minSize != "undefined"){
35844             this.split.minSize = this.config.minSize;
35845         }
35846         if(typeof this.config.maxSize != "undefined"){
35847             this.split.maxSize = this.config.maxSize;
35848         }
35849         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35850             this.hideSplitter();
35851         }
35852         
35853     },
35854
35855     getHMaxSize : function(){
35856          var cmax = this.config.maxSize || 10000;
35857          var center = this.mgr.getRegion("center");
35858          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35859     },
35860
35861     getVMaxSize : function(){
35862          var cmax = this.config.maxSize || 10000;
35863          var center = this.mgr.getRegion("center");
35864          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35865     },
35866
35867     onSplitMove : function(split, newSize){
35868         this.fireEvent("resized", this, newSize);
35869     },
35870     
35871     /** 
35872      * Returns the {@link Roo.SplitBar} for this region.
35873      * @return {Roo.SplitBar}
35874      */
35875     getSplitBar : function(){
35876         return this.split;
35877     },
35878     
35879     hide : function(){
35880         this.hideSplitter();
35881         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35882     },
35883
35884     hideSplitter : function(){
35885         if(this.split){
35886             this.split.el.setLocation(-2000,-2000);
35887             this.split.el.hide();
35888         }
35889     },
35890
35891     show : function(){
35892         if(this.split){
35893             this.split.el.show();
35894         }
35895         Roo.bootstrap.layout.Split.superclass.show.call(this);
35896     },
35897     
35898     beforeSlide: function(){
35899         if(Roo.isGecko){// firefox overflow auto bug workaround
35900             this.bodyEl.clip();
35901             if(this.tabs) {
35902                 this.tabs.bodyEl.clip();
35903             }
35904             if(this.activePanel){
35905                 this.activePanel.getEl().clip();
35906                 
35907                 if(this.activePanel.beforeSlide){
35908                     this.activePanel.beforeSlide();
35909                 }
35910             }
35911         }
35912     },
35913     
35914     afterSlide : function(){
35915         if(Roo.isGecko){// firefox overflow auto bug workaround
35916             this.bodyEl.unclip();
35917             if(this.tabs) {
35918                 this.tabs.bodyEl.unclip();
35919             }
35920             if(this.activePanel){
35921                 this.activePanel.getEl().unclip();
35922                 if(this.activePanel.afterSlide){
35923                     this.activePanel.afterSlide();
35924                 }
35925             }
35926         }
35927     },
35928
35929     initAutoHide : function(){
35930         if(this.autoHide !== false){
35931             if(!this.autoHideHd){
35932                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35933                 this.autoHideHd = {
35934                     "mouseout": function(e){
35935                         if(!e.within(this.el, true)){
35936                             st.delay(500);
35937                         }
35938                     },
35939                     "mouseover" : function(e){
35940                         st.cancel();
35941                     },
35942                     scope : this
35943                 };
35944             }
35945             this.el.on(this.autoHideHd);
35946         }
35947     },
35948
35949     clearAutoHide : function(){
35950         if(this.autoHide !== false){
35951             this.el.un("mouseout", this.autoHideHd.mouseout);
35952             this.el.un("mouseover", this.autoHideHd.mouseover);
35953         }
35954     },
35955
35956     clearMonitor : function(){
35957         Roo.get(document).un("click", this.slideInIf, this);
35958     },
35959
35960     // these names are backwards but not changed for compat
35961     slideOut : function(){
35962         if(this.isSlid || this.el.hasActiveFx()){
35963             return;
35964         }
35965         this.isSlid = true;
35966         if(this.collapseBtn){
35967             this.collapseBtn.hide();
35968         }
35969         this.closeBtnState = this.closeBtn.getStyle('display');
35970         this.closeBtn.hide();
35971         if(this.stickBtn){
35972             this.stickBtn.show();
35973         }
35974         this.el.show();
35975         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35976         this.beforeSlide();
35977         this.el.setStyle("z-index", 10001);
35978         this.el.slideIn(this.getSlideAnchor(), {
35979             callback: function(){
35980                 this.afterSlide();
35981                 this.initAutoHide();
35982                 Roo.get(document).on("click", this.slideInIf, this);
35983                 this.fireEvent("slideshow", this);
35984             },
35985             scope: this,
35986             block: true
35987         });
35988     },
35989
35990     afterSlideIn : function(){
35991         this.clearAutoHide();
35992         this.isSlid = false;
35993         this.clearMonitor();
35994         this.el.setStyle("z-index", "");
35995         if(this.collapseBtn){
35996             this.collapseBtn.show();
35997         }
35998         this.closeBtn.setStyle('display', this.closeBtnState);
35999         if(this.stickBtn){
36000             this.stickBtn.hide();
36001         }
36002         this.fireEvent("slidehide", this);
36003     },
36004
36005     slideIn : function(cb){
36006         if(!this.isSlid || this.el.hasActiveFx()){
36007             Roo.callback(cb);
36008             return;
36009         }
36010         this.isSlid = false;
36011         this.beforeSlide();
36012         this.el.slideOut(this.getSlideAnchor(), {
36013             callback: function(){
36014                 this.el.setLeftTop(-10000, -10000);
36015                 this.afterSlide();
36016                 this.afterSlideIn();
36017                 Roo.callback(cb);
36018             },
36019             scope: this,
36020             block: true
36021         });
36022     },
36023     
36024     slideInIf : function(e){
36025         if(!e.within(this.el)){
36026             this.slideIn();
36027         }
36028     },
36029
36030     animateCollapse : function(){
36031         this.beforeSlide();
36032         this.el.setStyle("z-index", 20000);
36033         var anchor = this.getSlideAnchor();
36034         this.el.slideOut(anchor, {
36035             callback : function(){
36036                 this.el.setStyle("z-index", "");
36037                 this.collapsedEl.slideIn(anchor, {duration:.3});
36038                 this.afterSlide();
36039                 this.el.setLocation(-10000,-10000);
36040                 this.el.hide();
36041                 this.fireEvent("collapsed", this);
36042             },
36043             scope: this,
36044             block: true
36045         });
36046     },
36047
36048     animateExpand : function(){
36049         this.beforeSlide();
36050         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36051         this.el.setStyle("z-index", 20000);
36052         this.collapsedEl.hide({
36053             duration:.1
36054         });
36055         this.el.slideIn(this.getSlideAnchor(), {
36056             callback : function(){
36057                 this.el.setStyle("z-index", "");
36058                 this.afterSlide();
36059                 if(this.split){
36060                     this.split.el.show();
36061                 }
36062                 this.fireEvent("invalidated", this);
36063                 this.fireEvent("expanded", this);
36064             },
36065             scope: this,
36066             block: true
36067         });
36068     },
36069
36070     anchors : {
36071         "west" : "left",
36072         "east" : "right",
36073         "north" : "top",
36074         "south" : "bottom"
36075     },
36076
36077     sanchors : {
36078         "west" : "l",
36079         "east" : "r",
36080         "north" : "t",
36081         "south" : "b"
36082     },
36083
36084     canchors : {
36085         "west" : "tl-tr",
36086         "east" : "tr-tl",
36087         "north" : "tl-bl",
36088         "south" : "bl-tl"
36089     },
36090
36091     getAnchor : function(){
36092         return this.anchors[this.position];
36093     },
36094
36095     getCollapseAnchor : function(){
36096         return this.canchors[this.position];
36097     },
36098
36099     getSlideAnchor : function(){
36100         return this.sanchors[this.position];
36101     },
36102
36103     getAlignAdj : function(){
36104         var cm = this.cmargins;
36105         switch(this.position){
36106             case "west":
36107                 return [0, 0];
36108             break;
36109             case "east":
36110                 return [0, 0];
36111             break;
36112             case "north":
36113                 return [0, 0];
36114             break;
36115             case "south":
36116                 return [0, 0];
36117             break;
36118         }
36119     },
36120
36121     getExpandAdj : function(){
36122         var c = this.collapsedEl, cm = this.cmargins;
36123         switch(this.position){
36124             case "west":
36125                 return [-(cm.right+c.getWidth()+cm.left), 0];
36126             break;
36127             case "east":
36128                 return [cm.right+c.getWidth()+cm.left, 0];
36129             break;
36130             case "north":
36131                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36132             break;
36133             case "south":
36134                 return [0, cm.top+cm.bottom+c.getHeight()];
36135             break;
36136         }
36137     }
36138 });/*
36139  * Based on:
36140  * Ext JS Library 1.1.1
36141  * Copyright(c) 2006-2007, Ext JS, LLC.
36142  *
36143  * Originally Released Under LGPL - original licence link has changed is not relivant.
36144  *
36145  * Fork - LGPL
36146  * <script type="text/javascript">
36147  */
36148 /*
36149  * These classes are private internal classes
36150  */
36151 Roo.bootstrap.layout.Center = function(config){
36152     config.region = "center";
36153     Roo.bootstrap.layout.Region.call(this, config);
36154     this.visible = true;
36155     this.minWidth = config.minWidth || 20;
36156     this.minHeight = config.minHeight || 20;
36157 };
36158
36159 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36160     hide : function(){
36161         // center panel can't be hidden
36162     },
36163     
36164     show : function(){
36165         // center panel can't be hidden
36166     },
36167     
36168     getMinWidth: function(){
36169         return this.minWidth;
36170     },
36171     
36172     getMinHeight: function(){
36173         return this.minHeight;
36174     }
36175 });
36176
36177
36178
36179
36180  
36181
36182
36183
36184
36185
36186 Roo.bootstrap.layout.North = function(config)
36187 {
36188     config.region = 'north';
36189     config.cursor = 'n-resize';
36190     
36191     Roo.bootstrap.layout.Split.call(this, config);
36192     
36193     
36194     if(this.split){
36195         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36196         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36197         this.split.el.addClass("roo-layout-split-v");
36198     }
36199     var size = config.initialSize || config.height;
36200     if(typeof size != "undefined"){
36201         this.el.setHeight(size);
36202     }
36203 };
36204 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36205 {
36206     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36207     
36208     
36209     
36210     getBox : function(){
36211         if(this.collapsed){
36212             return this.collapsedEl.getBox();
36213         }
36214         var box = this.el.getBox();
36215         if(this.split){
36216             box.height += this.split.el.getHeight();
36217         }
36218         return box;
36219     },
36220     
36221     updateBox : function(box){
36222         if(this.split && !this.collapsed){
36223             box.height -= this.split.el.getHeight();
36224             this.split.el.setLeft(box.x);
36225             this.split.el.setTop(box.y+box.height);
36226             this.split.el.setWidth(box.width);
36227         }
36228         if(this.collapsed){
36229             this.updateBody(box.width, null);
36230         }
36231         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36232     }
36233 });
36234
36235
36236
36237
36238
36239 Roo.bootstrap.layout.South = function(config){
36240     config.region = 'south';
36241     config.cursor = 's-resize';
36242     Roo.bootstrap.layout.Split.call(this, config);
36243     if(this.split){
36244         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36245         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36246         this.split.el.addClass("roo-layout-split-v");
36247     }
36248     var size = config.initialSize || config.height;
36249     if(typeof size != "undefined"){
36250         this.el.setHeight(size);
36251     }
36252 };
36253
36254 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36255     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36256     getBox : function(){
36257         if(this.collapsed){
36258             return this.collapsedEl.getBox();
36259         }
36260         var box = this.el.getBox();
36261         if(this.split){
36262             var sh = this.split.el.getHeight();
36263             box.height += sh;
36264             box.y -= sh;
36265         }
36266         return box;
36267     },
36268     
36269     updateBox : function(box){
36270         if(this.split && !this.collapsed){
36271             var sh = this.split.el.getHeight();
36272             box.height -= sh;
36273             box.y += sh;
36274             this.split.el.setLeft(box.x);
36275             this.split.el.setTop(box.y-sh);
36276             this.split.el.setWidth(box.width);
36277         }
36278         if(this.collapsed){
36279             this.updateBody(box.width, null);
36280         }
36281         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36282     }
36283 });
36284
36285 Roo.bootstrap.layout.East = function(config){
36286     config.region = "east";
36287     config.cursor = "e-resize";
36288     Roo.bootstrap.layout.Split.call(this, config);
36289     if(this.split){
36290         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36291         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36292         this.split.el.addClass("roo-layout-split-h");
36293     }
36294     var size = config.initialSize || config.width;
36295     if(typeof size != "undefined"){
36296         this.el.setWidth(size);
36297     }
36298 };
36299 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36300     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36301     getBox : function(){
36302         if(this.collapsed){
36303             return this.collapsedEl.getBox();
36304         }
36305         var box = this.el.getBox();
36306         if(this.split){
36307             var sw = this.split.el.getWidth();
36308             box.width += sw;
36309             box.x -= sw;
36310         }
36311         return box;
36312     },
36313
36314     updateBox : function(box){
36315         if(this.split && !this.collapsed){
36316             var sw = this.split.el.getWidth();
36317             box.width -= sw;
36318             this.split.el.setLeft(box.x);
36319             this.split.el.setTop(box.y);
36320             this.split.el.setHeight(box.height);
36321             box.x += sw;
36322         }
36323         if(this.collapsed){
36324             this.updateBody(null, box.height);
36325         }
36326         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36327     }
36328 });
36329
36330 Roo.bootstrap.layout.West = function(config){
36331     config.region = "west";
36332     config.cursor = "w-resize";
36333     
36334     Roo.bootstrap.layout.Split.call(this, config);
36335     if(this.split){
36336         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36337         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36338         this.split.el.addClass("roo-layout-split-h");
36339     }
36340     
36341 };
36342 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36343     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36344     
36345     onRender: function(ctr, pos)
36346     {
36347         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36348         var size = this.config.initialSize || this.config.width;
36349         if(typeof size != "undefined"){
36350             this.el.setWidth(size);
36351         }
36352     },
36353     
36354     getBox : function(){
36355         if(this.collapsed){
36356             return this.collapsedEl.getBox();
36357         }
36358         var box = this.el.getBox();
36359         if(this.split){
36360             box.width += this.split.el.getWidth();
36361         }
36362         return box;
36363     },
36364     
36365     updateBox : function(box){
36366         if(this.split && !this.collapsed){
36367             var sw = this.split.el.getWidth();
36368             box.width -= sw;
36369             this.split.el.setLeft(box.x+box.width);
36370             this.split.el.setTop(box.y);
36371             this.split.el.setHeight(box.height);
36372         }
36373         if(this.collapsed){
36374             this.updateBody(null, box.height);
36375         }
36376         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36377     }
36378 });
36379 Roo.namespace("Roo.bootstrap.panel");/*
36380  * Based on:
36381  * Ext JS Library 1.1.1
36382  * Copyright(c) 2006-2007, Ext JS, LLC.
36383  *
36384  * Originally Released Under LGPL - original licence link has changed is not relivant.
36385  *
36386  * Fork - LGPL
36387  * <script type="text/javascript">
36388  */
36389 /**
36390  * @class Roo.ContentPanel
36391  * @extends Roo.util.Observable
36392  * A basic ContentPanel element.
36393  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36394  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36395  * @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
36396  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36397  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36398  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36399  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36400  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36401  * @cfg {String} title          The title for this panel
36402  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36403  * @cfg {String} url            Calls {@link #setUrl} with this value
36404  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36405  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36406  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36407  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36408  * @cfg {Boolean} badges render the badges
36409
36410  * @constructor
36411  * Create a new ContentPanel.
36412  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36413  * @param {String/Object} config A string to set only the title or a config object
36414  * @param {String} content (optional) Set the HTML content for this panel
36415  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36416  */
36417 Roo.bootstrap.panel.Content = function( config){
36418     
36419     this.tpl = config.tpl || false;
36420     
36421     var el = config.el;
36422     var content = config.content;
36423
36424     if(config.autoCreate){ // xtype is available if this is called from factory
36425         el = Roo.id();
36426     }
36427     this.el = Roo.get(el);
36428     if(!this.el && config && config.autoCreate){
36429         if(typeof config.autoCreate == "object"){
36430             if(!config.autoCreate.id){
36431                 config.autoCreate.id = config.id||el;
36432             }
36433             this.el = Roo.DomHelper.append(document.body,
36434                         config.autoCreate, true);
36435         }else{
36436             var elcfg =  {   tag: "div",
36437                             cls: "roo-layout-inactive-content",
36438                             id: config.id||el
36439                             };
36440             if (config.html) {
36441                 elcfg.html = config.html;
36442                 
36443             }
36444                         
36445             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36446         }
36447     } 
36448     this.closable = false;
36449     this.loaded = false;
36450     this.active = false;
36451    
36452       
36453     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36454         
36455         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36456         
36457         this.wrapEl = this.el; //this.el.wrap();
36458         var ti = [];
36459         if (config.toolbar.items) {
36460             ti = config.toolbar.items ;
36461             delete config.toolbar.items ;
36462         }
36463         
36464         var nitems = [];
36465         this.toolbar.render(this.wrapEl, 'before');
36466         for(var i =0;i < ti.length;i++) {
36467           //  Roo.log(['add child', items[i]]);
36468             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36469         }
36470         this.toolbar.items = nitems;
36471         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36472         delete config.toolbar;
36473         
36474     }
36475     /*
36476     // xtype created footer. - not sure if will work as we normally have to render first..
36477     if (this.footer && !this.footer.el && this.footer.xtype) {
36478         if (!this.wrapEl) {
36479             this.wrapEl = this.el.wrap();
36480         }
36481     
36482         this.footer.container = this.wrapEl.createChild();
36483          
36484         this.footer = Roo.factory(this.footer, Roo);
36485         
36486     }
36487     */
36488     
36489      if(typeof config == "string"){
36490         this.title = config;
36491     }else{
36492         Roo.apply(this, config);
36493     }
36494     
36495     if(this.resizeEl){
36496         this.resizeEl = Roo.get(this.resizeEl, true);
36497     }else{
36498         this.resizeEl = this.el;
36499     }
36500     // handle view.xtype
36501     
36502  
36503     
36504     
36505     this.addEvents({
36506         /**
36507          * @event activate
36508          * Fires when this panel is activated. 
36509          * @param {Roo.ContentPanel} this
36510          */
36511         "activate" : true,
36512         /**
36513          * @event deactivate
36514          * Fires when this panel is activated. 
36515          * @param {Roo.ContentPanel} this
36516          */
36517         "deactivate" : true,
36518
36519         /**
36520          * @event resize
36521          * Fires when this panel is resized if fitToFrame is true.
36522          * @param {Roo.ContentPanel} this
36523          * @param {Number} width The width after any component adjustments
36524          * @param {Number} height The height after any component adjustments
36525          */
36526         "resize" : true,
36527         
36528          /**
36529          * @event render
36530          * Fires when this tab is created
36531          * @param {Roo.ContentPanel} this
36532          */
36533         "render" : true
36534         
36535         
36536         
36537     });
36538     
36539
36540     
36541     
36542     if(this.autoScroll){
36543         this.resizeEl.setStyle("overflow", "auto");
36544     } else {
36545         // fix randome scrolling
36546         //this.el.on('scroll', function() {
36547         //    Roo.log('fix random scolling');
36548         //    this.scrollTo('top',0); 
36549         //});
36550     }
36551     content = content || this.content;
36552     if(content){
36553         this.setContent(content);
36554     }
36555     if(config && config.url){
36556         this.setUrl(this.url, this.params, this.loadOnce);
36557     }
36558     
36559     
36560     
36561     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36562     
36563     if (this.view && typeof(this.view.xtype) != 'undefined') {
36564         this.view.el = this.el.appendChild(document.createElement("div"));
36565         this.view = Roo.factory(this.view); 
36566         this.view.render  &&  this.view.render(false, '');  
36567     }
36568     
36569     
36570     this.fireEvent('render', this);
36571 };
36572
36573 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36574     
36575     tabTip : '',
36576     
36577     setRegion : function(region){
36578         this.region = region;
36579         this.setActiveClass(region && !this.background);
36580     },
36581     
36582     
36583     setActiveClass: function(state)
36584     {
36585         if(state){
36586            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36587            this.el.setStyle('position','relative');
36588         }else{
36589            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36590            this.el.setStyle('position', 'absolute');
36591         } 
36592     },
36593     
36594     /**
36595      * Returns the toolbar for this Panel if one was configured. 
36596      * @return {Roo.Toolbar} 
36597      */
36598     getToolbar : function(){
36599         return this.toolbar;
36600     },
36601     
36602     setActiveState : function(active)
36603     {
36604         this.active = active;
36605         this.setActiveClass(active);
36606         if(!active){
36607             if(this.fireEvent("deactivate", this) === false){
36608                 return false;
36609             }
36610             return true;
36611         }
36612         this.fireEvent("activate", this);
36613         return true;
36614     },
36615     /**
36616      * Updates this panel's element
36617      * @param {String} content The new content
36618      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36619     */
36620     setContent : function(content, loadScripts){
36621         this.el.update(content, loadScripts);
36622     },
36623
36624     ignoreResize : function(w, h){
36625         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36626             return true;
36627         }else{
36628             this.lastSize = {width: w, height: h};
36629             return false;
36630         }
36631     },
36632     /**
36633      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36634      * @return {Roo.UpdateManager} The UpdateManager
36635      */
36636     getUpdateManager : function(){
36637         return this.el.getUpdateManager();
36638     },
36639      /**
36640      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36641      * @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:
36642 <pre><code>
36643 panel.load({
36644     url: "your-url.php",
36645     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36646     callback: yourFunction,
36647     scope: yourObject, //(optional scope)
36648     discardUrl: false,
36649     nocache: false,
36650     text: "Loading...",
36651     timeout: 30,
36652     scripts: false
36653 });
36654 </code></pre>
36655      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36656      * 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.
36657      * @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}
36658      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36659      * @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.
36660      * @return {Roo.ContentPanel} this
36661      */
36662     load : function(){
36663         var um = this.el.getUpdateManager();
36664         um.update.apply(um, arguments);
36665         return this;
36666     },
36667
36668
36669     /**
36670      * 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.
36671      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36672      * @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)
36673      * @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)
36674      * @return {Roo.UpdateManager} The UpdateManager
36675      */
36676     setUrl : function(url, params, loadOnce){
36677         if(this.refreshDelegate){
36678             this.removeListener("activate", this.refreshDelegate);
36679         }
36680         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36681         this.on("activate", this.refreshDelegate);
36682         return this.el.getUpdateManager();
36683     },
36684     
36685     _handleRefresh : function(url, params, loadOnce){
36686         if(!loadOnce || !this.loaded){
36687             var updater = this.el.getUpdateManager();
36688             updater.update(url, params, this._setLoaded.createDelegate(this));
36689         }
36690     },
36691     
36692     _setLoaded : function(){
36693         this.loaded = true;
36694     }, 
36695     
36696     /**
36697      * Returns this panel's id
36698      * @return {String} 
36699      */
36700     getId : function(){
36701         return this.el.id;
36702     },
36703     
36704     /** 
36705      * Returns this panel's element - used by regiosn to add.
36706      * @return {Roo.Element} 
36707      */
36708     getEl : function(){
36709         return this.wrapEl || this.el;
36710     },
36711     
36712    
36713     
36714     adjustForComponents : function(width, height)
36715     {
36716         //Roo.log('adjustForComponents ');
36717         if(this.resizeEl != this.el){
36718             width -= this.el.getFrameWidth('lr');
36719             height -= this.el.getFrameWidth('tb');
36720         }
36721         if(this.toolbar){
36722             var te = this.toolbar.getEl();
36723             te.setWidth(width);
36724             height -= te.getHeight();
36725         }
36726         if(this.footer){
36727             var te = this.footer.getEl();
36728             te.setWidth(width);
36729             height -= te.getHeight();
36730         }
36731         
36732         
36733         if(this.adjustments){
36734             width += this.adjustments[0];
36735             height += this.adjustments[1];
36736         }
36737         return {"width": width, "height": height};
36738     },
36739     
36740     setSize : function(width, height){
36741         if(this.fitToFrame && !this.ignoreResize(width, height)){
36742             if(this.fitContainer && this.resizeEl != this.el){
36743                 this.el.setSize(width, height);
36744             }
36745             var size = this.adjustForComponents(width, height);
36746             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36747             this.fireEvent('resize', this, size.width, size.height);
36748         }
36749     },
36750     
36751     /**
36752      * Returns this panel's title
36753      * @return {String} 
36754      */
36755     getTitle : function(){
36756         
36757         if (typeof(this.title) != 'object') {
36758             return this.title;
36759         }
36760         
36761         var t = '';
36762         for (var k in this.title) {
36763             if (!this.title.hasOwnProperty(k)) {
36764                 continue;
36765             }
36766             
36767             if (k.indexOf('-') >= 0) {
36768                 var s = k.split('-');
36769                 for (var i = 0; i<s.length; i++) {
36770                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36771                 }
36772             } else {
36773                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36774             }
36775         }
36776         return t;
36777     },
36778     
36779     /**
36780      * Set this panel's title
36781      * @param {String} title
36782      */
36783     setTitle : function(title){
36784         this.title = title;
36785         if(this.region){
36786             this.region.updatePanelTitle(this, title);
36787         }
36788     },
36789     
36790     /**
36791      * Returns true is this panel was configured to be closable
36792      * @return {Boolean} 
36793      */
36794     isClosable : function(){
36795         return this.closable;
36796     },
36797     
36798     beforeSlide : function(){
36799         this.el.clip();
36800         this.resizeEl.clip();
36801     },
36802     
36803     afterSlide : function(){
36804         this.el.unclip();
36805         this.resizeEl.unclip();
36806     },
36807     
36808     /**
36809      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36810      *   Will fail silently if the {@link #setUrl} method has not been called.
36811      *   This does not activate the panel, just updates its content.
36812      */
36813     refresh : function(){
36814         if(this.refreshDelegate){
36815            this.loaded = false;
36816            this.refreshDelegate();
36817         }
36818     },
36819     
36820     /**
36821      * Destroys this panel
36822      */
36823     destroy : function(){
36824         this.el.removeAllListeners();
36825         var tempEl = document.createElement("span");
36826         tempEl.appendChild(this.el.dom);
36827         tempEl.innerHTML = "";
36828         this.el.remove();
36829         this.el = null;
36830     },
36831     
36832     /**
36833      * form - if the content panel contains a form - this is a reference to it.
36834      * @type {Roo.form.Form}
36835      */
36836     form : false,
36837     /**
36838      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36839      *    This contains a reference to it.
36840      * @type {Roo.View}
36841      */
36842     view : false,
36843     
36844       /**
36845      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36846      * <pre><code>
36847
36848 layout.addxtype({
36849        xtype : 'Form',
36850        items: [ .... ]
36851    }
36852 );
36853
36854 </code></pre>
36855      * @param {Object} cfg Xtype definition of item to add.
36856      */
36857     
36858     
36859     getChildContainer: function () {
36860         return this.getEl();
36861     }
36862     
36863     
36864     /*
36865         var  ret = new Roo.factory(cfg);
36866         return ret;
36867         
36868         
36869         // add form..
36870         if (cfg.xtype.match(/^Form$/)) {
36871             
36872             var el;
36873             //if (this.footer) {
36874             //    el = this.footer.container.insertSibling(false, 'before');
36875             //} else {
36876                 el = this.el.createChild();
36877             //}
36878
36879             this.form = new  Roo.form.Form(cfg);
36880             
36881             
36882             if ( this.form.allItems.length) {
36883                 this.form.render(el.dom);
36884             }
36885             return this.form;
36886         }
36887         // should only have one of theses..
36888         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36889             // views.. should not be just added - used named prop 'view''
36890             
36891             cfg.el = this.el.appendChild(document.createElement("div"));
36892             // factory?
36893             
36894             var ret = new Roo.factory(cfg);
36895              
36896              ret.render && ret.render(false, ''); // render blank..
36897             this.view = ret;
36898             return ret;
36899         }
36900         return false;
36901     }
36902     \*/
36903 });
36904  
36905 /**
36906  * @class Roo.bootstrap.panel.Grid
36907  * @extends Roo.bootstrap.panel.Content
36908  * @constructor
36909  * Create a new GridPanel.
36910  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36911  * @param {Object} config A the config object
36912   
36913  */
36914
36915
36916
36917 Roo.bootstrap.panel.Grid = function(config)
36918 {
36919     
36920       
36921     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36922         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36923
36924     config.el = this.wrapper;
36925     //this.el = this.wrapper;
36926     
36927       if (config.container) {
36928         // ctor'ed from a Border/panel.grid
36929         
36930         
36931         this.wrapper.setStyle("overflow", "hidden");
36932         this.wrapper.addClass('roo-grid-container');
36933
36934     }
36935     
36936     
36937     if(config.toolbar){
36938         var tool_el = this.wrapper.createChild();    
36939         this.toolbar = Roo.factory(config.toolbar);
36940         var ti = [];
36941         if (config.toolbar.items) {
36942             ti = config.toolbar.items ;
36943             delete config.toolbar.items ;
36944         }
36945         
36946         var nitems = [];
36947         this.toolbar.render(tool_el);
36948         for(var i =0;i < ti.length;i++) {
36949           //  Roo.log(['add child', items[i]]);
36950             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36951         }
36952         this.toolbar.items = nitems;
36953         
36954         delete config.toolbar;
36955     }
36956     
36957     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36958     config.grid.scrollBody = true;;
36959     config.grid.monitorWindowResize = false; // turn off autosizing
36960     config.grid.autoHeight = false;
36961     config.grid.autoWidth = false;
36962     
36963     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36964     
36965     if (config.background) {
36966         // render grid on panel activation (if panel background)
36967         this.on('activate', function(gp) {
36968             if (!gp.grid.rendered) {
36969                 gp.grid.render(this.wrapper);
36970                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36971             }
36972         });
36973             
36974     } else {
36975         this.grid.render(this.wrapper);
36976         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36977
36978     }
36979     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36980     // ??? needed ??? config.el = this.wrapper;
36981     
36982     
36983     
36984   
36985     // xtype created footer. - not sure if will work as we normally have to render first..
36986     if (this.footer && !this.footer.el && this.footer.xtype) {
36987         
36988         var ctr = this.grid.getView().getFooterPanel(true);
36989         this.footer.dataSource = this.grid.dataSource;
36990         this.footer = Roo.factory(this.footer, Roo);
36991         this.footer.render(ctr);
36992         
36993     }
36994     
36995     
36996     
36997     
36998      
36999 };
37000
37001 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37002     getId : function(){
37003         return this.grid.id;
37004     },
37005     
37006     /**
37007      * Returns the grid for this panel
37008      * @return {Roo.bootstrap.Table} 
37009      */
37010     getGrid : function(){
37011         return this.grid;    
37012     },
37013     
37014     setSize : function(width, height){
37015         if(!this.ignoreResize(width, height)){
37016             var grid = this.grid;
37017             var size = this.adjustForComponents(width, height);
37018             var gridel = grid.getGridEl();
37019             gridel.setSize(size.width, size.height);
37020             /*
37021             var thd = grid.getGridEl().select('thead',true).first();
37022             var tbd = grid.getGridEl().select('tbody', true).first();
37023             if (tbd) {
37024                 tbd.setSize(width, height - thd.getHeight());
37025             }
37026             */
37027             grid.autoSize();
37028         }
37029     },
37030      
37031     
37032     
37033     beforeSlide : function(){
37034         this.grid.getView().scroller.clip();
37035     },
37036     
37037     afterSlide : function(){
37038         this.grid.getView().scroller.unclip();
37039     },
37040     
37041     destroy : function(){
37042         this.grid.destroy();
37043         delete this.grid;
37044         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37045     }
37046 });
37047
37048 /**
37049  * @class Roo.bootstrap.panel.Nest
37050  * @extends Roo.bootstrap.panel.Content
37051  * @constructor
37052  * Create a new Panel, that can contain a layout.Border.
37053  * 
37054  * 
37055  * @param {Roo.BorderLayout} layout The layout for this panel
37056  * @param {String/Object} config A string to set only the title or a config object
37057  */
37058 Roo.bootstrap.panel.Nest = function(config)
37059 {
37060     // construct with only one argument..
37061     /* FIXME - implement nicer consturctors
37062     if (layout.layout) {
37063         config = layout;
37064         layout = config.layout;
37065         delete config.layout;
37066     }
37067     if (layout.xtype && !layout.getEl) {
37068         // then layout needs constructing..
37069         layout = Roo.factory(layout, Roo);
37070     }
37071     */
37072     
37073     config.el =  config.layout.getEl();
37074     
37075     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37076     
37077     config.layout.monitorWindowResize = false; // turn off autosizing
37078     this.layout = config.layout;
37079     this.layout.getEl().addClass("roo-layout-nested-layout");
37080     
37081     
37082     
37083     
37084 };
37085
37086 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37087
37088     setSize : function(width, height){
37089         if(!this.ignoreResize(width, height)){
37090             var size = this.adjustForComponents(width, height);
37091             var el = this.layout.getEl();
37092             if (size.height < 1) {
37093                 el.setWidth(size.width);   
37094             } else {
37095                 el.setSize(size.width, size.height);
37096             }
37097             var touch = el.dom.offsetWidth;
37098             this.layout.layout();
37099             // ie requires a double layout on the first pass
37100             if(Roo.isIE && !this.initialized){
37101                 this.initialized = true;
37102                 this.layout.layout();
37103             }
37104         }
37105     },
37106     
37107     // activate all subpanels if not currently active..
37108     
37109     setActiveState : function(active){
37110         this.active = active;
37111         this.setActiveClass(active);
37112         
37113         if(!active){
37114             this.fireEvent("deactivate", this);
37115             return;
37116         }
37117         
37118         this.fireEvent("activate", this);
37119         // not sure if this should happen before or after..
37120         if (!this.layout) {
37121             return; // should not happen..
37122         }
37123         var reg = false;
37124         for (var r in this.layout.regions) {
37125             reg = this.layout.getRegion(r);
37126             if (reg.getActivePanel()) {
37127                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37128                 reg.setActivePanel(reg.getActivePanel());
37129                 continue;
37130             }
37131             if (!reg.panels.length) {
37132                 continue;
37133             }
37134             reg.showPanel(reg.getPanel(0));
37135         }
37136         
37137         
37138         
37139         
37140     },
37141     
37142     /**
37143      * Returns the nested BorderLayout for this panel
37144      * @return {Roo.BorderLayout} 
37145      */
37146     getLayout : function(){
37147         return this.layout;
37148     },
37149     
37150      /**
37151      * Adds a xtype elements to the layout of the nested panel
37152      * <pre><code>
37153
37154 panel.addxtype({
37155        xtype : 'ContentPanel',
37156        region: 'west',
37157        items: [ .... ]
37158    }
37159 );
37160
37161 panel.addxtype({
37162         xtype : 'NestedLayoutPanel',
37163         region: 'west',
37164         layout: {
37165            center: { },
37166            west: { }   
37167         },
37168         items : [ ... list of content panels or nested layout panels.. ]
37169    }
37170 );
37171 </code></pre>
37172      * @param {Object} cfg Xtype definition of item to add.
37173      */
37174     addxtype : function(cfg) {
37175         return this.layout.addxtype(cfg);
37176     
37177     }
37178 });        /*
37179  * Based on:
37180  * Ext JS Library 1.1.1
37181  * Copyright(c) 2006-2007, Ext JS, LLC.
37182  *
37183  * Originally Released Under LGPL - original licence link has changed is not relivant.
37184  *
37185  * Fork - LGPL
37186  * <script type="text/javascript">
37187  */
37188 /**
37189  * @class Roo.TabPanel
37190  * @extends Roo.util.Observable
37191  * A lightweight tab container.
37192  * <br><br>
37193  * Usage:
37194  * <pre><code>
37195 // basic tabs 1, built from existing content
37196 var tabs = new Roo.TabPanel("tabs1");
37197 tabs.addTab("script", "View Script");
37198 tabs.addTab("markup", "View Markup");
37199 tabs.activate("script");
37200
37201 // more advanced tabs, built from javascript
37202 var jtabs = new Roo.TabPanel("jtabs");
37203 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37204
37205 // set up the UpdateManager
37206 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37207 var updater = tab2.getUpdateManager();
37208 updater.setDefaultUrl("ajax1.htm");
37209 tab2.on('activate', updater.refresh, updater, true);
37210
37211 // Use setUrl for Ajax loading
37212 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37213 tab3.setUrl("ajax2.htm", null, true);
37214
37215 // Disabled tab
37216 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37217 tab4.disable();
37218
37219 jtabs.activate("jtabs-1");
37220  * </code></pre>
37221  * @constructor
37222  * Create a new TabPanel.
37223  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37224  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37225  */
37226 Roo.bootstrap.panel.Tabs = function(config){
37227     /**
37228     * The container element for this TabPanel.
37229     * @type Roo.Element
37230     */
37231     this.el = Roo.get(config.el);
37232     delete config.el;
37233     if(config){
37234         if(typeof config == "boolean"){
37235             this.tabPosition = config ? "bottom" : "top";
37236         }else{
37237             Roo.apply(this, config);
37238         }
37239     }
37240     
37241     if(this.tabPosition == "bottom"){
37242         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37243         this.el.addClass("roo-tabs-bottom");
37244     }
37245     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37246     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37247     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37248     if(Roo.isIE){
37249         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37250     }
37251     if(this.tabPosition != "bottom"){
37252         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37253          * @type Roo.Element
37254          */
37255         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37256         this.el.addClass("roo-tabs-top");
37257     }
37258     this.items = [];
37259
37260     this.bodyEl.setStyle("position", "relative");
37261
37262     this.active = null;
37263     this.activateDelegate = this.activate.createDelegate(this);
37264
37265     this.addEvents({
37266         /**
37267          * @event tabchange
37268          * Fires when the active tab changes
37269          * @param {Roo.TabPanel} this
37270          * @param {Roo.TabPanelItem} activePanel The new active tab
37271          */
37272         "tabchange": true,
37273         /**
37274          * @event beforetabchange
37275          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37276          * @param {Roo.TabPanel} this
37277          * @param {Object} e Set cancel to true on this object to cancel the tab change
37278          * @param {Roo.TabPanelItem} tab The tab being changed to
37279          */
37280         "beforetabchange" : true
37281     });
37282
37283     Roo.EventManager.onWindowResize(this.onResize, this);
37284     this.cpad = this.el.getPadding("lr");
37285     this.hiddenCount = 0;
37286
37287
37288     // toolbar on the tabbar support...
37289     if (this.toolbar) {
37290         alert("no toolbar support yet");
37291         this.toolbar  = false;
37292         /*
37293         var tcfg = this.toolbar;
37294         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37295         this.toolbar = new Roo.Toolbar(tcfg);
37296         if (Roo.isSafari) {
37297             var tbl = tcfg.container.child('table', true);
37298             tbl.setAttribute('width', '100%');
37299         }
37300         */
37301         
37302     }
37303    
37304
37305
37306     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37307 };
37308
37309 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37310     /*
37311      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37312      */
37313     tabPosition : "top",
37314     /*
37315      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37316      */
37317     currentTabWidth : 0,
37318     /*
37319      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37320      */
37321     minTabWidth : 40,
37322     /*
37323      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37324      */
37325     maxTabWidth : 250,
37326     /*
37327      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37328      */
37329     preferredTabWidth : 175,
37330     /*
37331      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37332      */
37333     resizeTabs : false,
37334     /*
37335      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37336      */
37337     monitorResize : true,
37338     /*
37339      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37340      */
37341     toolbar : false,
37342
37343     /**
37344      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37345      * @param {String} id The id of the div to use <b>or create</b>
37346      * @param {String} text The text for the tab
37347      * @param {String} content (optional) Content to put in the TabPanelItem body
37348      * @param {Boolean} closable (optional) True to create a close icon on the tab
37349      * @return {Roo.TabPanelItem} The created TabPanelItem
37350      */
37351     addTab : function(id, text, content, closable, tpl)
37352     {
37353         var item = new Roo.bootstrap.panel.TabItem({
37354             panel: this,
37355             id : id,
37356             text : text,
37357             closable : closable,
37358             tpl : tpl
37359         });
37360         this.addTabItem(item);
37361         if(content){
37362             item.setContent(content);
37363         }
37364         return item;
37365     },
37366
37367     /**
37368      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37369      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37370      * @return {Roo.TabPanelItem}
37371      */
37372     getTab : function(id){
37373         return this.items[id];
37374     },
37375
37376     /**
37377      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37378      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37379      */
37380     hideTab : function(id){
37381         var t = this.items[id];
37382         if(!t.isHidden()){
37383            t.setHidden(true);
37384            this.hiddenCount++;
37385            this.autoSizeTabs();
37386         }
37387     },
37388
37389     /**
37390      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37391      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37392      */
37393     unhideTab : function(id){
37394         var t = this.items[id];
37395         if(t.isHidden()){
37396            t.setHidden(false);
37397            this.hiddenCount--;
37398            this.autoSizeTabs();
37399         }
37400     },
37401
37402     /**
37403      * Adds an existing {@link Roo.TabPanelItem}.
37404      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37405      */
37406     addTabItem : function(item){
37407         this.items[item.id] = item;
37408         this.items.push(item);
37409       //  if(this.resizeTabs){
37410     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37411   //         this.autoSizeTabs();
37412 //        }else{
37413 //            item.autoSize();
37414        // }
37415     },
37416
37417     /**
37418      * Removes a {@link Roo.TabPanelItem}.
37419      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37420      */
37421     removeTab : function(id){
37422         var items = this.items;
37423         var tab = items[id];
37424         if(!tab) { return; }
37425         var index = items.indexOf(tab);
37426         if(this.active == tab && items.length > 1){
37427             var newTab = this.getNextAvailable(index);
37428             if(newTab) {
37429                 newTab.activate();
37430             }
37431         }
37432         this.stripEl.dom.removeChild(tab.pnode.dom);
37433         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37434             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37435         }
37436         items.splice(index, 1);
37437         delete this.items[tab.id];
37438         tab.fireEvent("close", tab);
37439         tab.purgeListeners();
37440         this.autoSizeTabs();
37441     },
37442
37443     getNextAvailable : function(start){
37444         var items = this.items;
37445         var index = start;
37446         // look for a next tab that will slide over to
37447         // replace the one being removed
37448         while(index < items.length){
37449             var item = items[++index];
37450             if(item && !item.isHidden()){
37451                 return item;
37452             }
37453         }
37454         // if one isn't found select the previous tab (on the left)
37455         index = start;
37456         while(index >= 0){
37457             var item = items[--index];
37458             if(item && !item.isHidden()){
37459                 return item;
37460             }
37461         }
37462         return null;
37463     },
37464
37465     /**
37466      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37467      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37468      */
37469     disableTab : function(id){
37470         var tab = this.items[id];
37471         if(tab && this.active != tab){
37472             tab.disable();
37473         }
37474     },
37475
37476     /**
37477      * Enables a {@link Roo.TabPanelItem} that is disabled.
37478      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37479      */
37480     enableTab : function(id){
37481         var tab = this.items[id];
37482         tab.enable();
37483     },
37484
37485     /**
37486      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37487      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37488      * @return {Roo.TabPanelItem} The TabPanelItem.
37489      */
37490     activate : function(id){
37491         var tab = this.items[id];
37492         if(!tab){
37493             return null;
37494         }
37495         if(tab == this.active || tab.disabled){
37496             return tab;
37497         }
37498         var e = {};
37499         this.fireEvent("beforetabchange", this, e, tab);
37500         if(e.cancel !== true && !tab.disabled){
37501             if(this.active){
37502                 this.active.hide();
37503             }
37504             this.active = this.items[id];
37505             this.active.show();
37506             this.fireEvent("tabchange", this, this.active);
37507         }
37508         return tab;
37509     },
37510
37511     /**
37512      * Gets the active {@link Roo.TabPanelItem}.
37513      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37514      */
37515     getActiveTab : function(){
37516         return this.active;
37517     },
37518
37519     /**
37520      * Updates the tab body element to fit the height of the container element
37521      * for overflow scrolling
37522      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37523      */
37524     syncHeight : function(targetHeight){
37525         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37526         var bm = this.bodyEl.getMargins();
37527         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37528         this.bodyEl.setHeight(newHeight);
37529         return newHeight;
37530     },
37531
37532     onResize : function(){
37533         if(this.monitorResize){
37534             this.autoSizeTabs();
37535         }
37536     },
37537
37538     /**
37539      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37540      */
37541     beginUpdate : function(){
37542         this.updating = true;
37543     },
37544
37545     /**
37546      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37547      */
37548     endUpdate : function(){
37549         this.updating = false;
37550         this.autoSizeTabs();
37551     },
37552
37553     /**
37554      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37555      */
37556     autoSizeTabs : function(){
37557         var count = this.items.length;
37558         var vcount = count - this.hiddenCount;
37559         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37560             return;
37561         }
37562         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37563         var availWidth = Math.floor(w / vcount);
37564         var b = this.stripBody;
37565         if(b.getWidth() > w){
37566             var tabs = this.items;
37567             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37568             if(availWidth < this.minTabWidth){
37569                 /*if(!this.sleft){    // incomplete scrolling code
37570                     this.createScrollButtons();
37571                 }
37572                 this.showScroll();
37573                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37574             }
37575         }else{
37576             if(this.currentTabWidth < this.preferredTabWidth){
37577                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37578             }
37579         }
37580     },
37581
37582     /**
37583      * Returns the number of tabs in this TabPanel.
37584      * @return {Number}
37585      */
37586      getCount : function(){
37587          return this.items.length;
37588      },
37589
37590     /**
37591      * Resizes all the tabs to the passed width
37592      * @param {Number} The new width
37593      */
37594     setTabWidth : function(width){
37595         this.currentTabWidth = width;
37596         for(var i = 0, len = this.items.length; i < len; i++) {
37597                 if(!this.items[i].isHidden()) {
37598                 this.items[i].setWidth(width);
37599             }
37600         }
37601     },
37602
37603     /**
37604      * Destroys this TabPanel
37605      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37606      */
37607     destroy : function(removeEl){
37608         Roo.EventManager.removeResizeListener(this.onResize, this);
37609         for(var i = 0, len = this.items.length; i < len; i++){
37610             this.items[i].purgeListeners();
37611         }
37612         if(removeEl === true){
37613             this.el.update("");
37614             this.el.remove();
37615         }
37616     },
37617     
37618     createStrip : function(container)
37619     {
37620         var strip = document.createElement("nav");
37621         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37622         container.appendChild(strip);
37623         return strip;
37624     },
37625     
37626     createStripList : function(strip)
37627     {
37628         // div wrapper for retard IE
37629         // returns the "tr" element.
37630         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37631         //'<div class="x-tabs-strip-wrap">'+
37632           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37633           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37634         return strip.firstChild; //.firstChild.firstChild.firstChild;
37635     },
37636     createBody : function(container)
37637     {
37638         var body = document.createElement("div");
37639         Roo.id(body, "tab-body");
37640         //Roo.fly(body).addClass("x-tabs-body");
37641         Roo.fly(body).addClass("tab-content");
37642         container.appendChild(body);
37643         return body;
37644     },
37645     createItemBody :function(bodyEl, id){
37646         var body = Roo.getDom(id);
37647         if(!body){
37648             body = document.createElement("div");
37649             body.id = id;
37650         }
37651         //Roo.fly(body).addClass("x-tabs-item-body");
37652         Roo.fly(body).addClass("tab-pane");
37653          bodyEl.insertBefore(body, bodyEl.firstChild);
37654         return body;
37655     },
37656     /** @private */
37657     createStripElements :  function(stripEl, text, closable, tpl)
37658     {
37659         var td = document.createElement("li"); // was td..
37660         
37661         
37662         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37663         
37664         
37665         stripEl.appendChild(td);
37666         /*if(closable){
37667             td.className = "x-tabs-closable";
37668             if(!this.closeTpl){
37669                 this.closeTpl = new Roo.Template(
37670                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37671                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37672                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37673                 );
37674             }
37675             var el = this.closeTpl.overwrite(td, {"text": text});
37676             var close = el.getElementsByTagName("div")[0];
37677             var inner = el.getElementsByTagName("em")[0];
37678             return {"el": el, "close": close, "inner": inner};
37679         } else {
37680         */
37681         // not sure what this is..
37682 //            if(!this.tabTpl){
37683                 //this.tabTpl = new Roo.Template(
37684                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37685                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37686                 //);
37687 //                this.tabTpl = new Roo.Template(
37688 //                   '<a href="#">' +
37689 //                   '<span unselectable="on"' +
37690 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37691 //                            ' >{text}</span></a>'
37692 //                );
37693 //                
37694 //            }
37695
37696
37697             var template = tpl || this.tabTpl || false;
37698             
37699             if(!template){
37700                 
37701                 template = new Roo.Template(
37702                    '<a href="#">' +
37703                    '<span unselectable="on"' +
37704                             (this.disableTooltips ? '' : ' title="{text}"') +
37705                             ' >{text}</span></a>'
37706                 );
37707             }
37708             
37709             switch (typeof(template)) {
37710                 case 'object' :
37711                     break;
37712                 case 'string' :
37713                     template = new Roo.Template(template);
37714                     break;
37715                 default :
37716                     break;
37717             }
37718             
37719             var el = template.overwrite(td, {"text": text});
37720             
37721             var inner = el.getElementsByTagName("span")[0];
37722             
37723             return {"el": el, "inner": inner};
37724             
37725     }
37726         
37727     
37728 });
37729
37730 /**
37731  * @class Roo.TabPanelItem
37732  * @extends Roo.util.Observable
37733  * Represents an individual item (tab plus body) in a TabPanel.
37734  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37735  * @param {String} id The id of this TabPanelItem
37736  * @param {String} text The text for the tab of this TabPanelItem
37737  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37738  */
37739 Roo.bootstrap.panel.TabItem = function(config){
37740     /**
37741      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37742      * @type Roo.TabPanel
37743      */
37744     this.tabPanel = config.panel;
37745     /**
37746      * The id for this TabPanelItem
37747      * @type String
37748      */
37749     this.id = config.id;
37750     /** @private */
37751     this.disabled = false;
37752     /** @private */
37753     this.text = config.text;
37754     /** @private */
37755     this.loaded = false;
37756     this.closable = config.closable;
37757
37758     /**
37759      * The body element for this TabPanelItem.
37760      * @type Roo.Element
37761      */
37762     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37763     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37764     this.bodyEl.setStyle("display", "block");
37765     this.bodyEl.setStyle("zoom", "1");
37766     //this.hideAction();
37767
37768     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37769     /** @private */
37770     this.el = Roo.get(els.el);
37771     this.inner = Roo.get(els.inner, true);
37772     this.textEl = Roo.get(this.el.dom.firstChild, true);
37773     this.pnode = Roo.get(els.el.parentNode, true);
37774 //    this.el.on("mousedown", this.onTabMouseDown, this);
37775     this.el.on("click", this.onTabClick, this);
37776     /** @private */
37777     if(config.closable){
37778         var c = Roo.get(els.close, true);
37779         c.dom.title = this.closeText;
37780         c.addClassOnOver("close-over");
37781         c.on("click", this.closeClick, this);
37782      }
37783
37784     this.addEvents({
37785          /**
37786          * @event activate
37787          * Fires when this tab becomes the active tab.
37788          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37789          * @param {Roo.TabPanelItem} this
37790          */
37791         "activate": true,
37792         /**
37793          * @event beforeclose
37794          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37795          * @param {Roo.TabPanelItem} this
37796          * @param {Object} e Set cancel to true on this object to cancel the close.
37797          */
37798         "beforeclose": true,
37799         /**
37800          * @event close
37801          * Fires when this tab is closed.
37802          * @param {Roo.TabPanelItem} this
37803          */
37804          "close": true,
37805         /**
37806          * @event deactivate
37807          * Fires when this tab is no longer the active tab.
37808          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37809          * @param {Roo.TabPanelItem} this
37810          */
37811          "deactivate" : true
37812     });
37813     this.hidden = false;
37814
37815     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37816 };
37817
37818 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37819            {
37820     purgeListeners : function(){
37821        Roo.util.Observable.prototype.purgeListeners.call(this);
37822        this.el.removeAllListeners();
37823     },
37824     /**
37825      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37826      */
37827     show : function(){
37828         this.pnode.addClass("active");
37829         this.showAction();
37830         if(Roo.isOpera){
37831             this.tabPanel.stripWrap.repaint();
37832         }
37833         this.fireEvent("activate", this.tabPanel, this);
37834     },
37835
37836     /**
37837      * Returns true if this tab is the active tab.
37838      * @return {Boolean}
37839      */
37840     isActive : function(){
37841         return this.tabPanel.getActiveTab() == this;
37842     },
37843
37844     /**
37845      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37846      */
37847     hide : function(){
37848         this.pnode.removeClass("active");
37849         this.hideAction();
37850         this.fireEvent("deactivate", this.tabPanel, this);
37851     },
37852
37853     hideAction : function(){
37854         this.bodyEl.hide();
37855         this.bodyEl.setStyle("position", "absolute");
37856         this.bodyEl.setLeft("-20000px");
37857         this.bodyEl.setTop("-20000px");
37858     },
37859
37860     showAction : function(){
37861         this.bodyEl.setStyle("position", "relative");
37862         this.bodyEl.setTop("");
37863         this.bodyEl.setLeft("");
37864         this.bodyEl.show();
37865     },
37866
37867     /**
37868      * Set the tooltip for the tab.
37869      * @param {String} tooltip The tab's tooltip
37870      */
37871     setTooltip : function(text){
37872         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37873             this.textEl.dom.qtip = text;
37874             this.textEl.dom.removeAttribute('title');
37875         }else{
37876             this.textEl.dom.title = text;
37877         }
37878     },
37879
37880     onTabClick : function(e){
37881         e.preventDefault();
37882         this.tabPanel.activate(this.id);
37883     },
37884
37885     onTabMouseDown : function(e){
37886         e.preventDefault();
37887         this.tabPanel.activate(this.id);
37888     },
37889 /*
37890     getWidth : function(){
37891         return this.inner.getWidth();
37892     },
37893
37894     setWidth : function(width){
37895         var iwidth = width - this.pnode.getPadding("lr");
37896         this.inner.setWidth(iwidth);
37897         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37898         this.pnode.setWidth(width);
37899     },
37900 */
37901     /**
37902      * Show or hide the tab
37903      * @param {Boolean} hidden True to hide or false to show.
37904      */
37905     setHidden : function(hidden){
37906         this.hidden = hidden;
37907         this.pnode.setStyle("display", hidden ? "none" : "");
37908     },
37909
37910     /**
37911      * Returns true if this tab is "hidden"
37912      * @return {Boolean}
37913      */
37914     isHidden : function(){
37915         return this.hidden;
37916     },
37917
37918     /**
37919      * Returns the text for this tab
37920      * @return {String}
37921      */
37922     getText : function(){
37923         return this.text;
37924     },
37925     /*
37926     autoSize : function(){
37927         //this.el.beginMeasure();
37928         this.textEl.setWidth(1);
37929         /*
37930          *  #2804 [new] Tabs in Roojs
37931          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37932          */
37933         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37934         //this.el.endMeasure();
37935     //},
37936
37937     /**
37938      * Sets the text for the tab (Note: this also sets the tooltip text)
37939      * @param {String} text The tab's text and tooltip
37940      */
37941     setText : function(text){
37942         this.text = text;
37943         this.textEl.update(text);
37944         this.setTooltip(text);
37945         //if(!this.tabPanel.resizeTabs){
37946         //    this.autoSize();
37947         //}
37948     },
37949     /**
37950      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37951      */
37952     activate : function(){
37953         this.tabPanel.activate(this.id);
37954     },
37955
37956     /**
37957      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37958      */
37959     disable : function(){
37960         if(this.tabPanel.active != this){
37961             this.disabled = true;
37962             this.pnode.addClass("disabled");
37963         }
37964     },
37965
37966     /**
37967      * Enables this TabPanelItem if it was previously disabled.
37968      */
37969     enable : function(){
37970         this.disabled = false;
37971         this.pnode.removeClass("disabled");
37972     },
37973
37974     /**
37975      * Sets the content for this TabPanelItem.
37976      * @param {String} content The content
37977      * @param {Boolean} loadScripts true to look for and load scripts
37978      */
37979     setContent : function(content, loadScripts){
37980         this.bodyEl.update(content, loadScripts);
37981     },
37982
37983     /**
37984      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37985      * @return {Roo.UpdateManager} The UpdateManager
37986      */
37987     getUpdateManager : function(){
37988         return this.bodyEl.getUpdateManager();
37989     },
37990
37991     /**
37992      * Set a URL to be used to load the content for this TabPanelItem.
37993      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37994      * @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)
37995      * @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)
37996      * @return {Roo.UpdateManager} The UpdateManager
37997      */
37998     setUrl : function(url, params, loadOnce){
37999         if(this.refreshDelegate){
38000             this.un('activate', this.refreshDelegate);
38001         }
38002         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38003         this.on("activate", this.refreshDelegate);
38004         return this.bodyEl.getUpdateManager();
38005     },
38006
38007     /** @private */
38008     _handleRefresh : function(url, params, loadOnce){
38009         if(!loadOnce || !this.loaded){
38010             var updater = this.bodyEl.getUpdateManager();
38011             updater.update(url, params, this._setLoaded.createDelegate(this));
38012         }
38013     },
38014
38015     /**
38016      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38017      *   Will fail silently if the setUrl method has not been called.
38018      *   This does not activate the panel, just updates its content.
38019      */
38020     refresh : function(){
38021         if(this.refreshDelegate){
38022            this.loaded = false;
38023            this.refreshDelegate();
38024         }
38025     },
38026
38027     /** @private */
38028     _setLoaded : function(){
38029         this.loaded = true;
38030     },
38031
38032     /** @private */
38033     closeClick : function(e){
38034         var o = {};
38035         e.stopEvent();
38036         this.fireEvent("beforeclose", this, o);
38037         if(o.cancel !== true){
38038             this.tabPanel.removeTab(this.id);
38039         }
38040     },
38041     /**
38042      * The text displayed in the tooltip for the close icon.
38043      * @type String
38044      */
38045     closeText : "Close this tab"
38046 });
38047 /**
38048 *    This script refer to:
38049 *    Title: International Telephone Input
38050 *    Author: Jack O'Connor
38051 *    Code version:  v12.1.12
38052 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38053 **/
38054
38055 Roo.bootstrap.PhoneInputData = function() {
38056     var d = [
38057       [
38058         "Afghanistan (‫افغانستان‬‎)",
38059         "af",
38060         "93"
38061       ],
38062       [
38063         "Albania (Shqipëri)",
38064         "al",
38065         "355"
38066       ],
38067       [
38068         "Algeria (‫الجزائر‬‎)",
38069         "dz",
38070         "213"
38071       ],
38072       [
38073         "American Samoa",
38074         "as",
38075         "1684"
38076       ],
38077       [
38078         "Andorra",
38079         "ad",
38080         "376"
38081       ],
38082       [
38083         "Angola",
38084         "ao",
38085         "244"
38086       ],
38087       [
38088         "Anguilla",
38089         "ai",
38090         "1264"
38091       ],
38092       [
38093         "Antigua and Barbuda",
38094         "ag",
38095         "1268"
38096       ],
38097       [
38098         "Argentina",
38099         "ar",
38100         "54"
38101       ],
38102       [
38103         "Armenia (Հայաստան)",
38104         "am",
38105         "374"
38106       ],
38107       [
38108         "Aruba",
38109         "aw",
38110         "297"
38111       ],
38112       [
38113         "Australia",
38114         "au",
38115         "61",
38116         0
38117       ],
38118       [
38119         "Austria (Österreich)",
38120         "at",
38121         "43"
38122       ],
38123       [
38124         "Azerbaijan (Azərbaycan)",
38125         "az",
38126         "994"
38127       ],
38128       [
38129         "Bahamas",
38130         "bs",
38131         "1242"
38132       ],
38133       [
38134         "Bahrain (‫البحرين‬‎)",
38135         "bh",
38136         "973"
38137       ],
38138       [
38139         "Bangladesh (বাংলাদেশ)",
38140         "bd",
38141         "880"
38142       ],
38143       [
38144         "Barbados",
38145         "bb",
38146         "1246"
38147       ],
38148       [
38149         "Belarus (Беларусь)",
38150         "by",
38151         "375"
38152       ],
38153       [
38154         "Belgium (België)",
38155         "be",
38156         "32"
38157       ],
38158       [
38159         "Belize",
38160         "bz",
38161         "501"
38162       ],
38163       [
38164         "Benin (Bénin)",
38165         "bj",
38166         "229"
38167       ],
38168       [
38169         "Bermuda",
38170         "bm",
38171         "1441"
38172       ],
38173       [
38174         "Bhutan (འབྲུག)",
38175         "bt",
38176         "975"
38177       ],
38178       [
38179         "Bolivia",
38180         "bo",
38181         "591"
38182       ],
38183       [
38184         "Bosnia and Herzegovina (Босна и Херцеговина)",
38185         "ba",
38186         "387"
38187       ],
38188       [
38189         "Botswana",
38190         "bw",
38191         "267"
38192       ],
38193       [
38194         "Brazil (Brasil)",
38195         "br",
38196         "55"
38197       ],
38198       [
38199         "British Indian Ocean Territory",
38200         "io",
38201         "246"
38202       ],
38203       [
38204         "British Virgin Islands",
38205         "vg",
38206         "1284"
38207       ],
38208       [
38209         "Brunei",
38210         "bn",
38211         "673"
38212       ],
38213       [
38214         "Bulgaria (България)",
38215         "bg",
38216         "359"
38217       ],
38218       [
38219         "Burkina Faso",
38220         "bf",
38221         "226"
38222       ],
38223       [
38224         "Burundi (Uburundi)",
38225         "bi",
38226         "257"
38227       ],
38228       [
38229         "Cambodia (កម្ពុជា)",
38230         "kh",
38231         "855"
38232       ],
38233       [
38234         "Cameroon (Cameroun)",
38235         "cm",
38236         "237"
38237       ],
38238       [
38239         "Canada",
38240         "ca",
38241         "1",
38242         1,
38243         ["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"]
38244       ],
38245       [
38246         "Cape Verde (Kabu Verdi)",
38247         "cv",
38248         "238"
38249       ],
38250       [
38251         "Caribbean Netherlands",
38252         "bq",
38253         "599",
38254         1
38255       ],
38256       [
38257         "Cayman Islands",
38258         "ky",
38259         "1345"
38260       ],
38261       [
38262         "Central African Republic (République centrafricaine)",
38263         "cf",
38264         "236"
38265       ],
38266       [
38267         "Chad (Tchad)",
38268         "td",
38269         "235"
38270       ],
38271       [
38272         "Chile",
38273         "cl",
38274         "56"
38275       ],
38276       [
38277         "China (中国)",
38278         "cn",
38279         "86"
38280       ],
38281       [
38282         "Christmas Island",
38283         "cx",
38284         "61",
38285         2
38286       ],
38287       [
38288         "Cocos (Keeling) Islands",
38289         "cc",
38290         "61",
38291         1
38292       ],
38293       [
38294         "Colombia",
38295         "co",
38296         "57"
38297       ],
38298       [
38299         "Comoros (‫جزر القمر‬‎)",
38300         "km",
38301         "269"
38302       ],
38303       [
38304         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38305         "cd",
38306         "243"
38307       ],
38308       [
38309         "Congo (Republic) (Congo-Brazzaville)",
38310         "cg",
38311         "242"
38312       ],
38313       [
38314         "Cook Islands",
38315         "ck",
38316         "682"
38317       ],
38318       [
38319         "Costa Rica",
38320         "cr",
38321         "506"
38322       ],
38323       [
38324         "Côte d’Ivoire",
38325         "ci",
38326         "225"
38327       ],
38328       [
38329         "Croatia (Hrvatska)",
38330         "hr",
38331         "385"
38332       ],
38333       [
38334         "Cuba",
38335         "cu",
38336         "53"
38337       ],
38338       [
38339         "Curaçao",
38340         "cw",
38341         "599",
38342         0
38343       ],
38344       [
38345         "Cyprus (Κύπρος)",
38346         "cy",
38347         "357"
38348       ],
38349       [
38350         "Czech Republic (Česká republika)",
38351         "cz",
38352         "420"
38353       ],
38354       [
38355         "Denmark (Danmark)",
38356         "dk",
38357         "45"
38358       ],
38359       [
38360         "Djibouti",
38361         "dj",
38362         "253"
38363       ],
38364       [
38365         "Dominica",
38366         "dm",
38367         "1767"
38368       ],
38369       [
38370         "Dominican Republic (República Dominicana)",
38371         "do",
38372         "1",
38373         2,
38374         ["809", "829", "849"]
38375       ],
38376       [
38377         "Ecuador",
38378         "ec",
38379         "593"
38380       ],
38381       [
38382         "Egypt (‫مصر‬‎)",
38383         "eg",
38384         "20"
38385       ],
38386       [
38387         "El Salvador",
38388         "sv",
38389         "503"
38390       ],
38391       [
38392         "Equatorial Guinea (Guinea Ecuatorial)",
38393         "gq",
38394         "240"
38395       ],
38396       [
38397         "Eritrea",
38398         "er",
38399         "291"
38400       ],
38401       [
38402         "Estonia (Eesti)",
38403         "ee",
38404         "372"
38405       ],
38406       [
38407         "Ethiopia",
38408         "et",
38409         "251"
38410       ],
38411       [
38412         "Falkland Islands (Islas Malvinas)",
38413         "fk",
38414         "500"
38415       ],
38416       [
38417         "Faroe Islands (Føroyar)",
38418         "fo",
38419         "298"
38420       ],
38421       [
38422         "Fiji",
38423         "fj",
38424         "679"
38425       ],
38426       [
38427         "Finland (Suomi)",
38428         "fi",
38429         "358",
38430         0
38431       ],
38432       [
38433         "France",
38434         "fr",
38435         "33"
38436       ],
38437       [
38438         "French Guiana (Guyane française)",
38439         "gf",
38440         "594"
38441       ],
38442       [
38443         "French Polynesia (Polynésie française)",
38444         "pf",
38445         "689"
38446       ],
38447       [
38448         "Gabon",
38449         "ga",
38450         "241"
38451       ],
38452       [
38453         "Gambia",
38454         "gm",
38455         "220"
38456       ],
38457       [
38458         "Georgia (საქართველო)",
38459         "ge",
38460         "995"
38461       ],
38462       [
38463         "Germany (Deutschland)",
38464         "de",
38465         "49"
38466       ],
38467       [
38468         "Ghana (Gaana)",
38469         "gh",
38470         "233"
38471       ],
38472       [
38473         "Gibraltar",
38474         "gi",
38475         "350"
38476       ],
38477       [
38478         "Greece (Ελλάδα)",
38479         "gr",
38480         "30"
38481       ],
38482       [
38483         "Greenland (Kalaallit Nunaat)",
38484         "gl",
38485         "299"
38486       ],
38487       [
38488         "Grenada",
38489         "gd",
38490         "1473"
38491       ],
38492       [
38493         "Guadeloupe",
38494         "gp",
38495         "590",
38496         0
38497       ],
38498       [
38499         "Guam",
38500         "gu",
38501         "1671"
38502       ],
38503       [
38504         "Guatemala",
38505         "gt",
38506         "502"
38507       ],
38508       [
38509         "Guernsey",
38510         "gg",
38511         "44",
38512         1
38513       ],
38514       [
38515         "Guinea (Guinée)",
38516         "gn",
38517         "224"
38518       ],
38519       [
38520         "Guinea-Bissau (Guiné Bissau)",
38521         "gw",
38522         "245"
38523       ],
38524       [
38525         "Guyana",
38526         "gy",
38527         "592"
38528       ],
38529       [
38530         "Haiti",
38531         "ht",
38532         "509"
38533       ],
38534       [
38535         "Honduras",
38536         "hn",
38537         "504"
38538       ],
38539       [
38540         "Hong Kong (香港)",
38541         "hk",
38542         "852"
38543       ],
38544       [
38545         "Hungary (Magyarország)",
38546         "hu",
38547         "36"
38548       ],
38549       [
38550         "Iceland (Ísland)",
38551         "is",
38552         "354"
38553       ],
38554       [
38555         "India (भारत)",
38556         "in",
38557         "91"
38558       ],
38559       [
38560         "Indonesia",
38561         "id",
38562         "62"
38563       ],
38564       [
38565         "Iran (‫ایران‬‎)",
38566         "ir",
38567         "98"
38568       ],
38569       [
38570         "Iraq (‫العراق‬‎)",
38571         "iq",
38572         "964"
38573       ],
38574       [
38575         "Ireland",
38576         "ie",
38577         "353"
38578       ],
38579       [
38580         "Isle of Man",
38581         "im",
38582         "44",
38583         2
38584       ],
38585       [
38586         "Israel (‫ישראל‬‎)",
38587         "il",
38588         "972"
38589       ],
38590       [
38591         "Italy (Italia)",
38592         "it",
38593         "39",
38594         0
38595       ],
38596       [
38597         "Jamaica",
38598         "jm",
38599         "1876"
38600       ],
38601       [
38602         "Japan (日本)",
38603         "jp",
38604         "81"
38605       ],
38606       [
38607         "Jersey",
38608         "je",
38609         "44",
38610         3
38611       ],
38612       [
38613         "Jordan (‫الأردن‬‎)",
38614         "jo",
38615         "962"
38616       ],
38617       [
38618         "Kazakhstan (Казахстан)",
38619         "kz",
38620         "7",
38621         1
38622       ],
38623       [
38624         "Kenya",
38625         "ke",
38626         "254"
38627       ],
38628       [
38629         "Kiribati",
38630         "ki",
38631         "686"
38632       ],
38633       [
38634         "Kosovo",
38635         "xk",
38636         "383"
38637       ],
38638       [
38639         "Kuwait (‫الكويت‬‎)",
38640         "kw",
38641         "965"
38642       ],
38643       [
38644         "Kyrgyzstan (Кыргызстан)",
38645         "kg",
38646         "996"
38647       ],
38648       [
38649         "Laos (ລາວ)",
38650         "la",
38651         "856"
38652       ],
38653       [
38654         "Latvia (Latvija)",
38655         "lv",
38656         "371"
38657       ],
38658       [
38659         "Lebanon (‫لبنان‬‎)",
38660         "lb",
38661         "961"
38662       ],
38663       [
38664         "Lesotho",
38665         "ls",
38666         "266"
38667       ],
38668       [
38669         "Liberia",
38670         "lr",
38671         "231"
38672       ],
38673       [
38674         "Libya (‫ليبيا‬‎)",
38675         "ly",
38676         "218"
38677       ],
38678       [
38679         "Liechtenstein",
38680         "li",
38681         "423"
38682       ],
38683       [
38684         "Lithuania (Lietuva)",
38685         "lt",
38686         "370"
38687       ],
38688       [
38689         "Luxembourg",
38690         "lu",
38691         "352"
38692       ],
38693       [
38694         "Macau (澳門)",
38695         "mo",
38696         "853"
38697       ],
38698       [
38699         "Macedonia (FYROM) (Македонија)",
38700         "mk",
38701         "389"
38702       ],
38703       [
38704         "Madagascar (Madagasikara)",
38705         "mg",
38706         "261"
38707       ],
38708       [
38709         "Malawi",
38710         "mw",
38711         "265"
38712       ],
38713       [
38714         "Malaysia",
38715         "my",
38716         "60"
38717       ],
38718       [
38719         "Maldives",
38720         "mv",
38721         "960"
38722       ],
38723       [
38724         "Mali",
38725         "ml",
38726         "223"
38727       ],
38728       [
38729         "Malta",
38730         "mt",
38731         "356"
38732       ],
38733       [
38734         "Marshall Islands",
38735         "mh",
38736         "692"
38737       ],
38738       [
38739         "Martinique",
38740         "mq",
38741         "596"
38742       ],
38743       [
38744         "Mauritania (‫موريتانيا‬‎)",
38745         "mr",
38746         "222"
38747       ],
38748       [
38749         "Mauritius (Moris)",
38750         "mu",
38751         "230"
38752       ],
38753       [
38754         "Mayotte",
38755         "yt",
38756         "262",
38757         1
38758       ],
38759       [
38760         "Mexico (México)",
38761         "mx",
38762         "52"
38763       ],
38764       [
38765         "Micronesia",
38766         "fm",
38767         "691"
38768       ],
38769       [
38770         "Moldova (Republica Moldova)",
38771         "md",
38772         "373"
38773       ],
38774       [
38775         "Monaco",
38776         "mc",
38777         "377"
38778       ],
38779       [
38780         "Mongolia (Монгол)",
38781         "mn",
38782         "976"
38783       ],
38784       [
38785         "Montenegro (Crna Gora)",
38786         "me",
38787         "382"
38788       ],
38789       [
38790         "Montserrat",
38791         "ms",
38792         "1664"
38793       ],
38794       [
38795         "Morocco (‫المغرب‬‎)",
38796         "ma",
38797         "212",
38798         0
38799       ],
38800       [
38801         "Mozambique (Moçambique)",
38802         "mz",
38803         "258"
38804       ],
38805       [
38806         "Myanmar (Burma) (မြန်မာ)",
38807         "mm",
38808         "95"
38809       ],
38810       [
38811         "Namibia (Namibië)",
38812         "na",
38813         "264"
38814       ],
38815       [
38816         "Nauru",
38817         "nr",
38818         "674"
38819       ],
38820       [
38821         "Nepal (नेपाल)",
38822         "np",
38823         "977"
38824       ],
38825       [
38826         "Netherlands (Nederland)",
38827         "nl",
38828         "31"
38829       ],
38830       [
38831         "New Caledonia (Nouvelle-Calédonie)",
38832         "nc",
38833         "687"
38834       ],
38835       [
38836         "New Zealand",
38837         "nz",
38838         "64"
38839       ],
38840       [
38841         "Nicaragua",
38842         "ni",
38843         "505"
38844       ],
38845       [
38846         "Niger (Nijar)",
38847         "ne",
38848         "227"
38849       ],
38850       [
38851         "Nigeria",
38852         "ng",
38853         "234"
38854       ],
38855       [
38856         "Niue",
38857         "nu",
38858         "683"
38859       ],
38860       [
38861         "Norfolk Island",
38862         "nf",
38863         "672"
38864       ],
38865       [
38866         "North Korea (조선 민주주의 인민 공화국)",
38867         "kp",
38868         "850"
38869       ],
38870       [
38871         "Northern Mariana Islands",
38872         "mp",
38873         "1670"
38874       ],
38875       [
38876         "Norway (Norge)",
38877         "no",
38878         "47",
38879         0
38880       ],
38881       [
38882         "Oman (‫عُمان‬‎)",
38883         "om",
38884         "968"
38885       ],
38886       [
38887         "Pakistan (‫پاکستان‬‎)",
38888         "pk",
38889         "92"
38890       ],
38891       [
38892         "Palau",
38893         "pw",
38894         "680"
38895       ],
38896       [
38897         "Palestine (‫فلسطين‬‎)",
38898         "ps",
38899         "970"
38900       ],
38901       [
38902         "Panama (Panamá)",
38903         "pa",
38904         "507"
38905       ],
38906       [
38907         "Papua New Guinea",
38908         "pg",
38909         "675"
38910       ],
38911       [
38912         "Paraguay",
38913         "py",
38914         "595"
38915       ],
38916       [
38917         "Peru (Perú)",
38918         "pe",
38919         "51"
38920       ],
38921       [
38922         "Philippines",
38923         "ph",
38924         "63"
38925       ],
38926       [
38927         "Poland (Polska)",
38928         "pl",
38929         "48"
38930       ],
38931       [
38932         "Portugal",
38933         "pt",
38934         "351"
38935       ],
38936       [
38937         "Puerto Rico",
38938         "pr",
38939         "1",
38940         3,
38941         ["787", "939"]
38942       ],
38943       [
38944         "Qatar (‫قطر‬‎)",
38945         "qa",
38946         "974"
38947       ],
38948       [
38949         "Réunion (La Réunion)",
38950         "re",
38951         "262",
38952         0
38953       ],
38954       [
38955         "Romania (România)",
38956         "ro",
38957         "40"
38958       ],
38959       [
38960         "Russia (Россия)",
38961         "ru",
38962         "7",
38963         0
38964       ],
38965       [
38966         "Rwanda",
38967         "rw",
38968         "250"
38969       ],
38970       [
38971         "Saint Barthélemy",
38972         "bl",
38973         "590",
38974         1
38975       ],
38976       [
38977         "Saint Helena",
38978         "sh",
38979         "290"
38980       ],
38981       [
38982         "Saint Kitts and Nevis",
38983         "kn",
38984         "1869"
38985       ],
38986       [
38987         "Saint Lucia",
38988         "lc",
38989         "1758"
38990       ],
38991       [
38992         "Saint Martin (Saint-Martin (partie française))",
38993         "mf",
38994         "590",
38995         2
38996       ],
38997       [
38998         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38999         "pm",
39000         "508"
39001       ],
39002       [
39003         "Saint Vincent and the Grenadines",
39004         "vc",
39005         "1784"
39006       ],
39007       [
39008         "Samoa",
39009         "ws",
39010         "685"
39011       ],
39012       [
39013         "San Marino",
39014         "sm",
39015         "378"
39016       ],
39017       [
39018         "São Tomé and Príncipe (São Tomé e Príncipe)",
39019         "st",
39020         "239"
39021       ],
39022       [
39023         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39024         "sa",
39025         "966"
39026       ],
39027       [
39028         "Senegal (Sénégal)",
39029         "sn",
39030         "221"
39031       ],
39032       [
39033         "Serbia (Србија)",
39034         "rs",
39035         "381"
39036       ],
39037       [
39038         "Seychelles",
39039         "sc",
39040         "248"
39041       ],
39042       [
39043         "Sierra Leone",
39044         "sl",
39045         "232"
39046       ],
39047       [
39048         "Singapore",
39049         "sg",
39050         "65"
39051       ],
39052       [
39053         "Sint Maarten",
39054         "sx",
39055         "1721"
39056       ],
39057       [
39058         "Slovakia (Slovensko)",
39059         "sk",
39060         "421"
39061       ],
39062       [
39063         "Slovenia (Slovenija)",
39064         "si",
39065         "386"
39066       ],
39067       [
39068         "Solomon Islands",
39069         "sb",
39070         "677"
39071       ],
39072       [
39073         "Somalia (Soomaaliya)",
39074         "so",
39075         "252"
39076       ],
39077       [
39078         "South Africa",
39079         "za",
39080         "27"
39081       ],
39082       [
39083         "South Korea (대한민국)",
39084         "kr",
39085         "82"
39086       ],
39087       [
39088         "South Sudan (‫جنوب السودان‬‎)",
39089         "ss",
39090         "211"
39091       ],
39092       [
39093         "Spain (España)",
39094         "es",
39095         "34"
39096       ],
39097       [
39098         "Sri Lanka (ශ්‍රී ලංකාව)",
39099         "lk",
39100         "94"
39101       ],
39102       [
39103         "Sudan (‫السودان‬‎)",
39104         "sd",
39105         "249"
39106       ],
39107       [
39108         "Suriname",
39109         "sr",
39110         "597"
39111       ],
39112       [
39113         "Svalbard and Jan Mayen",
39114         "sj",
39115         "47",
39116         1
39117       ],
39118       [
39119         "Swaziland",
39120         "sz",
39121         "268"
39122       ],
39123       [
39124         "Sweden (Sverige)",
39125         "se",
39126         "46"
39127       ],
39128       [
39129         "Switzerland (Schweiz)",
39130         "ch",
39131         "41"
39132       ],
39133       [
39134         "Syria (‫سوريا‬‎)",
39135         "sy",
39136         "963"
39137       ],
39138       [
39139         "Taiwan (台灣)",
39140         "tw",
39141         "886"
39142       ],
39143       [
39144         "Tajikistan",
39145         "tj",
39146         "992"
39147       ],
39148       [
39149         "Tanzania",
39150         "tz",
39151         "255"
39152       ],
39153       [
39154         "Thailand (ไทย)",
39155         "th",
39156         "66"
39157       ],
39158       [
39159         "Timor-Leste",
39160         "tl",
39161         "670"
39162       ],
39163       [
39164         "Togo",
39165         "tg",
39166         "228"
39167       ],
39168       [
39169         "Tokelau",
39170         "tk",
39171         "690"
39172       ],
39173       [
39174         "Tonga",
39175         "to",
39176         "676"
39177       ],
39178       [
39179         "Trinidad and Tobago",
39180         "tt",
39181         "1868"
39182       ],
39183       [
39184         "Tunisia (‫تونس‬‎)",
39185         "tn",
39186         "216"
39187       ],
39188       [
39189         "Turkey (Türkiye)",
39190         "tr",
39191         "90"
39192       ],
39193       [
39194         "Turkmenistan",
39195         "tm",
39196         "993"
39197       ],
39198       [
39199         "Turks and Caicos Islands",
39200         "tc",
39201         "1649"
39202       ],
39203       [
39204         "Tuvalu",
39205         "tv",
39206         "688"
39207       ],
39208       [
39209         "U.S. Virgin Islands",
39210         "vi",
39211         "1340"
39212       ],
39213       [
39214         "Uganda",
39215         "ug",
39216         "256"
39217       ],
39218       [
39219         "Ukraine (Україна)",
39220         "ua",
39221         "380"
39222       ],
39223       [
39224         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39225         "ae",
39226         "971"
39227       ],
39228       [
39229         "United Kingdom",
39230         "gb",
39231         "44",
39232         0
39233       ],
39234       [
39235         "United States",
39236         "us",
39237         "1",
39238         0
39239       ],
39240       [
39241         "Uruguay",
39242         "uy",
39243         "598"
39244       ],
39245       [
39246         "Uzbekistan (Oʻzbekiston)",
39247         "uz",
39248         "998"
39249       ],
39250       [
39251         "Vanuatu",
39252         "vu",
39253         "678"
39254       ],
39255       [
39256         "Vatican City (Città del Vaticano)",
39257         "va",
39258         "39",
39259         1
39260       ],
39261       [
39262         "Venezuela",
39263         "ve",
39264         "58"
39265       ],
39266       [
39267         "Vietnam (Việt Nam)",
39268         "vn",
39269         "84"
39270       ],
39271       [
39272         "Wallis and Futuna (Wallis-et-Futuna)",
39273         "wf",
39274         "681"
39275       ],
39276       [
39277         "Western Sahara (‫الصحراء الغربية‬‎)",
39278         "eh",
39279         "212",
39280         1
39281       ],
39282       [
39283         "Yemen (‫اليمن‬‎)",
39284         "ye",
39285         "967"
39286       ],
39287       [
39288         "Zambia",
39289         "zm",
39290         "260"
39291       ],
39292       [
39293         "Zimbabwe",
39294         "zw",
39295         "263"
39296       ],
39297       [
39298         "Åland Islands",
39299         "ax",
39300         "358",
39301         1
39302       ]
39303   ];
39304   
39305   return d;
39306 }/**
39307 *    This script refer to:
39308 *    Title: International Telephone Input
39309 *    Author: Jack O'Connor
39310 *    Code version:  v12.1.12
39311 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39312 **/
39313
39314 /**
39315  * @class Roo.bootstrap.PhoneInput
39316  * @extends Roo.bootstrap.TriggerField
39317  * An input with International dial-code selection
39318  
39319  * @cfg {String} defaultDialCode default '+852'
39320  * @cfg {Array} preferedCountries default []
39321   
39322  * @constructor
39323  * Create a new PhoneInput.
39324  * @param {Object} config Configuration options
39325  */
39326
39327 Roo.bootstrap.PhoneInput = function(config) {
39328     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39329 };
39330
39331 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39332         
39333         listWidth: undefined,
39334         
39335         selectedClass: 'active',
39336         
39337         invalidClass : "has-warning",
39338         
39339         validClass: 'has-success',
39340         
39341         allowed: '0123456789',
39342         
39343         /**
39344          * @cfg {String} defaultDialCode The default dial code when initializing the input
39345          */
39346         defaultDialCode: '+852',
39347         
39348         /**
39349          * @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
39350          */
39351         preferedCountries: false,
39352         
39353         getAutoCreate : function()
39354         {
39355             var data = Roo.bootstrap.PhoneInputData();
39356             var align = this.labelAlign || this.parentLabelAlign();
39357             var id = Roo.id();
39358             
39359             this.allCountries = [];
39360             this.dialCodeMapping = [];
39361             
39362             for (var i = 0; i < data.length; i++) {
39363               var c = data[i];
39364               this.allCountries[i] = {
39365                 name: c[0],
39366                 iso2: c[1],
39367                 dialCode: c[2],
39368                 priority: c[3] || 0,
39369                 areaCodes: c[4] || null
39370               };
39371               this.dialCodeMapping[c[2]] = {
39372                   name: c[0],
39373                   iso2: c[1],
39374                   priority: c[3] || 0,
39375                   areaCodes: c[4] || null
39376               };
39377             }
39378             
39379             var cfg = {
39380                 cls: 'form-group',
39381                 cn: []
39382             };
39383             
39384             var input =  {
39385                 tag: 'input',
39386                 id : id,
39387                 cls : 'form-control tel-input',
39388                 autocomplete: 'new-password'
39389             };
39390             
39391             var hiddenInput = {
39392                 tag: 'input',
39393                 type: 'hidden',
39394                 cls: 'hidden-tel-input'
39395             };
39396             
39397             if (this.name) {
39398                 hiddenInput.name = this.name;
39399             }
39400             
39401             if (this.disabled) {
39402                 input.disabled = true;
39403             }
39404             
39405             var flag_container = {
39406                 tag: 'div',
39407                 cls: 'flag-box',
39408                 cn: [
39409                     {
39410                         tag: 'div',
39411                         cls: 'flag'
39412                     },
39413                     {
39414                         tag: 'div',
39415                         cls: 'caret'
39416                     }
39417                 ]
39418             };
39419             
39420             var box = {
39421                 tag: 'div',
39422                 cls: this.hasFeedback ? 'has-feedback' : '',
39423                 cn: [
39424                     hiddenInput,
39425                     input,
39426                     {
39427                         tag: 'input',
39428                         cls: 'dial-code-holder',
39429                         disabled: true
39430                     }
39431                 ]
39432             };
39433             
39434             var container = {
39435                 cls: 'roo-select2-container input-group',
39436                 cn: [
39437                     flag_container,
39438                     box
39439                 ]
39440             };
39441             
39442             if (this.fieldLabel.length) {
39443                 var indicator = {
39444                     tag: 'i',
39445                     tooltip: 'This field is required'
39446                 };
39447                 
39448                 var label = {
39449                     tag: 'label',
39450                     'for':  id,
39451                     cls: 'control-label',
39452                     cn: []
39453                 };
39454                 
39455                 var label_text = {
39456                     tag: 'span',
39457                     html: this.fieldLabel
39458                 };
39459                 
39460                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39461                 label.cn = [
39462                     indicator,
39463                     label_text
39464                 ];
39465                 
39466                 if(this.indicatorpos == 'right') {
39467                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39468                     label.cn = [
39469                         label_text,
39470                         indicator
39471                     ];
39472                 }
39473                 
39474                 if(align == 'left') {
39475                     container = {
39476                         tag: 'div',
39477                         cn: [
39478                             container
39479                         ]
39480                     };
39481                     
39482                     if(this.labelWidth > 12){
39483                         label.style = "width: " + this.labelWidth + 'px';
39484                     }
39485                     if(this.labelWidth < 13 && this.labelmd == 0){
39486                         this.labelmd = this.labelWidth;
39487                     }
39488                     if(this.labellg > 0){
39489                         label.cls += ' col-lg-' + this.labellg;
39490                         input.cls += ' col-lg-' + (12 - this.labellg);
39491                     }
39492                     if(this.labelmd > 0){
39493                         label.cls += ' col-md-' + this.labelmd;
39494                         container.cls += ' col-md-' + (12 - this.labelmd);
39495                     }
39496                     if(this.labelsm > 0){
39497                         label.cls += ' col-sm-' + this.labelsm;
39498                         container.cls += ' col-sm-' + (12 - this.labelsm);
39499                     }
39500                     if(this.labelxs > 0){
39501                         label.cls += ' col-xs-' + this.labelxs;
39502                         container.cls += ' col-xs-' + (12 - this.labelxs);
39503                     }
39504                 }
39505             }
39506             
39507             cfg.cn = [
39508                 label,
39509                 container
39510             ];
39511             
39512             var settings = this;
39513             
39514             ['xs','sm','md','lg'].map(function(size){
39515                 if (settings[size]) {
39516                     cfg.cls += ' col-' + size + '-' + settings[size];
39517                 }
39518             });
39519             
39520             this.store = new Roo.data.Store({
39521                 proxy : new Roo.data.MemoryProxy({}),
39522                 reader : new Roo.data.JsonReader({
39523                     fields : [
39524                         {
39525                             'name' : 'name',
39526                             'type' : 'string'
39527                         },
39528                         {
39529                             'name' : 'iso2',
39530                             'type' : 'string'
39531                         },
39532                         {
39533                             'name' : 'dialCode',
39534                             'type' : 'string'
39535                         },
39536                         {
39537                             'name' : 'priority',
39538                             'type' : 'string'
39539                         },
39540                         {
39541                             'name' : 'areaCodes',
39542                             'type' : 'string'
39543                         }
39544                     ]
39545                 })
39546             });
39547             
39548             if(!this.preferedCountries) {
39549                 this.preferedCountries = [
39550                     'hk',
39551                     'gb',
39552                     'us'
39553                 ];
39554             }
39555             
39556             var p = this.preferedCountries.reverse();
39557             
39558             if(p) {
39559                 for (var i = 0; i < p.length; i++) {
39560                     for (var j = 0; j < this.allCountries.length; j++) {
39561                         if(this.allCountries[j].iso2 == p[i]) {
39562                             var t = this.allCountries[j];
39563                             this.allCountries.splice(j,1);
39564                             this.allCountries.unshift(t);
39565                         }
39566                     } 
39567                 }
39568             }
39569             
39570             this.store.proxy.data = {
39571                 success: true,
39572                 data: this.allCountries
39573             };
39574             
39575             return cfg;
39576         },
39577         
39578         initEvents : function()
39579         {
39580             this.createList();
39581             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39582             
39583             this.indicator = this.indicatorEl();
39584             this.flag = this.flagEl();
39585             this.dialCodeHolder = this.dialCodeHolderEl();
39586             
39587             this.trigger = this.el.select('div.flag-box',true).first();
39588             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39589             
39590             var _this = this;
39591             
39592             (function(){
39593                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39594                 _this.list.setWidth(lw);
39595             }).defer(100);
39596             
39597             this.list.on('mouseover', this.onViewOver, this);
39598             this.list.on('mousemove', this.onViewMove, this);
39599             this.inputEl().on("keyup", this.onKeyUp, this);
39600             
39601             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39602
39603             this.view = new Roo.View(this.list, this.tpl, {
39604                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39605             });
39606             
39607             this.view.on('click', this.onViewClick, this);
39608             this.setValue(this.defaultDialCode);
39609         },
39610         
39611         onTriggerClick : function(e)
39612         {
39613             Roo.log('trigger click');
39614             if(this.disabled){
39615                 return;
39616             }
39617             
39618             if(this.isExpanded()){
39619                 this.collapse();
39620                 this.hasFocus = false;
39621             }else {
39622                 this.store.load({});
39623                 this.hasFocus = true;
39624                 this.expand();
39625             }
39626         },
39627         
39628         isExpanded : function()
39629         {
39630             return this.list.isVisible();
39631         },
39632         
39633         collapse : function()
39634         {
39635             if(!this.isExpanded()){
39636                 return;
39637             }
39638             this.list.hide();
39639             Roo.get(document).un('mousedown', this.collapseIf, this);
39640             Roo.get(document).un('mousewheel', this.collapseIf, this);
39641             this.fireEvent('collapse', this);
39642             this.validate();
39643         },
39644         
39645         expand : function()
39646         {
39647             Roo.log('expand');
39648
39649             if(this.isExpanded() || !this.hasFocus){
39650                 return;
39651             }
39652             
39653             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39654             this.list.setWidth(lw);
39655             
39656             this.list.show();
39657             this.restrictHeight();
39658             
39659             Roo.get(document).on('mousedown', this.collapseIf, this);
39660             Roo.get(document).on('mousewheel', this.collapseIf, this);
39661             
39662             this.fireEvent('expand', this);
39663         },
39664         
39665         restrictHeight : function()
39666         {
39667             this.list.alignTo(this.inputEl(), this.listAlign);
39668             this.list.alignTo(this.inputEl(), this.listAlign);
39669         },
39670         
39671         onViewOver : function(e, t)
39672         {
39673             if(this.inKeyMode){
39674                 return;
39675             }
39676             var item = this.view.findItemFromChild(t);
39677             
39678             if(item){
39679                 var index = this.view.indexOf(item);
39680                 this.select(index, false);
39681             }
39682         },
39683
39684         // private
39685         onViewClick : function(view, doFocus, el, e)
39686         {
39687             var index = this.view.getSelectedIndexes()[0];
39688             
39689             var r = this.store.getAt(index);
39690             
39691             if(r){
39692                 this.onSelect(r, index);
39693             }
39694             if(doFocus !== false && !this.blockFocus){
39695                 this.inputEl().focus();
39696             }
39697         },
39698         
39699         onViewMove : function(e, t)
39700         {
39701             this.inKeyMode = false;
39702         },
39703         
39704         select : function(index, scrollIntoView)
39705         {
39706             this.selectedIndex = index;
39707             this.view.select(index);
39708             if(scrollIntoView !== false){
39709                 var el = this.view.getNode(index);
39710                 if(el){
39711                     this.list.scrollChildIntoView(el, false);
39712                 }
39713             }
39714         },
39715         
39716         createList : function()
39717         {
39718             this.list = Roo.get(document.body).createChild({
39719                 tag: 'ul',
39720                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39721                 style: 'display:none'
39722             });
39723             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39724         },
39725         
39726         collapseIf : function(e)
39727         {
39728             var in_combo  = e.within(this.el);
39729             var in_list =  e.within(this.list);
39730             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39731             
39732             if (in_combo || in_list || is_list) {
39733                 return;
39734             }
39735             this.collapse();
39736         },
39737         
39738         onSelect : function(record, index)
39739         {
39740             if(this.fireEvent('beforeselect', this, record, index) !== false){
39741                 
39742                 this.setFlagClass(record.data.iso2);
39743                 this.setDialCode(record.data.dialCode);
39744                 this.hasFocus = false;
39745                 this.collapse();
39746                 this.fireEvent('select', this, record, index);
39747             }
39748         },
39749         
39750         flagEl : function()
39751         {
39752             var flag = this.el.select('div.flag',true).first();
39753             if(!flag){
39754                 return false;
39755             }
39756             return flag;
39757         },
39758         
39759         dialCodeHolderEl : function()
39760         {
39761             var d = this.el.select('input.dial-code-holder',true).first();
39762             if(!d){
39763                 return false;
39764             }
39765             return d;
39766         },
39767         
39768         setDialCode : function(v)
39769         {
39770             this.dialCodeHolder.dom.value = '+'+v;
39771         },
39772         
39773         setFlagClass : function(n)
39774         {
39775             this.flag.dom.className = 'flag '+n;
39776         },
39777         
39778         getValue : function()
39779         {
39780             var v = this.inputEl().getValue();
39781             if(this.dialCodeHolder) {
39782                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39783             }
39784             return v;
39785         },
39786         
39787         setValue : function(v)
39788         {
39789             var d = this.getDialCode(v);
39790             
39791             //invalid dial code
39792             if(v.length == 0 || !d || d.length == 0) {
39793                 if(this.rendered){
39794                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39795                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39796                 }
39797                 return;
39798             }
39799             
39800             //valid dial code
39801             this.setFlagClass(this.dialCodeMapping[d].iso2);
39802             this.setDialCode(d);
39803             this.inputEl().dom.value = v.replace('+'+d,'');
39804             this.hiddenEl().dom.value = this.getValue();
39805             
39806             this.validate();
39807         },
39808         
39809         getDialCode : function(v = '')
39810         {
39811             if (v.length == 0) {
39812                 return this.dialCodeHolder.dom.value;
39813             }
39814             
39815             var dialCode = "";
39816             if (v.charAt(0) != "+") {
39817                 return false;
39818             }
39819             var numericChars = "";
39820             for (var i = 1; i < v.length; i++) {
39821               var c = v.charAt(i);
39822               if (!isNaN(c)) {
39823                 numericChars += c;
39824                 if (this.dialCodeMapping[numericChars]) {
39825                   dialCode = v.substr(1, i);
39826                 }
39827                 if (numericChars.length == 4) {
39828                   break;
39829                 }
39830               }
39831             }
39832             return dialCode;
39833         },
39834         
39835         reset : function()
39836         {
39837             this.setValue(this.defaultDialCode);
39838             this.validate();
39839         },
39840         
39841         hiddenEl : function()
39842         {
39843             return this.el.select('input.hidden-tel-input',true).first();
39844         },
39845         
39846         onKeyUp : function(e){
39847             
39848             var k = e.getKey();
39849             var c = e.getCharCode();
39850             
39851             if(
39852                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39853                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39854             ){
39855                 e.stopEvent();
39856             }
39857             
39858             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39859             //     return;
39860             // }
39861             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39862                 e.stopEvent();
39863             }
39864             
39865             this.setValue(this.getValue());
39866         }
39867         
39868 });
39869 /**
39870  * @class Roo.bootstrap.MoneyField
39871  * @extends Roo.bootstrap.ComboBox
39872  * Bootstrap MoneyField class
39873  * 
39874  * @constructor
39875  * Create a new MoneyField.
39876  * @param {Object} config Configuration options
39877  */
39878
39879 Roo.bootstrap.MoneyField = function(config) {
39880     
39881     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39882     
39883 };
39884
39885 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39886     
39887     /**
39888      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39889      */
39890     allowDecimals : true,
39891     /**
39892      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39893      */
39894     decimalSeparator : ".",
39895     /**
39896      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39897      */
39898     decimalPrecision : 2,
39899     /**
39900      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39901      */
39902     allowNegative : true,
39903     /**
39904      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39905      */
39906     minValue : Number.NEGATIVE_INFINITY,
39907     /**
39908      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39909      */
39910     maxValue : Number.MAX_VALUE,
39911     /**
39912      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39913      */
39914     minText : "The minimum value for this field is {0}",
39915     /**
39916      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39917      */
39918     maxText : "The maximum value for this field is {0}",
39919     /**
39920      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39921      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39922      */
39923     nanText : "{0} is not a valid number",
39924     /**
39925      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39926      */
39927     castInt : true,
39928     
39929     inputlg : 9,
39930     inputmd : 9,
39931     inputsm : 9,
39932     inputxs : 6,
39933     
39934     store : false,
39935     
39936     getAutoCreate : function()
39937     {
39938         var align = this.labelAlign || this.parentLabelAlign();
39939         
39940         var id = Roo.id();
39941
39942         var cfg = {
39943             cls: 'form-group',
39944             cn: []
39945         };
39946
39947         var input =  {
39948             tag: 'input',
39949             id : id,
39950             cls : 'form-control roo-money-amount-input',
39951             autocomplete: 'new-password'
39952         };
39953         
39954         if (this.name) {
39955             input.name = this.name;
39956         }
39957
39958         if (this.disabled) {
39959             input.disabled = true;
39960         }
39961
39962         var clg = 12 - this.inputlg;
39963         var cmd = 12 - this.inputmd;
39964         var csm = 12 - this.inputsm;
39965         var cxs = 12 - this.inputxs;
39966         
39967         var container = {
39968             tag : 'div',
39969             cls : 'row roo-money-field',
39970             cn : [
39971                 {
39972                     tag : 'div',
39973                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39974                     cn : [
39975                         {
39976                             tag : 'div',
39977                             cls: 'roo-select2-container input-group',
39978                             cn: [
39979                                 {
39980                                     tag : 'input',
39981                                     cls : 'form-control roo-money-currency-input',
39982                                     autocomplete: 'new-password',
39983                                     readOnly : 1,
39984                                     name : this.currencyName
39985                                 },
39986                                 {
39987                                     tag :'span',
39988                                     cls : 'input-group-addon',
39989                                     cn : [
39990                                         {
39991                                             tag: 'span',
39992                                             cls: 'caret'
39993                                         }
39994                                     ]
39995                                 }
39996                             ]
39997                         }
39998                     ]
39999                 },
40000                 {
40001                     tag : 'div',
40002                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40003                     cn : [
40004                         {
40005                             tag: 'div',
40006                             cls: this.hasFeedback ? 'has-feedback' : '',
40007                             cn: [
40008                                 input
40009                             ]
40010                         }
40011                     ]
40012                 }
40013             ]
40014             
40015         };
40016         
40017         if (this.fieldLabel.length) {
40018             var indicator = {
40019                 tag: 'i',
40020                 tooltip: 'This field is required'
40021             };
40022
40023             var label = {
40024                 tag: 'label',
40025                 'for':  id,
40026                 cls: 'control-label',
40027                 cn: []
40028             };
40029
40030             var label_text = {
40031                 tag: 'span',
40032                 html: this.fieldLabel
40033             };
40034
40035             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40036             label.cn = [
40037                 indicator,
40038                 label_text
40039             ];
40040
40041             if(this.indicatorpos == 'right') {
40042                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40043                 label.cn = [
40044                     label_text,
40045                     indicator
40046                 ];
40047             }
40048
40049             if(align == 'left') {
40050                 container = {
40051                     tag: 'div',
40052                     cn: [
40053                         container
40054                     ]
40055                 };
40056
40057                 if(this.labelWidth > 12){
40058                     label.style = "width: " + this.labelWidth + 'px';
40059                 }
40060                 if(this.labelWidth < 13 && this.labelmd == 0){
40061                     this.labelmd = this.labelWidth;
40062                 }
40063                 if(this.labellg > 0){
40064                     label.cls += ' col-lg-' + this.labellg;
40065                     input.cls += ' col-lg-' + (12 - this.labellg);
40066                 }
40067                 if(this.labelmd > 0){
40068                     label.cls += ' col-md-' + this.labelmd;
40069                     container.cls += ' col-md-' + (12 - this.labelmd);
40070                 }
40071                 if(this.labelsm > 0){
40072                     label.cls += ' col-sm-' + this.labelsm;
40073                     container.cls += ' col-sm-' + (12 - this.labelsm);
40074                 }
40075                 if(this.labelxs > 0){
40076                     label.cls += ' col-xs-' + this.labelxs;
40077                     container.cls += ' col-xs-' + (12 - this.labelxs);
40078                 }
40079             }
40080         }
40081
40082         cfg.cn = [
40083             label,
40084             container
40085         ];
40086
40087         var settings = this;
40088
40089         ['xs','sm','md','lg'].map(function(size){
40090             if (settings[size]) {
40091                 cfg.cls += ' col-' + size + '-' + settings[size];
40092             }
40093         });
40094         
40095         return cfg;
40096         
40097     },
40098     
40099     initEvents : function()
40100     {
40101         this.indicator = this.indicatorEl();
40102         
40103         this.initCurrencyEvent();
40104         
40105         this.initNumberEvent();
40106         
40107     },
40108     
40109     initCurrencyEvent : function()
40110     {
40111         if (!this.store) {
40112             throw "can not find store for combo";
40113         }
40114         
40115         this.store = Roo.factory(this.store, Roo.data);
40116         this.store.parent = this;
40117         
40118         this.createList();
40119         
40120         this.triggerEl = this.el.select('.input-group-addon', true).first();
40121         
40122         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40123         
40124         var _this = this;
40125         
40126         (function(){
40127             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40128             _this.list.setWidth(lw);
40129         }).defer(100);
40130         
40131         this.list.on('mouseover', this.onViewOver, this);
40132         this.list.on('mousemove', this.onViewMove, this);
40133         this.list.on('scroll', this.onViewScroll, this);
40134         
40135         if(!this.tpl){
40136             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40137         }
40138         
40139         this.view = new Roo.View(this.list, this.tpl, {
40140             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40141         });
40142         
40143         this.view.on('click', this.onViewClick, this);
40144         
40145         this.store.on('beforeload', this.onBeforeLoad, this);
40146         this.store.on('load', this.onLoad, this);
40147         this.store.on('loadexception', this.onLoadException, this);
40148         
40149         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40150             "up" : function(e){
40151                 this.inKeyMode = true;
40152                 this.selectPrev();
40153             },
40154
40155             "down" : function(e){
40156                 if(!this.isExpanded()){
40157                     this.onTriggerClick();
40158                 }else{
40159                     this.inKeyMode = true;
40160                     this.selectNext();
40161                 }
40162             },
40163
40164             "enter" : function(e){
40165                 this.collapse();
40166                 
40167                 if(this.fireEvent("specialkey", this, e)){
40168                     this.onViewClick(false);
40169                 }
40170                 
40171                 return true;
40172             },
40173
40174             "esc" : function(e){
40175                 this.collapse();
40176             },
40177
40178             "tab" : function(e){
40179                 this.collapse();
40180                 
40181                 if(this.fireEvent("specialkey", this, e)){
40182                     this.onViewClick(false);
40183                 }
40184                 
40185                 return true;
40186             },
40187
40188             scope : this,
40189
40190             doRelay : function(foo, bar, hname){
40191                 if(hname == 'down' || this.scope.isExpanded()){
40192                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40193                 }
40194                 return true;
40195             },
40196
40197             forceKeyDown: true
40198         });
40199         
40200         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40201         
40202     },
40203     
40204     initNumberEvent : function(e)
40205     {
40206         this.inputEl().on("keydown" , this.fireKey,  this);
40207         this.inputEl().on("focus", this.onFocus,  this);
40208         this.inputEl().on("blur", this.onBlur,  this);
40209         
40210         this.inputEl().relayEvent('keyup', this);
40211         
40212         if(this.indicator){
40213             this.indicator.addClass('invisible');
40214         }
40215  
40216         this.originalValue = this.getValue();
40217         
40218         if(this.validationEvent == 'keyup'){
40219             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40220             this.inputEl().on('keyup', this.filterValidation, this);
40221         }
40222         else if(this.validationEvent !== false){
40223             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40224         }
40225         
40226         if(this.selectOnFocus){
40227             this.on("focus", this.preFocus, this);
40228             
40229         }
40230         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40231             this.inputEl().on("keypress", this.filterKeys, this);
40232         } else {
40233             this.inputEl().relayEvent('keypress', this);
40234         }
40235         
40236         var allowed = "0123456789";
40237         
40238         if(this.allowDecimals){
40239             allowed += this.decimalSeparator;
40240         }
40241         
40242         if(this.allowNegative){
40243             allowed += "-";
40244         }
40245         
40246         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40247         
40248         var keyPress = function(e){
40249             
40250             var k = e.getKey();
40251             
40252             var c = e.getCharCode();
40253             
40254             if(
40255                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40256                     allowed.indexOf(String.fromCharCode(c)) === -1
40257             ){
40258                 e.stopEvent();
40259                 return;
40260             }
40261             
40262             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40263                 return;
40264             }
40265             
40266             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40267                 e.stopEvent();
40268             }
40269         };
40270         
40271         this.inputEl().on("keypress", keyPress, this);
40272         
40273     },
40274     
40275     onTriggerClick : function(e)
40276     {   
40277         if(this.disabled){
40278             return;
40279         }
40280         
40281         this.page = 0;
40282         this.loadNext = false;
40283         
40284         if(this.isExpanded()){
40285             this.collapse();
40286             return;
40287         }
40288         
40289         this.hasFocus = true;
40290         
40291         if(this.triggerAction == 'all') {
40292             this.doQuery(this.allQuery, true);
40293             return;
40294         }
40295         
40296         this.doQuery(this.getRawValue());
40297     },
40298     
40299     getCurrency : function()
40300     {   
40301         var v = this.currencyEl().getValue();
40302         
40303         return v;
40304     },
40305     
40306     restrictHeight : function()
40307     {
40308         this.list.alignTo(this.currencyEl(), this.listAlign);
40309         this.list.alignTo(this.currencyEl(), this.listAlign);
40310     },
40311     
40312     onViewClick : function(view, doFocus, el, e)
40313     {
40314         var index = this.view.getSelectedIndexes()[0];
40315         
40316         var r = this.store.getAt(index);
40317         
40318         if(r){
40319             this.onSelect(r, index);
40320         }
40321     },
40322     
40323     onSelect : function(record, index){
40324         
40325         if(this.fireEvent('beforeselect', this, record, index) !== false){
40326         
40327             this.setFromCurrencyData(index > -1 ? record.data : false);
40328             
40329             this.collapse();
40330             
40331             this.fireEvent('select', this, record, index);
40332         }
40333     },
40334     
40335     setFromCurrencyData : function(o)
40336     {
40337         var currency = '';
40338         
40339         this.lastCurrency = o;
40340         
40341         if (this.currencyField) {
40342             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40343         } else {
40344             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40345         }
40346         
40347         this.lastSelectionText = currency;
40348         
40349         this.setCurrency(currency);
40350     },
40351     
40352     setFromData : function(o)
40353     {
40354         var c = {};
40355         
40356         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40357         
40358         this.setFromCurrencyData(c);
40359         
40360         var value = '';
40361         
40362         if (this.name) {
40363             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40364         } else {
40365             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40366         }
40367         
40368         this.setValue(value);
40369         
40370     },
40371     
40372     setCurrency : function(v)
40373     {   
40374         this.currencyValue = v;
40375         
40376         if(this.rendered){
40377             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40378             this.validate();
40379         }
40380     },
40381     
40382     setValue : function(v)
40383     {
40384         v = this.fixPrecision(v);
40385         
40386         v = String(v).replace(".", this.decimalSeparator);
40387         
40388         this.value = v;
40389         
40390         if(this.rendered){
40391             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40392             this.validate();
40393         }
40394     },
40395     
40396     getRawValue : function()
40397     {
40398         var v = this.inputEl().getValue();
40399         
40400         return v;
40401     },
40402     
40403     getValue : function()
40404     {
40405         return this.fixPrecision(this.parseValue(this.getRawValue()));
40406     },
40407     
40408     parseValue : function(value)
40409     {
40410         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40411         return isNaN(value) ? '' : value;
40412     },
40413     
40414     fixPrecision : function(value)
40415     {
40416         var nan = isNaN(value);
40417         
40418         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40419             return nan ? '' : value;
40420         }
40421         
40422         return parseFloat(value).toFixed(this.decimalPrecision);
40423     },
40424     
40425     decimalPrecisionFcn : function(v)
40426     {
40427         return Math.floor(v);
40428     },
40429     
40430     validateValue : function(value)
40431     {
40432         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40433             return false;
40434         }
40435         
40436         var num = this.parseValue(value);
40437         
40438         if(isNaN(num)){
40439             this.markInvalid(String.format(this.nanText, value));
40440             return false;
40441         }
40442         
40443         if(num < this.minValue){
40444             this.markInvalid(String.format(this.minText, this.minValue));
40445             return false;
40446         }
40447         
40448         if(num > this.maxValue){
40449             this.markInvalid(String.format(this.maxText, this.maxValue));
40450             return false;
40451         }
40452         
40453         return true;
40454     },
40455     
40456     validate : function()
40457     {
40458         if(this.disabled || this.allowBlank){
40459             this.markValid();
40460             return true;
40461         }
40462         
40463         var currency = this.getCurrency();
40464         
40465         if(this.validateValue(this.getRawValue()) && currency.length){
40466             this.markValid();
40467             return true;
40468         }
40469         
40470         this.markInvalid();
40471         return false;
40472     },
40473     
40474     getName: function()
40475     {
40476         return this.name;
40477     },
40478     
40479     beforeBlur : function()
40480     {
40481         if(!this.castInt){
40482             return;
40483         }
40484         
40485         var v = this.parseValue(this.getRawValue());
40486         
40487         if(v){
40488             this.setValue(v);
40489         }
40490     },
40491     
40492     onBlur : function()
40493     {
40494         this.beforeBlur();
40495         
40496         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40497             //this.el.removeClass(this.focusClass);
40498         }
40499         
40500         this.hasFocus = false;
40501         
40502         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40503             this.validate();
40504         }
40505         
40506         var v = this.getValue();
40507         
40508         if(String(v) !== String(this.startValue)){
40509             this.fireEvent('change', this, v, this.startValue);
40510         }
40511         
40512         this.fireEvent("blur", this);
40513     },
40514     
40515     inputEl : function()
40516     {
40517         return this.el.select('.roo-money-amount-input', true).first();
40518     },
40519     
40520     currencyEl : function()
40521     {
40522         return this.el.select('.roo-money-currency-input', true).first();
40523     }
40524     
40525 });