docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
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         this.initEvents();
140         
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     addxtypeChild : function (tree, cntr, is_body)
234     {
235         Roo.debug && Roo.log('addxtypeChild:' + cntr);
236         var cn = this;
237         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
238         
239         
240         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
241                     (typeof(tree['flexy:foreach']) != 'undefined');
242           
243         
244         
245          skip_children = false;
246         // render the element if it's not BODY.
247         if (!is_body) {
248            
249             cn = Roo.factory(tree);
250            
251             cn.parentType = this.xtype; //??
252             cn.parentId = this.id;
253             
254             var build_from_html =  Roo.XComponent.build_from_html;
255             
256             
257             // does the container contain child eleemnts with 'xtype' attributes.
258             // that match this xtype..
259             // note - when we render we create these as well..
260             // so we should check to see if body has xtype set.
261             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
262                
263                 var self_cntr_el = Roo.get(this[cntr](false));
264                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
265                 if (echild) { 
266                     //Roo.log(Roo.XComponent.build_from_html);
267                     //Roo.log("got echild:");
268                     //Roo.log(echild);
269                 }
270                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
271                 // and are not displayed -this causes this to use up the wrong element when matching.
272                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
273                 
274                 
275                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
276                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
277                   
278                   
279                   
280                     cn.el = echild;
281                   //  Roo.log("GOT");
282                     //echild.dom.removeAttribute('xtype');
283                 } else {
284                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
285                     Roo.debug && Roo.log(self_cntr_el);
286                     Roo.debug && Roo.log(echild);
287                     Roo.debug && Roo.log(cn);
288                 }
289             }
290            
291             
292            
293             // if object has flexy:if - then it may or may not be rendered.
294             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
295                 // skip a flexy if element.
296                 Roo.debug && Roo.log('skipping render');
297                 Roo.debug && Roo.log(tree);
298                 if (!cn.el) {
299                     Roo.debug && Roo.log('skipping all children');
300                     skip_children = true;
301                 }
302                 
303              } else {
304                  
305                 // actually if flexy:foreach is found, we really want to create 
306                 // multiple copies here...
307                 //Roo.log('render');
308                 //Roo.log(this[cntr]());
309                 cn.render(this[cntr](true));
310              }
311             // then add the element..
312         }
313         
314         
315         // handle the kids..
316         
317         var nitems = [];
318         /*
319         if (typeof (tree.menu) != 'undefined') {
320             tree.menu.parentType = cn.xtype;
321             tree.menu.triggerEl = cn.el;
322             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
323             
324         }
325         */
326         if (!tree.items || !tree.items.length) {
327             cn.items = nitems;
328             //Roo.log(["no children", this]);
329             
330             return cn;
331         }
332          
333         var items = tree.items;
334         delete tree.items;
335         
336         //Roo.log(items.length);
337             // add the items..
338         if (!skip_children) {    
339             for(var i =0;i < items.length;i++) {
340               //  Roo.log(['add child', items[i]]);
341                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
342             }
343         }
344         
345         cn.items = nitems;
346         
347         //Roo.log("fire childrenrendered");
348         
349         cn.fireEvent('childrenrendered', this);
350         
351         return cn;
352     },
353     /**
354      * Show a component - removes 'hidden' class
355      */
356     show : function()
357     {
358         if (this.el) {
359             this.el.removeClass('hidden');
360         }
361     },
362     /**
363      * Hide a component - adds 'hidden' class
364      */
365     hide: function()
366     {
367         if (this.el && !this.el.hasClass('hidden')) {
368             this.el.addClass('hidden');
369         }
370         
371     }
372 });
373
374  /*
375  * - LGPL
376  *
377  * Body
378  * 
379  */
380
381 /**
382  * @class Roo.bootstrap.Body
383  * @extends Roo.bootstrap.Component
384  * Bootstrap Body class
385  * 
386  * @constructor
387  * Create a new body
388  * @param {Object} config The config object
389  */
390
391 Roo.bootstrap.Body = function(config){
392     Roo.bootstrap.Body.superclass.constructor.call(this, config);
393     this.el = Roo.get(document.body);
394     if (this.cls && this.cls.length) {
395         Roo.get(document.body).addClass(this.cls);
396     }
397 };
398
399 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
400     
401     is_body : true,// just to make sure it's constructed?
402     
403         autoCreate : {
404         cls: 'container'
405     },
406     onRender : function(ct, position)
407     {
408        /* Roo.log("Roo.bootstrap.Body - onRender");
409         if (this.cls && this.cls.length) {
410             Roo.get(document.body).addClass(this.cls);
411         }
412         // style??? xttr???
413         */
414     }
415     
416     
417  
418    
419 });
420
421  /*
422  * - LGPL
423  *
424  * button group
425  * 
426  */
427
428
429 /**
430  * @class Roo.bootstrap.ButtonGroup
431  * @extends Roo.bootstrap.Component
432  * Bootstrap ButtonGroup class
433  * @cfg {String} size lg | sm | xs (default empty normal)
434  * @cfg {String} align vertical | justified  (default none)
435  * @cfg {String} direction up | down (default down)
436  * @cfg {Boolean} toolbar false | true
437  * @cfg {Boolean} btn true | false
438  * 
439  * 
440  * @constructor
441  * Create a new Input
442  * @param {Object} config The config object
443  */
444
445 Roo.bootstrap.ButtonGroup = function(config){
446     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
447 };
448
449 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
450     
451     size: '',
452     align: '',
453     direction: '',
454     toolbar: false,
455     btn: true,
456
457     getAutoCreate : function(){
458         var cfg = {
459             cls: 'btn-group',
460             html : null
461         };
462         
463         cfg.html = this.html || cfg.html;
464         
465         if (this.toolbar) {
466             cfg = {
467                 cls: 'btn-toolbar',
468                 html: null
469             };
470             
471             return cfg;
472         }
473         
474         if (['vertical','justified'].indexOf(this.align)!==-1) {
475             cfg.cls = 'btn-group-' + this.align;
476             
477             if (this.align == 'justified') {
478                 console.log(this.items);
479             }
480         }
481         
482         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
483             cfg.cls += ' btn-group-' + this.size;
484         }
485         
486         if (this.direction == 'up') {
487             cfg.cls += ' dropup' ;
488         }
489         
490         return cfg;
491     }
492    
493 });
494
495  /*
496  * - LGPL
497  *
498  * button
499  * 
500  */
501
502 /**
503  * @class Roo.bootstrap.Button
504  * @extends Roo.bootstrap.Component
505  * Bootstrap Button class
506  * @cfg {String} html The button content
507  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
508  * @cfg {String} size ( lg | sm | xs)
509  * @cfg {String} tag ( a | input | submit)
510  * @cfg {String} href empty or href
511  * @cfg {Boolean} disabled default false;
512  * @cfg {Boolean} isClose default false;
513  * @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)
514  * @cfg {String} badge text for badge
515  * @cfg {String} theme default 
516  * @cfg {Boolean} inverse 
517  * @cfg {Boolean} toggle 
518  * @cfg {String} ontext text for on toggle state
519  * @cfg {String} offtext text for off toggle state
520  * @cfg {Boolean} defaulton 
521  * @cfg {Boolean} preventDefault  default true
522  * @cfg {Boolean} removeClass remove the standard class..
523  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
524  * 
525  * @constructor
526  * Create a new button
527  * @param {Object} config The config object
528  */
529
530
531 Roo.bootstrap.Button = function(config){
532     Roo.bootstrap.Button.superclass.constructor.call(this, config);
533     this.addEvents({
534         // raw events
535         /**
536          * @event click
537          * When a butotn is pressed
538          * @param {Roo.bootstrap.Button} this
539          * @param {Roo.EventObject} e
540          */
541         "click" : true,
542          /**
543          * @event toggle
544          * After the button has been toggles
545          * @param {Roo.EventObject} e
546          * @param {boolean} pressed (also available as button.pressed)
547          */
548         "toggle" : true
549     });
550 };
551
552 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
553     html: false,
554     active: false,
555     weight: '',
556     size: '',
557     tag: 'button',
558     href: '',
559     disabled: false,
560     isClose: false,
561     glyphicon: '',
562     badge: '',
563     theme: 'default',
564     inverse: false,
565     
566     toggle: false,
567     ontext: 'ON',
568     offtext: 'OFF',
569     defaulton: true,
570     preventDefault: true,
571     removeClass: false,
572     name: false,
573     target: false,
574     
575     
576     pressed : null,
577      
578     
579     getAutoCreate : function(){
580         
581         var cfg = {
582             tag : 'button',
583             cls : 'roo-button',
584             html: ''
585         };
586         
587         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
588             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
589             this.tag = 'button';
590         } else {
591             cfg.tag = this.tag;
592         }
593         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
594         
595         if (this.toggle == true) {
596             cfg={
597                 tag: 'div',
598                 cls: 'slider-frame roo-button',
599                 cn: [
600                     {
601                         tag: 'span',
602                         'data-on-text':'ON',
603                         'data-off-text':'OFF',
604                         cls: 'slider-button',
605                         html: this.offtext
606                     }
607                 ]
608             };
609             
610             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
611                 cfg.cls += ' '+this.weight;
612             }
613             
614             return cfg;
615         }
616         
617         if (this.isClose) {
618             cfg.cls += ' close';
619             
620             cfg["aria-hidden"] = true;
621             
622             cfg.html = "&times;";
623             
624             return cfg;
625         }
626         
627          
628         if (this.theme==='default') {
629             cfg.cls = 'btn roo-button';
630             
631             //if (this.parentType != 'Navbar') {
632             this.weight = this.weight.length ?  this.weight : 'default';
633             //}
634             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
635                 
636                 cfg.cls += ' btn-' + this.weight;
637             }
638         } else if (this.theme==='glow') {
639             
640             cfg.tag = 'a';
641             cfg.cls = 'btn-glow roo-button';
642             
643             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
644                 
645                 cfg.cls += ' ' + this.weight;
646             }
647         }
648    
649         
650         if (this.inverse) {
651             this.cls += ' inverse';
652         }
653         
654         
655         if (this.active) {
656             cfg.cls += ' active';
657         }
658         
659         if (this.disabled) {
660             cfg.disabled = 'disabled';
661         }
662         
663         if (this.items) {
664             Roo.log('changing to ul' );
665             cfg.tag = 'ul';
666             this.glyphicon = 'caret';
667         }
668         
669         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
670          
671         //gsRoo.log(this.parentType);
672         if (this.parentType === 'Navbar' && !this.parent().bar) {
673             Roo.log('changing to li?');
674             
675             cfg.tag = 'li';
676             
677             cfg.cls = '';
678             cfg.cn =  [{
679                 tag : 'a',
680                 cls : 'roo-button',
681                 html : this.html,
682                 href : this.href || '#'
683             }];
684             if (this.menu) {
685                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
686                 cfg.cls += ' dropdown';
687             }   
688             
689             delete cfg.html;
690             
691         }
692         
693        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
694         
695         if (this.glyphicon) {
696             cfg.html = ' ' + cfg.html;
697             
698             cfg.cn = [
699                 {
700                     tag: 'span',
701                     cls: 'glyphicon glyphicon-' + this.glyphicon
702                 }
703             ];
704         }
705         
706         if (this.badge) {
707             cfg.html += ' ';
708             
709             cfg.tag = 'a';
710             
711 //            cfg.cls='btn roo-button';
712             
713             cfg.href=this.href;
714             
715             var value = cfg.html;
716             
717             if(this.glyphicon){
718                 value = {
719                             tag: 'span',
720                             cls: 'glyphicon glyphicon-' + this.glyphicon,
721                             html: this.html
722                         };
723                 
724             }
725             
726             cfg.cn = [
727                 value,
728                 {
729                     tag: 'span',
730                     cls: 'badge',
731                     html: this.badge
732                 }
733             ];
734             
735             cfg.html='';
736         }
737         
738         if (this.menu) {
739             cfg.cls += ' dropdown';
740             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
741         }
742         
743         if (cfg.tag !== 'a' && this.href !== '') {
744             throw "Tag must be a to set href.";
745         } else if (this.href.length > 0) {
746             cfg.href = this.href;
747         }
748         
749         if(this.removeClass){
750             cfg.cls = '';
751         }
752         
753         if(this.target){
754             cfg.target = this.target;
755         }
756         
757         return cfg;
758     },
759     initEvents: function() {
760        // Roo.log('init events?');
761 //        Roo.log(this.el.dom);
762         // add the menu...
763         
764         if (typeof (this.menu) != 'undefined') {
765             this.menu.parentType = this.xtype;
766             this.menu.triggerEl = this.el;
767             this.addxtype(Roo.apply({}, this.menu));
768         }
769
770
771        if (this.el.hasClass('roo-button')) {
772             this.el.on('click', this.onClick, this);
773        } else {
774             this.el.select('.roo-button').on('click', this.onClick, this);
775        }
776        
777        if(this.removeClass){
778            this.el.on('click', this.onClick, this);
779        }
780        
781        this.el.enableDisplayMode();
782         
783     },
784     onClick : function(e)
785     {
786         if (this.disabled) {
787             return;
788         }
789         
790         
791         Roo.log('button on click ');
792         if(this.preventDefault){
793             e.preventDefault();
794         }
795         if (this.pressed === true || this.pressed === false) {
796             this.pressed = !this.pressed;
797             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
798             this.fireEvent('toggle', this, e, this.pressed);
799         }
800         
801         
802         this.fireEvent('click', this, e);
803     },
804     
805     /**
806      * Enables this button
807      */
808     enable : function()
809     {
810         this.disabled = false;
811         this.el.removeClass('disabled');
812     },
813     
814     /**
815      * Disable this button
816      */
817     disable : function()
818     {
819         this.disabled = true;
820         this.el.addClass('disabled');
821     },
822      /**
823      * sets the active state on/off, 
824      * @param {Boolean} state (optional) Force a particular state
825      */
826     setActive : function(v) {
827         
828         this.el[v ? 'addClass' : 'removeClass']('active');
829     },
830      /**
831      * toggles the current active state 
832      */
833     toggleActive : function()
834     {
835        var active = this.el.hasClass('active');
836        this.setActive(!active);
837        
838         
839     },
840     setText : function(str)
841     {
842         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
843     },
844     getText : function()
845     {
846         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
847     },
848     hide: function() {
849        
850      
851         this.el.hide();   
852     },
853     show: function() {
854        
855         this.el.show();   
856     }
857     
858     
859 });
860
861  /*
862  * - LGPL
863  *
864  * column
865  * 
866  */
867
868 /**
869  * @class Roo.bootstrap.Column
870  * @extends Roo.bootstrap.Component
871  * Bootstrap Column class
872  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
873  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
874  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
875  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
876  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
880  *
881  * 
882  * @cfg {Boolean} hidden (true|false) hide the element
883  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
884  * @cfg {String} fa (ban|check|...) font awesome icon
885  * @cfg {Number} fasize (1|2|....) font awsome size
886
887  * @cfg {String} icon (info-sign|check|...) glyphicon name
888
889  * @cfg {String} html content of column.
890  * 
891  * @constructor
892  * Create a new Column
893  * @param {Object} config The config object
894  */
895
896 Roo.bootstrap.Column = function(config){
897     Roo.bootstrap.Column.superclass.constructor.call(this, config);
898 };
899
900 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
901     
902     xs: false,
903     sm: false,
904     md: false,
905     lg: false,
906     xsoff: false,
907     smoff: false,
908     mdoff: false,
909     lgoff: false,
910     html: '',
911     offset: 0,
912     alert: false,
913     fa: false,
914     icon : false,
915     hidden : false,
916     fasize : 1,
917     
918     getAutoCreate : function(){
919         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
920         
921         cfg = {
922             tag: 'div',
923             cls: 'column'
924         };
925         
926         var settings=this;
927         ['xs','sm','md','lg'].map(function(size){
928             //Roo.log( size + ':' + settings[size]);
929             
930             if (settings[size+'off'] !== false) {
931                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
932             }
933             
934             if (settings[size] === false) {
935                 return;
936             }
937             
938             if (!settings[size]) { // 0 = hidden
939                 cfg.cls += ' hidden-' + size;
940                 return;
941             }
942             cfg.cls += ' col-' + size + '-' + settings[size];
943             
944         });
945         
946         if (this.hidden) {
947             cfg.cls += ' hidden';
948         }
949         
950         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
951             cfg.cls +=' alert alert-' + this.alert;
952         }
953         
954         
955         if (this.html.length) {
956             cfg.html = this.html;
957         }
958         if (this.fa) {
959             var fasize = '';
960             if (this.fasize > 1) {
961                 fasize = ' fa-' + this.fasize + 'x';
962             }
963             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
964             
965             
966         }
967         if (this.icon) {
968             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
969         }
970         
971         return cfg;
972     }
973    
974 });
975
976  
977
978  /*
979  * - LGPL
980  *
981  * page container.
982  * 
983  */
984
985
986 /**
987  * @class Roo.bootstrap.Container
988  * @extends Roo.bootstrap.Component
989  * Bootstrap Container class
990  * @cfg {Boolean} jumbotron is it a jumbotron element
991  * @cfg {String} html content of element
992  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
993  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
994  * @cfg {String} header content of header (for panel)
995  * @cfg {String} footer content of footer (for panel)
996  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
997  * @cfg {String} tag (header|aside|section) type of HTML tag.
998  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
999  * @cfg {String} fa font awesome icon
1000  * @cfg {String} icon (info-sign|check|...) glyphicon name
1001  * @cfg {Boolean} hidden (true|false) hide the element
1002  * @cfg {Boolean} expandable (true|false) default false
1003  * @cfg {Boolean} expanded (true|false) default true
1004  * @cfg {String} rheader contet on the right of header
1005  * @cfg {Boolean} clickable (true|false) default false
1006
1007  *     
1008  * @constructor
1009  * Create a new Container
1010  * @param {Object} config The config object
1011  */
1012
1013 Roo.bootstrap.Container = function(config){
1014     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1015     
1016     this.addEvents({
1017         // raw events
1018          /**
1019          * @event expand
1020          * After the panel has been expand
1021          * 
1022          * @param {Roo.bootstrap.Container} this
1023          */
1024         "expand" : true,
1025         /**
1026          * @event collapse
1027          * After the panel has been collapsed
1028          * 
1029          * @param {Roo.bootstrap.Container} this
1030          */
1031         "collapse" : true,
1032         /**
1033          * @event click
1034          * When a element is chick
1035          * @param {Roo.bootstrap.Container} this
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true
1039     });
1040 };
1041
1042 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1043     
1044     jumbotron : false,
1045     well: '',
1046     panel : '',
1047     header: '',
1048     footer : '',
1049     sticky: '',
1050     tag : false,
1051     alert : false,
1052     fa: false,
1053     icon : false,
1054     expandable : false,
1055     rheader : '',
1056     expanded : true,
1057     clickable: false,
1058   
1059      
1060     getChildContainer : function() {
1061         
1062         if(!this.el){
1063             return false;
1064         }
1065         
1066         if (this.panel.length) {
1067             return this.el.select('.panel-body',true).first();
1068         }
1069         
1070         return this.el;
1071     },
1072     
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : this.tag || 'div',
1078             html : '',
1079             cls : ''
1080         };
1081         if (this.jumbotron) {
1082             cfg.cls = 'jumbotron';
1083         }
1084         
1085         
1086         
1087         // - this is applied by the parent..
1088         //if (this.cls) {
1089         //    cfg.cls = this.cls + '';
1090         //}
1091         
1092         if (this.sticky.length) {
1093             
1094             var bd = Roo.get(document.body);
1095             if (!bd.hasClass('bootstrap-sticky')) {
1096                 bd.addClass('bootstrap-sticky');
1097                 Roo.select('html',true).setStyle('height', '100%');
1098             }
1099              
1100             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1101         }
1102         
1103         
1104         if (this.well.length) {
1105             switch (this.well) {
1106                 case 'lg':
1107                 case 'sm':
1108                     cfg.cls +=' well well-' +this.well;
1109                     break;
1110                 default:
1111                     cfg.cls +=' well';
1112                     break;
1113             }
1114         }
1115         
1116         if (this.hidden) {
1117             cfg.cls += ' hidden';
1118         }
1119         
1120         
1121         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1122             cfg.cls +=' alert alert-' + this.alert;
1123         }
1124         
1125         var body = cfg;
1126         
1127         if (this.panel.length) {
1128             cfg.cls += ' panel panel-' + this.panel;
1129             cfg.cn = [];
1130             if (this.header.length) {
1131                 
1132                 var h = [];
1133                 
1134                 if(this.expandable){
1135                     
1136                     cfg.cls = cfg.cls + ' expandable';
1137                     
1138                     h.push({
1139                         tag: 'i',
1140                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1141                     });
1142                     
1143                 }
1144                 
1145                 h.push(
1146                     {
1147                         tag: 'span',
1148                         cls : 'panel-title',
1149                         html : (this.expandable ? '&nbsp;' : '') + this.header
1150                     },
1151                     {
1152                         tag: 'span',
1153                         cls: 'panel-header-right',
1154                         html: this.rheader
1155                     }
1156                 );
1157                 
1158                 cfg.cn.push({
1159                     cls : 'panel-heading',
1160                     style : this.expandable ? 'cursor: pointer' : '',
1161                     cn : h
1162                 });
1163                 
1164             }
1165             
1166             body = false;
1167             cfg.cn.push({
1168                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1169                 html : this.html
1170             });
1171             
1172             
1173             if (this.footer.length) {
1174                 cfg.cn.push({
1175                     cls : 'panel-footer',
1176                     html : this.footer
1177                     
1178                 });
1179             }
1180             
1181         }
1182         
1183         if (body) {
1184             body.html = this.html || cfg.html;
1185             // prefix with the icons..
1186             if (this.fa) {
1187                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1188             }
1189             if (this.icon) {
1190                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1191             }
1192             
1193             
1194         }
1195         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1196             cfg.cls =  'container';
1197         }
1198         
1199         return cfg;
1200     },
1201     
1202     initEvents: function() 
1203     {
1204         if(this.expandable){
1205             var headerEl = this.headerEl();
1206         
1207             if(headerEl){
1208                 headerEl.on('click', this.onToggleClick, this);
1209             }
1210         }
1211         
1212         if(this.clickable){
1213             this.el.on('click', this.onClick, this);
1214         }
1215         
1216     },
1217     
1218     onToggleClick : function()
1219     {
1220         var headerEl = this.headerEl();
1221         
1222         if(!headerEl){
1223             return;
1224         }
1225         
1226         if(this.expanded){
1227             this.collapse();
1228             return;
1229         }
1230         
1231         this.expand();
1232     },
1233     
1234     expand : function()
1235     {
1236         if(this.fireEvent('expand', this)) {
1237             
1238             this.expanded = true;
1239             
1240             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1241             
1242             this.el.select('.panel-body',true).first().removeClass('hide');
1243             
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1251         }
1252         
1253     },
1254     
1255     collapse : function()
1256     {
1257         if(this.fireEvent('collapse', this)) {
1258             
1259             this.expanded = false;
1260             
1261             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1262             this.el.select('.panel-body',true).first().addClass('hide');
1263         
1264             var toggleEl = this.toggleEl();
1265
1266             if(!toggleEl){
1267                 return;
1268             }
1269
1270             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1271         }
1272     },
1273     
1274     toggleEl : function()
1275     {
1276         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1277             return;
1278         }
1279         
1280         return this.el.select('.panel-heading .fa',true).first();
1281     },
1282     
1283     headerEl : function()
1284     {
1285         if(!this.el || !this.panel.length || !this.header.length){
1286             return;
1287         }
1288         
1289         return this.el.select('.panel-heading',true).first()
1290     },
1291     
1292     titleEl : function()
1293     {
1294         if(!this.el || !this.panel.length || !this.header.length){
1295             return;
1296         }
1297         
1298         return this.el.select('.panel-title',true).first();
1299     },
1300     
1301     setTitle : function(v)
1302     {
1303         var titleEl = this.titleEl();
1304         
1305         if(!titleEl){
1306             return;
1307         }
1308         
1309         titleEl.dom.innerHTML = v;
1310     },
1311     
1312     getTitle : function()
1313     {
1314         
1315         var titleEl = this.titleEl();
1316         
1317         if(!titleEl){
1318             return '';
1319         }
1320         
1321         return titleEl.dom.innerHTML;
1322     },
1323     
1324     setRightTitle : function(v)
1325     {
1326         var t = this.el.select('.panel-header-right',true).first();
1327         
1328         if(!t){
1329             return;
1330         }
1331         
1332         t.dom.innerHTML = v;
1333     },
1334     
1335     onClick : function(e)
1336     {
1337         e.preventDefault();
1338         
1339         this.fireEvent('click', this, e);
1340     }
1341    
1342 });
1343
1344  /*
1345  * - LGPL
1346  *
1347  * image
1348  * 
1349  */
1350
1351
1352 /**
1353  * @class Roo.bootstrap.Img
1354  * @extends Roo.bootstrap.Component
1355  * Bootstrap Img class
1356  * @cfg {Boolean} imgResponsive false | true
1357  * @cfg {String} border rounded | circle | thumbnail
1358  * @cfg {String} src image source
1359  * @cfg {String} alt image alternative text
1360  * @cfg {String} href a tag href
1361  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1362  * @cfg {String} xsUrl xs image source
1363  * @cfg {String} smUrl sm image source
1364  * @cfg {String} mdUrl md image source
1365  * @cfg {String} lgUrl lg image source
1366  * 
1367  * @constructor
1368  * Create a new Input
1369  * @param {Object} config The config object
1370  */
1371
1372 Roo.bootstrap.Img = function(config){
1373     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1374     
1375     this.addEvents({
1376         // img events
1377         /**
1378          * @event click
1379          * The img click event for the img.
1380          * @param {Roo.EventObject} e
1381          */
1382         "click" : true
1383     });
1384 };
1385
1386 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1387     
1388     imgResponsive: true,
1389     border: '',
1390     src: 'about:blank',
1391     href: false,
1392     target: false,
1393     xsUrl: '',
1394     smUrl: '',
1395     mdUrl: '',
1396     lgUrl: '',
1397
1398     getAutoCreate : function()
1399     {   
1400         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1401             return this.createSingleImg();
1402         }
1403         
1404         var cfg = {
1405             tag: 'div',
1406             cls: 'roo-image-responsive-group',
1407             cn: []
1408         };
1409         var _this = this;
1410         
1411         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1412             
1413             if(!_this[size + 'Url']){
1414                 return;
1415             }
1416             
1417             var img = {
1418                 tag: 'img',
1419                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1420                 html: _this.html || cfg.html,
1421                 src: _this[size + 'Url']
1422             };
1423             
1424             img.cls += ' roo-image-responsive-' + size;
1425             
1426             var s = ['xs', 'sm', 'md', 'lg'];
1427             
1428             s.splice(s.indexOf(size), 1);
1429             
1430             Roo.each(s, function(ss){
1431                 img.cls += ' hidden-' + ss;
1432             });
1433             
1434             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1435                 cfg.cls += ' img-' + _this.border;
1436             }
1437             
1438             if(_this.alt){
1439                 cfg.alt = _this.alt;
1440             }
1441             
1442             if(_this.href){
1443                 var a = {
1444                     tag: 'a',
1445                     href: _this.href,
1446                     cn: [
1447                         img
1448                     ]
1449                 };
1450
1451                 if(this.target){
1452                     a.target = _this.target;
1453                 }
1454             }
1455             
1456             cfg.cn.push((_this.href) ? a : img);
1457             
1458         });
1459         
1460         return cfg;
1461     },
1462     
1463     createSingleImg : function()
1464     {
1465         var cfg = {
1466             tag: 'img',
1467             cls: (this.imgResponsive) ? 'img-responsive' : '',
1468             html : null,
1469             src : 'about:blank'  // just incase src get's set to undefined?!?
1470         };
1471         
1472         cfg.html = this.html || cfg.html;
1473         
1474         cfg.src = this.src || cfg.src;
1475         
1476         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1477             cfg.cls += ' img-' + this.border;
1478         }
1479         
1480         if(this.alt){
1481             cfg.alt = this.alt;
1482         }
1483         
1484         if(this.href){
1485             var a = {
1486                 tag: 'a',
1487                 href: this.href,
1488                 cn: [
1489                     cfg
1490                 ]
1491             };
1492             
1493             if(this.target){
1494                 a.target = this.target;
1495             }
1496             
1497         }
1498         
1499         return (this.href) ? a : cfg;
1500     },
1501     
1502     initEvents: function() 
1503     {
1504         if(!this.href){
1505             this.el.on('click', this.onClick, this);
1506         }
1507         
1508     },
1509     
1510     onClick : function(e)
1511     {
1512         Roo.log('img onclick');
1513         this.fireEvent('click', this, e);
1514     }
1515    
1516 });
1517
1518  /*
1519  * - LGPL
1520  *
1521  * image
1522  * 
1523  */
1524
1525
1526 /**
1527  * @class Roo.bootstrap.Link
1528  * @extends Roo.bootstrap.Component
1529  * Bootstrap Link Class
1530  * @cfg {String} alt image alternative text
1531  * @cfg {String} href a tag href
1532  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1533  * @cfg {String} html the content of the link.
1534  * @cfg {String} anchor name for the anchor link
1535  * @cfg {String} fa - favicon
1536
1537  * @cfg {Boolean} preventDefault (true | false) default false
1538
1539  * 
1540  * @constructor
1541  * Create a new Input
1542  * @param {Object} config The config object
1543  */
1544
1545 Roo.bootstrap.Link = function(config){
1546     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1547     
1548     this.addEvents({
1549         // img events
1550         /**
1551          * @event click
1552          * The img click event for the img.
1553          * @param {Roo.EventObject} e
1554          */
1555         "click" : true
1556     });
1557 };
1558
1559 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1560     
1561     href: false,
1562     target: false,
1563     preventDefault: false,
1564     anchor : false,
1565     alt : false,
1566     fa: false,
1567
1568
1569     getAutoCreate : function()
1570     {
1571         var html = this.html || '';
1572         
1573         if (this.fa !== false) {
1574             html = '<i class="fa fa-' + this.fa + '"></i>';
1575         }
1576         var cfg = {
1577             tag: 'a'
1578         };
1579         // anchor's do not require html/href...
1580         if (this.anchor === false) {
1581             cfg.html = html;
1582             cfg.href = this.href || '#';
1583         } else {
1584             cfg.name = this.anchor;
1585             if (this.html !== false || this.fa !== false) {
1586                 cfg.html = html;
1587             }
1588             if (this.href !== false) {
1589                 cfg.href = this.href;
1590             }
1591         }
1592         
1593         if(this.alt !== false){
1594             cfg.alt = this.alt;
1595         }
1596         
1597         
1598         if(this.target !== false) {
1599             cfg.target = this.target;
1600         }
1601         
1602         return cfg;
1603     },
1604     
1605     initEvents: function() {
1606         
1607         if(!this.href || this.preventDefault){
1608             this.el.on('click', this.onClick, this);
1609         }
1610     },
1611     
1612     onClick : function(e)
1613     {
1614         if(this.preventDefault){
1615             e.preventDefault();
1616         }
1617         //Roo.log('img onclick');
1618         this.fireEvent('click', this, e);
1619     }
1620    
1621 });
1622
1623  /*
1624  * - LGPL
1625  *
1626  * header
1627  * 
1628  */
1629
1630 /**
1631  * @class Roo.bootstrap.Header
1632  * @extends Roo.bootstrap.Component
1633  * Bootstrap Header class
1634  * @cfg {String} html content of header
1635  * @cfg {Number} level (1|2|3|4|5|6) default 1
1636  * 
1637  * @constructor
1638  * Create a new Header
1639  * @param {Object} config The config object
1640  */
1641
1642
1643 Roo.bootstrap.Header  = function(config){
1644     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1645 };
1646
1647 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1648     
1649     //href : false,
1650     html : false,
1651     level : 1,
1652     
1653     
1654     
1655     getAutoCreate : function(){
1656         
1657         
1658         
1659         var cfg = {
1660             tag: 'h' + (1 *this.level),
1661             html: this.html || ''
1662         } ;
1663         
1664         return cfg;
1665     }
1666    
1667 });
1668
1669  
1670
1671  /*
1672  * Based on:
1673  * Ext JS Library 1.1.1
1674  * Copyright(c) 2006-2007, Ext JS, LLC.
1675  *
1676  * Originally Released Under LGPL - original licence link has changed is not relivant.
1677  *
1678  * Fork - LGPL
1679  * <script type="text/javascript">
1680  */
1681  
1682 /**
1683  * @class Roo.bootstrap.MenuMgr
1684  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1685  * @singleton
1686  */
1687 Roo.bootstrap.MenuMgr = function(){
1688    var menus, active, groups = {}, attached = false, lastShow = new Date();
1689
1690    // private - called when first menu is created
1691    function init(){
1692        menus = {};
1693        active = new Roo.util.MixedCollection();
1694        Roo.get(document).addKeyListener(27, function(){
1695            if(active.length > 0){
1696                hideAll();
1697            }
1698        });
1699    }
1700
1701    // private
1702    function hideAll(){
1703        if(active && active.length > 0){
1704            var c = active.clone();
1705            c.each(function(m){
1706                m.hide();
1707            });
1708        }
1709    }
1710
1711    // private
1712    function onHide(m){
1713        active.remove(m);
1714        if(active.length < 1){
1715            Roo.get(document).un("mouseup", onMouseDown);
1716             
1717            attached = false;
1718        }
1719    }
1720
1721    // private
1722    function onShow(m){
1723        var last = active.last();
1724        lastShow = new Date();
1725        active.add(m);
1726        if(!attached){
1727           Roo.get(document).on("mouseup", onMouseDown);
1728            
1729            attached = true;
1730        }
1731        if(m.parentMenu){
1732           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1733           m.parentMenu.activeChild = m;
1734        }else if(last && last.isVisible()){
1735           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1736        }
1737    }
1738
1739    // private
1740    function onBeforeHide(m){
1741        if(m.activeChild){
1742            m.activeChild.hide();
1743        }
1744        if(m.autoHideTimer){
1745            clearTimeout(m.autoHideTimer);
1746            delete m.autoHideTimer;
1747        }
1748    }
1749
1750    // private
1751    function onBeforeShow(m){
1752        var pm = m.parentMenu;
1753        if(!pm && !m.allowOtherMenus){
1754            hideAll();
1755        }else if(pm && pm.activeChild && active != m){
1756            pm.activeChild.hide();
1757        }
1758    }
1759
1760    // private this should really trigger on mouseup..
1761    function onMouseDown(e){
1762         Roo.log("on Mouse Up");
1763         
1764         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1765             Roo.log("MenuManager hideAll");
1766             hideAll();
1767             e.stopEvent();
1768         }
1769         
1770         
1771    }
1772
1773    // private
1774    function onBeforeCheck(mi, state){
1775        if(state){
1776            var g = groups[mi.group];
1777            for(var i = 0, l = g.length; i < l; i++){
1778                if(g[i] != mi){
1779                    g[i].setChecked(false);
1780                }
1781            }
1782        }
1783    }
1784
1785    return {
1786
1787        /**
1788         * Hides all menus that are currently visible
1789         */
1790        hideAll : function(){
1791             hideAll();  
1792        },
1793
1794        // private
1795        register : function(menu){
1796            if(!menus){
1797                init();
1798            }
1799            menus[menu.id] = menu;
1800            menu.on("beforehide", onBeforeHide);
1801            menu.on("hide", onHide);
1802            menu.on("beforeshow", onBeforeShow);
1803            menu.on("show", onShow);
1804            var g = menu.group;
1805            if(g && menu.events["checkchange"]){
1806                if(!groups[g]){
1807                    groups[g] = [];
1808                }
1809                groups[g].push(menu);
1810                menu.on("checkchange", onCheck);
1811            }
1812        },
1813
1814         /**
1815          * Returns a {@link Roo.menu.Menu} object
1816          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1817          * be used to generate and return a new Menu instance.
1818          */
1819        get : function(menu){
1820            if(typeof menu == "string"){ // menu id
1821                return menus[menu];
1822            }else if(menu.events){  // menu instance
1823                return menu;
1824            }
1825            /*else if(typeof menu.length == 'number'){ // array of menu items?
1826                return new Roo.bootstrap.Menu({items:menu});
1827            }else{ // otherwise, must be a config
1828                return new Roo.bootstrap.Menu(menu);
1829            }
1830            */
1831            return false;
1832        },
1833
1834        // private
1835        unregister : function(menu){
1836            delete menus[menu.id];
1837            menu.un("beforehide", onBeforeHide);
1838            menu.un("hide", onHide);
1839            menu.un("beforeshow", onBeforeShow);
1840            menu.un("show", onShow);
1841            var g = menu.group;
1842            if(g && menu.events["checkchange"]){
1843                groups[g].remove(menu);
1844                menu.un("checkchange", onCheck);
1845            }
1846        },
1847
1848        // private
1849        registerCheckable : function(menuItem){
1850            var g = menuItem.group;
1851            if(g){
1852                if(!groups[g]){
1853                    groups[g] = [];
1854                }
1855                groups[g].push(menuItem);
1856                menuItem.on("beforecheckchange", onBeforeCheck);
1857            }
1858        },
1859
1860        // private
1861        unregisterCheckable : function(menuItem){
1862            var g = menuItem.group;
1863            if(g){
1864                groups[g].remove(menuItem);
1865                menuItem.un("beforecheckchange", onBeforeCheck);
1866            }
1867        }
1868    };
1869 }();/*
1870  * - LGPL
1871  *
1872  * menu
1873  * 
1874  */
1875
1876 /**
1877  * @class Roo.bootstrap.Menu
1878  * @extends Roo.bootstrap.Component
1879  * Bootstrap Menu class - container for MenuItems
1880  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1881  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1882  * 
1883  * @constructor
1884  * Create a new Menu
1885  * @param {Object} config The config object
1886  */
1887
1888
1889 Roo.bootstrap.Menu = function(config){
1890     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1891     if (this.registerMenu && this.type != 'treeview')  {
1892         Roo.bootstrap.MenuMgr.register(this);
1893     }
1894     this.addEvents({
1895         /**
1896          * @event beforeshow
1897          * Fires before this menu is displayed
1898          * @param {Roo.menu.Menu} this
1899          */
1900         beforeshow : true,
1901         /**
1902          * @event beforehide
1903          * Fires before this menu is hidden
1904          * @param {Roo.menu.Menu} this
1905          */
1906         beforehide : true,
1907         /**
1908          * @event show
1909          * Fires after this menu is displayed
1910          * @param {Roo.menu.Menu} this
1911          */
1912         show : true,
1913         /**
1914          * @event hide
1915          * Fires after this menu is hidden
1916          * @param {Roo.menu.Menu} this
1917          */
1918         hide : true,
1919         /**
1920          * @event click
1921          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1922          * @param {Roo.menu.Menu} this
1923          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1924          * @param {Roo.EventObject} e
1925          */
1926         click : true,
1927         /**
1928          * @event mouseover
1929          * Fires when the mouse is hovering over this menu
1930          * @param {Roo.menu.Menu} this
1931          * @param {Roo.EventObject} e
1932          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1933          */
1934         mouseover : true,
1935         /**
1936          * @event mouseout
1937          * Fires when the mouse exits this menu
1938          * @param {Roo.menu.Menu} this
1939          * @param {Roo.EventObject} e
1940          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1941          */
1942         mouseout : true,
1943         /**
1944          * @event itemclick
1945          * Fires when a menu item contained in this menu is clicked
1946          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1947          * @param {Roo.EventObject} e
1948          */
1949         itemclick: true
1950     });
1951     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1952 };
1953
1954 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1955     
1956    /// html : false,
1957     //align : '',
1958     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1959     type: false,
1960     /**
1961      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1962      */
1963     registerMenu : true,
1964     
1965     menuItems :false, // stores the menu items..
1966     
1967     hidden:true,
1968     
1969         
1970     parentMenu : false,
1971     
1972     getChildContainer : function() {
1973         return this.el;  
1974     },
1975     
1976     getAutoCreate : function(){
1977          
1978         //if (['right'].indexOf(this.align)!==-1) {
1979         //    cfg.cn[1].cls += ' pull-right'
1980         //}
1981         
1982         
1983         var cfg = {
1984             tag : 'ul',
1985             cls : 'dropdown-menu' ,
1986             style : 'z-index:1000'
1987             
1988         };
1989         
1990         if (this.type === 'submenu') {
1991             cfg.cls = 'submenu active';
1992         }
1993         if (this.type === 'treeview') {
1994             cfg.cls = 'treeview-menu';
1995         }
1996         
1997         return cfg;
1998     },
1999     initEvents : function() {
2000         
2001        // Roo.log("ADD event");
2002        // Roo.log(this.triggerEl.dom);
2003         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2004         
2005         this.triggerEl.addClass('dropdown-toggle');
2006         
2007         
2008         if (Roo.isTouch) {
2009             this.el.on('touchstart'  , this.onTouch, this);
2010         }
2011         this.el.on('click' , this.onClick, this);
2012
2013         this.el.on("mouseover", this.onMouseOver, this);
2014         this.el.on("mouseout", this.onMouseOut, this);
2015         
2016     },
2017     
2018     findTargetItem : function(e)
2019     {
2020         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2021         if(!t){
2022             return false;
2023         }
2024         //Roo.log(t);         Roo.log(t.id);
2025         if(t && t.id){
2026             //Roo.log(this.menuitems);
2027             return this.menuitems.get(t.id);
2028             
2029             //return this.items.get(t.menuItemId);
2030         }
2031         
2032         return false;
2033     },
2034     
2035     onTouch : function(e) 
2036     {
2037         //e.stopEvent(); this make the user popdown broken
2038         this.onClick(e);
2039     },
2040     
2041     onClick : function(e)
2042     {
2043         Roo.log("menu.onClick");
2044         var t = this.findTargetItem(e);
2045         if(!t || t.isContainer){
2046             return;
2047         }
2048         Roo.log(e);
2049         /*
2050         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2051             if(t == this.activeItem && t.shouldDeactivate(e)){
2052                 this.activeItem.deactivate();
2053                 delete this.activeItem;
2054                 return;
2055             }
2056             if(t.canActivate){
2057                 this.setActiveItem(t, true);
2058             }
2059             return;
2060             
2061             
2062         }
2063         */
2064        
2065         Roo.log('pass click event');
2066         
2067         t.onClick(e);
2068         
2069         this.fireEvent("click", this, t, e);
2070         
2071         this.hide();
2072     },
2073      onMouseOver : function(e){
2074         var t  = this.findTargetItem(e);
2075         //Roo.log(t);
2076         //if(t){
2077         //    if(t.canActivate && !t.disabled){
2078         //        this.setActiveItem(t, true);
2079         //    }
2080         //}
2081         
2082         this.fireEvent("mouseover", this, e, t);
2083     },
2084     isVisible : function(){
2085         return !this.hidden;
2086     },
2087      onMouseOut : function(e){
2088         var t  = this.findTargetItem(e);
2089         
2090         //if(t ){
2091         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2092         //        this.activeItem.deactivate();
2093         //        delete this.activeItem;
2094         //    }
2095         //}
2096         this.fireEvent("mouseout", this, e, t);
2097     },
2098     
2099     
2100     /**
2101      * Displays this menu relative to another element
2102      * @param {String/HTMLElement/Roo.Element} element The element to align to
2103      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2104      * the element (defaults to this.defaultAlign)
2105      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2106      */
2107     show : function(el, pos, parentMenu){
2108         this.parentMenu = parentMenu;
2109         if(!this.el){
2110             this.render();
2111         }
2112         this.fireEvent("beforeshow", this);
2113         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2114     },
2115      /**
2116      * Displays this menu at a specific xy position
2117      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2118      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2119      */
2120     showAt : function(xy, parentMenu, /* private: */_e){
2121         this.parentMenu = parentMenu;
2122         if(!this.el){
2123             this.render();
2124         }
2125         if(_e !== false){
2126             this.fireEvent("beforeshow", this);
2127             //xy = this.el.adjustForConstraints(xy);
2128         }
2129         
2130         //this.el.show();
2131         this.hideMenuItems();
2132         this.hidden = false;
2133         this.triggerEl.addClass('open');
2134         
2135         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2136             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2137         }
2138         
2139         this.el.setXY(xy);
2140         this.focus();
2141         this.fireEvent("show", this);
2142     },
2143     
2144     focus : function(){
2145         return;
2146         if(!this.hidden){
2147             this.doFocus.defer(50, this);
2148         }
2149     },
2150
2151     doFocus : function(){
2152         if(!this.hidden){
2153             this.focusEl.focus();
2154         }
2155     },
2156
2157     /**
2158      * Hides this menu and optionally all parent menus
2159      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2160      */
2161     hide : function(deep)
2162     {
2163         
2164         this.hideMenuItems();
2165         if(this.el && this.isVisible()){
2166             this.fireEvent("beforehide", this);
2167             if(this.activeItem){
2168                 this.activeItem.deactivate();
2169                 this.activeItem = null;
2170             }
2171             this.triggerEl.removeClass('open');;
2172             this.hidden = true;
2173             this.fireEvent("hide", this);
2174         }
2175         if(deep === true && this.parentMenu){
2176             this.parentMenu.hide(true);
2177         }
2178     },
2179     
2180     onTriggerPress  : function(e)
2181     {
2182         
2183         Roo.log('trigger press');
2184         //Roo.log(e.getTarget());
2185        // Roo.log(this.triggerEl.dom);
2186        
2187         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2188         var pel = Roo.get(e.getTarget());
2189         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2190            
2191             return;
2192         }
2193         
2194         if (this.isVisible()) {
2195             Roo.log('hide');
2196             this.hide();
2197         } else {
2198             Roo.log('show');
2199             this.show(this.triggerEl, false, false);
2200         }
2201         
2202         e.stopEvent();
2203     },
2204     
2205          
2206        
2207     
2208     hideMenuItems : function()
2209     {
2210         Roo.log("hide Menu Items");
2211         if (!this.el) { 
2212             return;
2213         }
2214         //$(backdrop).remove()
2215         this.el.select('.open',true).each(function(aa) {
2216             
2217             aa.removeClass('open');
2218           //var parent = getParent($(this))
2219           //var relatedTarget = { relatedTarget: this }
2220           
2221            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2222           //if (e.isDefaultPrevented()) return
2223            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2224         });
2225     },
2226     addxtypeChild : function (tree, cntr) {
2227         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2228           
2229         this.menuitems.add(comp);
2230         return comp;
2231
2232     },
2233     getEl : function()
2234     {
2235         Roo.log(this.el);
2236         return this.el;
2237     }
2238 });
2239
2240  
2241  /*
2242  * - LGPL
2243  *
2244  * menu item
2245  * 
2246  */
2247
2248
2249 /**
2250  * @class Roo.bootstrap.MenuItem
2251  * @extends Roo.bootstrap.Component
2252  * Bootstrap MenuItem class
2253  * @cfg {String} html the menu label
2254  * @cfg {String} href the link
2255  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2256  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2257  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2258  * @cfg {String} fa favicon to show on left of menu item.
2259  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2260  * 
2261  * 
2262  * @constructor
2263  * Create a new MenuItem
2264  * @param {Object} config The config object
2265  */
2266
2267
2268 Roo.bootstrap.MenuItem = function(config){
2269     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2270     this.addEvents({
2271         // raw events
2272         /**
2273          * @event click
2274          * The raw click event for the entire grid.
2275          * @param {Roo.bootstrap.MenuItem} this
2276          * @param {Roo.EventObject} e
2277          */
2278         "click" : true
2279     });
2280 };
2281
2282 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2283     
2284     href : false,
2285     html : false,
2286     preventDefault: true,
2287     isContainer : false,
2288     active : false,
2289     fa: false,
2290     
2291     getAutoCreate : function(){
2292         
2293         if(this.isContainer){
2294             return {
2295                 tag: 'li',
2296                 cls: 'dropdown-menu-item'
2297             };
2298         }
2299         var ctag = {
2300             tag: 'span',
2301             html: 'Link'
2302         };
2303         
2304         var anc = {
2305             tag : 'a',
2306             href : '#',
2307             cn : [  ]
2308         };
2309         
2310         if (this.fa !== false) {
2311             anc.cn.push({
2312                 tag : 'i',
2313                 cls : 'fa fa-' + this.fa
2314             });
2315         }
2316         
2317         anc.cn.push(ctag);
2318         
2319         
2320         var cfg= {
2321             tag: 'li',
2322             cls: 'dropdown-menu-item',
2323             cn: [ anc ]
2324         };
2325         if (this.parent().type == 'treeview') {
2326             cfg.cls = 'treeview-menu';
2327         }
2328         if (this.active) {
2329             cfg.cls += ' active';
2330         }
2331         
2332         
2333         
2334         anc.href = this.href || cfg.cn[0].href ;
2335         ctag.html = this.html || cfg.cn[0].html ;
2336         return cfg;
2337     },
2338     
2339     initEvents: function()
2340     {
2341         if (this.parent().type == 'treeview') {
2342             this.el.select('a').on('click', this.onClick, this);
2343         }
2344         if (this.menu) {
2345             this.menu.parentType = this.xtype;
2346             this.menu.triggerEl = this.el;
2347             this.menu = this.addxtype(Roo.apply({}, this.menu));
2348         }
2349         
2350     },
2351     onClick : function(e)
2352     {
2353         Roo.log('item on click ');
2354         //if(this.preventDefault){
2355         //    e.preventDefault();
2356         //}
2357         //this.parent().hideMenuItems();
2358         
2359         this.fireEvent('click', this, e);
2360     },
2361     getEl : function()
2362     {
2363         return this.el;
2364     }
2365 });
2366
2367  
2368
2369  /*
2370  * - LGPL
2371  *
2372  * menu separator
2373  * 
2374  */
2375
2376
2377 /**
2378  * @class Roo.bootstrap.MenuSeparator
2379  * @extends Roo.bootstrap.Component
2380  * Bootstrap MenuSeparator class
2381  * 
2382  * @constructor
2383  * Create a new MenuItem
2384  * @param {Object} config The config object
2385  */
2386
2387
2388 Roo.bootstrap.MenuSeparator = function(config){
2389     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2390 };
2391
2392 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2393     
2394     getAutoCreate : function(){
2395         var cfg = {
2396             cls: 'divider',
2397             tag : 'li'
2398         };
2399         
2400         return cfg;
2401     }
2402    
2403 });
2404
2405  
2406
2407  
2408 /*
2409 * Licence: LGPL
2410 */
2411
2412 /**
2413  * @class Roo.bootstrap.Modal
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap Modal class
2416  * @cfg {String} title Title of dialog
2417  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2418  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2419  * @cfg {Boolean} specificTitle default false
2420  * @cfg {Array} buttons Array of buttons or standard button set..
2421  * @cfg {String} buttonPosition (left|right|center) default right
2422  * @cfg {Boolean} animate default true
2423  * @cfg {Boolean} allow_close default true
2424  * 
2425  * @constructor
2426  * Create a new Modal Dialog
2427  * @param {Object} config The config object
2428  */
2429
2430 Roo.bootstrap.Modal = function(config){
2431     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2432     this.addEvents({
2433         // raw events
2434         /**
2435          * @event btnclick
2436          * The raw btnclick event for the button
2437          * @param {Roo.EventObject} e
2438          */
2439         "btnclick" : true
2440     });
2441     this.buttons = this.buttons || [];
2442      
2443     if (this.tmpl) {
2444         this.tmpl = Roo.factory(this.tmpl);
2445     }
2446     
2447 };
2448
2449 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2450     
2451     title : 'test dialog',
2452    
2453     buttons : false,
2454     
2455     // set on load...
2456      
2457     html: false,
2458     
2459     tmp: false,
2460     
2461     specificTitle: false,
2462     
2463     buttonPosition: 'right',
2464     
2465     allow_close : true,
2466     
2467     animate : true,
2468     
2469     
2470      // private
2471     bodyEl:  false,
2472     footerEl:  false,
2473     titleEl:  false,
2474     closeEl:  false,
2475     
2476     
2477     onRender : function(ct, position)
2478     {
2479         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2480      
2481         if(!this.el){
2482             var cfg = Roo.apply({},  this.getAutoCreate());
2483             cfg.id = Roo.id();
2484             //if(!cfg.name){
2485             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2486             //}
2487             //if (!cfg.name.length) {
2488             //    delete cfg.name;
2489            // }
2490             if (this.cls) {
2491                 cfg.cls += ' ' + this.cls;
2492             }
2493             if (this.style) {
2494                 cfg.style = this.style;
2495             }
2496             this.el = Roo.get(document.body).createChild(cfg, position);
2497         }
2498         //var type = this.el.dom.type;
2499         
2500         
2501         if(this.tabIndex !== undefined){
2502             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2503         }
2504         
2505         
2506         this.bodyEl = this.el.select('.modal-body',true).first();
2507         this.closeEl = this.el.select('.modal-header .close', true).first();
2508         this.footerEl = this.el.select('.modal-footer',true).first();
2509         this.titleEl = this.el.select('.modal-title',true).first();
2510         
2511         
2512          
2513         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2514         this.maskEl.enableDisplayMode("block");
2515         this.maskEl.hide();
2516         //this.el.addClass("x-dlg-modal");
2517     
2518         if (this.buttons.length) {
2519             Roo.each(this.buttons, function(bb) {
2520                 var b = Roo.apply({}, bb);
2521                 b.xns = b.xns || Roo.bootstrap;
2522                 b.xtype = b.xtype || 'Button';
2523                 if (typeof(b.listeners) == 'undefined') {
2524                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2525                 }
2526                 
2527                 var btn = Roo.factory(b);
2528                 
2529                 btn.render(this.el.select('.modal-footer div').first());
2530                 
2531             },this);
2532         }
2533         // render the children.
2534         var nitems = [];
2535         
2536         if(typeof(this.items) != 'undefined'){
2537             var items = this.items;
2538             delete this.items;
2539
2540             for(var i =0;i < items.length;i++) {
2541                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2542             }
2543         }
2544         
2545         this.items = nitems;
2546         
2547         // where are these used - they used to be body/close/footer
2548         
2549        
2550         this.initEvents();
2551         //this.el.addClass([this.fieldClass, this.cls]);
2552         
2553     },
2554     
2555     getAutoCreate : function(){
2556         
2557         
2558         var bdy = {
2559                 cls : 'modal-body',
2560                 html : this.html || ''
2561         };
2562         
2563         var title = {
2564             tag: 'h4',
2565             cls : 'modal-title',
2566             html : this.title
2567         };
2568         
2569         if(this.specificTitle){
2570             title = this.title;
2571             
2572         };
2573         
2574         var header = [];
2575         if (this.allow_close) {
2576             header.push({
2577                 tag: 'button',
2578                 cls : 'close',
2579                 html : '&times'
2580             });
2581         }
2582         header.push(title);
2583         
2584         var modal = {
2585             cls: "modal",
2586             style : 'display: none',
2587             cn : [
2588                 {
2589                     cls: "modal-dialog",
2590                     cn : [
2591                         {
2592                             cls : "modal-content",
2593                             cn : [
2594                                 {
2595                                     cls : 'modal-header',
2596                                     cn : header
2597                                 },
2598                                 bdy,
2599                                 {
2600                                     cls : 'modal-footer',
2601                                     cn : [
2602                                         {
2603                                             tag: 'div',
2604                                             cls: 'btn-' + this.buttonPosition
2605                                         }
2606                                     ]
2607                                     
2608                                 }
2609                                 
2610                                 
2611                             ]
2612                             
2613                         }
2614                     ]
2615                         
2616                 }
2617             ]
2618         };
2619         
2620         if(this.animate){
2621             modal.cls += ' fade';
2622         }
2623         
2624         return modal;
2625           
2626     },
2627     getChildContainer : function() {
2628          
2629          return this.bodyEl;
2630         
2631     },
2632     getButtonContainer : function() {
2633          return this.el.select('.modal-footer div',true).first();
2634         
2635     },
2636     initEvents : function()
2637     {
2638         if (this.allow_close) {
2639             this.closeEl.on('click', this.hide, this);
2640         }
2641         
2642         var _this = this;
2643         
2644         window.addEventListener("resize", function() { _this.resize(); } );
2645
2646     },
2647     
2648     resize : function()
2649     {
2650         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2651     },
2652     
2653     show : function() {
2654         
2655         if (!this.rendered) {
2656             this.render();
2657         }
2658         
2659         this.el.setStyle('display', 'block');
2660         
2661         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2662             var _this = this;
2663             (function(){
2664                 this.el.addClass('in');
2665             }).defer(50, this);
2666         }else{
2667             this.el.addClass('in');
2668             
2669         }
2670         
2671         // not sure how we can show data in here.. 
2672         //if (this.tmpl) {
2673         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2674         //}
2675         
2676         Roo.get(document.body).addClass("x-body-masked");
2677         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2678         this.maskEl.show();
2679         this.el.setStyle('zIndex', '10001');
2680        
2681         this.fireEvent('show', this);
2682          
2683         
2684         
2685     },
2686     hide : function()
2687     {
2688         this.maskEl.hide();
2689         Roo.get(document.body).removeClass("x-body-masked");
2690         this.el.removeClass('in');
2691         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2692         
2693         if(this.animate){ // why
2694             var _this = this;
2695             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2696         }else{
2697             this.el.setStyle('display', 'none');
2698         }
2699         
2700         this.fireEvent('hide', this);
2701     },
2702     
2703     addButton : function(str, cb)
2704     {
2705          
2706         
2707         var b = Roo.apply({}, { html : str } );
2708         b.xns = b.xns || Roo.bootstrap;
2709         b.xtype = b.xtype || 'Button';
2710         if (typeof(b.listeners) == 'undefined') {
2711             b.listeners = { click : cb.createDelegate(this)  };
2712         }
2713         
2714         var btn = Roo.factory(b);
2715            
2716         btn.render(this.el.select('.modal-footer div').first());
2717         
2718         return btn;   
2719        
2720     },
2721     
2722     setDefaultButton : function(btn)
2723     {
2724         //this.el.select('.modal-footer').()
2725     },
2726     diff : false,
2727     
2728     resizeTo: function(w,h)
2729     {
2730         // skip.. ?? why??
2731         
2732         this.el.select('.modal-dialog',true).first().setWidth(w);
2733         if (this.diff === false) {
2734             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2735         }
2736         
2737         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2738         
2739         
2740     },
2741     setContentSize  : function(w, h)
2742     {
2743         
2744     },
2745     onButtonClick: function(btn,e)
2746     {
2747         //Roo.log([a,b,c]);
2748         this.fireEvent('btnclick', btn.name, e);
2749     },
2750      /**
2751      * Set the title of the Dialog
2752      * @param {String} str new Title
2753      */
2754     setTitle: function(str) {
2755         this.titleEl.dom.innerHTML = str;    
2756     },
2757     /**
2758      * Set the body of the Dialog
2759      * @param {String} str new Title
2760      */
2761     setBody: function(str) {
2762         this.bodyEl.dom.innerHTML = str;    
2763     },
2764     /**
2765      * Set the body of the Dialog using the template
2766      * @param {Obj} data - apply this data to the template and replace the body contents.
2767      */
2768     applyBody: function(obj)
2769     {
2770         if (!this.tmpl) {
2771             Roo.log("Error - using apply Body without a template");
2772             //code
2773         }
2774         this.tmpl.overwrite(this.bodyEl, obj);
2775     }
2776     
2777 });
2778
2779
2780 Roo.apply(Roo.bootstrap.Modal,  {
2781     /**
2782          * Button config that displays a single OK button
2783          * @type Object
2784          */
2785         OK :  [{
2786             name : 'ok',
2787             weight : 'primary',
2788             html : 'OK'
2789         }], 
2790         /**
2791          * Button config that displays Yes and No buttons
2792          * @type Object
2793          */
2794         YESNO : [
2795             {
2796                 name  : 'no',
2797                 html : 'No'
2798             },
2799             {
2800                 name  :'yes',
2801                 weight : 'primary',
2802                 html : 'Yes'
2803             }
2804         ],
2805         
2806         /**
2807          * Button config that displays OK and Cancel buttons
2808          * @type Object
2809          */
2810         OKCANCEL : [
2811             {
2812                name : 'cancel',
2813                 html : 'Cancel'
2814             },
2815             {
2816                 name : 'ok',
2817                 weight : 'primary',
2818                 html : 'OK'
2819             }
2820         ],
2821         /**
2822          * Button config that displays Yes, No and Cancel buttons
2823          * @type Object
2824          */
2825         YESNOCANCEL : [
2826             {
2827                 name : 'yes',
2828                 weight : 'primary',
2829                 html : 'Yes'
2830             },
2831             {
2832                 name : 'no',
2833                 html : 'No'
2834             },
2835             {
2836                 name : 'cancel',
2837                 html : 'Cancel'
2838             }
2839         ]
2840 });
2841  
2842  /*
2843  * - LGPL
2844  *
2845  * messagebox - can be used as a replace
2846  * 
2847  */
2848 /**
2849  * @class Roo.MessageBox
2850  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2851  * Example usage:
2852  *<pre><code>
2853 // Basic alert:
2854 Roo.Msg.alert('Status', 'Changes saved successfully.');
2855
2856 // Prompt for user data:
2857 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2858     if (btn == 'ok'){
2859         // process text value...
2860     }
2861 });
2862
2863 // Show a dialog using config options:
2864 Roo.Msg.show({
2865    title:'Save Changes?',
2866    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2867    buttons: Roo.Msg.YESNOCANCEL,
2868    fn: processResult,
2869    animEl: 'elId'
2870 });
2871 </code></pre>
2872  * @singleton
2873  */
2874 Roo.bootstrap.MessageBox = function(){
2875     var dlg, opt, mask, waitTimer;
2876     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2877     var buttons, activeTextEl, bwidth;
2878
2879     
2880     // private
2881     var handleButton = function(button){
2882         dlg.hide();
2883         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2884     };
2885
2886     // private
2887     var handleHide = function(){
2888         if(opt && opt.cls){
2889             dlg.el.removeClass(opt.cls);
2890         }
2891         //if(waitTimer){
2892         //    Roo.TaskMgr.stop(waitTimer);
2893         //    waitTimer = null;
2894         //}
2895     };
2896
2897     // private
2898     var updateButtons = function(b){
2899         var width = 0;
2900         if(!b){
2901             buttons["ok"].hide();
2902             buttons["cancel"].hide();
2903             buttons["yes"].hide();
2904             buttons["no"].hide();
2905             //dlg.footer.dom.style.display = 'none';
2906             return width;
2907         }
2908         dlg.footerEl.dom.style.display = '';
2909         for(var k in buttons){
2910             if(typeof buttons[k] != "function"){
2911                 if(b[k]){
2912                     buttons[k].show();
2913                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2914                     width += buttons[k].el.getWidth()+15;
2915                 }else{
2916                     buttons[k].hide();
2917                 }
2918             }
2919         }
2920         return width;
2921     };
2922
2923     // private
2924     var handleEsc = function(d, k, e){
2925         if(opt && opt.closable !== false){
2926             dlg.hide();
2927         }
2928         if(e){
2929             e.stopEvent();
2930         }
2931     };
2932
2933     return {
2934         /**
2935          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2936          * @return {Roo.BasicDialog} The BasicDialog element
2937          */
2938         getDialog : function(){
2939            if(!dlg){
2940                 dlg = new Roo.bootstrap.Modal( {
2941                     //draggable: true,
2942                     //resizable:false,
2943                     //constraintoviewport:false,
2944                     //fixedcenter:true,
2945                     //collapsible : false,
2946                     //shim:true,
2947                     //modal: true,
2948                   //  width:400,
2949                   //  height:100,
2950                     //buttonAlign:"center",
2951                     closeClick : function(){
2952                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2953                             handleButton("no");
2954                         }else{
2955                             handleButton("cancel");
2956                         }
2957                     }
2958                 });
2959                 dlg.render();
2960                 dlg.on("hide", handleHide);
2961                 mask = dlg.mask;
2962                 //dlg.addKeyListener(27, handleEsc);
2963                 buttons = {};
2964                 this.buttons = buttons;
2965                 var bt = this.buttonText;
2966                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2967                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2968                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2969                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2970                 //Roo.log(buttons);
2971                 bodyEl = dlg.bodyEl.createChild({
2972
2973                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2974                         '<textarea class="roo-mb-textarea"></textarea>' +
2975                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2976                 });
2977                 msgEl = bodyEl.dom.firstChild;
2978                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2979                 textboxEl.enableDisplayMode();
2980                 textboxEl.addKeyListener([10,13], function(){
2981                     if(dlg.isVisible() && opt && opt.buttons){
2982                         if(opt.buttons.ok){
2983                             handleButton("ok");
2984                         }else if(opt.buttons.yes){
2985                             handleButton("yes");
2986                         }
2987                     }
2988                 });
2989                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2990                 textareaEl.enableDisplayMode();
2991                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2992                 progressEl.enableDisplayMode();
2993                 var pf = progressEl.dom.firstChild;
2994                 if (pf) {
2995                     pp = Roo.get(pf.firstChild);
2996                     pp.setHeight(pf.offsetHeight);
2997                 }
2998                 
2999             }
3000             return dlg;
3001         },
3002
3003         /**
3004          * Updates the message box body text
3005          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3006          * the XHTML-compliant non-breaking space character '&amp;#160;')
3007          * @return {Roo.MessageBox} This message box
3008          */
3009         updateText : function(text){
3010             if(!dlg.isVisible() && !opt.width){
3011                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3012             }
3013             msgEl.innerHTML = text || '&#160;';
3014       
3015             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3016             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3017             var w = Math.max(
3018                     Math.min(opt.width || cw , this.maxWidth), 
3019                     Math.max(opt.minWidth || this.minWidth, bwidth)
3020             );
3021             if(opt.prompt){
3022                 activeTextEl.setWidth(w);
3023             }
3024             if(dlg.isVisible()){
3025                 dlg.fixedcenter = false;
3026             }
3027             // to big, make it scroll. = But as usual stupid IE does not support
3028             // !important..
3029             
3030             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3031                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3032                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3033             } else {
3034                 bodyEl.dom.style.height = '';
3035                 bodyEl.dom.style.overflowY = '';
3036             }
3037             if (cw > w) {
3038                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3039             } else {
3040                 bodyEl.dom.style.overflowX = '';
3041             }
3042             
3043             dlg.setContentSize(w, bodyEl.getHeight());
3044             if(dlg.isVisible()){
3045                 dlg.fixedcenter = true;
3046             }
3047             return this;
3048         },
3049
3050         /**
3051          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3052          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3053          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3054          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3055          * @return {Roo.MessageBox} This message box
3056          */
3057         updateProgress : function(value, text){
3058             if(text){
3059                 this.updateText(text);
3060             }
3061             if (pp) { // weird bug on my firefox - for some reason this is not defined
3062                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3063             }
3064             return this;
3065         },        
3066
3067         /**
3068          * Returns true if the message box is currently displayed
3069          * @return {Boolean} True if the message box is visible, else false
3070          */
3071         isVisible : function(){
3072             return dlg && dlg.isVisible();  
3073         },
3074
3075         /**
3076          * Hides the message box if it is displayed
3077          */
3078         hide : function(){
3079             if(this.isVisible()){
3080                 dlg.hide();
3081             }  
3082         },
3083
3084         /**
3085          * Displays a new message box, or reinitializes an existing message box, based on the config options
3086          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3087          * The following config object properties are supported:
3088          * <pre>
3089 Property    Type             Description
3090 ----------  ---------------  ------------------------------------------------------------------------------------
3091 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3092                                    closes (defaults to undefined)
3093 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3094                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3095 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3096                                    progress and wait dialogs will ignore this property and always hide the
3097                                    close button as they can only be closed programmatically.
3098 cls               String           A custom CSS class to apply to the message box element
3099 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3100                                    displayed (defaults to 75)
3101 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3102                                    function will be btn (the name of the button that was clicked, if applicable,
3103                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3104                                    Progress and wait dialogs will ignore this option since they do not respond to
3105                                    user actions and can only be closed programmatically, so any required function
3106                                    should be called by the same code after it closes the dialog.
3107 icon              String           A CSS class that provides a background image to be used as an icon for
3108                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3109 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3110 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3111 modal             Boolean          False to allow user interaction with the page while the message box is
3112                                    displayed (defaults to true)
3113 msg               String           A string that will replace the existing message box body text (defaults
3114                                    to the XHTML-compliant non-breaking space character '&#160;')
3115 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3116 progress          Boolean          True to display a progress bar (defaults to false)
3117 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3118 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3119 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3120 title             String           The title text
3121 value             String           The string value to set into the active textbox element if displayed
3122 wait              Boolean          True to display a progress bar (defaults to false)
3123 width             Number           The width of the dialog in pixels
3124 </pre>
3125          *
3126          * Example usage:
3127          * <pre><code>
3128 Roo.Msg.show({
3129    title: 'Address',
3130    msg: 'Please enter your address:',
3131    width: 300,
3132    buttons: Roo.MessageBox.OKCANCEL,
3133    multiline: true,
3134    fn: saveAddress,
3135    animEl: 'addAddressBtn'
3136 });
3137 </code></pre>
3138          * @param {Object} config Configuration options
3139          * @return {Roo.MessageBox} This message box
3140          */
3141         show : function(options)
3142         {
3143             
3144             // this causes nightmares if you show one dialog after another
3145             // especially on callbacks..
3146              
3147             if(this.isVisible()){
3148                 
3149                 this.hide();
3150                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3151                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3152                 Roo.log("New Dialog Message:" +  options.msg )
3153                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3154                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3155                 
3156             }
3157             var d = this.getDialog();
3158             opt = options;
3159             d.setTitle(opt.title || "&#160;");
3160             d.closeEl.setDisplayed(opt.closable !== false);
3161             activeTextEl = textboxEl;
3162             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3163             if(opt.prompt){
3164                 if(opt.multiline){
3165                     textboxEl.hide();
3166                     textareaEl.show();
3167                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3168                         opt.multiline : this.defaultTextHeight);
3169                     activeTextEl = textareaEl;
3170                 }else{
3171                     textboxEl.show();
3172                     textareaEl.hide();
3173                 }
3174             }else{
3175                 textboxEl.hide();
3176                 textareaEl.hide();
3177             }
3178             progressEl.setDisplayed(opt.progress === true);
3179             this.updateProgress(0);
3180             activeTextEl.dom.value = opt.value || "";
3181             if(opt.prompt){
3182                 dlg.setDefaultButton(activeTextEl);
3183             }else{
3184                 var bs = opt.buttons;
3185                 var db = null;
3186                 if(bs && bs.ok){
3187                     db = buttons["ok"];
3188                 }else if(bs && bs.yes){
3189                     db = buttons["yes"];
3190                 }
3191                 dlg.setDefaultButton(db);
3192             }
3193             bwidth = updateButtons(opt.buttons);
3194             this.updateText(opt.msg);
3195             if(opt.cls){
3196                 d.el.addClass(opt.cls);
3197             }
3198             d.proxyDrag = opt.proxyDrag === true;
3199             d.modal = opt.modal !== false;
3200             d.mask = opt.modal !== false ? mask : false;
3201             if(!d.isVisible()){
3202                 // force it to the end of the z-index stack so it gets a cursor in FF
3203                 document.body.appendChild(dlg.el.dom);
3204                 d.animateTarget = null;
3205                 d.show(options.animEl);
3206             }
3207             return this;
3208         },
3209
3210         /**
3211          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3212          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3213          * and closing the message box when the process is complete.
3214          * @param {String} title The title bar text
3215          * @param {String} msg The message box body text
3216          * @return {Roo.MessageBox} This message box
3217          */
3218         progress : function(title, msg){
3219             this.show({
3220                 title : title,
3221                 msg : msg,
3222                 buttons: false,
3223                 progress:true,
3224                 closable:false,
3225                 minWidth: this.minProgressWidth,
3226                 modal : true
3227             });
3228             return this;
3229         },
3230
3231         /**
3232          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3233          * If a callback function is passed it will be called after the user clicks the button, and the
3234          * id of the button that was clicked will be passed as the only parameter to the callback
3235          * (could also be the top-right close button).
3236          * @param {String} title The title bar text
3237          * @param {String} msg The message box body text
3238          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3239          * @param {Object} scope (optional) The scope of the callback function
3240          * @return {Roo.MessageBox} This message box
3241          */
3242         alert : function(title, msg, fn, scope){
3243             this.show({
3244                 title : title,
3245                 msg : msg,
3246                 buttons: this.OK,
3247                 fn: fn,
3248                 scope : scope,
3249                 modal : true
3250             });
3251             return this;
3252         },
3253
3254         /**
3255          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3256          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3257          * You are responsible for closing the message box when the process is complete.
3258          * @param {String} msg The message box body text
3259          * @param {String} title (optional) The title bar text
3260          * @return {Roo.MessageBox} This message box
3261          */
3262         wait : function(msg, title){
3263             this.show({
3264                 title : title,
3265                 msg : msg,
3266                 buttons: false,
3267                 closable:false,
3268                 progress:true,
3269                 modal:true,
3270                 width:300,
3271                 wait:true
3272             });
3273             waitTimer = Roo.TaskMgr.start({
3274                 run: function(i){
3275                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3276                 },
3277                 interval: 1000
3278             });
3279             return this;
3280         },
3281
3282         /**
3283          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3284          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3285          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3286          * @param {String} title The title bar text
3287          * @param {String} msg The message box body text
3288          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3289          * @param {Object} scope (optional) The scope of the callback function
3290          * @return {Roo.MessageBox} This message box
3291          */
3292         confirm : function(title, msg, fn, scope){
3293             this.show({
3294                 title : title,
3295                 msg : msg,
3296                 buttons: this.YESNO,
3297                 fn: fn,
3298                 scope : scope,
3299                 modal : true
3300             });
3301             return this;
3302         },
3303
3304         /**
3305          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3306          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3307          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3308          * (could also be the top-right close button) and the text that was entered will be passed as the two
3309          * parameters to the callback.
3310          * @param {String} title The title bar text
3311          * @param {String} msg The message box body text
3312          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3313          * @param {Object} scope (optional) The scope of the callback function
3314          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3315          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3316          * @return {Roo.MessageBox} This message box
3317          */
3318         prompt : function(title, msg, fn, scope, multiline){
3319             this.show({
3320                 title : title,
3321                 msg : msg,
3322                 buttons: this.OKCANCEL,
3323                 fn: fn,
3324                 minWidth:250,
3325                 scope : scope,
3326                 prompt:true,
3327                 multiline: multiline,
3328                 modal : true
3329             });
3330             return this;
3331         },
3332
3333         /**
3334          * Button config that displays a single OK button
3335          * @type Object
3336          */
3337         OK : {ok:true},
3338         /**
3339          * Button config that displays Yes and No buttons
3340          * @type Object
3341          */
3342         YESNO : {yes:true, no:true},
3343         /**
3344          * Button config that displays OK and Cancel buttons
3345          * @type Object
3346          */
3347         OKCANCEL : {ok:true, cancel:true},
3348         /**
3349          * Button config that displays Yes, No and Cancel buttons
3350          * @type Object
3351          */
3352         YESNOCANCEL : {yes:true, no:true, cancel:true},
3353
3354         /**
3355          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3356          * @type Number
3357          */
3358         defaultTextHeight : 75,
3359         /**
3360          * The maximum width in pixels of the message box (defaults to 600)
3361          * @type Number
3362          */
3363         maxWidth : 600,
3364         /**
3365          * The minimum width in pixels of the message box (defaults to 100)
3366          * @type Number
3367          */
3368         minWidth : 100,
3369         /**
3370          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3371          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3372          * @type Number
3373          */
3374         minProgressWidth : 250,
3375         /**
3376          * An object containing the default button text strings that can be overriden for localized language support.
3377          * Supported properties are: ok, cancel, yes and no.
3378          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3379          * @type Object
3380          */
3381         buttonText : {
3382             ok : "OK",
3383             cancel : "Cancel",
3384             yes : "Yes",
3385             no : "No"
3386         }
3387     };
3388 }();
3389
3390 /**
3391  * Shorthand for {@link Roo.MessageBox}
3392  */
3393 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3394 Roo.Msg = Roo.Msg || Roo.MessageBox;
3395 /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.Navbar
3404  * @extends Roo.bootstrap.Component
3405  * Bootstrap Navbar class
3406
3407  * @constructor
3408  * Create a new Navbar
3409  * @param {Object} config The config object
3410  */
3411
3412
3413 Roo.bootstrap.Navbar = function(config){
3414     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3415     
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3419     
3420     
3421    
3422     // private
3423     navItems : false,
3424     loadMask : false,
3425     
3426     
3427     getAutoCreate : function(){
3428         
3429         
3430         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3431         
3432     },
3433     
3434     initEvents :function ()
3435     {
3436         //Roo.log(this.el.select('.navbar-toggle',true));
3437         this.el.select('.navbar-toggle',true).on('click', function() {
3438            // Roo.log('click');
3439             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3440         }, this);
3441         
3442         var mark = {
3443             tag: "div",
3444             cls:"x-dlg-mask"
3445         };
3446         
3447         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3448         
3449         var size = this.el.getSize();
3450         this.maskEl.setSize(size.width, size.height);
3451         this.maskEl.enableDisplayMode("block");
3452         this.maskEl.hide();
3453         
3454         if(this.loadMask){
3455             this.maskEl.show();
3456         }
3457     },
3458     
3459     
3460     getChildContainer : function()
3461     {
3462         if (this.el.select('.collapse').getCount()) {
3463             return this.el.select('.collapse',true).first();
3464         }
3465         
3466         return this.el;
3467     },
3468     
3469     mask : function()
3470     {
3471         this.maskEl.show();
3472     },
3473     
3474     unmask : function()
3475     {
3476         this.maskEl.hide();
3477     } 
3478     
3479     
3480     
3481     
3482 });
3483
3484
3485
3486  
3487
3488  /*
3489  * - LGPL
3490  *
3491  * navbar
3492  * 
3493  */
3494
3495 /**
3496  * @class Roo.bootstrap.NavSimplebar
3497  * @extends Roo.bootstrap.Navbar
3498  * Bootstrap Sidebar class
3499  *
3500  * @cfg {Boolean} inverse is inverted color
3501  * 
3502  * @cfg {String} type (nav | pills | tabs)
3503  * @cfg {Boolean} arrangement stacked | justified
3504  * @cfg {String} align (left | right) alignment
3505  * 
3506  * @cfg {Boolean} main (true|false) main nav bar? default false
3507  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3508  * 
3509  * @cfg {String} tag (header|footer|nav|div) default is nav 
3510
3511  * 
3512  * 
3513  * 
3514  * @constructor
3515  * Create a new Sidebar
3516  * @param {Object} config The config object
3517  */
3518
3519
3520 Roo.bootstrap.NavSimplebar = function(config){
3521     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3522 };
3523
3524 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3525     
3526     inverse: false,
3527     
3528     type: false,
3529     arrangement: '',
3530     align : false,
3531     
3532     
3533     
3534     main : false,
3535     
3536     
3537     tag : false,
3538     
3539     
3540     getAutoCreate : function(){
3541         
3542         
3543         var cfg = {
3544             tag : this.tag || 'div',
3545             cls : 'navbar'
3546         };
3547           
3548         
3549         cfg.cn = [
3550             {
3551                 cls: 'nav',
3552                 tag : 'ul'
3553             }
3554         ];
3555         
3556          
3557         this.type = this.type || 'nav';
3558         if (['tabs','pills'].indexOf(this.type)!==-1) {
3559             cfg.cn[0].cls += ' nav-' + this.type
3560         
3561         
3562         } else {
3563             if (this.type!=='nav') {
3564                 Roo.log('nav type must be nav/tabs/pills')
3565             }
3566             cfg.cn[0].cls += ' navbar-nav'
3567         }
3568         
3569         
3570         
3571         
3572         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3573             cfg.cn[0].cls += ' nav-' + this.arrangement;
3574         }
3575         
3576         
3577         if (this.align === 'right') {
3578             cfg.cn[0].cls += ' navbar-right';
3579         }
3580         
3581         if (this.inverse) {
3582             cfg.cls += ' navbar-inverse';
3583             
3584         }
3585         
3586         
3587         return cfg;
3588     
3589         
3590     }
3591     
3592     
3593     
3594 });
3595
3596
3597
3598  
3599
3600  
3601        /*
3602  * - LGPL
3603  *
3604  * navbar
3605  * 
3606  */
3607
3608 /**
3609  * @class Roo.bootstrap.NavHeaderbar
3610  * @extends Roo.bootstrap.NavSimplebar
3611  * Bootstrap Sidebar class
3612  *
3613  * @cfg {String} brand what is brand
3614  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3615  * @cfg {String} brand_href href of the brand
3616  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3617  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3618  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3619  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3620  * 
3621  * @constructor
3622  * Create a new Sidebar
3623  * @param {Object} config The config object
3624  */
3625
3626
3627 Roo.bootstrap.NavHeaderbar = function(config){
3628     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3629       
3630 };
3631
3632 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3633     
3634     position: '',
3635     brand: '',
3636     brand_href: false,
3637     srButton : true,
3638     autohide : false,
3639     desktopCenter : false,
3640    
3641     
3642     getAutoCreate : function(){
3643         
3644         var   cfg = {
3645             tag: this.nav || 'nav',
3646             cls: 'navbar',
3647             role: 'navigation',
3648             cn: []
3649         };
3650         
3651         var cn = cfg.cn;
3652         if (this.desktopCenter) {
3653             cn.push({cls : 'container', cn : []});
3654             cn = cn[0].cn;
3655         }
3656         
3657         if(this.srButton){
3658             cn.push({
3659                 tag: 'div',
3660                 cls: 'navbar-header',
3661                 cn: [
3662                     {
3663                         tag: 'button',
3664                         type: 'button',
3665                         cls: 'navbar-toggle',
3666                         'data-toggle': 'collapse',
3667                         cn: [
3668                             {
3669                                 tag: 'span',
3670                                 cls: 'sr-only',
3671                                 html: 'Toggle navigation'
3672                             },
3673                             {
3674                                 tag: 'span',
3675                                 cls: 'icon-bar'
3676                             },
3677                             {
3678                                 tag: 'span',
3679                                 cls: 'icon-bar'
3680                             },
3681                             {
3682                                 tag: 'span',
3683                                 cls: 'icon-bar'
3684                             }
3685                         ]
3686                     }
3687                 ]
3688             });
3689         }
3690         
3691         cn.push({
3692             tag: 'div',
3693             cls: 'collapse navbar-collapse',
3694             cn : []
3695         });
3696         
3697         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3698         
3699         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3700             cfg.cls += ' navbar-' + this.position;
3701             
3702             // tag can override this..
3703             
3704             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3705         }
3706         
3707         if (this.brand !== '') {
3708             cn[0].cn.push({
3709                 tag: 'a',
3710                 href: this.brand_href ? this.brand_href : '#',
3711                 cls: 'navbar-brand',
3712                 cn: [
3713                 this.brand
3714                 ]
3715             });
3716         }
3717         
3718         if(this.main){
3719             cfg.cls += ' main-nav';
3720         }
3721         
3722         
3723         return cfg;
3724
3725         
3726     },
3727     getHeaderChildContainer : function()
3728     {
3729         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3730             return this.el.select('.navbar-header',true).first();
3731         }
3732         
3733         return this.getChildContainer();
3734     },
3735     
3736     
3737     initEvents : function()
3738     {
3739         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3740         
3741         if (this.autohide) {
3742             
3743             var prevScroll = 0;
3744             var ft = this.el;
3745             
3746             Roo.get(document).on('scroll',function(e) {
3747                 var ns = Roo.get(document).getScroll().top;
3748                 var os = prevScroll;
3749                 prevScroll = ns;
3750                 
3751                 if(ns > os){
3752                     ft.removeClass('slideDown');
3753                     ft.addClass('slideUp');
3754                     return;
3755                 }
3756                 ft.removeClass('slideUp');
3757                 ft.addClass('slideDown');
3758                  
3759               
3760           },this);
3761         }
3762     }    
3763     
3764 });
3765
3766
3767
3768  
3769
3770  /*
3771  * - LGPL
3772  *
3773  * navbar
3774  * 
3775  */
3776
3777 /**
3778  * @class Roo.bootstrap.NavSidebar
3779  * @extends Roo.bootstrap.Navbar
3780  * Bootstrap Sidebar class
3781  * 
3782  * @constructor
3783  * Create a new Sidebar
3784  * @param {Object} config The config object
3785  */
3786
3787
3788 Roo.bootstrap.NavSidebar = function(config){
3789     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3790 };
3791
3792 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3793     
3794     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3795     
3796     getAutoCreate : function(){
3797         
3798         
3799         return  {
3800             tag: 'div',
3801             cls: 'sidebar sidebar-nav'
3802         };
3803     
3804         
3805     }
3806     
3807     
3808     
3809 });
3810
3811
3812
3813  
3814
3815  /*
3816  * - LGPL
3817  *
3818  * nav group
3819  * 
3820  */
3821
3822 /**
3823  * @class Roo.bootstrap.NavGroup
3824  * @extends Roo.bootstrap.Component
3825  * Bootstrap NavGroup class
3826  * @cfg {String} align (left|right)
3827  * @cfg {Boolean} inverse
3828  * @cfg {String} type (nav|pills|tab) default nav
3829  * @cfg {String} navId - reference Id for navbar.
3830
3831  * 
3832  * @constructor
3833  * Create a new nav group
3834  * @param {Object} config The config object
3835  */
3836
3837 Roo.bootstrap.NavGroup = function(config){
3838     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3839     this.navItems = [];
3840    
3841     Roo.bootstrap.NavGroup.register(this);
3842      this.addEvents({
3843         /**
3844              * @event changed
3845              * Fires when the active item changes
3846              * @param {Roo.bootstrap.NavGroup} this
3847              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3848              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3849          */
3850         'changed': true
3851      });
3852     
3853 };
3854
3855 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3856     
3857     align: '',
3858     inverse: false,
3859     form: false,
3860     type: 'nav',
3861     navId : '',
3862     // private
3863     
3864     navItems : false, 
3865     
3866     getAutoCreate : function()
3867     {
3868         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3869         
3870         cfg = {
3871             tag : 'ul',
3872             cls: 'nav' 
3873         };
3874         
3875         if (['tabs','pills'].indexOf(this.type)!==-1) {
3876             cfg.cls += ' nav-' + this.type
3877         } else {
3878             if (this.type!=='nav') {
3879                 Roo.log('nav type must be nav/tabs/pills')
3880             }
3881             cfg.cls += ' navbar-nav'
3882         }
3883         
3884         if (this.parent().sidebar) {
3885             cfg = {
3886                 tag: 'ul',
3887                 cls: 'dashboard-menu sidebar-menu'
3888             };
3889             
3890             return cfg;
3891         }
3892         
3893         if (this.form === true) {
3894             cfg = {
3895                 tag: 'form',
3896                 cls: 'navbar-form'
3897             };
3898             
3899             if (this.align === 'right') {
3900                 cfg.cls += ' navbar-right';
3901             } else {
3902                 cfg.cls += ' navbar-left';
3903             }
3904         }
3905         
3906         if (this.align === 'right') {
3907             cfg.cls += ' navbar-right';
3908         }
3909         
3910         if (this.inverse) {
3911             cfg.cls += ' navbar-inverse';
3912             
3913         }
3914         
3915         
3916         return cfg;
3917     },
3918     /**
3919     * sets the active Navigation item
3920     * @param {Roo.bootstrap.NavItem} the new current navitem
3921     */
3922     setActiveItem : function(item)
3923     {
3924         var prev = false;
3925         Roo.each(this.navItems, function(v){
3926             if (v == item) {
3927                 return ;
3928             }
3929             if (v.isActive()) {
3930                 v.setActive(false, true);
3931                 prev = v;
3932                 
3933             }
3934             
3935         });
3936
3937         item.setActive(true, true);
3938         this.fireEvent('changed', this, item, prev);
3939         
3940         
3941     },
3942     /**
3943     * gets the active Navigation item
3944     * @return {Roo.bootstrap.NavItem} the current navitem
3945     */
3946     getActive : function()
3947     {
3948         
3949         var prev = false;
3950         Roo.each(this.navItems, function(v){
3951             
3952             if (v.isActive()) {
3953                 prev = v;
3954                 
3955             }
3956             
3957         });
3958         return prev;
3959     },
3960     
3961     indexOfNav : function()
3962     {
3963         
3964         var prev = false;
3965         Roo.each(this.navItems, function(v,i){
3966             
3967             if (v.isActive()) {
3968                 prev = i;
3969                 
3970             }
3971             
3972         });
3973         return prev;
3974     },
3975     /**
3976     * adds a Navigation item
3977     * @param {Roo.bootstrap.NavItem} the navitem to add
3978     */
3979     addItem : function(cfg)
3980     {
3981         var cn = new Roo.bootstrap.NavItem(cfg);
3982         this.register(cn);
3983         cn.parentId = this.id;
3984         cn.onRender(this.el, null);
3985         return cn;
3986     },
3987     /**
3988     * register a Navigation item
3989     * @param {Roo.bootstrap.NavItem} the navitem to add
3990     */
3991     register : function(item)
3992     {
3993         this.navItems.push( item);
3994         item.navId = this.navId;
3995     
3996     },
3997     
3998     /**
3999     * clear all the Navigation item
4000     */
4001    
4002     clearAll : function()
4003     {
4004         this.navItems = [];
4005         this.el.dom.innerHTML = '';
4006     },
4007     
4008     getNavItem: function(tabId)
4009     {
4010         var ret = false;
4011         Roo.each(this.navItems, function(e) {
4012             if (e.tabId == tabId) {
4013                ret =  e;
4014                return false;
4015             }
4016             return true;
4017             
4018         });
4019         return ret;
4020     },
4021     
4022     setActiveNext : function()
4023     {
4024         var i = this.indexOfNav(this.getActive());
4025         if (i > this.navItems.length) {
4026             return;
4027         }
4028         this.setActiveItem(this.navItems[i+1]);
4029     },
4030     setActivePrev : function()
4031     {
4032         var i = this.indexOfNav(this.getActive());
4033         if (i  < 1) {
4034             return;
4035         }
4036         this.setActiveItem(this.navItems[i-1]);
4037     },
4038     clearWasActive : function(except) {
4039         Roo.each(this.navItems, function(e) {
4040             if (e.tabId != except.tabId && e.was_active) {
4041                e.was_active = false;
4042                return false;
4043             }
4044             return true;
4045             
4046         });
4047     },
4048     getWasActive : function ()
4049     {
4050         var r = false;
4051         Roo.each(this.navItems, function(e) {
4052             if (e.was_active) {
4053                r = e;
4054                return false;
4055             }
4056             return true;
4057             
4058         });
4059         return r;
4060     }
4061     
4062     
4063 });
4064
4065  
4066 Roo.apply(Roo.bootstrap.NavGroup, {
4067     
4068     groups: {},
4069      /**
4070     * register a Navigation Group
4071     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4072     */
4073     register : function(navgrp)
4074     {
4075         this.groups[navgrp.navId] = navgrp;
4076         
4077     },
4078     /**
4079     * fetch a Navigation Group based on the navigation ID
4080     * @param {string} the navgroup to add
4081     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4082     */
4083     get: function(navId) {
4084         if (typeof(this.groups[navId]) == 'undefined') {
4085             return false;
4086             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4087         }
4088         return this.groups[navId] ;
4089     }
4090     
4091     
4092     
4093 });
4094
4095  /*
4096  * - LGPL
4097  *
4098  * row
4099  * 
4100  */
4101
4102 /**
4103  * @class Roo.bootstrap.NavItem
4104  * @extends Roo.bootstrap.Component
4105  * Bootstrap Navbar.NavItem class
4106  * @cfg {String} href  link to
4107  * @cfg {String} html content of button
4108  * @cfg {String} badge text inside badge
4109  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4110  * @cfg {String} glyphicon name of glyphicon
4111  * @cfg {String} icon name of font awesome icon
4112  * @cfg {Boolean} active Is item active
4113  * @cfg {Boolean} disabled Is item disabled
4114  
4115  * @cfg {Boolean} preventDefault (true | false) default false
4116  * @cfg {String} tabId the tab that this item activates.
4117  * @cfg {String} tagtype (a|span) render as a href or span?
4118  * @cfg {Boolean} animateRef (true|false) link to element default false  
4119   
4120  * @constructor
4121  * Create a new Navbar Item
4122  * @param {Object} config The config object
4123  */
4124 Roo.bootstrap.NavItem = function(config){
4125     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4126     this.addEvents({
4127         // raw events
4128         /**
4129          * @event click
4130          * The raw click event for the entire grid.
4131          * @param {Roo.EventObject} e
4132          */
4133         "click" : true,
4134          /**
4135             * @event changed
4136             * Fires when the active item active state changes
4137             * @param {Roo.bootstrap.NavItem} this
4138             * @param {boolean} state the new state
4139              
4140          */
4141         'changed': true,
4142         /**
4143             * @event scrollto
4144             * Fires when scroll to element
4145             * @param {Roo.bootstrap.NavItem} this
4146             * @param {Object} options
4147             * @param {Roo.EventObject} e
4148              
4149          */
4150         'scrollto': true
4151     });
4152    
4153 };
4154
4155 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4156     
4157     href: false,
4158     html: '',
4159     badge: '',
4160     icon: false,
4161     glyphicon: false,
4162     active: false,
4163     preventDefault : false,
4164     tabId : false,
4165     tagtype : 'a',
4166     disabled : false,
4167     animateRef : false,
4168     was_active : false,
4169     
4170     getAutoCreate : function(){
4171          
4172         var cfg = {
4173             tag: 'li',
4174             cls: 'nav-item'
4175             
4176         };
4177         
4178         if (this.active) {
4179             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4180         }
4181         if (this.disabled) {
4182             cfg.cls += ' disabled';
4183         }
4184         
4185         if (this.href || this.html || this.glyphicon || this.icon) {
4186             cfg.cn = [
4187                 {
4188                     tag: this.tagtype,
4189                     href : this.href || "#",
4190                     html: this.html || ''
4191                 }
4192             ];
4193             
4194             if (this.icon) {
4195                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4196             }
4197
4198             if(this.glyphicon) {
4199                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4200             }
4201             
4202             if (this.menu) {
4203                 
4204                 cfg.cn[0].html += " <span class='caret'></span>";
4205              
4206             }
4207             
4208             if (this.badge !== '') {
4209                  
4210                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4211             }
4212         }
4213         
4214         
4215         
4216         return cfg;
4217     },
4218     initEvents: function() 
4219     {
4220         if (typeof (this.menu) != 'undefined') {
4221             this.menu.parentType = this.xtype;
4222             this.menu.triggerEl = this.el;
4223             this.menu = this.addxtype(Roo.apply({}, this.menu));
4224         }
4225         
4226         this.el.select('a',true).on('click', this.onClick, this);
4227         
4228         if(this.tagtype == 'span'){
4229             this.el.select('span',true).on('click', this.onClick, this);
4230         }
4231        
4232         // at this point parent should be available..
4233         this.parent().register(this);
4234     },
4235     
4236     onClick : function(e)
4237     {
4238         if (e.getTarget('.dropdown-menu-item')) {
4239             // did you click on a menu itemm.... - then don't trigger onclick..
4240             return;
4241         }
4242         
4243         if(
4244                 this.preventDefault || 
4245                 this.href == '#' 
4246         ){
4247             Roo.log("NavItem - prevent Default?");
4248             e.preventDefault();
4249         }
4250         
4251         if (this.disabled) {
4252             return;
4253         }
4254         
4255         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4256         if (tg && tg.transition) {
4257             Roo.log("waiting for the transitionend");
4258             return;
4259         }
4260         
4261         
4262         
4263         //Roo.log("fire event clicked");
4264         if(this.fireEvent('click', this, e) === false){
4265             return;
4266         };
4267         
4268         if(this.tagtype == 'span'){
4269             return;
4270         }
4271         
4272         //Roo.log(this.href);
4273         var ael = this.el.select('a',true).first();
4274         //Roo.log(ael);
4275         
4276         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4277             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4278             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4279                 return; // ignore... - it's a 'hash' to another page.
4280             }
4281             Roo.log("NavItem - prevent Default?");
4282             e.preventDefault();
4283             this.scrollToElement(e);
4284         }
4285         
4286         
4287         var p =  this.parent();
4288    
4289         if (['tabs','pills'].indexOf(p.type)!==-1) {
4290             if (typeof(p.setActiveItem) !== 'undefined') {
4291                 p.setActiveItem(this);
4292             }
4293         }
4294         
4295         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4296         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4297             // remove the collapsed menu expand...
4298             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4299         }
4300     },
4301     
4302     isActive: function () {
4303         return this.active
4304     },
4305     setActive : function(state, fire, is_was_active)
4306     {
4307         if (this.active && !state && this.navId) {
4308             this.was_active = true;
4309             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4310             if (nv) {
4311                 nv.clearWasActive(this);
4312             }
4313             
4314         }
4315         this.active = state;
4316         
4317         if (!state ) {
4318             this.el.removeClass('active');
4319         } else if (!this.el.hasClass('active')) {
4320             this.el.addClass('active');
4321         }
4322         if (fire) {
4323             this.fireEvent('changed', this, state);
4324         }
4325         
4326         // show a panel if it's registered and related..
4327         
4328         if (!this.navId || !this.tabId || !state || is_was_active) {
4329             return;
4330         }
4331         
4332         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4333         if (!tg) {
4334             return;
4335         }
4336         var pan = tg.getPanelByName(this.tabId);
4337         if (!pan) {
4338             return;
4339         }
4340         // if we can not flip to new panel - go back to old nav highlight..
4341         if (false == tg.showPanel(pan)) {
4342             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4343             if (nv) {
4344                 var onav = nv.getWasActive();
4345                 if (onav) {
4346                     onav.setActive(true, false, true);
4347                 }
4348             }
4349             
4350         }
4351         
4352         
4353         
4354     },
4355      // this should not be here...
4356     setDisabled : function(state)
4357     {
4358         this.disabled = state;
4359         if (!state ) {
4360             this.el.removeClass('disabled');
4361         } else if (!this.el.hasClass('disabled')) {
4362             this.el.addClass('disabled');
4363         }
4364         
4365     },
4366     
4367     /**
4368      * Fetch the element to display the tooltip on.
4369      * @return {Roo.Element} defaults to this.el
4370      */
4371     tooltipEl : function()
4372     {
4373         return this.el.select('' + this.tagtype + '', true).first();
4374     },
4375     
4376     scrollToElement : function(e)
4377     {
4378         var c = document.body;
4379         
4380         /*
4381          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4382          */
4383         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4384             c = document.documentElement;
4385         }
4386         
4387         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4388         
4389         if(!target){
4390             return;
4391         }
4392
4393         var o = target.calcOffsetsTo(c);
4394         
4395         var options = {
4396             target : target,
4397             value : o[1]
4398         };
4399         
4400         this.fireEvent('scrollto', this, options, e);
4401         
4402         Roo.get(c).scrollTo('top', options.value, true);
4403         
4404         return;
4405     }
4406 });
4407  
4408
4409  /*
4410  * - LGPL
4411  *
4412  * sidebar item
4413  *
4414  *  li
4415  *    <span> icon </span>
4416  *    <span> text </span>
4417  *    <span>badge </span>
4418  */
4419
4420 /**
4421  * @class Roo.bootstrap.NavSidebarItem
4422  * @extends Roo.bootstrap.NavItem
4423  * Bootstrap Navbar.NavSidebarItem class
4424  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4425  * {bool} open is the menu open
4426  * @constructor
4427  * Create a new Navbar Button
4428  * @param {Object} config The config object
4429  */
4430 Roo.bootstrap.NavSidebarItem = function(config){
4431     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4432     this.addEvents({
4433         // raw events
4434         /**
4435          * @event click
4436          * The raw click event for the entire grid.
4437          * @param {Roo.EventObject} e
4438          */
4439         "click" : true,
4440          /**
4441             * @event changed
4442             * Fires when the active item active state changes
4443             * @param {Roo.bootstrap.NavSidebarItem} this
4444             * @param {boolean} state the new state
4445              
4446          */
4447         'changed': true
4448     });
4449    
4450 };
4451
4452 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4453     
4454     badgeWeight : 'default',
4455     
4456     open: false,
4457     
4458     getAutoCreate : function(){
4459         
4460         
4461         var a = {
4462                 tag: 'a',
4463                 href : this.href || '#',
4464                 cls: '',
4465                 html : '',
4466                 cn : []
4467         };
4468         var cfg = {
4469             tag: 'li',
4470             cls: '',
4471             cn: [ a ]
4472         };
4473         var span = {
4474             tag: 'span',
4475             html : this.html || ''
4476         };
4477         
4478         
4479         if (this.active) {
4480             cfg.cls += ' active';
4481         }
4482         
4483         if (this.disabled) {
4484             cfg.cls += ' disabled';
4485         }
4486         if (this.open) {
4487             cfg.cls += ' open x-open';
4488         }
4489         // left icon..
4490         if (this.glyphicon || this.icon) {
4491             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4492             a.cn.push({ tag : 'i', cls : c }) ;
4493         }
4494         // html..
4495         a.cn.push(span);
4496         // then badge..
4497         if (this.badge !== '') {
4498             
4499             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4500         }
4501         // fi
4502         if (this.menu) {
4503             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4504             a.cls += 'dropdown-toggle treeview' ;
4505             
4506         }
4507         
4508         
4509         
4510         return cfg;
4511          
4512            
4513     },
4514     
4515     initEvents : function()
4516     { 
4517         if (typeof (this.menu) != 'undefined') {
4518             this.menu.parentType = this.xtype;
4519             this.menu.triggerEl = this.el;
4520             this.menu = this.addxtype(Roo.apply({}, this.menu));
4521         }
4522         
4523         this.el.on('click', this.onClick, this);
4524        
4525     
4526         if(this.badge !== ''){
4527  
4528             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4529         }
4530         
4531     },
4532     
4533     onClick : function(e)
4534     {
4535         if(this.disabled){
4536             e.preventDefault();
4537             return;
4538         }
4539         
4540         if(this.preventDefault){
4541             e.preventDefault();
4542         }
4543         
4544         this.fireEvent('click', this);
4545     },
4546     
4547     disable : function()
4548     {
4549         this.setDisabled(true);
4550     },
4551     
4552     enable : function()
4553     {
4554         this.setDisabled(false);
4555     },
4556     
4557     setDisabled : function(state)
4558     {
4559         if(this.disabled == state){
4560             return;
4561         }
4562         
4563         this.disabled = state;
4564         
4565         if (state) {
4566             this.el.addClass('disabled');
4567             return;
4568         }
4569         
4570         this.el.removeClass('disabled');
4571         
4572         return;
4573     },
4574     
4575     setActive : function(state)
4576     {
4577         if(this.active == state){
4578             return;
4579         }
4580         
4581         this.active = state;
4582         
4583         if (state) {
4584             this.el.addClass('active');
4585             return;
4586         }
4587         
4588         this.el.removeClass('active');
4589         
4590         return;
4591     },
4592     
4593     isActive: function () 
4594     {
4595         return this.active;
4596     },
4597     
4598     setBadge : function(str)
4599     {
4600         if(!this.badgeEl){
4601             return;
4602         }
4603         
4604         this.badgeEl.dom.innerHTML = str;
4605     }
4606     
4607    
4608      
4609  
4610 });
4611  
4612
4613  /*
4614  * - LGPL
4615  *
4616  * row
4617  * 
4618  */
4619
4620 /**
4621  * @class Roo.bootstrap.Row
4622  * @extends Roo.bootstrap.Component
4623  * Bootstrap Row class (contains columns...)
4624  * 
4625  * @constructor
4626  * Create a new Row
4627  * @param {Object} config The config object
4628  */
4629
4630 Roo.bootstrap.Row = function(config){
4631     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4632 };
4633
4634 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4635     
4636     getAutoCreate : function(){
4637        return {
4638             cls: 'row clearfix'
4639        };
4640     }
4641     
4642     
4643 });
4644
4645  
4646
4647  /*
4648  * - LGPL
4649  *
4650  * element
4651  * 
4652  */
4653
4654 /**
4655  * @class Roo.bootstrap.Element
4656  * @extends Roo.bootstrap.Component
4657  * Bootstrap Element class
4658  * @cfg {String} html contents of the element
4659  * @cfg {String} tag tag of the element
4660  * @cfg {String} cls class of the element
4661  * @cfg {Boolean} preventDefault (true|false) default false
4662  * @cfg {Boolean} clickable (true|false) default false
4663  * 
4664  * @constructor
4665  * Create a new Element
4666  * @param {Object} config The config object
4667  */
4668
4669 Roo.bootstrap.Element = function(config){
4670     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4671     
4672     this.addEvents({
4673         // raw events
4674         /**
4675          * @event click
4676          * When a element is chick
4677          * @param {Roo.bootstrap.Element} this
4678          * @param {Roo.EventObject} e
4679          */
4680         "click" : true
4681     });
4682 };
4683
4684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4685     
4686     tag: 'div',
4687     cls: '',
4688     html: '',
4689     preventDefault: false, 
4690     clickable: false,
4691     
4692     getAutoCreate : function(){
4693         
4694         var cfg = {
4695             tag: this.tag,
4696             cls: this.cls,
4697             html: this.html
4698         };
4699         
4700         return cfg;
4701     },
4702     
4703     initEvents: function() 
4704     {
4705         Roo.bootstrap.Element.superclass.initEvents.call(this);
4706         
4707         if(this.clickable){
4708             this.el.on('click', this.onClick, this);
4709         }
4710         
4711     },
4712     
4713     onClick : function(e)
4714     {
4715         if(this.preventDefault){
4716             e.preventDefault();
4717         }
4718         
4719         this.fireEvent('click', this, e);
4720     },
4721     
4722     getValue : function()
4723     {
4724         return this.el.dom.innerHTML;
4725     },
4726     
4727     setValue : function(value)
4728     {
4729         this.el.dom.innerHTML = value;
4730     }
4731    
4732 });
4733
4734  
4735
4736  /*
4737  * - LGPL
4738  *
4739  * pagination
4740  * 
4741  */
4742
4743 /**
4744  * @class Roo.bootstrap.Pagination
4745  * @extends Roo.bootstrap.Component
4746  * Bootstrap Pagination class
4747  * @cfg {String} size xs | sm | md | lg
4748  * @cfg {Boolean} inverse false | true
4749  * 
4750  * @constructor
4751  * Create a new Pagination
4752  * @param {Object} config The config object
4753  */
4754
4755 Roo.bootstrap.Pagination = function(config){
4756     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4757 };
4758
4759 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4760     
4761     cls: false,
4762     size: false,
4763     inverse: false,
4764     
4765     getAutoCreate : function(){
4766         var cfg = {
4767             tag: 'ul',
4768                 cls: 'pagination'
4769         };
4770         if (this.inverse) {
4771             cfg.cls += ' inverse';
4772         }
4773         if (this.html) {
4774             cfg.html=this.html;
4775         }
4776         if (this.cls) {
4777             cfg.cls += " " + this.cls;
4778         }
4779         return cfg;
4780     }
4781    
4782 });
4783
4784  
4785
4786  /*
4787  * - LGPL
4788  *
4789  * Pagination item
4790  * 
4791  */
4792
4793
4794 /**
4795  * @class Roo.bootstrap.PaginationItem
4796  * @extends Roo.bootstrap.Component
4797  * Bootstrap PaginationItem class
4798  * @cfg {String} html text
4799  * @cfg {String} href the link
4800  * @cfg {Boolean} preventDefault (true | false) default true
4801  * @cfg {Boolean} active (true | false) default false
4802  * @cfg {Boolean} disabled default false
4803  * 
4804  * 
4805  * @constructor
4806  * Create a new PaginationItem
4807  * @param {Object} config The config object
4808  */
4809
4810
4811 Roo.bootstrap.PaginationItem = function(config){
4812     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4813     this.addEvents({
4814         // raw events
4815         /**
4816          * @event click
4817          * The raw click event for the entire grid.
4818          * @param {Roo.EventObject} e
4819          */
4820         "click" : true
4821     });
4822 };
4823
4824 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4825     
4826     href : false,
4827     html : false,
4828     preventDefault: true,
4829     active : false,
4830     cls : false,
4831     disabled: false,
4832     
4833     getAutoCreate : function(){
4834         var cfg= {
4835             tag: 'li',
4836             cn: [
4837                 {
4838                     tag : 'a',
4839                     href : this.href ? this.href : '#',
4840                     html : this.html ? this.html : ''
4841                 }
4842             ]
4843         };
4844         
4845         if(this.cls){
4846             cfg.cls = this.cls;
4847         }
4848         
4849         if(this.disabled){
4850             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4851         }
4852         
4853         if(this.active){
4854             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4855         }
4856         
4857         return cfg;
4858     },
4859     
4860     initEvents: function() {
4861         
4862         this.el.on('click', this.onClick, this);
4863         
4864     },
4865     onClick : function(e)
4866     {
4867         Roo.log('PaginationItem on click ');
4868         if(this.preventDefault){
4869             e.preventDefault();
4870         }
4871         
4872         if(this.disabled){
4873             return;
4874         }
4875         
4876         this.fireEvent('click', this, e);
4877     }
4878    
4879 });
4880
4881  
4882
4883  /*
4884  * - LGPL
4885  *
4886  * slider
4887  * 
4888  */
4889
4890
4891 /**
4892  * @class Roo.bootstrap.Slider
4893  * @extends Roo.bootstrap.Component
4894  * Bootstrap Slider class
4895  *    
4896  * @constructor
4897  * Create a new Slider
4898  * @param {Object} config The config object
4899  */
4900
4901 Roo.bootstrap.Slider = function(config){
4902     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4903 };
4904
4905 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4906     
4907     getAutoCreate : function(){
4908         
4909         var cfg = {
4910             tag: 'div',
4911             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4912             cn: [
4913                 {
4914                     tag: 'a',
4915                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4916                 }
4917             ]
4918         };
4919         
4920         return cfg;
4921     }
4922    
4923 });
4924
4925  /*
4926  * Based on:
4927  * Ext JS Library 1.1.1
4928  * Copyright(c) 2006-2007, Ext JS, LLC.
4929  *
4930  * Originally Released Under LGPL - original licence link has changed is not relivant.
4931  *
4932  * Fork - LGPL
4933  * <script type="text/javascript">
4934  */
4935  
4936
4937 /**
4938  * @class Roo.grid.ColumnModel
4939  * @extends Roo.util.Observable
4940  * This is the default implementation of a ColumnModel used by the Grid. It defines
4941  * the columns in the grid.
4942  * <br>Usage:<br>
4943  <pre><code>
4944  var colModel = new Roo.grid.ColumnModel([
4945         {header: "Ticker", width: 60, sortable: true, locked: true},
4946         {header: "Company Name", width: 150, sortable: true},
4947         {header: "Market Cap.", width: 100, sortable: true},
4948         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4949         {header: "Employees", width: 100, sortable: true, resizable: false}
4950  ]);
4951  </code></pre>
4952  * <p>
4953  
4954  * The config options listed for this class are options which may appear in each
4955  * individual column definition.
4956  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4957  * @constructor
4958  * @param {Object} config An Array of column config objects. See this class's
4959  * config objects for details.
4960 */
4961 Roo.grid.ColumnModel = function(config){
4962         /**
4963      * The config passed into the constructor
4964      */
4965     this.config = config;
4966     this.lookup = {};
4967
4968     // if no id, create one
4969     // if the column does not have a dataIndex mapping,
4970     // map it to the order it is in the config
4971     for(var i = 0, len = config.length; i < len; i++){
4972         var c = config[i];
4973         if(typeof c.dataIndex == "undefined"){
4974             c.dataIndex = i;
4975         }
4976         if(typeof c.renderer == "string"){
4977             c.renderer = Roo.util.Format[c.renderer];
4978         }
4979         if(typeof c.id == "undefined"){
4980             c.id = Roo.id();
4981         }
4982         if(c.editor && c.editor.xtype){
4983             c.editor  = Roo.factory(c.editor, Roo.grid);
4984         }
4985         if(c.editor && c.editor.isFormField){
4986             c.editor = new Roo.grid.GridEditor(c.editor);
4987         }
4988         this.lookup[c.id] = c;
4989     }
4990
4991     /**
4992      * The width of columns which have no width specified (defaults to 100)
4993      * @type Number
4994      */
4995     this.defaultWidth = 100;
4996
4997     /**
4998      * Default sortable of columns which have no sortable specified (defaults to false)
4999      * @type Boolean
5000      */
5001     this.defaultSortable = false;
5002
5003     this.addEvents({
5004         /**
5005              * @event widthchange
5006              * Fires when the width of a column changes.
5007              * @param {ColumnModel} this
5008              * @param {Number} columnIndex The column index
5009              * @param {Number} newWidth The new width
5010              */
5011             "widthchange": true,
5012         /**
5013              * @event headerchange
5014              * Fires when the text of a header changes.
5015              * @param {ColumnModel} this
5016              * @param {Number} columnIndex The column index
5017              * @param {Number} newText The new header text
5018              */
5019             "headerchange": true,
5020         /**
5021              * @event hiddenchange
5022              * Fires when a column is hidden or "unhidden".
5023              * @param {ColumnModel} this
5024              * @param {Number} columnIndex The column index
5025              * @param {Boolean} hidden true if hidden, false otherwise
5026              */
5027             "hiddenchange": true,
5028             /**
5029          * @event columnmoved
5030          * Fires when a column is moved.
5031          * @param {ColumnModel} this
5032          * @param {Number} oldIndex
5033          * @param {Number} newIndex
5034          */
5035         "columnmoved" : true,
5036         /**
5037          * @event columlockchange
5038          * Fires when a column's locked state is changed
5039          * @param {ColumnModel} this
5040          * @param {Number} colIndex
5041          * @param {Boolean} locked true if locked
5042          */
5043         "columnlockchange" : true
5044     });
5045     Roo.grid.ColumnModel.superclass.constructor.call(this);
5046 };
5047 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5048     /**
5049      * @cfg {String} header The header text to display in the Grid view.
5050      */
5051     /**
5052      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5053      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5054      * specified, the column's index is used as an index into the Record's data Array.
5055      */
5056     /**
5057      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5058      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5059      */
5060     /**
5061      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5062      * Defaults to the value of the {@link #defaultSortable} property.
5063      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5064      */
5065     /**
5066      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5067      */
5068     /**
5069      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5070      */
5071     /**
5072      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5073      */
5074     /**
5075      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5076      */
5077     /**
5078      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5079      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5080      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5081      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5082      */
5083        /**
5084      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5085      */
5086     /**
5087      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5088      */
5089     /**
5090      * @cfg {String} cursor (Optional)
5091      */
5092     /**
5093      * @cfg {String} tooltip (Optional)
5094      */
5095     /**
5096      * @cfg {Number} xs (Optional)
5097      */
5098     /**
5099      * @cfg {Number} sm (Optional)
5100      */
5101     /**
5102      * @cfg {Number} md (Optional)
5103      */
5104     /**
5105      * @cfg {Number} lg (Optional)
5106      */
5107     /**
5108      * Returns the id of the column at the specified index.
5109      * @param {Number} index The column index
5110      * @return {String} the id
5111      */
5112     getColumnId : function(index){
5113         return this.config[index].id;
5114     },
5115
5116     /**
5117      * Returns the column for a specified id.
5118      * @param {String} id The column id
5119      * @return {Object} the column
5120      */
5121     getColumnById : function(id){
5122         return this.lookup[id];
5123     },
5124
5125     
5126     /**
5127      * Returns the column for a specified dataIndex.
5128      * @param {String} dataIndex The column dataIndex
5129      * @return {Object|Boolean} the column or false if not found
5130      */
5131     getColumnByDataIndex: function(dataIndex){
5132         var index = this.findColumnIndex(dataIndex);
5133         return index > -1 ? this.config[index] : false;
5134     },
5135     
5136     /**
5137      * Returns the index for a specified column id.
5138      * @param {String} id The column id
5139      * @return {Number} the index, or -1 if not found
5140      */
5141     getIndexById : function(id){
5142         for(var i = 0, len = this.config.length; i < len; i++){
5143             if(this.config[i].id == id){
5144                 return i;
5145             }
5146         }
5147         return -1;
5148     },
5149     
5150     /**
5151      * Returns the index for a specified column dataIndex.
5152      * @param {String} dataIndex The column dataIndex
5153      * @return {Number} the index, or -1 if not found
5154      */
5155     
5156     findColumnIndex : function(dataIndex){
5157         for(var i = 0, len = this.config.length; i < len; i++){
5158             if(this.config[i].dataIndex == dataIndex){
5159                 return i;
5160             }
5161         }
5162         return -1;
5163     },
5164     
5165     
5166     moveColumn : function(oldIndex, newIndex){
5167         var c = this.config[oldIndex];
5168         this.config.splice(oldIndex, 1);
5169         this.config.splice(newIndex, 0, c);
5170         this.dataMap = null;
5171         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5172     },
5173
5174     isLocked : function(colIndex){
5175         return this.config[colIndex].locked === true;
5176     },
5177
5178     setLocked : function(colIndex, value, suppressEvent){
5179         if(this.isLocked(colIndex) == value){
5180             return;
5181         }
5182         this.config[colIndex].locked = value;
5183         if(!suppressEvent){
5184             this.fireEvent("columnlockchange", this, colIndex, value);
5185         }
5186     },
5187
5188     getTotalLockedWidth : function(){
5189         var totalWidth = 0;
5190         for(var i = 0; i < this.config.length; i++){
5191             if(this.isLocked(i) && !this.isHidden(i)){
5192                 this.totalWidth += this.getColumnWidth(i);
5193             }
5194         }
5195         return totalWidth;
5196     },
5197
5198     getLockedCount : function(){
5199         for(var i = 0, len = this.config.length; i < len; i++){
5200             if(!this.isLocked(i)){
5201                 return i;
5202             }
5203         }
5204         
5205         return this.config.length;
5206     },
5207
5208     /**
5209      * Returns the number of columns.
5210      * @return {Number}
5211      */
5212     getColumnCount : function(visibleOnly){
5213         if(visibleOnly === true){
5214             var c = 0;
5215             for(var i = 0, len = this.config.length; i < len; i++){
5216                 if(!this.isHidden(i)){
5217                     c++;
5218                 }
5219             }
5220             return c;
5221         }
5222         return this.config.length;
5223     },
5224
5225     /**
5226      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5227      * @param {Function} fn
5228      * @param {Object} scope (optional)
5229      * @return {Array} result
5230      */
5231     getColumnsBy : function(fn, scope){
5232         var r = [];
5233         for(var i = 0, len = this.config.length; i < len; i++){
5234             var c = this.config[i];
5235             if(fn.call(scope||this, c, i) === true){
5236                 r[r.length] = c;
5237             }
5238         }
5239         return r;
5240     },
5241
5242     /**
5243      * Returns true if the specified column is sortable.
5244      * @param {Number} col The column index
5245      * @return {Boolean}
5246      */
5247     isSortable : function(col){
5248         if(typeof this.config[col].sortable == "undefined"){
5249             return this.defaultSortable;
5250         }
5251         return this.config[col].sortable;
5252     },
5253
5254     /**
5255      * Returns the rendering (formatting) function defined for the column.
5256      * @param {Number} col The column index.
5257      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5258      */
5259     getRenderer : function(col){
5260         if(!this.config[col].renderer){
5261             return Roo.grid.ColumnModel.defaultRenderer;
5262         }
5263         return this.config[col].renderer;
5264     },
5265
5266     /**
5267      * Sets the rendering (formatting) function for a column.
5268      * @param {Number} col The column index
5269      * @param {Function} fn The function to use to process the cell's raw data
5270      * to return HTML markup for the grid view. The render function is called with
5271      * the following parameters:<ul>
5272      * <li>Data value.</li>
5273      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5274      * <li>css A CSS style string to apply to the table cell.</li>
5275      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5276      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5277      * <li>Row index</li>
5278      * <li>Column index</li>
5279      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5280      */
5281     setRenderer : function(col, fn){
5282         this.config[col].renderer = fn;
5283     },
5284
5285     /**
5286      * Returns the width for the specified column.
5287      * @param {Number} col The column index
5288      * @return {Number}
5289      */
5290     getColumnWidth : function(col){
5291         return this.config[col].width * 1 || this.defaultWidth;
5292     },
5293
5294     /**
5295      * Sets the width for a column.
5296      * @param {Number} col The column index
5297      * @param {Number} width The new width
5298      */
5299     setColumnWidth : function(col, width, suppressEvent){
5300         this.config[col].width = width;
5301         this.totalWidth = null;
5302         if(!suppressEvent){
5303              this.fireEvent("widthchange", this, col, width);
5304         }
5305     },
5306
5307     /**
5308      * Returns the total width of all columns.
5309      * @param {Boolean} includeHidden True to include hidden column widths
5310      * @return {Number}
5311      */
5312     getTotalWidth : function(includeHidden){
5313         if(!this.totalWidth){
5314             this.totalWidth = 0;
5315             for(var i = 0, len = this.config.length; i < len; i++){
5316                 if(includeHidden || !this.isHidden(i)){
5317                     this.totalWidth += this.getColumnWidth(i);
5318                 }
5319             }
5320         }
5321         return this.totalWidth;
5322     },
5323
5324     /**
5325      * Returns the header for the specified column.
5326      * @param {Number} col The column index
5327      * @return {String}
5328      */
5329     getColumnHeader : function(col){
5330         return this.config[col].header;
5331     },
5332
5333     /**
5334      * Sets the header for a column.
5335      * @param {Number} col The column index
5336      * @param {String} header The new header
5337      */
5338     setColumnHeader : function(col, header){
5339         this.config[col].header = header;
5340         this.fireEvent("headerchange", this, col, header);
5341     },
5342
5343     /**
5344      * Returns the tooltip for the specified column.
5345      * @param {Number} col The column index
5346      * @return {String}
5347      */
5348     getColumnTooltip : function(col){
5349             return this.config[col].tooltip;
5350     },
5351     /**
5352      * Sets the tooltip for a column.
5353      * @param {Number} col The column index
5354      * @param {String} tooltip The new tooltip
5355      */
5356     setColumnTooltip : function(col, tooltip){
5357             this.config[col].tooltip = tooltip;
5358     },
5359
5360     /**
5361      * Returns the dataIndex for the specified column.
5362      * @param {Number} col The column index
5363      * @return {Number}
5364      */
5365     getDataIndex : function(col){
5366         return this.config[col].dataIndex;
5367     },
5368
5369     /**
5370      * Sets the dataIndex for a column.
5371      * @param {Number} col The column index
5372      * @param {Number} dataIndex The new dataIndex
5373      */
5374     setDataIndex : function(col, dataIndex){
5375         this.config[col].dataIndex = dataIndex;
5376     },
5377
5378     
5379     
5380     /**
5381      * Returns true if the cell is editable.
5382      * @param {Number} colIndex The column index
5383      * @param {Number} rowIndex The row index - this is nto actually used..?
5384      * @return {Boolean}
5385      */
5386     isCellEditable : function(colIndex, rowIndex){
5387         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5388     },
5389
5390     /**
5391      * Returns the editor defined for the cell/column.
5392      * return false or null to disable editing.
5393      * @param {Number} colIndex The column index
5394      * @param {Number} rowIndex The row index
5395      * @return {Object}
5396      */
5397     getCellEditor : function(colIndex, rowIndex){
5398         return this.config[colIndex].editor;
5399     },
5400
5401     /**
5402      * Sets if a column is editable.
5403      * @param {Number} col The column index
5404      * @param {Boolean} editable True if the column is editable
5405      */
5406     setEditable : function(col, editable){
5407         this.config[col].editable = editable;
5408     },
5409
5410
5411     /**
5412      * Returns true if the column is hidden.
5413      * @param {Number} colIndex The column index
5414      * @return {Boolean}
5415      */
5416     isHidden : function(colIndex){
5417         return this.config[colIndex].hidden;
5418     },
5419
5420
5421     /**
5422      * Returns true if the column width cannot be changed
5423      */
5424     isFixed : function(colIndex){
5425         return this.config[colIndex].fixed;
5426     },
5427
5428     /**
5429      * Returns true if the column can be resized
5430      * @return {Boolean}
5431      */
5432     isResizable : function(colIndex){
5433         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5434     },
5435     /**
5436      * Sets if a column is hidden.
5437      * @param {Number} colIndex The column index
5438      * @param {Boolean} hidden True if the column is hidden
5439      */
5440     setHidden : function(colIndex, hidden){
5441         this.config[colIndex].hidden = hidden;
5442         this.totalWidth = null;
5443         this.fireEvent("hiddenchange", this, colIndex, hidden);
5444     },
5445
5446     /**
5447      * Sets the editor for a column.
5448      * @param {Number} col The column index
5449      * @param {Object} editor The editor object
5450      */
5451     setEditor : function(col, editor){
5452         this.config[col].editor = editor;
5453     }
5454 });
5455
5456 Roo.grid.ColumnModel.defaultRenderer = function(value){
5457         if(typeof value == "string" && value.length < 1){
5458             return "&#160;";
5459         }
5460         return value;
5461 };
5462
5463 // Alias for backwards compatibility
5464 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5465 /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475  
5476 /**
5477  * @class Roo.LoadMask
5478  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5479  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5480  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5481  * element's UpdateManager load indicator and will be destroyed after the initial load.
5482  * @constructor
5483  * Create a new LoadMask
5484  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5485  * @param {Object} config The config object
5486  */
5487 Roo.LoadMask = function(el, config){
5488     this.el = Roo.get(el);
5489     Roo.apply(this, config);
5490     if(this.store){
5491         this.store.on('beforeload', this.onBeforeLoad, this);
5492         this.store.on('load', this.onLoad, this);
5493         this.store.on('loadexception', this.onLoadException, this);
5494         this.removeMask = false;
5495     }else{
5496         var um = this.el.getUpdateManager();
5497         um.showLoadIndicator = false; // disable the default indicator
5498         um.on('beforeupdate', this.onBeforeLoad, this);
5499         um.on('update', this.onLoad, this);
5500         um.on('failure', this.onLoad, this);
5501         this.removeMask = true;
5502     }
5503 };
5504
5505 Roo.LoadMask.prototype = {
5506     /**
5507      * @cfg {Boolean} removeMask
5508      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5509      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5510      */
5511     /**
5512      * @cfg {String} msg
5513      * The text to display in a centered loading message box (defaults to 'Loading...')
5514      */
5515     msg : 'Loading...',
5516     /**
5517      * @cfg {String} msgCls
5518      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5519      */
5520     msgCls : 'x-mask-loading',
5521
5522     /**
5523      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5524      * @type Boolean
5525      */
5526     disabled: false,
5527
5528     /**
5529      * Disables the mask to prevent it from being displayed
5530      */
5531     disable : function(){
5532        this.disabled = true;
5533     },
5534
5535     /**
5536      * Enables the mask so that it can be displayed
5537      */
5538     enable : function(){
5539         this.disabled = false;
5540     },
5541     
5542     onLoadException : function()
5543     {
5544         Roo.log(arguments);
5545         
5546         if (typeof(arguments[3]) != 'undefined') {
5547             Roo.MessageBox.alert("Error loading",arguments[3]);
5548         } 
5549         /*
5550         try {
5551             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5552                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5553             }   
5554         } catch(e) {
5555             
5556         }
5557         */
5558     
5559         
5560         
5561         this.el.unmask(this.removeMask);
5562     },
5563     // private
5564     onLoad : function()
5565     {
5566         this.el.unmask(this.removeMask);
5567     },
5568
5569     // private
5570     onBeforeLoad : function(){
5571         if(!this.disabled){
5572             this.el.mask(this.msg, this.msgCls);
5573         }
5574     },
5575
5576     // private
5577     destroy : function(){
5578         if(this.store){
5579             this.store.un('beforeload', this.onBeforeLoad, this);
5580             this.store.un('load', this.onLoad, this);
5581             this.store.un('loadexception', this.onLoadException, this);
5582         }else{
5583             var um = this.el.getUpdateManager();
5584             um.un('beforeupdate', this.onBeforeLoad, this);
5585             um.un('update', this.onLoad, this);
5586             um.un('failure', this.onLoad, this);
5587         }
5588     }
5589 };/*
5590  * - LGPL
5591  *
5592  * table
5593  * 
5594  */
5595
5596 /**
5597  * @class Roo.bootstrap.Table
5598  * @extends Roo.bootstrap.Component
5599  * Bootstrap Table class
5600  * @cfg {String} cls table class
5601  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5602  * @cfg {String} bgcolor Specifies the background color for a table
5603  * @cfg {Number} border Specifies whether the table cells should have borders or not
5604  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5605  * @cfg {Number} cellspacing Specifies the space between cells
5606  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5607  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5608  * @cfg {String} sortable Specifies that the table should be sortable
5609  * @cfg {String} summary Specifies a summary of the content of a table
5610  * @cfg {Number} width Specifies the width of a table
5611  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5612  * 
5613  * @cfg {boolean} striped Should the rows be alternative striped
5614  * @cfg {boolean} bordered Add borders to the table
5615  * @cfg {boolean} hover Add hover highlighting
5616  * @cfg {boolean} condensed Format condensed
5617  * @cfg {boolean} responsive Format condensed
5618  * @cfg {Boolean} loadMask (true|false) default false
5619  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5620  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5621  * @cfg {Boolean} rowSelection (true|false) default false
5622  * @cfg {Boolean} cellSelection (true|false) default false
5623  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5624  
5625  * 
5626  * @constructor
5627  * Create a new Table
5628  * @param {Object} config The config object
5629  */
5630
5631 Roo.bootstrap.Table = function(config){
5632     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5633     
5634     // BC...
5635     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5636     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5637     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5638     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5639     
5640     
5641     if (this.sm) {
5642         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5643         this.sm = this.selModel;
5644         this.sm.xmodule = this.xmodule || false;
5645     }
5646     if (this.cm && typeof(this.cm.config) == 'undefined') {
5647         this.colModel = new Roo.grid.ColumnModel(this.cm);
5648         this.cm = this.colModel;
5649         this.cm.xmodule = this.xmodule || false;
5650     }
5651     if (this.store) {
5652         this.store= Roo.factory(this.store, Roo.data);
5653         this.ds = this.store;
5654         this.ds.xmodule = this.xmodule || false;
5655          
5656     }
5657     if (this.footer && this.store) {
5658         this.footer.dataSource = this.ds;
5659         this.footer = Roo.factory(this.footer);
5660     }
5661     
5662     /** @private */
5663     this.addEvents({
5664         /**
5665          * @event cellclick
5666          * Fires when a cell is clicked
5667          * @param {Roo.bootstrap.Table} this
5668          * @param {Roo.Element} el
5669          * @param {Number} rowIndex
5670          * @param {Number} columnIndex
5671          * @param {Roo.EventObject} e
5672          */
5673         "cellclick" : true,
5674         /**
5675          * @event celldblclick
5676          * Fires when a cell is double clicked
5677          * @param {Roo.bootstrap.Table} this
5678          * @param {Roo.Element} el
5679          * @param {Number} rowIndex
5680          * @param {Number} columnIndex
5681          * @param {Roo.EventObject} e
5682          */
5683         "celldblclick" : true,
5684         /**
5685          * @event rowclick
5686          * Fires when a row is clicked
5687          * @param {Roo.bootstrap.Table} this
5688          * @param {Roo.Element} el
5689          * @param {Number} rowIndex
5690          * @param {Roo.EventObject} e
5691          */
5692         "rowclick" : true,
5693         /**
5694          * @event rowdblclick
5695          * Fires when a row is double clicked
5696          * @param {Roo.bootstrap.Table} this
5697          * @param {Roo.Element} el
5698          * @param {Number} rowIndex
5699          * @param {Roo.EventObject} e
5700          */
5701         "rowdblclick" : true,
5702         /**
5703          * @event mouseover
5704          * Fires when a mouseover occur
5705          * @param {Roo.bootstrap.Table} this
5706          * @param {Roo.Element} el
5707          * @param {Number} rowIndex
5708          * @param {Number} columnIndex
5709          * @param {Roo.EventObject} e
5710          */
5711         "mouseover" : true,
5712         /**
5713          * @event mouseout
5714          * Fires when a mouseout occur
5715          * @param {Roo.bootstrap.Table} this
5716          * @param {Roo.Element} el
5717          * @param {Number} rowIndex
5718          * @param {Number} columnIndex
5719          * @param {Roo.EventObject} e
5720          */
5721         "mouseout" : true,
5722         /**
5723          * @event rowclass
5724          * Fires when a row is rendered, so you can change add a style to it.
5725          * @param {Roo.bootstrap.Table} this
5726          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5727          */
5728         'rowclass' : true,
5729           /**
5730          * @event rowsrendered
5731          * Fires when all the  rows have been rendered
5732          * @param {Roo.bootstrap.Table} this
5733          */
5734         'rowsrendered' : true
5735         
5736     });
5737 };
5738
5739 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5740     
5741     cls: false,
5742     align: false,
5743     bgcolor: false,
5744     border: false,
5745     cellpadding: false,
5746     cellspacing: false,
5747     frame: false,
5748     rules: false,
5749     sortable: false,
5750     summary: false,
5751     width: false,
5752     striped : false,
5753     bordered: false,
5754     hover:  false,
5755     condensed : false,
5756     responsive : false,
5757     sm : false,
5758     cm : false,
5759     store : false,
5760     loadMask : false,
5761     footerShow : true,
5762     headerShow : true,
5763   
5764     rowSelection : false,
5765     cellSelection : false,
5766     layout : false,
5767     
5768     // Roo.Element - the tbody
5769     mainBody: false, 
5770     
5771     getAutoCreate : function(){
5772         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5773         
5774         cfg = {
5775             tag: 'table',
5776             cls : 'table',
5777             cn : []
5778         };
5779             
5780         if (this.striped) {
5781             cfg.cls += ' table-striped';
5782         }
5783         
5784         if (this.hover) {
5785             cfg.cls += ' table-hover';
5786         }
5787         if (this.bordered) {
5788             cfg.cls += ' table-bordered';
5789         }
5790         if (this.condensed) {
5791             cfg.cls += ' table-condensed';
5792         }
5793         if (this.responsive) {
5794             cfg.cls += ' table-responsive';
5795         }
5796         
5797         if (this.cls) {
5798             cfg.cls+=  ' ' +this.cls;
5799         }
5800         
5801         // this lot should be simplifed...
5802         
5803         if (this.align) {
5804             cfg.align=this.align;
5805         }
5806         if (this.bgcolor) {
5807             cfg.bgcolor=this.bgcolor;
5808         }
5809         if (this.border) {
5810             cfg.border=this.border;
5811         }
5812         if (this.cellpadding) {
5813             cfg.cellpadding=this.cellpadding;
5814         }
5815         if (this.cellspacing) {
5816             cfg.cellspacing=this.cellspacing;
5817         }
5818         if (this.frame) {
5819             cfg.frame=this.frame;
5820         }
5821         if (this.rules) {
5822             cfg.rules=this.rules;
5823         }
5824         if (this.sortable) {
5825             cfg.sortable=this.sortable;
5826         }
5827         if (this.summary) {
5828             cfg.summary=this.summary;
5829         }
5830         if (this.width) {
5831             cfg.width=this.width;
5832         }
5833         if (this.layout) {
5834             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5835         }
5836         
5837         if(this.store || this.cm){
5838             if(this.headerShow){
5839                 cfg.cn.push(this.renderHeader());
5840             }
5841             
5842             cfg.cn.push(this.renderBody());
5843             
5844             if(this.footerShow){
5845                 cfg.cn.push(this.renderFooter());
5846             }
5847             
5848             cfg.cls+=  ' TableGrid';
5849         }
5850         
5851         return { cn : [ cfg ] };
5852     },
5853     
5854     initEvents : function()
5855     {   
5856         if(!this.store || !this.cm){
5857             return;
5858         }
5859         
5860         //Roo.log('initEvents with ds!!!!');
5861         
5862         this.mainBody = this.el.select('tbody', true).first();
5863         
5864         
5865         var _this = this;
5866         
5867         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5868             e.on('click', _this.sort, _this);
5869         });
5870         
5871         this.el.on("click", this.onClick, this);
5872         this.el.on("dblclick", this.onDblClick, this);
5873         
5874         // why is this done????? = it breaks dialogs??
5875         //this.parent().el.setStyle('position', 'relative');
5876         
5877         
5878         if (this.footer) {
5879             this.footer.parentId = this.id;
5880             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5881         }
5882         
5883         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5884         
5885         this.store.on('load', this.onLoad, this);
5886         this.store.on('beforeload', this.onBeforeLoad, this);
5887         this.store.on('update', this.onUpdate, this);
5888         this.store.on('add', this.onAdd, this);
5889         
5890     },
5891     
5892     onMouseover : function(e, el)
5893     {
5894         var cell = Roo.get(el);
5895         
5896         if(!cell){
5897             return;
5898         }
5899         
5900         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5901             cell = cell.findParent('td', false, true);
5902         }
5903         
5904         var row = cell.findParent('tr', false, true);
5905         var cellIndex = cell.dom.cellIndex;
5906         var rowIndex = row.dom.rowIndex - 1; // start from 0
5907         
5908         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5909         
5910     },
5911     
5912     onMouseout : function(e, el)
5913     {
5914         var cell = Roo.get(el);
5915         
5916         if(!cell){
5917             return;
5918         }
5919         
5920         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5921             cell = cell.findParent('td', false, true);
5922         }
5923         
5924         var row = cell.findParent('tr', false, true);
5925         var cellIndex = cell.dom.cellIndex;
5926         var rowIndex = row.dom.rowIndex - 1; // start from 0
5927         
5928         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5929         
5930     },
5931     
5932     onClick : function(e, el)
5933     {
5934         var cell = Roo.get(el);
5935         
5936         if(!cell || (!this.cellSelection && !this.rowSelection)){
5937             return;
5938         }
5939         
5940         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5941             cell = cell.findParent('td', false, true);
5942         }
5943         
5944         if(!cell || typeof(cell) == 'undefined'){
5945             return;
5946         }
5947         
5948         var row = cell.findParent('tr', false, true);
5949         
5950         if(!row || typeof(row) == 'undefined'){
5951             return;
5952         }
5953         
5954         var cellIndex = cell.dom.cellIndex;
5955         var rowIndex = this.getRowIndex(row);
5956         
5957         // why??? - should these not be based on SelectionModel?
5958         if(this.cellSelection){
5959             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5960         }
5961         
5962         if(this.rowSelection){
5963             this.fireEvent('rowclick', this, row, rowIndex, e);
5964         }
5965         
5966         
5967     },
5968     
5969     onDblClick : function(e,el)
5970     {
5971         var cell = Roo.get(el);
5972         
5973         if(!cell || (!this.CellSelection && !this.RowSelection)){
5974             return;
5975         }
5976         
5977         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5978             cell = cell.findParent('td', false, true);
5979         }
5980         
5981         if(!cell || typeof(cell) == 'undefined'){
5982             return;
5983         }
5984         
5985         var row = cell.findParent('tr', false, true);
5986         
5987         if(!row || typeof(row) == 'undefined'){
5988             return;
5989         }
5990         
5991         var cellIndex = cell.dom.cellIndex;
5992         var rowIndex = this.getRowIndex(row);
5993         
5994         if(this.CellSelection){
5995             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5996         }
5997         
5998         if(this.RowSelection){
5999             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6000         }
6001     },
6002     
6003     sort : function(e,el)
6004     {
6005         var col = Roo.get(el);
6006         
6007         if(!col.hasClass('sortable')){
6008             return;
6009         }
6010         
6011         var sort = col.attr('sort');
6012         var dir = 'ASC';
6013         
6014         if(col.hasClass('glyphicon-arrow-up')){
6015             dir = 'DESC';
6016         }
6017         
6018         this.store.sortInfo = {field : sort, direction : dir};
6019         
6020         if (this.footer) {
6021             Roo.log("calling footer first");
6022             this.footer.onClick('first');
6023         } else {
6024         
6025             this.store.load({ params : { start : 0 } });
6026         }
6027     },
6028     
6029     renderHeader : function()
6030     {
6031         var header = {
6032             tag: 'thead',
6033             cn : []
6034         };
6035         
6036         var cm = this.cm;
6037         
6038         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6039             
6040             var config = cm.config[i];
6041             
6042             var c = {
6043                 tag: 'th',
6044                 style : '',
6045                 html: cm.getColumnHeader(i)
6046             };
6047             
6048             var hh = '';
6049             
6050             if(typeof(config.lgHeader) != 'undefined'){
6051                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6052             }
6053             
6054             if(typeof(config.mdHeader) != 'undefined'){
6055                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6056             }
6057             
6058             if(typeof(config.smHeader) != 'undefined'){
6059                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6060             }
6061             
6062             if(typeof(config.xsHeader) != 'undefined'){
6063                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6064             }
6065             
6066             if(hh.length){
6067                 c.html = hh;
6068             }
6069             
6070             if(typeof(config.tooltip) != 'undefined'){
6071                 c.tooltip = config.tooltip;
6072             }
6073             
6074             if(typeof(config.colspan) != 'undefined'){
6075                 c.colspan = config.colspan;
6076             }
6077             
6078             if(typeof(config.hidden) != 'undefined' && config.hidden){
6079                 c.style += ' display:none;';
6080             }
6081             
6082             if(typeof(config.dataIndex) != 'undefined'){
6083                 c.sort = config.dataIndex;
6084             }
6085             
6086             if(typeof(config.sortable) != 'undefined' && config.sortable){
6087                 c.cls = 'sortable';
6088             }
6089             
6090             if(typeof(config.align) != 'undefined' && config.align.length){
6091                 c.style += ' text-align:' + config.align + ';';
6092             }
6093             
6094             if(typeof(config.width) != 'undefined'){
6095                 c.style += ' width:' + config.width + 'px;';
6096             }
6097             
6098             if(typeof(config.cls) != 'undefined'){
6099                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6100             }
6101             
6102             ['xs','sm','md','lg'].map(function(size){
6103                 
6104                 if(typeof(config[size]) == 'undefined'){
6105                     return;
6106                 }
6107                 
6108                 if (!config[size]) { // 0 = hidden
6109                     c.cls += ' hidden-' + size;
6110                     return;
6111                 }
6112                 
6113                 c.cls += ' col-' + size + '-' + config[size];
6114
6115             });
6116             
6117             header.cn.push(c)
6118         }
6119         
6120         return header;
6121     },
6122     
6123     renderBody : function()
6124     {
6125         var body = {
6126             tag: 'tbody',
6127             cn : [
6128                 {
6129                     tag: 'tr',
6130                     cn : [
6131                         {
6132                             tag : 'td',
6133                             colspan :  this.cm.getColumnCount()
6134                         }
6135                     ]
6136                 }
6137             ]
6138         };
6139         
6140         return body;
6141     },
6142     
6143     renderFooter : function()
6144     {
6145         var footer = {
6146             tag: 'tfoot',
6147             cn : [
6148                 {
6149                     tag: 'tr',
6150                     cn : [
6151                         {
6152                             tag : 'td',
6153                             colspan :  this.cm.getColumnCount()
6154                         }
6155                     ]
6156                 }
6157             ]
6158         };
6159         
6160         return footer;
6161     },
6162     
6163     
6164     
6165     onLoad : function()
6166     {
6167 //        Roo.log('ds onload');
6168         this.clear();
6169         
6170         var _this = this;
6171         var cm = this.cm;
6172         var ds = this.store;
6173         
6174         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6175             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6176             
6177             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6178                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6179             }
6180             
6181             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6182                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6183             }
6184         });
6185         
6186         var tbody =  this.mainBody;
6187               
6188         if(ds.getCount() > 0){
6189             ds.data.each(function(d,rowIndex){
6190                 var row =  this.renderRow(cm, ds, rowIndex);
6191                 
6192                 tbody.createChild(row);
6193                 
6194                 var _this = this;
6195                 
6196                 if(row.cellObjects.length){
6197                     Roo.each(row.cellObjects, function(r){
6198                         _this.renderCellObject(r);
6199                     })
6200                 }
6201                 
6202             }, this);
6203         }
6204         
6205         Roo.each(this.el.select('tbody td', true).elements, function(e){
6206             e.on('mouseover', _this.onMouseover, _this);
6207         });
6208         
6209         Roo.each(this.el.select('tbody td', true).elements, function(e){
6210             e.on('mouseout', _this.onMouseout, _this);
6211         });
6212         this.fireEvent('rowsrendered', this);
6213         //if(this.loadMask){
6214         //    this.maskEl.hide();
6215         //}
6216     },
6217     
6218     
6219     onUpdate : function(ds,record)
6220     {
6221         this.refreshRow(record);
6222     },
6223     
6224     onRemove : function(ds, record, index, isUpdate){
6225         if(isUpdate !== true){
6226             this.fireEvent("beforerowremoved", this, index, record);
6227         }
6228         var bt = this.mainBody.dom;
6229         
6230         var rows = this.el.select('tbody > tr', true).elements;
6231         
6232         if(typeof(rows[index]) != 'undefined'){
6233             bt.removeChild(rows[index].dom);
6234         }
6235         
6236 //        if(bt.rows[index]){
6237 //            bt.removeChild(bt.rows[index]);
6238 //        }
6239         
6240         if(isUpdate !== true){
6241             //this.stripeRows(index);
6242             //this.syncRowHeights(index, index);
6243             //this.layout();
6244             this.fireEvent("rowremoved", this, index, record);
6245         }
6246     },
6247     
6248     onAdd : function(ds, records, rowIndex)
6249     {
6250         //Roo.log('on Add called');
6251         // - note this does not handle multiple adding very well..
6252         var bt = this.mainBody.dom;
6253         for (var i =0 ; i < records.length;i++) {
6254             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6255             //Roo.log(records[i]);
6256             //Roo.log(this.store.getAt(rowIndex+i));
6257             this.insertRow(this.store, rowIndex + i, false);
6258             return;
6259         }
6260         
6261     },
6262     
6263     
6264     refreshRow : function(record){
6265         var ds = this.store, index;
6266         if(typeof record == 'number'){
6267             index = record;
6268             record = ds.getAt(index);
6269         }else{
6270             index = ds.indexOf(record);
6271         }
6272         this.insertRow(ds, index, true);
6273         this.onRemove(ds, record, index+1, true);
6274         //this.syncRowHeights(index, index);
6275         //this.layout();
6276         this.fireEvent("rowupdated", this, index, record);
6277     },
6278     
6279     insertRow : function(dm, rowIndex, isUpdate){
6280         
6281         if(!isUpdate){
6282             this.fireEvent("beforerowsinserted", this, rowIndex);
6283         }
6284             //var s = this.getScrollState();
6285         var row = this.renderRow(this.cm, this.store, rowIndex);
6286         // insert before rowIndex..
6287         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6288         
6289         var _this = this;
6290                 
6291         if(row.cellObjects.length){
6292             Roo.each(row.cellObjects, function(r){
6293                 _this.renderCellObject(r);
6294             })
6295         }
6296             
6297         if(!isUpdate){
6298             this.fireEvent("rowsinserted", this, rowIndex);
6299             //this.syncRowHeights(firstRow, lastRow);
6300             //this.stripeRows(firstRow);
6301             //this.layout();
6302         }
6303         
6304     },
6305     
6306     
6307     getRowDom : function(rowIndex)
6308     {
6309         var rows = this.el.select('tbody > tr', true).elements;
6310         
6311         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6312         
6313     },
6314     // returns the object tree for a tr..
6315   
6316     
6317     renderRow : function(cm, ds, rowIndex) 
6318     {
6319         
6320         var d = ds.getAt(rowIndex);
6321         
6322         var row = {
6323             tag : 'tr',
6324             cn : []
6325         };
6326             
6327         var cellObjects = [];
6328         
6329         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6330             var config = cm.config[i];
6331             
6332             var renderer = cm.getRenderer(i);
6333             var value = '';
6334             var id = false;
6335             
6336             if(typeof(renderer) !== 'undefined'){
6337                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6338             }
6339             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6340             // and are rendered into the cells after the row is rendered - using the id for the element.
6341             
6342             if(typeof(value) === 'object'){
6343                 id = Roo.id();
6344                 cellObjects.push({
6345                     container : id,
6346                     cfg : value 
6347                 })
6348             }
6349             
6350             var rowcfg = {
6351                 record: d,
6352                 rowIndex : rowIndex,
6353                 colIndex : i,
6354                 rowClass : ''
6355             };
6356
6357             this.fireEvent('rowclass', this, rowcfg);
6358             
6359             var td = {
6360                 tag: 'td',
6361                 cls : rowcfg.rowClass,
6362                 style: '',
6363                 html: (typeof(value) === 'object') ? '' : value
6364             };
6365             
6366             if (id) {
6367                 td.id = id;
6368             }
6369             
6370             if(typeof(config.colspan) != 'undefined'){
6371                 td.colspan = config.colspan;
6372             }
6373             
6374             if(typeof(config.hidden) != 'undefined' && config.hidden){
6375                 td.style += ' display:none;';
6376             }
6377             
6378             if(typeof(config.align) != 'undefined' && config.align.length){
6379                 td.style += ' text-align:' + config.align + ';';
6380             }
6381             
6382             if(typeof(config.width) != 'undefined'){
6383                 td.style += ' width:' +  config.width + 'px;';
6384             }
6385             
6386             if(typeof(config.cursor) != 'undefined'){
6387                 td.style += ' cursor:' +  config.cursor + ';';
6388             }
6389             
6390             if(typeof(config.cls) != 'undefined'){
6391                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6392             }
6393             
6394             ['xs','sm','md','lg'].map(function(size){
6395                 
6396                 if(typeof(config[size]) == 'undefined'){
6397                     return;
6398                 }
6399                 
6400                 if (!config[size]) { // 0 = hidden
6401                     td.cls += ' hidden-' + size;
6402                     return;
6403                 }
6404                 
6405                 td.cls += ' col-' + size + '-' + config[size];
6406
6407             });
6408              
6409             row.cn.push(td);
6410            
6411         }
6412         
6413         row.cellObjects = cellObjects;
6414         
6415         return row;
6416           
6417     },
6418     
6419     
6420     
6421     onBeforeLoad : function()
6422     {
6423         //Roo.log('ds onBeforeLoad');
6424         
6425         //this.clear();
6426         
6427         //if(this.loadMask){
6428         //    this.maskEl.show();
6429         //}
6430     },
6431      /**
6432      * Remove all rows
6433      */
6434     clear : function()
6435     {
6436         this.el.select('tbody', true).first().dom.innerHTML = '';
6437     },
6438     /**
6439      * Show or hide a row.
6440      * @param {Number} rowIndex to show or hide
6441      * @param {Boolean} state hide
6442      */
6443     setRowVisibility : function(rowIndex, state)
6444     {
6445         var bt = this.mainBody.dom;
6446         
6447         var rows = this.el.select('tbody > tr', true).elements;
6448         
6449         if(typeof(rows[rowIndex]) == 'undefined'){
6450             return;
6451         }
6452         rows[rowIndex].dom.style.display = state ? '' : 'none';
6453     },
6454     
6455     
6456     getSelectionModel : function(){
6457         if(!this.selModel){
6458             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6459         }
6460         return this.selModel;
6461     },
6462     /*
6463      * Render the Roo.bootstrap object from renderder
6464      */
6465     renderCellObject : function(r)
6466     {
6467         var _this = this;
6468         
6469         var t = r.cfg.render(r.container);
6470         
6471         if(r.cfg.cn){
6472             Roo.each(r.cfg.cn, function(c){
6473                 var child = {
6474                     container: t.getChildContainer(),
6475                     cfg: c
6476                 };
6477                 _this.renderCellObject(child);
6478             })
6479         }
6480     },
6481     
6482     getRowIndex : function(row)
6483     {
6484         var rowIndex = -1;
6485         
6486         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6487             if(el != row){
6488                 return;
6489             }
6490             
6491             rowIndex = index;
6492         });
6493         
6494         return rowIndex;
6495     }
6496    
6497 });
6498
6499  
6500
6501  /*
6502  * - LGPL
6503  *
6504  * table cell
6505  * 
6506  */
6507
6508 /**
6509  * @class Roo.bootstrap.TableCell
6510  * @extends Roo.bootstrap.Component
6511  * Bootstrap TableCell class
6512  * @cfg {String} html cell contain text
6513  * @cfg {String} cls cell class
6514  * @cfg {String} tag cell tag (td|th) default td
6515  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6516  * @cfg {String} align Aligns the content in a cell
6517  * @cfg {String} axis Categorizes cells
6518  * @cfg {String} bgcolor Specifies the background color of a cell
6519  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6520  * @cfg {Number} colspan Specifies the number of columns a cell should span
6521  * @cfg {String} headers Specifies one or more header cells a cell is related to
6522  * @cfg {Number} height Sets the height of a cell
6523  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6524  * @cfg {Number} rowspan Sets the number of rows a cell should span
6525  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6526  * @cfg {String} valign Vertical aligns the content in a cell
6527  * @cfg {Number} width Specifies the width of a cell
6528  * 
6529  * @constructor
6530  * Create a new TableCell
6531  * @param {Object} config The config object
6532  */
6533
6534 Roo.bootstrap.TableCell = function(config){
6535     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6536 };
6537
6538 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6539     
6540     html: false,
6541     cls: false,
6542     tag: false,
6543     abbr: false,
6544     align: false,
6545     axis: false,
6546     bgcolor: false,
6547     charoff: false,
6548     colspan: false,
6549     headers: false,
6550     height: false,
6551     nowrap: false,
6552     rowspan: false,
6553     scope: false,
6554     valign: false,
6555     width: false,
6556     
6557     
6558     getAutoCreate : function(){
6559         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6560         
6561         cfg = {
6562             tag: 'td'
6563         };
6564         
6565         if(this.tag){
6566             cfg.tag = this.tag;
6567         }
6568         
6569         if (this.html) {
6570             cfg.html=this.html
6571         }
6572         if (this.cls) {
6573             cfg.cls=this.cls
6574         }
6575         if (this.abbr) {
6576             cfg.abbr=this.abbr
6577         }
6578         if (this.align) {
6579             cfg.align=this.align
6580         }
6581         if (this.axis) {
6582             cfg.axis=this.axis
6583         }
6584         if (this.bgcolor) {
6585             cfg.bgcolor=this.bgcolor
6586         }
6587         if (this.charoff) {
6588             cfg.charoff=this.charoff
6589         }
6590         if (this.colspan) {
6591             cfg.colspan=this.colspan
6592         }
6593         if (this.headers) {
6594             cfg.headers=this.headers
6595         }
6596         if (this.height) {
6597             cfg.height=this.height
6598         }
6599         if (this.nowrap) {
6600             cfg.nowrap=this.nowrap
6601         }
6602         if (this.rowspan) {
6603             cfg.rowspan=this.rowspan
6604         }
6605         if (this.scope) {
6606             cfg.scope=this.scope
6607         }
6608         if (this.valign) {
6609             cfg.valign=this.valign
6610         }
6611         if (this.width) {
6612             cfg.width=this.width
6613         }
6614         
6615         
6616         return cfg;
6617     }
6618    
6619 });
6620
6621  
6622
6623  /*
6624  * - LGPL
6625  *
6626  * table row
6627  * 
6628  */
6629
6630 /**
6631  * @class Roo.bootstrap.TableRow
6632  * @extends Roo.bootstrap.Component
6633  * Bootstrap TableRow class
6634  * @cfg {String} cls row class
6635  * @cfg {String} align Aligns the content in a table row
6636  * @cfg {String} bgcolor Specifies a background color for a table row
6637  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6638  * @cfg {String} valign Vertical aligns the content in a table row
6639  * 
6640  * @constructor
6641  * Create a new TableRow
6642  * @param {Object} config The config object
6643  */
6644
6645 Roo.bootstrap.TableRow = function(config){
6646     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6647 };
6648
6649 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6650     
6651     cls: false,
6652     align: false,
6653     bgcolor: false,
6654     charoff: false,
6655     valign: false,
6656     
6657     getAutoCreate : function(){
6658         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6659         
6660         cfg = {
6661             tag: 'tr'
6662         };
6663             
6664         if(this.cls){
6665             cfg.cls = this.cls;
6666         }
6667         if(this.align){
6668             cfg.align = this.align;
6669         }
6670         if(this.bgcolor){
6671             cfg.bgcolor = this.bgcolor;
6672         }
6673         if(this.charoff){
6674             cfg.charoff = this.charoff;
6675         }
6676         if(this.valign){
6677             cfg.valign = this.valign;
6678         }
6679         
6680         return cfg;
6681     }
6682    
6683 });
6684
6685  
6686
6687  /*
6688  * - LGPL
6689  *
6690  * table body
6691  * 
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.TableBody
6696  * @extends Roo.bootstrap.Component
6697  * Bootstrap TableBody class
6698  * @cfg {String} cls element class
6699  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6700  * @cfg {String} align Aligns the content inside the element
6701  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6702  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6703  * 
6704  * @constructor
6705  * Create a new TableBody
6706  * @param {Object} config The config object
6707  */
6708
6709 Roo.bootstrap.TableBody = function(config){
6710     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6711 };
6712
6713 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6714     
6715     cls: false,
6716     tag: false,
6717     align: false,
6718     charoff: false,
6719     valign: false,
6720     
6721     getAutoCreate : function(){
6722         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6723         
6724         cfg = {
6725             tag: 'tbody'
6726         };
6727             
6728         if (this.cls) {
6729             cfg.cls=this.cls
6730         }
6731         if(this.tag){
6732             cfg.tag = this.tag;
6733         }
6734         
6735         if(this.align){
6736             cfg.align = this.align;
6737         }
6738         if(this.charoff){
6739             cfg.charoff = this.charoff;
6740         }
6741         if(this.valign){
6742             cfg.valign = this.valign;
6743         }
6744         
6745         return cfg;
6746     }
6747     
6748     
6749 //    initEvents : function()
6750 //    {
6751 //        
6752 //        if(!this.store){
6753 //            return;
6754 //        }
6755 //        
6756 //        this.store = Roo.factory(this.store, Roo.data);
6757 //        this.store.on('load', this.onLoad, this);
6758 //        
6759 //        this.store.load();
6760 //        
6761 //    },
6762 //    
6763 //    onLoad: function () 
6764 //    {   
6765 //        this.fireEvent('load', this);
6766 //    }
6767 //    
6768 //   
6769 });
6770
6771  
6772
6773  /*
6774  * Based on:
6775  * Ext JS Library 1.1.1
6776  * Copyright(c) 2006-2007, Ext JS, LLC.
6777  *
6778  * Originally Released Under LGPL - original licence link has changed is not relivant.
6779  *
6780  * Fork - LGPL
6781  * <script type="text/javascript">
6782  */
6783
6784 // as we use this in bootstrap.
6785 Roo.namespace('Roo.form');
6786  /**
6787  * @class Roo.form.Action
6788  * Internal Class used to handle form actions
6789  * @constructor
6790  * @param {Roo.form.BasicForm} el The form element or its id
6791  * @param {Object} config Configuration options
6792  */
6793
6794  
6795  
6796 // define the action interface
6797 Roo.form.Action = function(form, options){
6798     this.form = form;
6799     this.options = options || {};
6800 };
6801 /**
6802  * Client Validation Failed
6803  * @const 
6804  */
6805 Roo.form.Action.CLIENT_INVALID = 'client';
6806 /**
6807  * Server Validation Failed
6808  * @const 
6809  */
6810 Roo.form.Action.SERVER_INVALID = 'server';
6811  /**
6812  * Connect to Server Failed
6813  * @const 
6814  */
6815 Roo.form.Action.CONNECT_FAILURE = 'connect';
6816 /**
6817  * Reading Data from Server Failed
6818  * @const 
6819  */
6820 Roo.form.Action.LOAD_FAILURE = 'load';
6821
6822 Roo.form.Action.prototype = {
6823     type : 'default',
6824     failureType : undefined,
6825     response : undefined,
6826     result : undefined,
6827
6828     // interface method
6829     run : function(options){
6830
6831     },
6832
6833     // interface method
6834     success : function(response){
6835
6836     },
6837
6838     // interface method
6839     handleResponse : function(response){
6840
6841     },
6842
6843     // default connection failure
6844     failure : function(response){
6845         
6846         this.response = response;
6847         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6848         this.form.afterAction(this, false);
6849     },
6850
6851     processResponse : function(response){
6852         this.response = response;
6853         if(!response.responseText){
6854             return true;
6855         }
6856         this.result = this.handleResponse(response);
6857         return this.result;
6858     },
6859
6860     // utility functions used internally
6861     getUrl : function(appendParams){
6862         var url = this.options.url || this.form.url || this.form.el.dom.action;
6863         if(appendParams){
6864             var p = this.getParams();
6865             if(p){
6866                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6867             }
6868         }
6869         return url;
6870     },
6871
6872     getMethod : function(){
6873         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6874     },
6875
6876     getParams : function(){
6877         var bp = this.form.baseParams;
6878         var p = this.options.params;
6879         if(p){
6880             if(typeof p == "object"){
6881                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6882             }else if(typeof p == 'string' && bp){
6883                 p += '&' + Roo.urlEncode(bp);
6884             }
6885         }else if(bp){
6886             p = Roo.urlEncode(bp);
6887         }
6888         return p;
6889     },
6890
6891     createCallback : function(){
6892         return {
6893             success: this.success,
6894             failure: this.failure,
6895             scope: this,
6896             timeout: (this.form.timeout*1000),
6897             upload: this.form.fileUpload ? this.success : undefined
6898         };
6899     }
6900 };
6901
6902 Roo.form.Action.Submit = function(form, options){
6903     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6904 };
6905
6906 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6907     type : 'submit',
6908
6909     haveProgress : false,
6910     uploadComplete : false,
6911     
6912     // uploadProgress indicator.
6913     uploadProgress : function()
6914     {
6915         if (!this.form.progressUrl) {
6916             return;
6917         }
6918         
6919         if (!this.haveProgress) {
6920             Roo.MessageBox.progress("Uploading", "Uploading");
6921         }
6922         if (this.uploadComplete) {
6923            Roo.MessageBox.hide();
6924            return;
6925         }
6926         
6927         this.haveProgress = true;
6928    
6929         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6930         
6931         var c = new Roo.data.Connection();
6932         c.request({
6933             url : this.form.progressUrl,
6934             params: {
6935                 id : uid
6936             },
6937             method: 'GET',
6938             success : function(req){
6939                //console.log(data);
6940                 var rdata = false;
6941                 var edata;
6942                 try  {
6943                    rdata = Roo.decode(req.responseText)
6944                 } catch (e) {
6945                     Roo.log("Invalid data from server..");
6946                     Roo.log(edata);
6947                     return;
6948                 }
6949                 if (!rdata || !rdata.success) {
6950                     Roo.log(rdata);
6951                     Roo.MessageBox.alert(Roo.encode(rdata));
6952                     return;
6953                 }
6954                 var data = rdata.data;
6955                 
6956                 if (this.uploadComplete) {
6957                    Roo.MessageBox.hide();
6958                    return;
6959                 }
6960                    
6961                 if (data){
6962                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6963                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6964                     );
6965                 }
6966                 this.uploadProgress.defer(2000,this);
6967             },
6968        
6969             failure: function(data) {
6970                 Roo.log('progress url failed ');
6971                 Roo.log(data);
6972             },
6973             scope : this
6974         });
6975            
6976     },
6977     
6978     
6979     run : function()
6980     {
6981         // run get Values on the form, so it syncs any secondary forms.
6982         this.form.getValues();
6983         
6984         var o = this.options;
6985         var method = this.getMethod();
6986         var isPost = method == 'POST';
6987         if(o.clientValidation === false || this.form.isValid()){
6988             
6989             if (this.form.progressUrl) {
6990                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6991                     (new Date() * 1) + '' + Math.random());
6992                     
6993             } 
6994             
6995             
6996             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6997                 form:this.form.el.dom,
6998                 url:this.getUrl(!isPost),
6999                 method: method,
7000                 params:isPost ? this.getParams() : null,
7001                 isUpload: this.form.fileUpload
7002             }));
7003             
7004             this.uploadProgress();
7005
7006         }else if (o.clientValidation !== false){ // client validation failed
7007             this.failureType = Roo.form.Action.CLIENT_INVALID;
7008             this.form.afterAction(this, false);
7009         }
7010     },
7011
7012     success : function(response)
7013     {
7014         this.uploadComplete= true;
7015         if (this.haveProgress) {
7016             Roo.MessageBox.hide();
7017         }
7018         
7019         
7020         var result = this.processResponse(response);
7021         if(result === true || result.success){
7022             this.form.afterAction(this, true);
7023             return;
7024         }
7025         if(result.errors){
7026             this.form.markInvalid(result.errors);
7027             this.failureType = Roo.form.Action.SERVER_INVALID;
7028         }
7029         this.form.afterAction(this, false);
7030     },
7031     failure : function(response)
7032     {
7033         this.uploadComplete= true;
7034         if (this.haveProgress) {
7035             Roo.MessageBox.hide();
7036         }
7037         
7038         this.response = response;
7039         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7040         this.form.afterAction(this, false);
7041     },
7042     
7043     handleResponse : function(response){
7044         if(this.form.errorReader){
7045             var rs = this.form.errorReader.read(response);
7046             var errors = [];
7047             if(rs.records){
7048                 for(var i = 0, len = rs.records.length; i < len; i++) {
7049                     var r = rs.records[i];
7050                     errors[i] = r.data;
7051                 }
7052             }
7053             if(errors.length < 1){
7054                 errors = null;
7055             }
7056             return {
7057                 success : rs.success,
7058                 errors : errors
7059             };
7060         }
7061         var ret = false;
7062         try {
7063             ret = Roo.decode(response.responseText);
7064         } catch (e) {
7065             ret = {
7066                 success: false,
7067                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7068                 errors : []
7069             };
7070         }
7071         return ret;
7072         
7073     }
7074 });
7075
7076
7077 Roo.form.Action.Load = function(form, options){
7078     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7079     this.reader = this.form.reader;
7080 };
7081
7082 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7083     type : 'load',
7084
7085     run : function(){
7086         
7087         Roo.Ajax.request(Roo.apply(
7088                 this.createCallback(), {
7089                     method:this.getMethod(),
7090                     url:this.getUrl(false),
7091                     params:this.getParams()
7092         }));
7093     },
7094
7095     success : function(response){
7096         
7097         var result = this.processResponse(response);
7098         if(result === true || !result.success || !result.data){
7099             this.failureType = Roo.form.Action.LOAD_FAILURE;
7100             this.form.afterAction(this, false);
7101             return;
7102         }
7103         this.form.clearInvalid();
7104         this.form.setValues(result.data);
7105         this.form.afterAction(this, true);
7106     },
7107
7108     handleResponse : function(response){
7109         if(this.form.reader){
7110             var rs = this.form.reader.read(response);
7111             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7112             return {
7113                 success : rs.success,
7114                 data : data
7115             };
7116         }
7117         return Roo.decode(response.responseText);
7118     }
7119 });
7120
7121 Roo.form.Action.ACTION_TYPES = {
7122     'load' : Roo.form.Action.Load,
7123     'submit' : Roo.form.Action.Submit
7124 };/*
7125  * - LGPL
7126  *
7127  * form
7128  * 
7129  */
7130
7131 /**
7132  * @class Roo.bootstrap.Form
7133  * @extends Roo.bootstrap.Component
7134  * Bootstrap Form class
7135  * @cfg {String} method  GET | POST (default POST)
7136  * @cfg {String} labelAlign top | left (default top)
7137  * @cfg {String} align left  | right - for navbars
7138  * @cfg {Boolean} loadMask load mask when submit (default true)
7139
7140  * 
7141  * @constructor
7142  * Create a new Form
7143  * @param {Object} config The config object
7144  */
7145
7146
7147 Roo.bootstrap.Form = function(config){
7148     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7149     this.addEvents({
7150         /**
7151          * @event clientvalidation
7152          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7153          * @param {Form} this
7154          * @param {Boolean} valid true if the form has passed client-side validation
7155          */
7156         clientvalidation: true,
7157         /**
7158          * @event beforeaction
7159          * Fires before any action is performed. Return false to cancel the action.
7160          * @param {Form} this
7161          * @param {Action} action The action to be performed
7162          */
7163         beforeaction: true,
7164         /**
7165          * @event actionfailed
7166          * Fires when an action fails.
7167          * @param {Form} this
7168          * @param {Action} action The action that failed
7169          */
7170         actionfailed : true,
7171         /**
7172          * @event actioncomplete
7173          * Fires when an action is completed.
7174          * @param {Form} this
7175          * @param {Action} action The action that completed
7176          */
7177         actioncomplete : true
7178     });
7179     
7180 };
7181
7182 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7183       
7184      /**
7185      * @cfg {String} method
7186      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7187      */
7188     method : 'POST',
7189     /**
7190      * @cfg {String} url
7191      * The URL to use for form actions if one isn't supplied in the action options.
7192      */
7193     /**
7194      * @cfg {Boolean} fileUpload
7195      * Set to true if this form is a file upload.
7196      */
7197      
7198     /**
7199      * @cfg {Object} baseParams
7200      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7201      */
7202       
7203     /**
7204      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7205      */
7206     timeout: 30,
7207     /**
7208      * @cfg {Sting} align (left|right) for navbar forms
7209      */
7210     align : 'left',
7211
7212     // private
7213     activeAction : null,
7214  
7215     /**
7216      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7217      * element by passing it or its id or mask the form itself by passing in true.
7218      * @type Mixed
7219      */
7220     waitMsgTarget : false,
7221     
7222     loadMask : true,
7223     
7224     getAutoCreate : function(){
7225         
7226         var cfg = {
7227             tag: 'form',
7228             method : this.method || 'POST',
7229             id : this.id || Roo.id(),
7230             cls : ''
7231         };
7232         if (this.parent().xtype.match(/^Nav/)) {
7233             cfg.cls = 'navbar-form navbar-' + this.align;
7234             
7235         }
7236         
7237         if (this.labelAlign == 'left' ) {
7238             cfg.cls += ' form-horizontal';
7239         }
7240         
7241         
7242         return cfg;
7243     },
7244     initEvents : function()
7245     {
7246         this.el.on('submit', this.onSubmit, this);
7247         // this was added as random key presses on the form where triggering form submit.
7248         this.el.on('keypress', function(e) {
7249             if (e.getCharCode() != 13) {
7250                 return true;
7251             }
7252             // we might need to allow it for textareas.. and some other items.
7253             // check e.getTarget().
7254             
7255             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7256                 return true;
7257             }
7258         
7259             Roo.log("keypress blocked");
7260             
7261             e.preventDefault();
7262             return false;
7263         });
7264         
7265     },
7266     // private
7267     onSubmit : function(e){
7268         e.stopEvent();
7269     },
7270     
7271      /**
7272      * Returns true if client-side validation on the form is successful.
7273      * @return Boolean
7274      */
7275     isValid : function(){
7276         var items = this.getItems();
7277         var valid = true;
7278         items.each(function(f){
7279            if(!f.validate()){
7280                valid = false;
7281                
7282            }
7283         });
7284         return valid;
7285     },
7286     /**
7287      * Returns true if any fields in this form have changed since their original load.
7288      * @return Boolean
7289      */
7290     isDirty : function(){
7291         var dirty = false;
7292         var items = this.getItems();
7293         items.each(function(f){
7294            if(f.isDirty()){
7295                dirty = true;
7296                return false;
7297            }
7298            return true;
7299         });
7300         return dirty;
7301     },
7302      /**
7303      * Performs a predefined action (submit or load) or custom actions you define on this form.
7304      * @param {String} actionName The name of the action type
7305      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7306      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7307      * accept other config options):
7308      * <pre>
7309 Property          Type             Description
7310 ----------------  ---------------  ----------------------------------------------------------------------------------
7311 url               String           The url for the action (defaults to the form's url)
7312 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7313 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7314 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7315                                    validate the form on the client (defaults to false)
7316      * </pre>
7317      * @return {BasicForm} this
7318      */
7319     doAction : function(action, options){
7320         if(typeof action == 'string'){
7321             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7322         }
7323         if(this.fireEvent('beforeaction', this, action) !== false){
7324             this.beforeAction(action);
7325             action.run.defer(100, action);
7326         }
7327         return this;
7328     },
7329     
7330     // private
7331     beforeAction : function(action){
7332         var o = action.options;
7333         
7334         if(this.loadMask){
7335             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7336         }
7337         // not really supported yet.. ??
7338         
7339         //if(this.waitMsgTarget === true){
7340         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7341         //}else if(this.waitMsgTarget){
7342         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7343         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7344         //}else {
7345         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7346        // }
7347          
7348     },
7349
7350     // private
7351     afterAction : function(action, success){
7352         this.activeAction = null;
7353         var o = action.options;
7354         
7355         //if(this.waitMsgTarget === true){
7356             this.el.unmask();
7357         //}else if(this.waitMsgTarget){
7358         //    this.waitMsgTarget.unmask();
7359         //}else{
7360         //    Roo.MessageBox.updateProgress(1);
7361         //    Roo.MessageBox.hide();
7362        // }
7363         // 
7364         if(success){
7365             if(o.reset){
7366                 this.reset();
7367             }
7368             Roo.callback(o.success, o.scope, [this, action]);
7369             this.fireEvent('actioncomplete', this, action);
7370             
7371         }else{
7372             
7373             // failure condition..
7374             // we have a scenario where updates need confirming.
7375             // eg. if a locking scenario exists..
7376             // we look for { errors : { needs_confirm : true }} in the response.
7377             if (
7378                 (typeof(action.result) != 'undefined')  &&
7379                 (typeof(action.result.errors) != 'undefined')  &&
7380                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7381            ){
7382                 var _t = this;
7383                 Roo.log("not supported yet");
7384                  /*
7385                 
7386                 Roo.MessageBox.confirm(
7387                     "Change requires confirmation",
7388                     action.result.errorMsg,
7389                     function(r) {
7390                         if (r != 'yes') {
7391                             return;
7392                         }
7393                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7394                     }
7395                     
7396                 );
7397                 */
7398                 
7399                 
7400                 return;
7401             }
7402             
7403             Roo.callback(o.failure, o.scope, [this, action]);
7404             // show an error message if no failed handler is set..
7405             if (!this.hasListener('actionfailed')) {
7406                 Roo.log("need to add dialog support");
7407                 /*
7408                 Roo.MessageBox.alert("Error",
7409                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7410                         action.result.errorMsg :
7411                         "Saving Failed, please check your entries or try again"
7412                 );
7413                 */
7414             }
7415             
7416             this.fireEvent('actionfailed', this, action);
7417         }
7418         
7419     },
7420     /**
7421      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7422      * @param {String} id The value to search for
7423      * @return Field
7424      */
7425     findField : function(id){
7426         var items = this.getItems();
7427         var field = items.get(id);
7428         if(!field){
7429              items.each(function(f){
7430                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7431                     field = f;
7432                     return false;
7433                 }
7434                 return true;
7435             });
7436         }
7437         return field || null;
7438     },
7439      /**
7440      * Mark fields in this form invalid in bulk.
7441      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7442      * @return {BasicForm} this
7443      */
7444     markInvalid : function(errors){
7445         if(errors instanceof Array){
7446             for(var i = 0, len = errors.length; i < len; i++){
7447                 var fieldError = errors[i];
7448                 var f = this.findField(fieldError.id);
7449                 if(f){
7450                     f.markInvalid(fieldError.msg);
7451                 }
7452             }
7453         }else{
7454             var field, id;
7455             for(id in errors){
7456                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7457                     field.markInvalid(errors[id]);
7458                 }
7459             }
7460         }
7461         //Roo.each(this.childForms || [], function (f) {
7462         //    f.markInvalid(errors);
7463         //});
7464         
7465         return this;
7466     },
7467
7468     /**
7469      * Set values for fields in this form in bulk.
7470      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7471      * @return {BasicForm} this
7472      */
7473     setValues : function(values){
7474         if(values instanceof Array){ // array of objects
7475             for(var i = 0, len = values.length; i < len; i++){
7476                 var v = values[i];
7477                 var f = this.findField(v.id);
7478                 if(f){
7479                     f.setValue(v.value);
7480                     if(this.trackResetOnLoad){
7481                         f.originalValue = f.getValue();
7482                     }
7483                 }
7484             }
7485         }else{ // object hash
7486             var field, id;
7487             for(id in values){
7488                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7489                     
7490                     if (field.setFromData && 
7491                         field.valueField && 
7492                         field.displayField &&
7493                         // combos' with local stores can 
7494                         // be queried via setValue()
7495                         // to set their value..
7496                         (field.store && !field.store.isLocal)
7497                         ) {
7498                         // it's a combo
7499                         var sd = { };
7500                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7501                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7502                         field.setFromData(sd);
7503                         
7504                     } else {
7505                         field.setValue(values[id]);
7506                     }
7507                     
7508                     
7509                     if(this.trackResetOnLoad){
7510                         field.originalValue = field.getValue();
7511                     }
7512                 }
7513             }
7514         }
7515          
7516         //Roo.each(this.childForms || [], function (f) {
7517         //    f.setValues(values);
7518         //});
7519                 
7520         return this;
7521     },
7522
7523     /**
7524      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7525      * they are returned as an array.
7526      * @param {Boolean} asString
7527      * @return {Object}
7528      */
7529     getValues : function(asString){
7530         //if (this.childForms) {
7531             // copy values from the child forms
7532         //    Roo.each(this.childForms, function (f) {
7533         //        this.setValues(f.getValues());
7534         //    }, this);
7535         //}
7536         
7537         
7538         
7539         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7540         if(asString === true){
7541             return fs;
7542         }
7543         return Roo.urlDecode(fs);
7544     },
7545     
7546     /**
7547      * Returns the fields in this form as an object with key/value pairs. 
7548      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7549      * @return {Object}
7550      */
7551     getFieldValues : function(with_hidden)
7552     {
7553         var items = this.getItems();
7554         var ret = {};
7555         items.each(function(f){
7556             if (!f.getName()) {
7557                 return;
7558             }
7559             var v = f.getValue();
7560             if (f.inputType =='radio') {
7561                 if (typeof(ret[f.getName()]) == 'undefined') {
7562                     ret[f.getName()] = ''; // empty..
7563                 }
7564                 
7565                 if (!f.el.dom.checked) {
7566                     return;
7567                     
7568                 }
7569                 v = f.el.dom.value;
7570                 
7571             }
7572             
7573             // not sure if this supported any more..
7574             if ((typeof(v) == 'object') && f.getRawValue) {
7575                 v = f.getRawValue() ; // dates..
7576             }
7577             // combo boxes where name != hiddenName...
7578             if (f.name != f.getName()) {
7579                 ret[f.name] = f.getRawValue();
7580             }
7581             ret[f.getName()] = v;
7582         });
7583         
7584         return ret;
7585     },
7586
7587     /**
7588      * Clears all invalid messages in this form.
7589      * @return {BasicForm} this
7590      */
7591     clearInvalid : function(){
7592         var items = this.getItems();
7593         
7594         items.each(function(f){
7595            f.clearInvalid();
7596         });
7597         
7598         
7599         
7600         return this;
7601     },
7602
7603     /**
7604      * Resets this form.
7605      * @return {BasicForm} this
7606      */
7607     reset : function(){
7608         var items = this.getItems();
7609         items.each(function(f){
7610             f.reset();
7611         });
7612         
7613         Roo.each(this.childForms || [], function (f) {
7614             f.reset();
7615         });
7616        
7617         
7618         return this;
7619     },
7620     getItems : function()
7621     {
7622         var r=new Roo.util.MixedCollection(false, function(o){
7623             return o.id || (o.id = Roo.id());
7624         });
7625         var iter = function(el) {
7626             if (el.inputEl) {
7627                 r.add(el);
7628             }
7629             if (!el.items) {
7630                 return;
7631             }
7632             Roo.each(el.items,function(e) {
7633                 iter(e);
7634             });
7635             
7636             
7637         };
7638         
7639         iter(this);
7640         return r;
7641         
7642         
7643         
7644         
7645     }
7646     
7647 });
7648
7649  
7650 /*
7651  * Based on:
7652  * Ext JS Library 1.1.1
7653  * Copyright(c) 2006-2007, Ext JS, LLC.
7654  *
7655  * Originally Released Under LGPL - original licence link has changed is not relivant.
7656  *
7657  * Fork - LGPL
7658  * <script type="text/javascript">
7659  */
7660 /**
7661  * @class Roo.form.VTypes
7662  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7663  * @singleton
7664  */
7665 Roo.form.VTypes = function(){
7666     // closure these in so they are only created once.
7667     var alpha = /^[a-zA-Z_]+$/;
7668     var alphanum = /^[a-zA-Z0-9_]+$/;
7669     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7670     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7671
7672     // All these messages and functions are configurable
7673     return {
7674         /**
7675          * The function used to validate email addresses
7676          * @param {String} value The email address
7677          */
7678         'email' : function(v){
7679             return email.test(v);
7680         },
7681         /**
7682          * The error text to display when the email validation function returns false
7683          * @type String
7684          */
7685         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7686         /**
7687          * The keystroke filter mask to be applied on email input
7688          * @type RegExp
7689          */
7690         'emailMask' : /[a-z0-9_\.\-@]/i,
7691
7692         /**
7693          * The function used to validate URLs
7694          * @param {String} value The URL
7695          */
7696         'url' : function(v){
7697             return url.test(v);
7698         },
7699         /**
7700          * The error text to display when the url validation function returns false
7701          * @type String
7702          */
7703         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7704         
7705         /**
7706          * The function used to validate alpha values
7707          * @param {String} value The value
7708          */
7709         'alpha' : function(v){
7710             return alpha.test(v);
7711         },
7712         /**
7713          * The error text to display when the alpha validation function returns false
7714          * @type String
7715          */
7716         'alphaText' : 'This field should only contain letters and _',
7717         /**
7718          * The keystroke filter mask to be applied on alpha input
7719          * @type RegExp
7720          */
7721         'alphaMask' : /[a-z_]/i,
7722
7723         /**
7724          * The function used to validate alphanumeric values
7725          * @param {String} value The value
7726          */
7727         'alphanum' : function(v){
7728             return alphanum.test(v);
7729         },
7730         /**
7731          * The error text to display when the alphanumeric validation function returns false
7732          * @type String
7733          */
7734         'alphanumText' : 'This field should only contain letters, numbers and _',
7735         /**
7736          * The keystroke filter mask to be applied on alphanumeric input
7737          * @type RegExp
7738          */
7739         'alphanumMask' : /[a-z0-9_]/i
7740     };
7741 }();/*
7742  * - LGPL
7743  *
7744  * Input
7745  * 
7746  */
7747
7748 /**
7749  * @class Roo.bootstrap.Input
7750  * @extends Roo.bootstrap.Component
7751  * Bootstrap Input class
7752  * @cfg {Boolean} disabled is it disabled
7753  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7754  * @cfg {String} name name of the input
7755  * @cfg {string} fieldLabel - the label associated
7756  * @cfg {string} placeholder - placeholder to put in text.
7757  * @cfg {string}  before - input group add on before
7758  * @cfg {string} after - input group add on after
7759  * @cfg {string} size - (lg|sm) or leave empty..
7760  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7761  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7762  * @cfg {Number} md colspan out of 12 for computer-sized screens
7763  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7764  * @cfg {string} value default value of the input
7765  * @cfg {Number} labelWidth set the width of label (0-12)
7766  * @cfg {String} labelAlign (top|left)
7767  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7768  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7769
7770  * @cfg {String} align (left|center|right) Default left
7771  * @cfg {Boolean} forceFeedback (true|false) Default false
7772  * 
7773  * 
7774  * 
7775  * 
7776  * @constructor
7777  * Create a new Input
7778  * @param {Object} config The config object
7779  */
7780
7781 Roo.bootstrap.Input = function(config){
7782     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7783    
7784         this.addEvents({
7785             /**
7786              * @event focus
7787              * Fires when this field receives input focus.
7788              * @param {Roo.form.Field} this
7789              */
7790             focus : true,
7791             /**
7792              * @event blur
7793              * Fires when this field loses input focus.
7794              * @param {Roo.form.Field} this
7795              */
7796             blur : true,
7797             /**
7798              * @event specialkey
7799              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7800              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7801              * @param {Roo.form.Field} this
7802              * @param {Roo.EventObject} e The event object
7803              */
7804             specialkey : true,
7805             /**
7806              * @event change
7807              * Fires just before the field blurs if the field value has changed.
7808              * @param {Roo.form.Field} this
7809              * @param {Mixed} newValue The new value
7810              * @param {Mixed} oldValue The original value
7811              */
7812             change : true,
7813             /**
7814              * @event invalid
7815              * Fires after the field has been marked as invalid.
7816              * @param {Roo.form.Field} this
7817              * @param {String} msg The validation message
7818              */
7819             invalid : true,
7820             /**
7821              * @event valid
7822              * Fires after the field has been validated with no errors.
7823              * @param {Roo.form.Field} this
7824              */
7825             valid : true,
7826              /**
7827              * @event keyup
7828              * Fires after the key up
7829              * @param {Roo.form.Field} this
7830              * @param {Roo.EventObject}  e The event Object
7831              */
7832             keyup : true
7833         });
7834 };
7835
7836 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7837      /**
7838      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7839       automatic validation (defaults to "keyup").
7840      */
7841     validationEvent : "keyup",
7842      /**
7843      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7844      */
7845     validateOnBlur : true,
7846     /**
7847      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7848      */
7849     validationDelay : 250,
7850      /**
7851      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7852      */
7853     focusClass : "x-form-focus",  // not needed???
7854     
7855        
7856     /**
7857      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7858      */
7859     invalidClass : "has-warning",
7860     
7861     /**
7862      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7863      */
7864     validClass : "has-success",
7865     
7866     /**
7867      * @cfg {Boolean} hasFeedback (true|false) default true
7868      */
7869     hasFeedback : true,
7870     
7871     /**
7872      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7873      */
7874     invalidFeedbackClass : "glyphicon-warning-sign",
7875     
7876     /**
7877      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7878      */
7879     validFeedbackClass : "glyphicon-ok",
7880     
7881     /**
7882      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7883      */
7884     selectOnFocus : false,
7885     
7886      /**
7887      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7888      */
7889     maskRe : null,
7890        /**
7891      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7892      */
7893     vtype : null,
7894     
7895       /**
7896      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7897      */
7898     disableKeyFilter : false,
7899     
7900        /**
7901      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7902      */
7903     disabled : false,
7904      /**
7905      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7906      */
7907     allowBlank : true,
7908     /**
7909      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7910      */
7911     blankText : "This field is required",
7912     
7913      /**
7914      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7915      */
7916     minLength : 0,
7917     /**
7918      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7919      */
7920     maxLength : Number.MAX_VALUE,
7921     /**
7922      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7923      */
7924     minLengthText : "The minimum length for this field is {0}",
7925     /**
7926      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7927      */
7928     maxLengthText : "The maximum length for this field is {0}",
7929   
7930     
7931     /**
7932      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7933      * If available, this function will be called only after the basic validators all return true, and will be passed the
7934      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7935      */
7936     validator : null,
7937     /**
7938      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7939      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7940      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7941      */
7942     regex : null,
7943     /**
7944      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7945      */
7946     regexText : "",
7947     
7948     autocomplete: false,
7949     
7950     
7951     fieldLabel : '',
7952     inputType : 'text',
7953     
7954     name : false,
7955     placeholder: false,
7956     before : false,
7957     after : false,
7958     size : false,
7959     hasFocus : false,
7960     preventMark: false,
7961     isFormField : true,
7962     value : '',
7963     labelWidth : 2,
7964     labelAlign : false,
7965     readOnly : false,
7966     align : false,
7967     formatedValue : false,
7968     forceFeedback : false,
7969     
7970     parentLabelAlign : function()
7971     {
7972         var parent = this;
7973         while (parent.parent()) {
7974             parent = parent.parent();
7975             if (typeof(parent.labelAlign) !='undefined') {
7976                 return parent.labelAlign;
7977             }
7978         }
7979         return 'left';
7980         
7981     },
7982     
7983     getAutoCreate : function(){
7984         
7985         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7986         
7987         var id = Roo.id();
7988         
7989         var cfg = {};
7990         
7991         if(this.inputType != 'hidden'){
7992             cfg.cls = 'form-group' //input-group
7993         }
7994         
7995         var input =  {
7996             tag: 'input',
7997             id : id,
7998             type : this.inputType,
7999             value : this.value,
8000             cls : 'form-control',
8001             placeholder : this.placeholder || '',
8002             autocomplete : this.autocomplete || 'new-password'
8003         };
8004         
8005         
8006         if(this.align){
8007             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8008         }
8009         
8010         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8011             input.maxLength = this.maxLength;
8012         }
8013         
8014         if (this.disabled) {
8015             input.disabled=true;
8016         }
8017         
8018         if (this.readOnly) {
8019             input.readonly=true;
8020         }
8021         
8022         if (this.name) {
8023             input.name = this.name;
8024         }
8025         if (this.size) {
8026             input.cls += ' input-' + this.size;
8027         }
8028         var settings=this;
8029         ['xs','sm','md','lg'].map(function(size){
8030             if (settings[size]) {
8031                 cfg.cls += ' col-' + size + '-' + settings[size];
8032             }
8033         });
8034         
8035         var inputblock = input;
8036         
8037         var feedback = {
8038             tag: 'span',
8039             cls: 'glyphicon form-control-feedback'
8040         };
8041             
8042         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8043             
8044             inputblock = {
8045                 cls : 'has-feedback',
8046                 cn :  [
8047                     input,
8048                     feedback
8049                 ] 
8050             };  
8051         }
8052         
8053         if (this.before || this.after) {
8054             
8055             inputblock = {
8056                 cls : 'input-group',
8057                 cn :  [] 
8058             };
8059             
8060             if (this.before && typeof(this.before) == 'string') {
8061                 
8062                 inputblock.cn.push({
8063                     tag :'span',
8064                     cls : 'roo-input-before input-group-addon',
8065                     html : this.before
8066                 });
8067             }
8068             if (this.before && typeof(this.before) == 'object') {
8069                 this.before = Roo.factory(this.before);
8070                 
8071                 inputblock.cn.push({
8072                     tag :'span',
8073                     cls : 'roo-input-before input-group-' +
8074                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8075                 });
8076             }
8077             
8078             inputblock.cn.push(input);
8079             
8080             if (this.after && typeof(this.after) == 'string') {
8081                 inputblock.cn.push({
8082                     tag :'span',
8083                     cls : 'roo-input-after input-group-addon',
8084                     html : this.after
8085                 });
8086             }
8087             if (this.after && typeof(this.after) == 'object') {
8088                 this.after = Roo.factory(this.after);
8089                 
8090                 inputblock.cn.push({
8091                     tag :'span',
8092                     cls : 'roo-input-after input-group-' +
8093                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8094                 });
8095             }
8096             
8097             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8098                 inputblock.cls += ' has-feedback';
8099                 inputblock.cn.push(feedback);
8100             }
8101         };
8102         
8103         if (align ==='left' && this.fieldLabel.length) {
8104                 
8105                 cfg.cn = [
8106                     
8107                     {
8108                         tag: 'label',
8109                         'for' :  id,
8110                         cls : 'control-label col-sm-' + this.labelWidth,
8111                         html : this.fieldLabel
8112                         
8113                     },
8114                     {
8115                         cls : "col-sm-" + (12 - this.labelWidth), 
8116                         cn: [
8117                             inputblock
8118                         ]
8119                     }
8120                     
8121                 ];
8122         } else if ( this.fieldLabel.length) {
8123                 
8124                  cfg.cn = [
8125                    
8126                     {
8127                         tag: 'label',
8128                         //cls : 'input-group-addon',
8129                         html : this.fieldLabel
8130                         
8131                     },
8132                     
8133                     inputblock
8134                     
8135                 ];
8136
8137         } else {
8138             
8139                 cfg.cn = [
8140                     
8141                         inputblock
8142                     
8143                 ];
8144                 
8145                 
8146         };
8147         
8148         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8149            cfg.cls += ' navbar-form';
8150         }
8151         
8152         return cfg;
8153         
8154     },
8155     /**
8156      * return the real input element.
8157      */
8158     inputEl: function ()
8159     {
8160         return this.el.select('input.form-control',true).first();
8161     },
8162     
8163     tooltipEl : function()
8164     {
8165         return this.inputEl();
8166     },
8167     
8168     setDisabled : function(v)
8169     {
8170         var i  = this.inputEl().dom;
8171         if (!v) {
8172             i.removeAttribute('disabled');
8173             return;
8174             
8175         }
8176         i.setAttribute('disabled','true');
8177     },
8178     initEvents : function()
8179     {
8180           
8181         this.inputEl().on("keydown" , this.fireKey,  this);
8182         this.inputEl().on("focus", this.onFocus,  this);
8183         this.inputEl().on("blur", this.onBlur,  this);
8184         
8185         this.inputEl().relayEvent('keyup', this);
8186  
8187         // reference to original value for reset
8188         this.originalValue = this.getValue();
8189         //Roo.form.TextField.superclass.initEvents.call(this);
8190         if(this.validationEvent == 'keyup'){
8191             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8192             this.inputEl().on('keyup', this.filterValidation, this);
8193         }
8194         else if(this.validationEvent !== false){
8195             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8196         }
8197         
8198         if(this.selectOnFocus){
8199             this.on("focus", this.preFocus, this);
8200             
8201         }
8202         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8203             this.inputEl().on("keypress", this.filterKeys, this);
8204         }
8205        /* if(this.grow){
8206             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8207             this.el.on("click", this.autoSize,  this);
8208         }
8209         */
8210         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8211             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8212         }
8213         
8214         if (typeof(this.before) == 'object') {
8215             this.before.render(this.el.select('.roo-input-before',true).first());
8216         }
8217         if (typeof(this.after) == 'object') {
8218             this.after.render(this.el.select('.roo-input-after',true).first());
8219         }
8220         
8221         
8222     },
8223     filterValidation : function(e){
8224         if(!e.isNavKeyPress()){
8225             this.validationTask.delay(this.validationDelay);
8226         }
8227     },
8228      /**
8229      * Validates the field value
8230      * @return {Boolean} True if the value is valid, else false
8231      */
8232     validate : function(){
8233         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8234         if(this.disabled || this.validateValue(this.getRawValue())){
8235             this.markValid();
8236             return true;
8237         }
8238         
8239         this.markInvalid();
8240         return false;
8241     },
8242     
8243     
8244     /**
8245      * Validates a value according to the field's validation rules and marks the field as invalid
8246      * if the validation fails
8247      * @param {Mixed} value The value to validate
8248      * @return {Boolean} True if the value is valid, else false
8249      */
8250     validateValue : function(value){
8251         if(value.length < 1)  { // if it's blank
8252             if(this.allowBlank){
8253                 return true;
8254             }
8255             return false;
8256         }
8257         
8258         if(value.length < this.minLength){
8259             return false;
8260         }
8261         if(value.length > this.maxLength){
8262             return false;
8263         }
8264         if(this.vtype){
8265             var vt = Roo.form.VTypes;
8266             if(!vt[this.vtype](value, this)){
8267                 return false;
8268             }
8269         }
8270         if(typeof this.validator == "function"){
8271             var msg = this.validator(value);
8272             if(msg !== true){
8273                 return false;
8274             }
8275         }
8276         
8277         if(this.regex && !this.regex.test(value)){
8278             return false;
8279         }
8280         
8281         return true;
8282     },
8283
8284     
8285     
8286      // private
8287     fireKey : function(e){
8288         //Roo.log('field ' + e.getKey());
8289         if(e.isNavKeyPress()){
8290             this.fireEvent("specialkey", this, e);
8291         }
8292     },
8293     focus : function (selectText){
8294         if(this.rendered){
8295             this.inputEl().focus();
8296             if(selectText === true){
8297                 this.inputEl().dom.select();
8298             }
8299         }
8300         return this;
8301     } ,
8302     
8303     onFocus : function(){
8304         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8305            // this.el.addClass(this.focusClass);
8306         }
8307         if(!this.hasFocus){
8308             this.hasFocus = true;
8309             this.startValue = this.getValue();
8310             this.fireEvent("focus", this);
8311         }
8312     },
8313     
8314     beforeBlur : Roo.emptyFn,
8315
8316     
8317     // private
8318     onBlur : function(){
8319         this.beforeBlur();
8320         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8321             //this.el.removeClass(this.focusClass);
8322         }
8323         this.hasFocus = false;
8324         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8325             this.validate();
8326         }
8327         var v = this.getValue();
8328         if(String(v) !== String(this.startValue)){
8329             this.fireEvent('change', this, v, this.startValue);
8330         }
8331         this.fireEvent("blur", this);
8332     },
8333     
8334     /**
8335      * Resets the current field value to the originally loaded value and clears any validation messages
8336      */
8337     reset : function(){
8338         this.setValue(this.originalValue);
8339         this.validate();
8340     },
8341      /**
8342      * Returns the name of the field
8343      * @return {Mixed} name The name field
8344      */
8345     getName: function(){
8346         return this.name;
8347     },
8348      /**
8349      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8350      * @return {Mixed} value The field value
8351      */
8352     getValue : function(){
8353         
8354         var v = this.inputEl().getValue();
8355         
8356         return v;
8357     },
8358     /**
8359      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8360      * @return {Mixed} value The field value
8361      */
8362     getRawValue : function(){
8363         var v = this.inputEl().getValue();
8364         
8365         return v;
8366     },
8367     
8368     /**
8369      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8370      * @param {Mixed} value The value to set
8371      */
8372     setRawValue : function(v){
8373         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8374     },
8375     
8376     selectText : function(start, end){
8377         var v = this.getRawValue();
8378         if(v.length > 0){
8379             start = start === undefined ? 0 : start;
8380             end = end === undefined ? v.length : end;
8381             var d = this.inputEl().dom;
8382             if(d.setSelectionRange){
8383                 d.setSelectionRange(start, end);
8384             }else if(d.createTextRange){
8385                 var range = d.createTextRange();
8386                 range.moveStart("character", start);
8387                 range.moveEnd("character", v.length-end);
8388                 range.select();
8389             }
8390         }
8391     },
8392     
8393     /**
8394      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8395      * @param {Mixed} value The value to set
8396      */
8397     setValue : function(v){
8398         this.value = v;
8399         if(this.rendered){
8400             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8401             this.validate();
8402         }
8403     },
8404     
8405     /*
8406     processValue : function(value){
8407         if(this.stripCharsRe){
8408             var newValue = value.replace(this.stripCharsRe, '');
8409             if(newValue !== value){
8410                 this.setRawValue(newValue);
8411                 return newValue;
8412             }
8413         }
8414         return value;
8415     },
8416   */
8417     preFocus : function(){
8418         
8419         if(this.selectOnFocus){
8420             this.inputEl().dom.select();
8421         }
8422     },
8423     filterKeys : function(e){
8424         var k = e.getKey();
8425         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8426             return;
8427         }
8428         var c = e.getCharCode(), cc = String.fromCharCode(c);
8429         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8430             return;
8431         }
8432         if(!this.maskRe.test(cc)){
8433             e.stopEvent();
8434         }
8435     },
8436      /**
8437      * Clear any invalid styles/messages for this field
8438      */
8439     clearInvalid : function(){
8440         
8441         if(!this.el || this.preventMark){ // not rendered
8442             return;
8443         }
8444         
8445         var label = this.el.select('label', true).first();
8446         var icon = this.el.select('i.fa-star', true).first();
8447         
8448         if(label && icon){
8449             icon.remove();
8450         }
8451         
8452         this.el.removeClass(this.invalidClass);
8453         
8454         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8455             
8456             var feedback = this.el.select('.form-control-feedback', true).first();
8457             
8458             if(feedback){
8459                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8460             }
8461             
8462         }
8463         
8464         this.fireEvent('valid', this);
8465     },
8466     
8467      /**
8468      * Mark this field as valid
8469      */
8470     markValid : function()
8471     {
8472         if(!this.el  || this.preventMark){ // not rendered
8473             return;
8474         }
8475         
8476         this.el.removeClass([this.invalidClass, this.validClass]);
8477         
8478         var feedback = this.el.select('.form-control-feedback', true).first();
8479             
8480         if(feedback){
8481             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8482         }
8483
8484         if(this.disabled || this.allowBlank){
8485             return;
8486         }
8487         
8488         var formGroup = this.el.findParent('.form-group', false, true);
8489         
8490         if(formGroup){
8491             
8492             var label = formGroup.select('label', true).first();
8493             var icon = formGroup.select('i.fa-star', true).first();
8494             
8495             if(label && icon){
8496                 icon.remove();
8497             }
8498         }
8499         
8500         this.el.addClass(this.validClass);
8501         
8502         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8503             
8504             var feedback = this.el.select('.form-control-feedback', true).first();
8505             
8506             if(feedback){
8507                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8508                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8509             }
8510             
8511         }
8512         
8513         this.fireEvent('valid', this);
8514     },
8515     
8516      /**
8517      * Mark this field as invalid
8518      * @param {String} msg The validation message
8519      */
8520     markInvalid : function(msg)
8521     {
8522         if(!this.el  || this.preventMark){ // not rendered
8523             return;
8524         }
8525         
8526         this.el.removeClass([this.invalidClass, this.validClass]);
8527         
8528         var feedback = this.el.select('.form-control-feedback', true).first();
8529             
8530         if(feedback){
8531             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8532         }
8533
8534         if(this.disabled || this.allowBlank){
8535             return;
8536         }
8537         
8538         var formGroup = this.el.findParent('.form-group', false, true);
8539         
8540         if(formGroup){
8541             var label = formGroup.select('label', true).first();
8542             var icon = formGroup.select('i.fa-star', true).first();
8543
8544             if(!this.getValue().length && label && !icon){
8545                 this.el.findParent('.form-group', false, true).createChild({
8546                     tag : 'i',
8547                     cls : 'text-danger fa fa-lg fa-star',
8548                     tooltip : 'This field is required',
8549                     style : 'margin-right:5px;'
8550                 }, label, true);
8551             }
8552         }
8553         
8554         
8555         this.el.addClass(this.invalidClass);
8556         
8557         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8558             
8559             var feedback = this.el.select('.form-control-feedback', true).first();
8560             
8561             if(feedback){
8562                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8563                 
8564                 if(this.getValue().length || this.forceFeedback){
8565                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8566                 }
8567                 
8568             }
8569             
8570         }
8571         
8572         this.fireEvent('invalid', this, msg);
8573     },
8574     // private
8575     SafariOnKeyDown : function(event)
8576     {
8577         // this is a workaround for a password hang bug on chrome/ webkit.
8578         
8579         var isSelectAll = false;
8580         
8581         if(this.inputEl().dom.selectionEnd > 0){
8582             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8583         }
8584         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8585             event.preventDefault();
8586             this.setValue('');
8587             return;
8588         }
8589         
8590         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8591             
8592             event.preventDefault();
8593             // this is very hacky as keydown always get's upper case.
8594             //
8595             var cc = String.fromCharCode(event.getCharCode());
8596             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8597             
8598         }
8599     },
8600     adjustWidth : function(tag, w){
8601         tag = tag.toLowerCase();
8602         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8603             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8604                 if(tag == 'input'){
8605                     return w + 2;
8606                 }
8607                 if(tag == 'textarea'){
8608                     return w-2;
8609                 }
8610             }else if(Roo.isOpera){
8611                 if(tag == 'input'){
8612                     return w + 2;
8613                 }
8614                 if(tag == 'textarea'){
8615                     return w-2;
8616                 }
8617             }
8618         }
8619         return w;
8620     }
8621     
8622 });
8623
8624  
8625 /*
8626  * - LGPL
8627  *
8628  * Input
8629  * 
8630  */
8631
8632 /**
8633  * @class Roo.bootstrap.TextArea
8634  * @extends Roo.bootstrap.Input
8635  * Bootstrap TextArea class
8636  * @cfg {Number} cols Specifies the visible width of a text area
8637  * @cfg {Number} rows Specifies the visible number of lines in a text area
8638  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8639  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8640  * @cfg {string} html text
8641  * 
8642  * @constructor
8643  * Create a new TextArea
8644  * @param {Object} config The config object
8645  */
8646
8647 Roo.bootstrap.TextArea = function(config){
8648     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8649    
8650 };
8651
8652 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8653      
8654     cols : false,
8655     rows : 5,
8656     readOnly : false,
8657     warp : 'soft',
8658     resize : false,
8659     value: false,
8660     html: false,
8661     
8662     getAutoCreate : function(){
8663         
8664         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8665         
8666         var id = Roo.id();
8667         
8668         var cfg = {};
8669         
8670         var input =  {
8671             tag: 'textarea',
8672             id : id,
8673             warp : this.warp,
8674             rows : this.rows,
8675             value : this.value || '',
8676             html: this.html || '',
8677             cls : 'form-control',
8678             placeholder : this.placeholder || '' 
8679             
8680         };
8681         
8682         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8683             input.maxLength = this.maxLength;
8684         }
8685         
8686         if(this.resize){
8687             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8688         }
8689         
8690         if(this.cols){
8691             input.cols = this.cols;
8692         }
8693         
8694         if (this.readOnly) {
8695             input.readonly = true;
8696         }
8697         
8698         if (this.name) {
8699             input.name = this.name;
8700         }
8701         
8702         if (this.size) {
8703             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8704         }
8705         
8706         var settings=this;
8707         ['xs','sm','md','lg'].map(function(size){
8708             if (settings[size]) {
8709                 cfg.cls += ' col-' + size + '-' + settings[size];
8710             }
8711         });
8712         
8713         var inputblock = input;
8714         
8715         if(this.hasFeedback && !this.allowBlank){
8716             
8717             var feedback = {
8718                 tag: 'span',
8719                 cls: 'glyphicon form-control-feedback'
8720             };
8721
8722             inputblock = {
8723                 cls : 'has-feedback',
8724                 cn :  [
8725                     input,
8726                     feedback
8727                 ] 
8728             };  
8729         }
8730         
8731         
8732         if (this.before || this.after) {
8733             
8734             inputblock = {
8735                 cls : 'input-group',
8736                 cn :  [] 
8737             };
8738             if (this.before) {
8739                 inputblock.cn.push({
8740                     tag :'span',
8741                     cls : 'input-group-addon',
8742                     html : this.before
8743                 });
8744             }
8745             
8746             inputblock.cn.push(input);
8747             
8748             if(this.hasFeedback && !this.allowBlank){
8749                 inputblock.cls += ' has-feedback';
8750                 inputblock.cn.push(feedback);
8751             }
8752             
8753             if (this.after) {
8754                 inputblock.cn.push({
8755                     tag :'span',
8756                     cls : 'input-group-addon',
8757                     html : this.after
8758                 });
8759             }
8760             
8761         }
8762         
8763         if (align ==='left' && this.fieldLabel.length) {
8764 //                Roo.log("left and has label");
8765                 cfg.cn = [
8766                     
8767                     {
8768                         tag: 'label',
8769                         'for' :  id,
8770                         cls : 'control-label col-sm-' + this.labelWidth,
8771                         html : this.fieldLabel
8772                         
8773                     },
8774                     {
8775                         cls : "col-sm-" + (12 - this.labelWidth), 
8776                         cn: [
8777                             inputblock
8778                         ]
8779                     }
8780                     
8781                 ];
8782         } else if ( this.fieldLabel.length) {
8783 //                Roo.log(" label");
8784                  cfg.cn = [
8785                    
8786                     {
8787                         tag: 'label',
8788                         //cls : 'input-group-addon',
8789                         html : this.fieldLabel
8790                         
8791                     },
8792                     
8793                     inputblock
8794                     
8795                 ];
8796
8797         } else {
8798             
8799 //                   Roo.log(" no label && no align");
8800                 cfg.cn = [
8801                     
8802                         inputblock
8803                     
8804                 ];
8805                 
8806                 
8807         }
8808         
8809         if (this.disabled) {
8810             input.disabled=true;
8811         }
8812         
8813         return cfg;
8814         
8815     },
8816     /**
8817      * return the real textarea element.
8818      */
8819     inputEl: function ()
8820     {
8821         return this.el.select('textarea.form-control',true).first();
8822     },
8823     
8824     /**
8825      * Clear any invalid styles/messages for this field
8826      */
8827     clearInvalid : function()
8828     {
8829         
8830         if(!this.el || this.preventMark){ // not rendered
8831             return;
8832         }
8833         
8834         var label = this.el.select('label', true).first();
8835         var icon = this.el.select('i.fa-star', true).first();
8836         
8837         if(label && icon){
8838             icon.remove();
8839         }
8840         
8841         this.el.removeClass(this.invalidClass);
8842         
8843         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8844             
8845             var feedback = this.el.select('.form-control-feedback', true).first();
8846             
8847             if(feedback){
8848                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8849             }
8850             
8851         }
8852         
8853         this.fireEvent('valid', this);
8854     },
8855     
8856      /**
8857      * Mark this field as valid
8858      */
8859     markValid : function()
8860     {
8861         if(!this.el  || this.preventMark){ // not rendered
8862             return;
8863         }
8864         
8865         this.el.removeClass([this.invalidClass, this.validClass]);
8866         
8867         var feedback = this.el.select('.form-control-feedback', true).first();
8868             
8869         if(feedback){
8870             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8871         }
8872
8873         if(this.disabled || this.allowBlank){
8874             return;
8875         }
8876         
8877         var label = this.el.select('label', true).first();
8878         var icon = this.el.select('i.fa-star', true).first();
8879         
8880         if(label && icon){
8881             icon.remove();
8882         }
8883         
8884         this.el.addClass(this.validClass);
8885         
8886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8887             
8888             var feedback = this.el.select('.form-control-feedback', true).first();
8889             
8890             if(feedback){
8891                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8892                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8893             }
8894             
8895         }
8896         
8897         this.fireEvent('valid', this);
8898     },
8899     
8900      /**
8901      * Mark this field as invalid
8902      * @param {String} msg The validation message
8903      */
8904     markInvalid : function(msg)
8905     {
8906         if(!this.el  || this.preventMark){ // not rendered
8907             return;
8908         }
8909         
8910         this.el.removeClass([this.invalidClass, this.validClass]);
8911         
8912         var feedback = this.el.select('.form-control-feedback', true).first();
8913             
8914         if(feedback){
8915             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8916         }
8917
8918         if(this.disabled || this.allowBlank){
8919             return;
8920         }
8921         
8922         var label = this.el.select('label', true).first();
8923         var icon = this.el.select('i.fa-star', true).first();
8924         
8925         if(!this.getValue().length && label && !icon){
8926             this.el.createChild({
8927                 tag : 'i',
8928                 cls : 'text-danger fa fa-lg fa-star',
8929                 tooltip : 'This field is required',
8930                 style : 'margin-right:5px;'
8931             }, label, true);
8932         }
8933
8934         this.el.addClass(this.invalidClass);
8935         
8936         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8937             
8938             var feedback = this.el.select('.form-control-feedback', true).first();
8939             
8940             if(feedback){
8941                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8942                 
8943                 if(this.getValue().length || this.forceFeedback){
8944                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8945                 }
8946                 
8947             }
8948             
8949         }
8950         
8951         this.fireEvent('invalid', this, msg);
8952     }
8953 });
8954
8955  
8956 /*
8957  * - LGPL
8958  *
8959  * trigger field - base class for combo..
8960  * 
8961  */
8962  
8963 /**
8964  * @class Roo.bootstrap.TriggerField
8965  * @extends Roo.bootstrap.Input
8966  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8967  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8968  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8969  * for which you can provide a custom implementation.  For example:
8970  * <pre><code>
8971 var trigger = new Roo.bootstrap.TriggerField();
8972 trigger.onTriggerClick = myTriggerFn;
8973 trigger.applyTo('my-field');
8974 </code></pre>
8975  *
8976  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8977  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8978  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8979  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8980  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8981
8982  * @constructor
8983  * Create a new TriggerField.
8984  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8985  * to the base TextField)
8986  */
8987 Roo.bootstrap.TriggerField = function(config){
8988     this.mimicing = false;
8989     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8990 };
8991
8992 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8993     /**
8994      * @cfg {String} triggerClass A CSS class to apply to the trigger
8995      */
8996      /**
8997      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8998      */
8999     hideTrigger:false,
9000
9001     /**
9002      * @cfg {Boolean} removable (true|false) special filter default false
9003      */
9004     removable : false,
9005     
9006     /** @cfg {Boolean} grow @hide */
9007     /** @cfg {Number} growMin @hide */
9008     /** @cfg {Number} growMax @hide */
9009
9010     /**
9011      * @hide 
9012      * @method
9013      */
9014     autoSize: Roo.emptyFn,
9015     // private
9016     monitorTab : true,
9017     // private
9018     deferHeight : true,
9019
9020     
9021     actionMode : 'wrap',
9022     
9023     caret : false,
9024     
9025     
9026     getAutoCreate : function(){
9027        
9028         var align = this.labelAlign || this.parentLabelAlign();
9029         
9030         var id = Roo.id();
9031         
9032         var cfg = {
9033             cls: 'form-group' //input-group
9034         };
9035         
9036         
9037         var input =  {
9038             tag: 'input',
9039             id : id,
9040             type : this.inputType,
9041             cls : 'form-control',
9042             autocomplete: 'new-password',
9043             placeholder : this.placeholder || '' 
9044             
9045         };
9046         if (this.name) {
9047             input.name = this.name;
9048         }
9049         if (this.size) {
9050             input.cls += ' input-' + this.size;
9051         }
9052         
9053         if (this.disabled) {
9054             input.disabled=true;
9055         }
9056         
9057         var inputblock = input;
9058         
9059         if(this.hasFeedback && !this.allowBlank){
9060             
9061             var feedback = {
9062                 tag: 'span',
9063                 cls: 'glyphicon form-control-feedback'
9064             };
9065             
9066             if(this.removable && !this.editable && !this.tickable){
9067                 inputblock = {
9068                     cls : 'has-feedback',
9069                     cn :  [
9070                         inputblock,
9071                         {
9072                             tag: 'button',
9073                             html : 'x',
9074                             cls : 'roo-combo-removable-btn close'
9075                         },
9076                         feedback
9077                     ] 
9078                 };
9079             } else {
9080                 inputblock = {
9081                     cls : 'has-feedback',
9082                     cn :  [
9083                         inputblock,
9084                         feedback
9085                     ] 
9086                 };
9087             }
9088
9089         } else {
9090             if(this.removable && !this.editable && !this.tickable){
9091                 inputblock = {
9092                     cls : 'roo-removable',
9093                     cn :  [
9094                         inputblock,
9095                         {
9096                             tag: 'button',
9097                             html : 'x',
9098                             cls : 'roo-combo-removable-btn close'
9099                         }
9100                     ] 
9101                 };
9102             }
9103         }
9104         
9105         if (this.before || this.after) {
9106             
9107             inputblock = {
9108                 cls : 'input-group',
9109                 cn :  [] 
9110             };
9111             if (this.before) {
9112                 inputblock.cn.push({
9113                     tag :'span',
9114                     cls : 'input-group-addon',
9115                     html : this.before
9116                 });
9117             }
9118             
9119             inputblock.cn.push(input);
9120             
9121             if(this.hasFeedback && !this.allowBlank){
9122                 inputblock.cls += ' has-feedback';
9123                 inputblock.cn.push(feedback);
9124             }
9125             
9126             if (this.after) {
9127                 inputblock.cn.push({
9128                     tag :'span',
9129                     cls : 'input-group-addon',
9130                     html : this.after
9131                 });
9132             }
9133             
9134         };
9135         
9136         var box = {
9137             tag: 'div',
9138             cn: [
9139                 {
9140                     tag: 'input',
9141                     type : 'hidden',
9142                     cls: 'form-hidden-field'
9143                 },
9144                 inputblock
9145             ]
9146             
9147         };
9148         
9149         if(this.multiple){
9150             box = {
9151                 tag: 'div',
9152                 cn: [
9153                     {
9154                         tag: 'input',
9155                         type : 'hidden',
9156                         cls: 'form-hidden-field'
9157                     },
9158                     {
9159                         tag: 'ul',
9160                         cls: 'select2-choices',
9161                         cn:[
9162                             {
9163                                 tag: 'li',
9164                                 cls: 'select2-search-field',
9165                                 cn: [
9166
9167                                     inputblock
9168                                 ]
9169                             }
9170                         ]
9171                     }
9172                 ]
9173             }
9174         };
9175         
9176         var combobox = {
9177             cls: 'select2-container input-group',
9178             cn: [
9179                 box
9180 //                {
9181 //                    tag: 'ul',
9182 //                    cls: 'typeahead typeahead-long dropdown-menu',
9183 //                    style: 'display:none'
9184 //                }
9185             ]
9186         };
9187         
9188         if(!this.multiple && this.showToggleBtn){
9189             
9190             var caret = {
9191                         tag: 'span',
9192                         cls: 'caret'
9193              };
9194             if (this.caret != false) {
9195                 caret = {
9196                      tag: 'i',
9197                      cls: 'fa fa-' + this.caret
9198                 };
9199                 
9200             }
9201             
9202             combobox.cn.push({
9203                 tag :'span',
9204                 cls : 'input-group-addon btn dropdown-toggle',
9205                 cn : [
9206                     caret,
9207                     {
9208                         tag: 'span',
9209                         cls: 'combobox-clear',
9210                         cn  : [
9211                             {
9212                                 tag : 'i',
9213                                 cls: 'icon-remove'
9214                             }
9215                         ]
9216                     }
9217                 ]
9218
9219             })
9220         }
9221         
9222         if(this.multiple){
9223             combobox.cls += ' select2-container-multi';
9224         }
9225         
9226         if (align ==='left' && this.fieldLabel.length) {
9227             
9228 //                Roo.log("left and has label");
9229                 cfg.cn = [
9230                     
9231                     {
9232                         tag: 'label',
9233                         'for' :  id,
9234                         cls : 'control-label col-sm-' + this.labelWidth,
9235                         html : this.fieldLabel
9236                         
9237                     },
9238                     {
9239                         cls : "col-sm-" + (12 - this.labelWidth), 
9240                         cn: [
9241                             combobox
9242                         ]
9243                     }
9244                     
9245                 ];
9246         } else if ( this.fieldLabel.length) {
9247 //                Roo.log(" label");
9248                  cfg.cn = [
9249                    
9250                     {
9251                         tag: 'label',
9252                         //cls : 'input-group-addon',
9253                         html : this.fieldLabel
9254                         
9255                     },
9256                     
9257                     combobox
9258                     
9259                 ];
9260
9261         } else {
9262             
9263 //                Roo.log(" no label && no align");
9264                 cfg = combobox
9265                      
9266                 
9267         }
9268          
9269         var settings=this;
9270         ['xs','sm','md','lg'].map(function(size){
9271             if (settings[size]) {
9272                 cfg.cls += ' col-' + size + '-' + settings[size];
9273             }
9274         });
9275         
9276         return cfg;
9277         
9278     },
9279     
9280     
9281     
9282     // private
9283     onResize : function(w, h){
9284 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9285 //        if(typeof w == 'number'){
9286 //            var x = w - this.trigger.getWidth();
9287 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9288 //            this.trigger.setStyle('left', x+'px');
9289 //        }
9290     },
9291
9292     // private
9293     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9294
9295     // private
9296     getResizeEl : function(){
9297         return this.inputEl();
9298     },
9299
9300     // private
9301     getPositionEl : function(){
9302         return this.inputEl();
9303     },
9304
9305     // private
9306     alignErrorIcon : function(){
9307         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9308     },
9309
9310     // private
9311     initEvents : function(){
9312         
9313         this.createList();
9314         
9315         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9316         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9317         if(!this.multiple && this.showToggleBtn){
9318             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9319             if(this.hideTrigger){
9320                 this.trigger.setDisplayed(false);
9321             }
9322             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9323         }
9324         
9325         if(this.multiple){
9326             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9327         }
9328         
9329         if(this.removable && !this.editable && !this.tickable){
9330             var close = this.closeTriggerEl();
9331             
9332             if(close){
9333                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9334                 close.on('click', this.removeBtnClick, this, close);
9335             }
9336         }
9337         
9338         //this.trigger.addClassOnOver('x-form-trigger-over');
9339         //this.trigger.addClassOnClick('x-form-trigger-click');
9340         
9341         //if(!this.width){
9342         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9343         //}
9344     },
9345     
9346     closeTriggerEl : function()
9347     {
9348         var close = this.el.select('.roo-combo-removable-btn', true).first();
9349         return close ? close : false;
9350     },
9351     
9352     removeBtnClick : function(e, h, el)
9353     {
9354         e.preventDefault();
9355         
9356         if(this.fireEvent("remove", this) !== false){
9357             this.reset();
9358         }
9359     },
9360     
9361     createList : function()
9362     {
9363         this.list = Roo.get(document.body).createChild({
9364             tag: 'ul',
9365             cls: 'typeahead typeahead-long dropdown-menu',
9366             style: 'display:none'
9367         });
9368         
9369         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9370         
9371     },
9372
9373     // private
9374     initTrigger : function(){
9375        
9376     },
9377
9378     // private
9379     onDestroy : function(){
9380         if(this.trigger){
9381             this.trigger.removeAllListeners();
9382           //  this.trigger.remove();
9383         }
9384         //if(this.wrap){
9385         //    this.wrap.remove();
9386         //}
9387         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9388     },
9389
9390     // private
9391     onFocus : function(){
9392         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9393         /*
9394         if(!this.mimicing){
9395             this.wrap.addClass('x-trigger-wrap-focus');
9396             this.mimicing = true;
9397             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9398             if(this.monitorTab){
9399                 this.el.on("keydown", this.checkTab, this);
9400             }
9401         }
9402         */
9403     },
9404
9405     // private
9406     checkTab : function(e){
9407         if(e.getKey() == e.TAB){
9408             this.triggerBlur();
9409         }
9410     },
9411
9412     // private
9413     onBlur : function(){
9414         // do nothing
9415     },
9416
9417     // private
9418     mimicBlur : function(e, t){
9419         /*
9420         if(!this.wrap.contains(t) && this.validateBlur()){
9421             this.triggerBlur();
9422         }
9423         */
9424     },
9425
9426     // private
9427     triggerBlur : function(){
9428         this.mimicing = false;
9429         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9430         if(this.monitorTab){
9431             this.el.un("keydown", this.checkTab, this);
9432         }
9433         //this.wrap.removeClass('x-trigger-wrap-focus');
9434         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9435     },
9436
9437     // private
9438     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9439     validateBlur : function(e, t){
9440         return true;
9441     },
9442
9443     // private
9444     onDisable : function(){
9445         this.inputEl().dom.disabled = true;
9446         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9447         //if(this.wrap){
9448         //    this.wrap.addClass('x-item-disabled');
9449         //}
9450     },
9451
9452     // private
9453     onEnable : function(){
9454         this.inputEl().dom.disabled = false;
9455         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9456         //if(this.wrap){
9457         //    this.el.removeClass('x-item-disabled');
9458         //}
9459     },
9460
9461     // private
9462     onShow : function(){
9463         var ae = this.getActionEl();
9464         
9465         if(ae){
9466             ae.dom.style.display = '';
9467             ae.dom.style.visibility = 'visible';
9468         }
9469     },
9470
9471     // private
9472     
9473     onHide : function(){
9474         var ae = this.getActionEl();
9475         ae.dom.style.display = 'none';
9476     },
9477
9478     /**
9479      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9480      * by an implementing function.
9481      * @method
9482      * @param {EventObject} e
9483      */
9484     onTriggerClick : Roo.emptyFn
9485 });
9486  /*
9487  * Based on:
9488  * Ext JS Library 1.1.1
9489  * Copyright(c) 2006-2007, Ext JS, LLC.
9490  *
9491  * Originally Released Under LGPL - original licence link has changed is not relivant.
9492  *
9493  * Fork - LGPL
9494  * <script type="text/javascript">
9495  */
9496
9497
9498 /**
9499  * @class Roo.data.SortTypes
9500  * @singleton
9501  * Defines the default sorting (casting?) comparison functions used when sorting data.
9502  */
9503 Roo.data.SortTypes = {
9504     /**
9505      * Default sort that does nothing
9506      * @param {Mixed} s The value being converted
9507      * @return {Mixed} The comparison value
9508      */
9509     none : function(s){
9510         return s;
9511     },
9512     
9513     /**
9514      * The regular expression used to strip tags
9515      * @type {RegExp}
9516      * @property
9517      */
9518     stripTagsRE : /<\/?[^>]+>/gi,
9519     
9520     /**
9521      * Strips all HTML tags to sort on text only
9522      * @param {Mixed} s The value being converted
9523      * @return {String} The comparison value
9524      */
9525     asText : function(s){
9526         return String(s).replace(this.stripTagsRE, "");
9527     },
9528     
9529     /**
9530      * Strips all HTML tags to sort on text only - Case insensitive
9531      * @param {Mixed} s The value being converted
9532      * @return {String} The comparison value
9533      */
9534     asUCText : function(s){
9535         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9536     },
9537     
9538     /**
9539      * Case insensitive string
9540      * @param {Mixed} s The value being converted
9541      * @return {String} The comparison value
9542      */
9543     asUCString : function(s) {
9544         return String(s).toUpperCase();
9545     },
9546     
9547     /**
9548      * Date sorting
9549      * @param {Mixed} s The value being converted
9550      * @return {Number} The comparison value
9551      */
9552     asDate : function(s) {
9553         if(!s){
9554             return 0;
9555         }
9556         if(s instanceof Date){
9557             return s.getTime();
9558         }
9559         return Date.parse(String(s));
9560     },
9561     
9562     /**
9563      * Float sorting
9564      * @param {Mixed} s The value being converted
9565      * @return {Float} The comparison value
9566      */
9567     asFloat : function(s) {
9568         var val = parseFloat(String(s).replace(/,/g, ""));
9569         if(isNaN(val)) {
9570             val = 0;
9571         }
9572         return val;
9573     },
9574     
9575     /**
9576      * Integer sorting
9577      * @param {Mixed} s The value being converted
9578      * @return {Number} The comparison value
9579      */
9580     asInt : function(s) {
9581         var val = parseInt(String(s).replace(/,/g, ""));
9582         if(isNaN(val)) {
9583             val = 0;
9584         }
9585         return val;
9586     }
9587 };/*
9588  * Based on:
9589  * Ext JS Library 1.1.1
9590  * Copyright(c) 2006-2007, Ext JS, LLC.
9591  *
9592  * Originally Released Under LGPL - original licence link has changed is not relivant.
9593  *
9594  * Fork - LGPL
9595  * <script type="text/javascript">
9596  */
9597
9598 /**
9599 * @class Roo.data.Record
9600  * Instances of this class encapsulate both record <em>definition</em> information, and record
9601  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9602  * to access Records cached in an {@link Roo.data.Store} object.<br>
9603  * <p>
9604  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9605  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9606  * objects.<br>
9607  * <p>
9608  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9609  * @constructor
9610  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9611  * {@link #create}. The parameters are the same.
9612  * @param {Array} data An associative Array of data values keyed by the field name.
9613  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9614  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9615  * not specified an integer id is generated.
9616  */
9617 Roo.data.Record = function(data, id){
9618     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9619     this.data = data;
9620 };
9621
9622 /**
9623  * Generate a constructor for a specific record layout.
9624  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9625  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9626  * Each field definition object may contain the following properties: <ul>
9627  * <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,
9628  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9629  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9630  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9631  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9632  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9633  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9634  * this may be omitted.</p></li>
9635  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9636  * <ul><li>auto (Default, implies no conversion)</li>
9637  * <li>string</li>
9638  * <li>int</li>
9639  * <li>float</li>
9640  * <li>boolean</li>
9641  * <li>date</li></ul></p></li>
9642  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9643  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9644  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9645  * by the Reader into an object that will be stored in the Record. It is passed the
9646  * following parameters:<ul>
9647  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9648  * </ul></p></li>
9649  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9650  * </ul>
9651  * <br>usage:<br><pre><code>
9652 var TopicRecord = Roo.data.Record.create(
9653     {name: 'title', mapping: 'topic_title'},
9654     {name: 'author', mapping: 'username'},
9655     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9656     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9657     {name: 'lastPoster', mapping: 'user2'},
9658     {name: 'excerpt', mapping: 'post_text'}
9659 );
9660
9661 var myNewRecord = new TopicRecord({
9662     title: 'Do my job please',
9663     author: 'noobie',
9664     totalPosts: 1,
9665     lastPost: new Date(),
9666     lastPoster: 'Animal',
9667     excerpt: 'No way dude!'
9668 });
9669 myStore.add(myNewRecord);
9670 </code></pre>
9671  * @method create
9672  * @static
9673  */
9674 Roo.data.Record.create = function(o){
9675     var f = function(){
9676         f.superclass.constructor.apply(this, arguments);
9677     };
9678     Roo.extend(f, Roo.data.Record);
9679     var p = f.prototype;
9680     p.fields = new Roo.util.MixedCollection(false, function(field){
9681         return field.name;
9682     });
9683     for(var i = 0, len = o.length; i < len; i++){
9684         p.fields.add(new Roo.data.Field(o[i]));
9685     }
9686     f.getField = function(name){
9687         return p.fields.get(name);  
9688     };
9689     return f;
9690 };
9691
9692 Roo.data.Record.AUTO_ID = 1000;
9693 Roo.data.Record.EDIT = 'edit';
9694 Roo.data.Record.REJECT = 'reject';
9695 Roo.data.Record.COMMIT = 'commit';
9696
9697 Roo.data.Record.prototype = {
9698     /**
9699      * Readonly flag - true if this record has been modified.
9700      * @type Boolean
9701      */
9702     dirty : false,
9703     editing : false,
9704     error: null,
9705     modified: null,
9706
9707     // private
9708     join : function(store){
9709         this.store = store;
9710     },
9711
9712     /**
9713      * Set the named field to the specified value.
9714      * @param {String} name The name of the field to set.
9715      * @param {Object} value The value to set the field to.
9716      */
9717     set : function(name, value){
9718         if(this.data[name] == value){
9719             return;
9720         }
9721         this.dirty = true;
9722         if(!this.modified){
9723             this.modified = {};
9724         }
9725         if(typeof this.modified[name] == 'undefined'){
9726             this.modified[name] = this.data[name];
9727         }
9728         this.data[name] = value;
9729         if(!this.editing && this.store){
9730             this.store.afterEdit(this);
9731         }       
9732     },
9733
9734     /**
9735      * Get the value of the named field.
9736      * @param {String} name The name of the field to get the value of.
9737      * @return {Object} The value of the field.
9738      */
9739     get : function(name){
9740         return this.data[name]; 
9741     },
9742
9743     // private
9744     beginEdit : function(){
9745         this.editing = true;
9746         this.modified = {}; 
9747     },
9748
9749     // private
9750     cancelEdit : function(){
9751         this.editing = false;
9752         delete this.modified;
9753     },
9754
9755     // private
9756     endEdit : function(){
9757         this.editing = false;
9758         if(this.dirty && this.store){
9759             this.store.afterEdit(this);
9760         }
9761     },
9762
9763     /**
9764      * Usually called by the {@link Roo.data.Store} which owns the Record.
9765      * Rejects all changes made to the Record since either creation, or the last commit operation.
9766      * Modified fields are reverted to their original values.
9767      * <p>
9768      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9769      * of reject operations.
9770      */
9771     reject : function(){
9772         var m = this.modified;
9773         for(var n in m){
9774             if(typeof m[n] != "function"){
9775                 this.data[n] = m[n];
9776             }
9777         }
9778         this.dirty = false;
9779         delete this.modified;
9780         this.editing = false;
9781         if(this.store){
9782             this.store.afterReject(this);
9783         }
9784     },
9785
9786     /**
9787      * Usually called by the {@link Roo.data.Store} which owns the Record.
9788      * Commits all changes made to the Record since either creation, or the last commit operation.
9789      * <p>
9790      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9791      * of commit operations.
9792      */
9793     commit : function(){
9794         this.dirty = false;
9795         delete this.modified;
9796         this.editing = false;
9797         if(this.store){
9798             this.store.afterCommit(this);
9799         }
9800     },
9801
9802     // private
9803     hasError : function(){
9804         return this.error != null;
9805     },
9806
9807     // private
9808     clearError : function(){
9809         this.error = null;
9810     },
9811
9812     /**
9813      * Creates a copy of this record.
9814      * @param {String} id (optional) A new record id if you don't want to use this record's id
9815      * @return {Record}
9816      */
9817     copy : function(newId) {
9818         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9819     }
9820 };/*
9821  * Based on:
9822  * Ext JS Library 1.1.1
9823  * Copyright(c) 2006-2007, Ext JS, LLC.
9824  *
9825  * Originally Released Under LGPL - original licence link has changed is not relivant.
9826  *
9827  * Fork - LGPL
9828  * <script type="text/javascript">
9829  */
9830
9831
9832
9833 /**
9834  * @class Roo.data.Store
9835  * @extends Roo.util.Observable
9836  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9837  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9838  * <p>
9839  * 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
9840  * has no knowledge of the format of the data returned by the Proxy.<br>
9841  * <p>
9842  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9843  * instances from the data object. These records are cached and made available through accessor functions.
9844  * @constructor
9845  * Creates a new Store.
9846  * @param {Object} config A config object containing the objects needed for the Store to access data,
9847  * and read the data into Records.
9848  */
9849 Roo.data.Store = function(config){
9850     this.data = new Roo.util.MixedCollection(false);
9851     this.data.getKey = function(o){
9852         return o.id;
9853     };
9854     this.baseParams = {};
9855     // private
9856     this.paramNames = {
9857         "start" : "start",
9858         "limit" : "limit",
9859         "sort" : "sort",
9860         "dir" : "dir",
9861         "multisort" : "_multisort"
9862     };
9863
9864     if(config && config.data){
9865         this.inlineData = config.data;
9866         delete config.data;
9867     }
9868
9869     Roo.apply(this, config);
9870     
9871     if(this.reader){ // reader passed
9872         this.reader = Roo.factory(this.reader, Roo.data);
9873         this.reader.xmodule = this.xmodule || false;
9874         if(!this.recordType){
9875             this.recordType = this.reader.recordType;
9876         }
9877         if(this.reader.onMetaChange){
9878             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9879         }
9880     }
9881
9882     if(this.recordType){
9883         this.fields = this.recordType.prototype.fields;
9884     }
9885     this.modified = [];
9886
9887     this.addEvents({
9888         /**
9889          * @event datachanged
9890          * Fires when the data cache has changed, and a widget which is using this Store
9891          * as a Record cache should refresh its view.
9892          * @param {Store} this
9893          */
9894         datachanged : true,
9895         /**
9896          * @event metachange
9897          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9898          * @param {Store} this
9899          * @param {Object} meta The JSON metadata
9900          */
9901         metachange : true,
9902         /**
9903          * @event add
9904          * Fires when Records have been added to the Store
9905          * @param {Store} this
9906          * @param {Roo.data.Record[]} records The array of Records added
9907          * @param {Number} index The index at which the record(s) were added
9908          */
9909         add : true,
9910         /**
9911          * @event remove
9912          * Fires when a Record has been removed from the Store
9913          * @param {Store} this
9914          * @param {Roo.data.Record} record The Record that was removed
9915          * @param {Number} index The index at which the record was removed
9916          */
9917         remove : true,
9918         /**
9919          * @event update
9920          * Fires when a Record has been updated
9921          * @param {Store} this
9922          * @param {Roo.data.Record} record The Record that was updated
9923          * @param {String} operation The update operation being performed.  Value may be one of:
9924          * <pre><code>
9925  Roo.data.Record.EDIT
9926  Roo.data.Record.REJECT
9927  Roo.data.Record.COMMIT
9928          * </code></pre>
9929          */
9930         update : true,
9931         /**
9932          * @event clear
9933          * Fires when the data cache has been cleared.
9934          * @param {Store} this
9935          */
9936         clear : true,
9937         /**
9938          * @event beforeload
9939          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9940          * the load action will be canceled.
9941          * @param {Store} this
9942          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9943          */
9944         beforeload : true,
9945         /**
9946          * @event beforeloadadd
9947          * Fires after a new set of Records has been loaded.
9948          * @param {Store} this
9949          * @param {Roo.data.Record[]} records The Records that were loaded
9950          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9951          */
9952         beforeloadadd : true,
9953         /**
9954          * @event load
9955          * Fires after a new set of Records has been loaded, before they are added to the store.
9956          * @param {Store} this
9957          * @param {Roo.data.Record[]} records The Records that were loaded
9958          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9959          * @params {Object} return from reader
9960          */
9961         load : true,
9962         /**
9963          * @event loadexception
9964          * Fires if an exception occurs in the Proxy during loading.
9965          * Called with the signature of the Proxy's "loadexception" event.
9966          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9967          * 
9968          * @param {Proxy} 
9969          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9970          * @param {Object} load options 
9971          * @param {Object} jsonData from your request (normally this contains the Exception)
9972          */
9973         loadexception : true
9974     });
9975     
9976     if(this.proxy){
9977         this.proxy = Roo.factory(this.proxy, Roo.data);
9978         this.proxy.xmodule = this.xmodule || false;
9979         this.relayEvents(this.proxy,  ["loadexception"]);
9980     }
9981     this.sortToggle = {};
9982     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9983
9984     Roo.data.Store.superclass.constructor.call(this);
9985
9986     if(this.inlineData){
9987         this.loadData(this.inlineData);
9988         delete this.inlineData;
9989     }
9990 };
9991
9992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9993      /**
9994     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9995     * without a remote query - used by combo/forms at present.
9996     */
9997     
9998     /**
9999     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10000     */
10001     /**
10002     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10003     */
10004     /**
10005     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10006     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10007     */
10008     /**
10009     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10010     * on any HTTP request
10011     */
10012     /**
10013     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10014     */
10015     /**
10016     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10017     */
10018     multiSort: false,
10019     /**
10020     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10021     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10022     */
10023     remoteSort : false,
10024
10025     /**
10026     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10027      * loaded or when a record is removed. (defaults to false).
10028     */
10029     pruneModifiedRecords : false,
10030
10031     // private
10032     lastOptions : null,
10033
10034     /**
10035      * Add Records to the Store and fires the add event.
10036      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10037      */
10038     add : function(records){
10039         records = [].concat(records);
10040         for(var i = 0, len = records.length; i < len; i++){
10041             records[i].join(this);
10042         }
10043         var index = this.data.length;
10044         this.data.addAll(records);
10045         this.fireEvent("add", this, records, index);
10046     },
10047
10048     /**
10049      * Remove a Record from the Store and fires the remove event.
10050      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10051      */
10052     remove : function(record){
10053         var index = this.data.indexOf(record);
10054         this.data.removeAt(index);
10055         if(this.pruneModifiedRecords){
10056             this.modified.remove(record);
10057         }
10058         this.fireEvent("remove", this, record, index);
10059     },
10060
10061     /**
10062      * Remove all Records from the Store and fires the clear event.
10063      */
10064     removeAll : function(){
10065         this.data.clear();
10066         if(this.pruneModifiedRecords){
10067             this.modified = [];
10068         }
10069         this.fireEvent("clear", this);
10070     },
10071
10072     /**
10073      * Inserts Records to the Store at the given index and fires the add event.
10074      * @param {Number} index The start index at which to insert the passed Records.
10075      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10076      */
10077     insert : function(index, records){
10078         records = [].concat(records);
10079         for(var i = 0, len = records.length; i < len; i++){
10080             this.data.insert(index, records[i]);
10081             records[i].join(this);
10082         }
10083         this.fireEvent("add", this, records, index);
10084     },
10085
10086     /**
10087      * Get the index within the cache of the passed Record.
10088      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10089      * @return {Number} The index of the passed Record. Returns -1 if not found.
10090      */
10091     indexOf : function(record){
10092         return this.data.indexOf(record);
10093     },
10094
10095     /**
10096      * Get the index within the cache of the Record with the passed id.
10097      * @param {String} id The id of the Record to find.
10098      * @return {Number} The index of the Record. Returns -1 if not found.
10099      */
10100     indexOfId : function(id){
10101         return this.data.indexOfKey(id);
10102     },
10103
10104     /**
10105      * Get the Record with the specified id.
10106      * @param {String} id The id of the Record to find.
10107      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10108      */
10109     getById : function(id){
10110         return this.data.key(id);
10111     },
10112
10113     /**
10114      * Get the Record at the specified index.
10115      * @param {Number} index The index of the Record to find.
10116      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10117      */
10118     getAt : function(index){
10119         return this.data.itemAt(index);
10120     },
10121
10122     /**
10123      * Returns a range of Records between specified indices.
10124      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10125      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10126      * @return {Roo.data.Record[]} An array of Records
10127      */
10128     getRange : function(start, end){
10129         return this.data.getRange(start, end);
10130     },
10131
10132     // private
10133     storeOptions : function(o){
10134         o = Roo.apply({}, o);
10135         delete o.callback;
10136         delete o.scope;
10137         this.lastOptions = o;
10138     },
10139
10140     /**
10141      * Loads the Record cache from the configured Proxy using the configured Reader.
10142      * <p>
10143      * If using remote paging, then the first load call must specify the <em>start</em>
10144      * and <em>limit</em> properties in the options.params property to establish the initial
10145      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10146      * <p>
10147      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10148      * and this call will return before the new data has been loaded. Perform any post-processing
10149      * in a callback function, or in a "load" event handler.</strong>
10150      * <p>
10151      * @param {Object} options An object containing properties which control loading options:<ul>
10152      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10153      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10154      * passed the following arguments:<ul>
10155      * <li>r : Roo.data.Record[]</li>
10156      * <li>options: Options object from the load call</li>
10157      * <li>success: Boolean success indicator</li></ul></li>
10158      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10159      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10160      * </ul>
10161      */
10162     load : function(options){
10163         options = options || {};
10164         if(this.fireEvent("beforeload", this, options) !== false){
10165             this.storeOptions(options);
10166             var p = Roo.apply(options.params || {}, this.baseParams);
10167             // if meta was not loaded from remote source.. try requesting it.
10168             if (!this.reader.metaFromRemote) {
10169                 p._requestMeta = 1;
10170             }
10171             if(this.sortInfo && this.remoteSort){
10172                 var pn = this.paramNames;
10173                 p[pn["sort"]] = this.sortInfo.field;
10174                 p[pn["dir"]] = this.sortInfo.direction;
10175             }
10176             if (this.multiSort) {
10177                 var pn = this.paramNames;
10178                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10179             }
10180             
10181             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10182         }
10183     },
10184
10185     /**
10186      * Reloads the Record cache from the configured Proxy using the configured Reader and
10187      * the options from the last load operation performed.
10188      * @param {Object} options (optional) An object containing properties which may override the options
10189      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10190      * the most recently used options are reused).
10191      */
10192     reload : function(options){
10193         this.load(Roo.applyIf(options||{}, this.lastOptions));
10194     },
10195
10196     // private
10197     // Called as a callback by the Reader during a load operation.
10198     loadRecords : function(o, options, success){
10199         if(!o || success === false){
10200             if(success !== false){
10201                 this.fireEvent("load", this, [], options, o);
10202             }
10203             if(options.callback){
10204                 options.callback.call(options.scope || this, [], options, false);
10205             }
10206             return;
10207         }
10208         // if data returned failure - throw an exception.
10209         if (o.success === false) {
10210             // show a message if no listener is registered.
10211             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10212                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10213             }
10214             // loadmask wil be hooked into this..
10215             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10216             return;
10217         }
10218         var r = o.records, t = o.totalRecords || r.length;
10219         
10220         this.fireEvent("beforeloadadd", this, r, options, o);
10221         
10222         if(!options || options.add !== true){
10223             if(this.pruneModifiedRecords){
10224                 this.modified = [];
10225             }
10226             for(var i = 0, len = r.length; i < len; i++){
10227                 r[i].join(this);
10228             }
10229             if(this.snapshot){
10230                 this.data = this.snapshot;
10231                 delete this.snapshot;
10232             }
10233             this.data.clear();
10234             this.data.addAll(r);
10235             this.totalLength = t;
10236             this.applySort();
10237             this.fireEvent("datachanged", this);
10238         }else{
10239             this.totalLength = Math.max(t, this.data.length+r.length);
10240             this.add(r);
10241         }
10242         this.fireEvent("load", this, r, options, o);
10243         if(options.callback){
10244             options.callback.call(options.scope || this, r, options, true);
10245         }
10246     },
10247
10248
10249     /**
10250      * Loads data from a passed data block. A Reader which understands the format of the data
10251      * must have been configured in the constructor.
10252      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10253      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10254      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10255      */
10256     loadData : function(o, append){
10257         var r = this.reader.readRecords(o);
10258         this.loadRecords(r, {add: append}, true);
10259     },
10260
10261     /**
10262      * Gets the number of cached records.
10263      * <p>
10264      * <em>If using paging, this may not be the total size of the dataset. If the data object
10265      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10266      * the data set size</em>
10267      */
10268     getCount : function(){
10269         return this.data.length || 0;
10270     },
10271
10272     /**
10273      * Gets the total number of records in the dataset as returned by the server.
10274      * <p>
10275      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10276      * the dataset size</em>
10277      */
10278     getTotalCount : function(){
10279         return this.totalLength || 0;
10280     },
10281
10282     /**
10283      * Returns the sort state of the Store as an object with two properties:
10284      * <pre><code>
10285  field {String} The name of the field by which the Records are sorted
10286  direction {String} The sort order, "ASC" or "DESC"
10287      * </code></pre>
10288      */
10289     getSortState : function(){
10290         return this.sortInfo;
10291     },
10292
10293     // private
10294     applySort : function(){
10295         if(this.sortInfo && !this.remoteSort){
10296             var s = this.sortInfo, f = s.field;
10297             var st = this.fields.get(f).sortType;
10298             var fn = function(r1, r2){
10299                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10300                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10301             };
10302             this.data.sort(s.direction, fn);
10303             if(this.snapshot && this.snapshot != this.data){
10304                 this.snapshot.sort(s.direction, fn);
10305             }
10306         }
10307     },
10308
10309     /**
10310      * Sets the default sort column and order to be used by the next load operation.
10311      * @param {String} fieldName The name of the field to sort by.
10312      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10313      */
10314     setDefaultSort : function(field, dir){
10315         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10316     },
10317
10318     /**
10319      * Sort the Records.
10320      * If remote sorting is used, the sort is performed on the server, and the cache is
10321      * reloaded. If local sorting is used, the cache is sorted internally.
10322      * @param {String} fieldName The name of the field to sort by.
10323      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10324      */
10325     sort : function(fieldName, dir){
10326         var f = this.fields.get(fieldName);
10327         if(!dir){
10328             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10329             
10330             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10331                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10332             }else{
10333                 dir = f.sortDir;
10334             }
10335         }
10336         this.sortToggle[f.name] = dir;
10337         this.sortInfo = {field: f.name, direction: dir};
10338         if(!this.remoteSort){
10339             this.applySort();
10340             this.fireEvent("datachanged", this);
10341         }else{
10342             this.load(this.lastOptions);
10343         }
10344     },
10345
10346     /**
10347      * Calls the specified function for each of the Records in the cache.
10348      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10349      * Returning <em>false</em> aborts and exits the iteration.
10350      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10351      */
10352     each : function(fn, scope){
10353         this.data.each(fn, scope);
10354     },
10355
10356     /**
10357      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10358      * (e.g., during paging).
10359      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10360      */
10361     getModifiedRecords : function(){
10362         return this.modified;
10363     },
10364
10365     // private
10366     createFilterFn : function(property, value, anyMatch){
10367         if(!value.exec){ // not a regex
10368             value = String(value);
10369             if(value.length == 0){
10370                 return false;
10371             }
10372             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10373         }
10374         return function(r){
10375             return value.test(r.data[property]);
10376         };
10377     },
10378
10379     /**
10380      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10381      * @param {String} property A field on your records
10382      * @param {Number} start The record index to start at (defaults to 0)
10383      * @param {Number} end The last record index to include (defaults to length - 1)
10384      * @return {Number} The sum
10385      */
10386     sum : function(property, start, end){
10387         var rs = this.data.items, v = 0;
10388         start = start || 0;
10389         end = (end || end === 0) ? end : rs.length-1;
10390
10391         for(var i = start; i <= end; i++){
10392             v += (rs[i].data[property] || 0);
10393         }
10394         return v;
10395     },
10396
10397     /**
10398      * Filter the records by a specified property.
10399      * @param {String} field A field on your records
10400      * @param {String/RegExp} value Either a string that the field
10401      * should start with or a RegExp to test against the field
10402      * @param {Boolean} anyMatch True to match any part not just the beginning
10403      */
10404     filter : function(property, value, anyMatch){
10405         var fn = this.createFilterFn(property, value, anyMatch);
10406         return fn ? this.filterBy(fn) : this.clearFilter();
10407     },
10408
10409     /**
10410      * Filter by a function. The specified function will be called with each
10411      * record in this data source. If the function returns true the record is included,
10412      * otherwise it is filtered.
10413      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10414      * @param {Object} scope (optional) The scope of the function (defaults to this)
10415      */
10416     filterBy : function(fn, scope){
10417         this.snapshot = this.snapshot || this.data;
10418         this.data = this.queryBy(fn, scope||this);
10419         this.fireEvent("datachanged", this);
10420     },
10421
10422     /**
10423      * Query the records by a specified property.
10424      * @param {String} field A field on your records
10425      * @param {String/RegExp} value Either a string that the field
10426      * should start with or a RegExp to test against the field
10427      * @param {Boolean} anyMatch True to match any part not just the beginning
10428      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10429      */
10430     query : function(property, value, anyMatch){
10431         var fn = this.createFilterFn(property, value, anyMatch);
10432         return fn ? this.queryBy(fn) : this.data.clone();
10433     },
10434
10435     /**
10436      * Query by a function. The specified function will be called with each
10437      * record in this data source. If the function returns true the record is included
10438      * in the results.
10439      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10440      * @param {Object} scope (optional) The scope of the function (defaults to this)
10441       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10442      **/
10443     queryBy : function(fn, scope){
10444         var data = this.snapshot || this.data;
10445         return data.filterBy(fn, scope||this);
10446     },
10447
10448     /**
10449      * Collects unique values for a particular dataIndex from this store.
10450      * @param {String} dataIndex The property to collect
10451      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10452      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10453      * @return {Array} An array of the unique values
10454      **/
10455     collect : function(dataIndex, allowNull, bypassFilter){
10456         var d = (bypassFilter === true && this.snapshot) ?
10457                 this.snapshot.items : this.data.items;
10458         var v, sv, r = [], l = {};
10459         for(var i = 0, len = d.length; i < len; i++){
10460             v = d[i].data[dataIndex];
10461             sv = String(v);
10462             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10463                 l[sv] = true;
10464                 r[r.length] = v;
10465             }
10466         }
10467         return r;
10468     },
10469
10470     /**
10471      * Revert to a view of the Record cache with no filtering applied.
10472      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10473      */
10474     clearFilter : function(suppressEvent){
10475         if(this.snapshot && this.snapshot != this.data){
10476             this.data = this.snapshot;
10477             delete this.snapshot;
10478             if(suppressEvent !== true){
10479                 this.fireEvent("datachanged", this);
10480             }
10481         }
10482     },
10483
10484     // private
10485     afterEdit : function(record){
10486         if(this.modified.indexOf(record) == -1){
10487             this.modified.push(record);
10488         }
10489         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10490     },
10491     
10492     // private
10493     afterReject : function(record){
10494         this.modified.remove(record);
10495         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10496     },
10497
10498     // private
10499     afterCommit : function(record){
10500         this.modified.remove(record);
10501         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10502     },
10503
10504     /**
10505      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10506      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10507      */
10508     commitChanges : function(){
10509         var m = this.modified.slice(0);
10510         this.modified = [];
10511         for(var i = 0, len = m.length; i < len; i++){
10512             m[i].commit();
10513         }
10514     },
10515
10516     /**
10517      * Cancel outstanding changes on all changed records.
10518      */
10519     rejectChanges : function(){
10520         var m = this.modified.slice(0);
10521         this.modified = [];
10522         for(var i = 0, len = m.length; i < len; i++){
10523             m[i].reject();
10524         }
10525     },
10526
10527     onMetaChange : function(meta, rtype, o){
10528         this.recordType = rtype;
10529         this.fields = rtype.prototype.fields;
10530         delete this.snapshot;
10531         this.sortInfo = meta.sortInfo || this.sortInfo;
10532         this.modified = [];
10533         this.fireEvent('metachange', this, this.reader.meta);
10534     },
10535     
10536     moveIndex : function(data, type)
10537     {
10538         var index = this.indexOf(data);
10539         
10540         var newIndex = index + type;
10541         
10542         this.remove(data);
10543         
10544         this.insert(newIndex, data);
10545         
10546     }
10547 });/*
10548  * Based on:
10549  * Ext JS Library 1.1.1
10550  * Copyright(c) 2006-2007, Ext JS, LLC.
10551  *
10552  * Originally Released Under LGPL - original licence link has changed is not relivant.
10553  *
10554  * Fork - LGPL
10555  * <script type="text/javascript">
10556  */
10557
10558 /**
10559  * @class Roo.data.SimpleStore
10560  * @extends Roo.data.Store
10561  * Small helper class to make creating Stores from Array data easier.
10562  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10563  * @cfg {Array} fields An array of field definition objects, or field name strings.
10564  * @cfg {Array} data The multi-dimensional array of data
10565  * @constructor
10566  * @param {Object} config
10567  */
10568 Roo.data.SimpleStore = function(config){
10569     Roo.data.SimpleStore.superclass.constructor.call(this, {
10570         isLocal : true,
10571         reader: new Roo.data.ArrayReader({
10572                 id: config.id
10573             },
10574             Roo.data.Record.create(config.fields)
10575         ),
10576         proxy : new Roo.data.MemoryProxy(config.data)
10577     });
10578     this.load();
10579 };
10580 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10581  * Based on:
10582  * Ext JS Library 1.1.1
10583  * Copyright(c) 2006-2007, Ext JS, LLC.
10584  *
10585  * Originally Released Under LGPL - original licence link has changed is not relivant.
10586  *
10587  * Fork - LGPL
10588  * <script type="text/javascript">
10589  */
10590
10591 /**
10592 /**
10593  * @extends Roo.data.Store
10594  * @class Roo.data.JsonStore
10595  * Small helper class to make creating Stores for JSON data easier. <br/>
10596 <pre><code>
10597 var store = new Roo.data.JsonStore({
10598     url: 'get-images.php',
10599     root: 'images',
10600     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10601 });
10602 </code></pre>
10603  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10604  * JsonReader and HttpProxy (unless inline data is provided).</b>
10605  * @cfg {Array} fields An array of field definition objects, or field name strings.
10606  * @constructor
10607  * @param {Object} config
10608  */
10609 Roo.data.JsonStore = function(c){
10610     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10611         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10612         reader: new Roo.data.JsonReader(c, c.fields)
10613     }));
10614 };
10615 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10616  * Based on:
10617  * Ext JS Library 1.1.1
10618  * Copyright(c) 2006-2007, Ext JS, LLC.
10619  *
10620  * Originally Released Under LGPL - original licence link has changed is not relivant.
10621  *
10622  * Fork - LGPL
10623  * <script type="text/javascript">
10624  */
10625
10626  
10627 Roo.data.Field = function(config){
10628     if(typeof config == "string"){
10629         config = {name: config};
10630     }
10631     Roo.apply(this, config);
10632     
10633     if(!this.type){
10634         this.type = "auto";
10635     }
10636     
10637     var st = Roo.data.SortTypes;
10638     // named sortTypes are supported, here we look them up
10639     if(typeof this.sortType == "string"){
10640         this.sortType = st[this.sortType];
10641     }
10642     
10643     // set default sortType for strings and dates
10644     if(!this.sortType){
10645         switch(this.type){
10646             case "string":
10647                 this.sortType = st.asUCString;
10648                 break;
10649             case "date":
10650                 this.sortType = st.asDate;
10651                 break;
10652             default:
10653                 this.sortType = st.none;
10654         }
10655     }
10656
10657     // define once
10658     var stripRe = /[\$,%]/g;
10659
10660     // prebuilt conversion function for this field, instead of
10661     // switching every time we're reading a value
10662     if(!this.convert){
10663         var cv, dateFormat = this.dateFormat;
10664         switch(this.type){
10665             case "":
10666             case "auto":
10667             case undefined:
10668                 cv = function(v){ return v; };
10669                 break;
10670             case "string":
10671                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10672                 break;
10673             case "int":
10674                 cv = function(v){
10675                     return v !== undefined && v !== null && v !== '' ?
10676                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10677                     };
10678                 break;
10679             case "float":
10680                 cv = function(v){
10681                     return v !== undefined && v !== null && v !== '' ?
10682                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10683                     };
10684                 break;
10685             case "bool":
10686             case "boolean":
10687                 cv = function(v){ return v === true || v === "true" || v == 1; };
10688                 break;
10689             case "date":
10690                 cv = function(v){
10691                     if(!v){
10692                         return '';
10693                     }
10694                     if(v instanceof Date){
10695                         return v;
10696                     }
10697                     if(dateFormat){
10698                         if(dateFormat == "timestamp"){
10699                             return new Date(v*1000);
10700                         }
10701                         return Date.parseDate(v, dateFormat);
10702                     }
10703                     var parsed = Date.parse(v);
10704                     return parsed ? new Date(parsed) : null;
10705                 };
10706              break;
10707             
10708         }
10709         this.convert = cv;
10710     }
10711 };
10712
10713 Roo.data.Field.prototype = {
10714     dateFormat: null,
10715     defaultValue: "",
10716     mapping: null,
10717     sortType : null,
10718     sortDir : "ASC"
10719 };/*
10720  * Based on:
10721  * Ext JS Library 1.1.1
10722  * Copyright(c) 2006-2007, Ext JS, LLC.
10723  *
10724  * Originally Released Under LGPL - original licence link has changed is not relivant.
10725  *
10726  * Fork - LGPL
10727  * <script type="text/javascript">
10728  */
10729  
10730 // Base class for reading structured data from a data source.  This class is intended to be
10731 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10732
10733 /**
10734  * @class Roo.data.DataReader
10735  * Base class for reading structured data from a data source.  This class is intended to be
10736  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10737  */
10738
10739 Roo.data.DataReader = function(meta, recordType){
10740     
10741     this.meta = meta;
10742     
10743     this.recordType = recordType instanceof Array ? 
10744         Roo.data.Record.create(recordType) : recordType;
10745 };
10746
10747 Roo.data.DataReader.prototype = {
10748      /**
10749      * Create an empty record
10750      * @param {Object} data (optional) - overlay some values
10751      * @return {Roo.data.Record} record created.
10752      */
10753     newRow :  function(d) {
10754         var da =  {};
10755         this.recordType.prototype.fields.each(function(c) {
10756             switch( c.type) {
10757                 case 'int' : da[c.name] = 0; break;
10758                 case 'date' : da[c.name] = new Date(); break;
10759                 case 'float' : da[c.name] = 0.0; break;
10760                 case 'boolean' : da[c.name] = false; break;
10761                 default : da[c.name] = ""; break;
10762             }
10763             
10764         });
10765         return new this.recordType(Roo.apply(da, d));
10766     }
10767     
10768 };/*
10769  * Based on:
10770  * Ext JS Library 1.1.1
10771  * Copyright(c) 2006-2007, Ext JS, LLC.
10772  *
10773  * Originally Released Under LGPL - original licence link has changed is not relivant.
10774  *
10775  * Fork - LGPL
10776  * <script type="text/javascript">
10777  */
10778
10779 /**
10780  * @class Roo.data.DataProxy
10781  * @extends Roo.data.Observable
10782  * This class is an abstract base class for implementations which provide retrieval of
10783  * unformatted data objects.<br>
10784  * <p>
10785  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10786  * (of the appropriate type which knows how to parse the data object) to provide a block of
10787  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10788  * <p>
10789  * Custom implementations must implement the load method as described in
10790  * {@link Roo.data.HttpProxy#load}.
10791  */
10792 Roo.data.DataProxy = function(){
10793     this.addEvents({
10794         /**
10795          * @event beforeload
10796          * Fires before a network request is made to retrieve a data object.
10797          * @param {Object} This DataProxy object.
10798          * @param {Object} params The params parameter to the load function.
10799          */
10800         beforeload : true,
10801         /**
10802          * @event load
10803          * Fires before the load method's callback is called.
10804          * @param {Object} This DataProxy object.
10805          * @param {Object} o The data object.
10806          * @param {Object} arg The callback argument object passed to the load function.
10807          */
10808         load : true,
10809         /**
10810          * @event loadexception
10811          * Fires if an Exception occurs during data retrieval.
10812          * @param {Object} This DataProxy object.
10813          * @param {Object} o The data object.
10814          * @param {Object} arg The callback argument object passed to the load function.
10815          * @param {Object} e The Exception.
10816          */
10817         loadexception : true
10818     });
10819     Roo.data.DataProxy.superclass.constructor.call(this);
10820 };
10821
10822 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10823
10824     /**
10825      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10826      */
10827 /*
10828  * Based on:
10829  * Ext JS Library 1.1.1
10830  * Copyright(c) 2006-2007, Ext JS, LLC.
10831  *
10832  * Originally Released Under LGPL - original licence link has changed is not relivant.
10833  *
10834  * Fork - LGPL
10835  * <script type="text/javascript">
10836  */
10837 /**
10838  * @class Roo.data.MemoryProxy
10839  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10840  * to the Reader when its load method is called.
10841  * @constructor
10842  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10843  */
10844 Roo.data.MemoryProxy = function(data){
10845     if (data.data) {
10846         data = data.data;
10847     }
10848     Roo.data.MemoryProxy.superclass.constructor.call(this);
10849     this.data = data;
10850 };
10851
10852 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10853     /**
10854      * Load data from the requested source (in this case an in-memory
10855      * data object passed to the constructor), read the data object into
10856      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10857      * process that block using the passed callback.
10858      * @param {Object} params This parameter is not used by the MemoryProxy class.
10859      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10860      * object into a block of Roo.data.Records.
10861      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10862      * The function must be passed <ul>
10863      * <li>The Record block object</li>
10864      * <li>The "arg" argument from the load function</li>
10865      * <li>A boolean success indicator</li>
10866      * </ul>
10867      * @param {Object} scope The scope in which to call the callback
10868      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10869      */
10870     load : function(params, reader, callback, scope, arg){
10871         params = params || {};
10872         var result;
10873         try {
10874             result = reader.readRecords(this.data);
10875         }catch(e){
10876             this.fireEvent("loadexception", this, arg, null, e);
10877             callback.call(scope, null, arg, false);
10878             return;
10879         }
10880         callback.call(scope, result, arg, true);
10881     },
10882     
10883     // private
10884     update : function(params, records){
10885         
10886     }
10887 });/*
10888  * Based on:
10889  * Ext JS Library 1.1.1
10890  * Copyright(c) 2006-2007, Ext JS, LLC.
10891  *
10892  * Originally Released Under LGPL - original licence link has changed is not relivant.
10893  *
10894  * Fork - LGPL
10895  * <script type="text/javascript">
10896  */
10897 /**
10898  * @class Roo.data.HttpProxy
10899  * @extends Roo.data.DataProxy
10900  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10901  * configured to reference a certain URL.<br><br>
10902  * <p>
10903  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10904  * from which the running page was served.<br><br>
10905  * <p>
10906  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10907  * <p>
10908  * Be aware that to enable the browser to parse an XML document, the server must set
10909  * the Content-Type header in the HTTP response to "text/xml".
10910  * @constructor
10911  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10912  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10913  * will be used to make the request.
10914  */
10915 Roo.data.HttpProxy = function(conn){
10916     Roo.data.HttpProxy.superclass.constructor.call(this);
10917     // is conn a conn config or a real conn?
10918     this.conn = conn;
10919     this.useAjax = !conn || !conn.events;
10920   
10921 };
10922
10923 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10924     // thse are take from connection...
10925     
10926     /**
10927      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10928      */
10929     /**
10930      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10931      * extra parameters to each request made by this object. (defaults to undefined)
10932      */
10933     /**
10934      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10935      *  to each request made by this object. (defaults to undefined)
10936      */
10937     /**
10938      * @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)
10939      */
10940     /**
10941      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10942      */
10943      /**
10944      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10945      * @type Boolean
10946      */
10947   
10948
10949     /**
10950      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10951      * @type Boolean
10952      */
10953     /**
10954      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10955      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10956      * a finer-grained basis than the DataProxy events.
10957      */
10958     getConnection : function(){
10959         return this.useAjax ? Roo.Ajax : this.conn;
10960     },
10961
10962     /**
10963      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10964      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10965      * process that block using the passed callback.
10966      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10967      * for the request to the remote server.
10968      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10969      * object into a block of Roo.data.Records.
10970      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10971      * The function must be passed <ul>
10972      * <li>The Record block object</li>
10973      * <li>The "arg" argument from the load function</li>
10974      * <li>A boolean success indicator</li>
10975      * </ul>
10976      * @param {Object} scope The scope in which to call the callback
10977      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10978      */
10979     load : function(params, reader, callback, scope, arg){
10980         if(this.fireEvent("beforeload", this, params) !== false){
10981             var  o = {
10982                 params : params || {},
10983                 request: {
10984                     callback : callback,
10985                     scope : scope,
10986                     arg : arg
10987                 },
10988                 reader: reader,
10989                 callback : this.loadResponse,
10990                 scope: this
10991             };
10992             if(this.useAjax){
10993                 Roo.applyIf(o, this.conn);
10994                 if(this.activeRequest){
10995                     Roo.Ajax.abort(this.activeRequest);
10996                 }
10997                 this.activeRequest = Roo.Ajax.request(o);
10998             }else{
10999                 this.conn.request(o);
11000             }
11001         }else{
11002             callback.call(scope||this, null, arg, false);
11003         }
11004     },
11005
11006     // private
11007     loadResponse : function(o, success, response){
11008         delete this.activeRequest;
11009         if(!success){
11010             this.fireEvent("loadexception", this, o, response);
11011             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11012             return;
11013         }
11014         var result;
11015         try {
11016             result = o.reader.read(response);
11017         }catch(e){
11018             this.fireEvent("loadexception", this, o, response, e);
11019             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11020             return;
11021         }
11022         
11023         this.fireEvent("load", this, o, o.request.arg);
11024         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11025     },
11026
11027     // private
11028     update : function(dataSet){
11029
11030     },
11031
11032     // private
11033     updateResponse : function(dataSet){
11034
11035     }
11036 });/*
11037  * Based on:
11038  * Ext JS Library 1.1.1
11039  * Copyright(c) 2006-2007, Ext JS, LLC.
11040  *
11041  * Originally Released Under LGPL - original licence link has changed is not relivant.
11042  *
11043  * Fork - LGPL
11044  * <script type="text/javascript">
11045  */
11046
11047 /**
11048  * @class Roo.data.ScriptTagProxy
11049  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11050  * other than the originating domain of the running page.<br><br>
11051  * <p>
11052  * <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
11053  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11054  * <p>
11055  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11056  * source code that is used as the source inside a &lt;script> tag.<br><br>
11057  * <p>
11058  * In order for the browser to process the returned data, the server must wrap the data object
11059  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11060  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11061  * depending on whether the callback name was passed:
11062  * <p>
11063  * <pre><code>
11064 boolean scriptTag = false;
11065 String cb = request.getParameter("callback");
11066 if (cb != null) {
11067     scriptTag = true;
11068     response.setContentType("text/javascript");
11069 } else {
11070     response.setContentType("application/x-json");
11071 }
11072 Writer out = response.getWriter();
11073 if (scriptTag) {
11074     out.write(cb + "(");
11075 }
11076 out.print(dataBlock.toJsonString());
11077 if (scriptTag) {
11078     out.write(");");
11079 }
11080 </pre></code>
11081  *
11082  * @constructor
11083  * @param {Object} config A configuration object.
11084  */
11085 Roo.data.ScriptTagProxy = function(config){
11086     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11087     Roo.apply(this, config);
11088     this.head = document.getElementsByTagName("head")[0];
11089 };
11090
11091 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11092
11093 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11094     /**
11095      * @cfg {String} url The URL from which to request the data object.
11096      */
11097     /**
11098      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11099      */
11100     timeout : 30000,
11101     /**
11102      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11103      * the server the name of the callback function set up by the load call to process the returned data object.
11104      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11105      * javascript output which calls this named function passing the data object as its only parameter.
11106      */
11107     callbackParam : "callback",
11108     /**
11109      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11110      * name to the request.
11111      */
11112     nocache : true,
11113
11114     /**
11115      * Load data from the configured URL, read the data object into
11116      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11117      * process that block using the passed callback.
11118      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11119      * for the request to the remote server.
11120      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11121      * object into a block of Roo.data.Records.
11122      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11123      * The function must be passed <ul>
11124      * <li>The Record block object</li>
11125      * <li>The "arg" argument from the load function</li>
11126      * <li>A boolean success indicator</li>
11127      * </ul>
11128      * @param {Object} scope The scope in which to call the callback
11129      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11130      */
11131     load : function(params, reader, callback, scope, arg){
11132         if(this.fireEvent("beforeload", this, params) !== false){
11133
11134             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11135
11136             var url = this.url;
11137             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11138             if(this.nocache){
11139                 url += "&_dc=" + (new Date().getTime());
11140             }
11141             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11142             var trans = {
11143                 id : transId,
11144                 cb : "stcCallback"+transId,
11145                 scriptId : "stcScript"+transId,
11146                 params : params,
11147                 arg : arg,
11148                 url : url,
11149                 callback : callback,
11150                 scope : scope,
11151                 reader : reader
11152             };
11153             var conn = this;
11154
11155             window[trans.cb] = function(o){
11156                 conn.handleResponse(o, trans);
11157             };
11158
11159             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11160
11161             if(this.autoAbort !== false){
11162                 this.abort();
11163             }
11164
11165             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11166
11167             var script = document.createElement("script");
11168             script.setAttribute("src", url);
11169             script.setAttribute("type", "text/javascript");
11170             script.setAttribute("id", trans.scriptId);
11171             this.head.appendChild(script);
11172
11173             this.trans = trans;
11174         }else{
11175             callback.call(scope||this, null, arg, false);
11176         }
11177     },
11178
11179     // private
11180     isLoading : function(){
11181         return this.trans ? true : false;
11182     },
11183
11184     /**
11185      * Abort the current server request.
11186      */
11187     abort : function(){
11188         if(this.isLoading()){
11189             this.destroyTrans(this.trans);
11190         }
11191     },
11192
11193     // private
11194     destroyTrans : function(trans, isLoaded){
11195         this.head.removeChild(document.getElementById(trans.scriptId));
11196         clearTimeout(trans.timeoutId);
11197         if(isLoaded){
11198             window[trans.cb] = undefined;
11199             try{
11200                 delete window[trans.cb];
11201             }catch(e){}
11202         }else{
11203             // if hasn't been loaded, wait for load to remove it to prevent script error
11204             window[trans.cb] = function(){
11205                 window[trans.cb] = undefined;
11206                 try{
11207                     delete window[trans.cb];
11208                 }catch(e){}
11209             };
11210         }
11211     },
11212
11213     // private
11214     handleResponse : function(o, trans){
11215         this.trans = false;
11216         this.destroyTrans(trans, true);
11217         var result;
11218         try {
11219             result = trans.reader.readRecords(o);
11220         }catch(e){
11221             this.fireEvent("loadexception", this, o, trans.arg, e);
11222             trans.callback.call(trans.scope||window, null, trans.arg, false);
11223             return;
11224         }
11225         this.fireEvent("load", this, o, trans.arg);
11226         trans.callback.call(trans.scope||window, result, trans.arg, true);
11227     },
11228
11229     // private
11230     handleFailure : function(trans){
11231         this.trans = false;
11232         this.destroyTrans(trans, false);
11233         this.fireEvent("loadexception", this, null, trans.arg);
11234         trans.callback.call(trans.scope||window, null, trans.arg, false);
11235     }
11236 });/*
11237  * Based on:
11238  * Ext JS Library 1.1.1
11239  * Copyright(c) 2006-2007, Ext JS, LLC.
11240  *
11241  * Originally Released Under LGPL - original licence link has changed is not relivant.
11242  *
11243  * Fork - LGPL
11244  * <script type="text/javascript">
11245  */
11246
11247 /**
11248  * @class Roo.data.JsonReader
11249  * @extends Roo.data.DataReader
11250  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11251  * based on mappings in a provided Roo.data.Record constructor.
11252  * 
11253  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11254  * in the reply previously. 
11255  * 
11256  * <p>
11257  * Example code:
11258  * <pre><code>
11259 var RecordDef = Roo.data.Record.create([
11260     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11261     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11262 ]);
11263 var myReader = new Roo.data.JsonReader({
11264     totalProperty: "results",    // The property which contains the total dataset size (optional)
11265     root: "rows",                // The property which contains an Array of row objects
11266     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11267 }, RecordDef);
11268 </code></pre>
11269  * <p>
11270  * This would consume a JSON file like this:
11271  * <pre><code>
11272 { 'results': 2, 'rows': [
11273     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11274     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11275 }
11276 </code></pre>
11277  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11278  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11279  * paged from the remote server.
11280  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11281  * @cfg {String} root name of the property which contains the Array of row objects.
11282  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11283  * @cfg {Array} fields Array of field definition objects
11284  * @constructor
11285  * Create a new JsonReader
11286  * @param {Object} meta Metadata configuration options
11287  * @param {Object} recordType Either an Array of field definition objects,
11288  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11289  */
11290 Roo.data.JsonReader = function(meta, recordType){
11291     
11292     meta = meta || {};
11293     // set some defaults:
11294     Roo.applyIf(meta, {
11295         totalProperty: 'total',
11296         successProperty : 'success',
11297         root : 'data',
11298         id : 'id'
11299     });
11300     
11301     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11302 };
11303 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11304     
11305     /**
11306      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11307      * Used by Store query builder to append _requestMeta to params.
11308      * 
11309      */
11310     metaFromRemote : false,
11311     /**
11312      * This method is only used by a DataProxy which has retrieved data from a remote server.
11313      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11314      * @return {Object} data A data block which is used by an Roo.data.Store object as
11315      * a cache of Roo.data.Records.
11316      */
11317     read : function(response){
11318         var json = response.responseText;
11319        
11320         var o = /* eval:var:o */ eval("("+json+")");
11321         if(!o) {
11322             throw {message: "JsonReader.read: Json object not found"};
11323         }
11324         
11325         if(o.metaData){
11326             
11327             delete this.ef;
11328             this.metaFromRemote = true;
11329             this.meta = o.metaData;
11330             this.recordType = Roo.data.Record.create(o.metaData.fields);
11331             this.onMetaChange(this.meta, this.recordType, o);
11332         }
11333         return this.readRecords(o);
11334     },
11335
11336     // private function a store will implement
11337     onMetaChange : function(meta, recordType, o){
11338
11339     },
11340
11341     /**
11342          * @ignore
11343          */
11344     simpleAccess: function(obj, subsc) {
11345         return obj[subsc];
11346     },
11347
11348         /**
11349          * @ignore
11350          */
11351     getJsonAccessor: function(){
11352         var re = /[\[\.]/;
11353         return function(expr) {
11354             try {
11355                 return(re.test(expr))
11356                     ? new Function("obj", "return obj." + expr)
11357                     : function(obj){
11358                         return obj[expr];
11359                     };
11360             } catch(e){}
11361             return Roo.emptyFn;
11362         };
11363     }(),
11364
11365     /**
11366      * Create a data block containing Roo.data.Records from an XML document.
11367      * @param {Object} o An object which contains an Array of row objects in the property specified
11368      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11369      * which contains the total size of the dataset.
11370      * @return {Object} data A data block which is used by an Roo.data.Store object as
11371      * a cache of Roo.data.Records.
11372      */
11373     readRecords : function(o){
11374         /**
11375          * After any data loads, the raw JSON data is available for further custom processing.
11376          * @type Object
11377          */
11378         this.o = o;
11379         var s = this.meta, Record = this.recordType,
11380             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11381
11382 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11383         if (!this.ef) {
11384             if(s.totalProperty) {
11385                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11386                 }
11387                 if(s.successProperty) {
11388                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11389                 }
11390                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11391                 if (s.id) {
11392                         var g = this.getJsonAccessor(s.id);
11393                         this.getId = function(rec) {
11394                                 var r = g(rec);  
11395                                 return (r === undefined || r === "") ? null : r;
11396                         };
11397                 } else {
11398                         this.getId = function(){return null;};
11399                 }
11400             this.ef = [];
11401             for(var jj = 0; jj < fl; jj++){
11402                 f = fi[jj];
11403                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11404                 this.ef[jj] = this.getJsonAccessor(map);
11405             }
11406         }
11407
11408         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11409         if(s.totalProperty){
11410             var vt = parseInt(this.getTotal(o), 10);
11411             if(!isNaN(vt)){
11412                 totalRecords = vt;
11413             }
11414         }
11415         if(s.successProperty){
11416             var vs = this.getSuccess(o);
11417             if(vs === false || vs === 'false'){
11418                 success = false;
11419             }
11420         }
11421         var records = [];
11422         for(var i = 0; i < c; i++){
11423                 var n = root[i];
11424             var values = {};
11425             var id = this.getId(n);
11426             for(var j = 0; j < fl; j++){
11427                 f = fi[j];
11428             var v = this.ef[j](n);
11429             if (!f.convert) {
11430                 Roo.log('missing convert for ' + f.name);
11431                 Roo.log(f);
11432                 continue;
11433             }
11434             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11435             }
11436             var record = new Record(values, id);
11437             record.json = n;
11438             records[i] = record;
11439         }
11440         return {
11441             raw : o,
11442             success : success,
11443             records : records,
11444             totalRecords : totalRecords
11445         };
11446     }
11447 });/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458 /**
11459  * @class Roo.data.ArrayReader
11460  * @extends Roo.data.DataReader
11461  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11462  * Each element of that Array represents a row of data fields. The
11463  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11464  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11465  * <p>
11466  * Example code:.
11467  * <pre><code>
11468 var RecordDef = Roo.data.Record.create([
11469     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11470     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11471 ]);
11472 var myReader = new Roo.data.ArrayReader({
11473     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11474 }, RecordDef);
11475 </code></pre>
11476  * <p>
11477  * This would consume an Array like this:
11478  * <pre><code>
11479 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11480   </code></pre>
11481  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11482  * @constructor
11483  * Create a new JsonReader
11484  * @param {Object} meta Metadata configuration options.
11485  * @param {Object} recordType Either an Array of field definition objects
11486  * as specified to {@link Roo.data.Record#create},
11487  * or an {@link Roo.data.Record} object
11488  * created using {@link Roo.data.Record#create}.
11489  */
11490 Roo.data.ArrayReader = function(meta, recordType){
11491     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11492 };
11493
11494 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11495     /**
11496      * Create a data block containing Roo.data.Records from an XML document.
11497      * @param {Object} o An Array of row objects which represents the dataset.
11498      * @return {Object} data A data block which is used by an Roo.data.Store object as
11499      * a cache of Roo.data.Records.
11500      */
11501     readRecords : function(o){
11502         var sid = this.meta ? this.meta.id : null;
11503         var recordType = this.recordType, fields = recordType.prototype.fields;
11504         var records = [];
11505         var root = o;
11506             for(var i = 0; i < root.length; i++){
11507                     var n = root[i];
11508                 var values = {};
11509                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11510                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11511                 var f = fields.items[j];
11512                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11513                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11514                 v = f.convert(v);
11515                 values[f.name] = v;
11516             }
11517                 var record = new recordType(values, id);
11518                 record.json = n;
11519                 records[records.length] = record;
11520             }
11521             return {
11522                 records : records,
11523                 totalRecords : records.length
11524             };
11525     }
11526 });/*
11527  * - LGPL
11528  * * 
11529  */
11530
11531 /**
11532  * @class Roo.bootstrap.ComboBox
11533  * @extends Roo.bootstrap.TriggerField
11534  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11535  * @cfg {Boolean} append (true|false) default false
11536  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11537  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11538  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11539  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11540  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11541  * @cfg {Boolean} animate default true
11542  * @cfg {Boolean} emptyResultText only for touch device
11543  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11544  * @constructor
11545  * Create a new ComboBox.
11546  * @param {Object} config Configuration options
11547  */
11548 Roo.bootstrap.ComboBox = function(config){
11549     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11550     this.addEvents({
11551         /**
11552          * @event expand
11553          * Fires when the dropdown list is expanded
11554              * @param {Roo.bootstrap.ComboBox} combo This combo box
11555              */
11556         'expand' : true,
11557         /**
11558          * @event collapse
11559          * Fires when the dropdown list is collapsed
11560              * @param {Roo.bootstrap.ComboBox} combo This combo box
11561              */
11562         'collapse' : true,
11563         /**
11564          * @event beforeselect
11565          * Fires before a list item is selected. Return false to cancel the selection.
11566              * @param {Roo.bootstrap.ComboBox} combo This combo box
11567              * @param {Roo.data.Record} record The data record returned from the underlying store
11568              * @param {Number} index The index of the selected item in the dropdown list
11569              */
11570         'beforeselect' : true,
11571         /**
11572          * @event select
11573          * Fires when a list item is selected
11574              * @param {Roo.bootstrap.ComboBox} combo This combo box
11575              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11576              * @param {Number} index The index of the selected item in the dropdown list
11577              */
11578         'select' : true,
11579         /**
11580          * @event beforequery
11581          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11582          * The event object passed has these properties:
11583              * @param {Roo.bootstrap.ComboBox} combo This combo box
11584              * @param {String} query The query
11585              * @param {Boolean} forceAll true to force "all" query
11586              * @param {Boolean} cancel true to cancel the query
11587              * @param {Object} e The query event object
11588              */
11589         'beforequery': true,
11590          /**
11591          * @event add
11592          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11593              * @param {Roo.bootstrap.ComboBox} combo This combo box
11594              */
11595         'add' : true,
11596         /**
11597          * @event edit
11598          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11599              * @param {Roo.bootstrap.ComboBox} combo This combo box
11600              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11601              */
11602         'edit' : true,
11603         /**
11604          * @event remove
11605          * Fires when the remove value from the combobox array
11606              * @param {Roo.bootstrap.ComboBox} combo This combo box
11607              */
11608         'remove' : true,
11609         /**
11610          * @event specialfilter
11611          * Fires when specialfilter
11612             * @param {Roo.bootstrap.ComboBox} combo This combo box
11613             */
11614         'specialfilter' : true,
11615         /**
11616          * @event tick
11617          * Fires when tick the element
11618             * @param {Roo.bootstrap.ComboBox} combo This combo box
11619             */
11620         'tick' : true,
11621         /**
11622          * @event touchviewdisplay
11623          * Fires when touch view require special display (default is using displayField)
11624             * @param {Roo.bootstrap.ComboBox} combo This combo box
11625             * @param {Object} cfg set html .
11626             */
11627         'touchviewdisplay' : true
11628         
11629     });
11630     
11631     this.item = [];
11632     this.tickItems = [];
11633     
11634     this.selectedIndex = -1;
11635     if(this.mode == 'local'){
11636         if(config.queryDelay === undefined){
11637             this.queryDelay = 10;
11638         }
11639         if(config.minChars === undefined){
11640             this.minChars = 0;
11641         }
11642     }
11643 };
11644
11645 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11646      
11647     /**
11648      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11649      * rendering into an Roo.Editor, defaults to false)
11650      */
11651     /**
11652      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11653      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11654      */
11655     /**
11656      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11657      */
11658     /**
11659      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11660      * the dropdown list (defaults to undefined, with no header element)
11661      */
11662
11663      /**
11664      * @cfg {String/Roo.Template} tpl The template to use to render the output
11665      */
11666      
11667      /**
11668      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11669      */
11670     listWidth: undefined,
11671     /**
11672      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11673      * mode = 'remote' or 'text' if mode = 'local')
11674      */
11675     displayField: undefined,
11676     
11677     /**
11678      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11679      * mode = 'remote' or 'value' if mode = 'local'). 
11680      * Note: use of a valueField requires the user make a selection
11681      * in order for a value to be mapped.
11682      */
11683     valueField: undefined,
11684     
11685     
11686     /**
11687      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11688      * field's data value (defaults to the underlying DOM element's name)
11689      */
11690     hiddenName: undefined,
11691     /**
11692      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11693      */
11694     listClass: '',
11695     /**
11696      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11697      */
11698     selectedClass: 'active',
11699     
11700     /**
11701      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11702      */
11703     shadow:'sides',
11704     /**
11705      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11706      * anchor positions (defaults to 'tl-bl')
11707      */
11708     listAlign: 'tl-bl?',
11709     /**
11710      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11711      */
11712     maxHeight: 300,
11713     /**
11714      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11715      * query specified by the allQuery config option (defaults to 'query')
11716      */
11717     triggerAction: 'query',
11718     /**
11719      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11720      * (defaults to 4, does not apply if editable = false)
11721      */
11722     minChars : 4,
11723     /**
11724      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11725      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11726      */
11727     typeAhead: false,
11728     /**
11729      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11730      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11731      */
11732     queryDelay: 500,
11733     /**
11734      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11735      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11736      */
11737     pageSize: 0,
11738     /**
11739      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11740      * when editable = true (defaults to false)
11741      */
11742     selectOnFocus:false,
11743     /**
11744      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11745      */
11746     queryParam: 'query',
11747     /**
11748      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11749      * when mode = 'remote' (defaults to 'Loading...')
11750      */
11751     loadingText: 'Loading...',
11752     /**
11753      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11754      */
11755     resizable: false,
11756     /**
11757      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11758      */
11759     handleHeight : 8,
11760     /**
11761      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11762      * traditional select (defaults to true)
11763      */
11764     editable: true,
11765     /**
11766      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11767      */
11768     allQuery: '',
11769     /**
11770      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11771      */
11772     mode: 'remote',
11773     /**
11774      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11775      * listWidth has a higher value)
11776      */
11777     minListWidth : 70,
11778     /**
11779      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11780      * allow the user to set arbitrary text into the field (defaults to false)
11781      */
11782     forceSelection:false,
11783     /**
11784      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11785      * if typeAhead = true (defaults to 250)
11786      */
11787     typeAheadDelay : 250,
11788     /**
11789      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11790      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11791      */
11792     valueNotFoundText : undefined,
11793     /**
11794      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11795      */
11796     blockFocus : false,
11797     
11798     /**
11799      * @cfg {Boolean} disableClear Disable showing of clear button.
11800      */
11801     disableClear : false,
11802     /**
11803      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11804      */
11805     alwaysQuery : false,
11806     
11807     /**
11808      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11809      */
11810     multiple : false,
11811     
11812     /**
11813      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11814      */
11815     invalidClass : "has-warning",
11816     
11817     /**
11818      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11819      */
11820     validClass : "has-success",
11821     
11822     /**
11823      * @cfg {Boolean} specialFilter (true|false) special filter default false
11824      */
11825     specialFilter : false,
11826     
11827     /**
11828      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11829      */
11830     mobileTouchView : true,
11831     
11832     //private
11833     addicon : false,
11834     editicon: false,
11835     
11836     page: 0,
11837     hasQuery: false,
11838     append: false,
11839     loadNext: false,
11840     autoFocus : true,
11841     tickable : false,
11842     btnPosition : 'right',
11843     triggerList : true,
11844     showToggleBtn : true,
11845     animate : true,
11846     emptyResultText: 'Empty',
11847     triggerText : 'Select',
11848     
11849     // element that contains real text value.. (when hidden is used..)
11850     
11851     getAutoCreate : function()
11852     {
11853         var cfg = false;
11854         
11855         /*
11856          * Touch Devices
11857          */
11858         
11859         if(Roo.isTouch && this.mobileTouchView){
11860             cfg = this.getAutoCreateTouchView();
11861             return cfg;;
11862         }
11863         
11864         /*
11865          *  Normal ComboBox
11866          */
11867         if(!this.tickable){
11868             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11869             return cfg;
11870         }
11871         
11872         /*
11873          *  ComboBox with tickable selections
11874          */
11875              
11876         var align = this.labelAlign || this.parentLabelAlign();
11877         
11878         cfg = {
11879             cls : 'form-group roo-combobox-tickable' //input-group
11880         };
11881         
11882         var buttons = {
11883             tag : 'div',
11884             cls : 'tickable-buttons',
11885             cn : [
11886                 {
11887                     tag : 'button',
11888                     type : 'button',
11889                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11890                     html : this.triggerText
11891                 },
11892                 {
11893                     tag : 'button',
11894                     type : 'button',
11895                     name : 'ok',
11896                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11897                     html : 'Done'
11898                 },
11899                 {
11900                     tag : 'button',
11901                     type : 'button',
11902                     name : 'cancel',
11903                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11904                     html : 'Cancel'
11905                 }
11906             ]
11907         };
11908         
11909         if(this.editable){
11910             buttons.cn.unshift({
11911                 tag: 'input',
11912                 cls: 'select2-search-field-input'
11913             });
11914         }
11915         
11916         var _this = this;
11917         
11918         Roo.each(buttons.cn, function(c){
11919             if (_this.size) {
11920                 c.cls += ' btn-' + _this.size;
11921             }
11922
11923             if (_this.disabled) {
11924                 c.disabled = true;
11925             }
11926         });
11927         
11928         var box = {
11929             tag: 'div',
11930             cn: [
11931                 {
11932                     tag: 'input',
11933                     type : 'hidden',
11934                     cls: 'form-hidden-field'
11935                 },
11936                 {
11937                     tag: 'ul',
11938                     cls: 'select2-choices',
11939                     cn:[
11940                         {
11941                             tag: 'li',
11942                             cls: 'select2-search-field',
11943                             cn: [
11944
11945                                 buttons
11946                             ]
11947                         }
11948                     ]
11949                 }
11950             ]
11951         };
11952         
11953         var combobox = {
11954             cls: 'select2-container input-group select2-container-multi',
11955             cn: [
11956                 box
11957 //                {
11958 //                    tag: 'ul',
11959 //                    cls: 'typeahead typeahead-long dropdown-menu',
11960 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11961 //                }
11962             ]
11963         };
11964         
11965         if(this.hasFeedback && !this.allowBlank){
11966             
11967             var feedback = {
11968                 tag: 'span',
11969                 cls: 'glyphicon form-control-feedback'
11970             };
11971
11972             combobox.cn.push(feedback);
11973         }
11974         
11975         if (align ==='left' && this.fieldLabel.length) {
11976             
11977 //                Roo.log("left and has label");
11978                 cfg.cn = [
11979                     
11980                     {
11981                         tag: 'label',
11982                         'for' :  id,
11983                         cls : 'control-label col-sm-' + this.labelWidth,
11984                         html : this.fieldLabel
11985                         
11986                     },
11987                     {
11988                         cls : "col-sm-" + (12 - this.labelWidth), 
11989                         cn: [
11990                             combobox
11991                         ]
11992                     }
11993                     
11994                 ];
11995         } else if ( this.fieldLabel.length) {
11996 //                Roo.log(" label");
11997                  cfg.cn = [
11998                    
11999                     {
12000                         tag: 'label',
12001                         //cls : 'input-group-addon',
12002                         html : this.fieldLabel
12003                         
12004                     },
12005                     
12006                     combobox
12007                     
12008                 ];
12009
12010         } else {
12011             
12012 //                Roo.log(" no label && no align");
12013                 cfg = combobox
12014                      
12015                 
12016         }
12017          
12018         var settings=this;
12019         ['xs','sm','md','lg'].map(function(size){
12020             if (settings[size]) {
12021                 cfg.cls += ' col-' + size + '-' + settings[size];
12022             }
12023         });
12024         
12025         return cfg;
12026         
12027     },
12028     
12029     _initEventsCalled : false,
12030     
12031     // private
12032     initEvents: function()
12033     {
12034         
12035         if (this._initEventsCalled) { // as we call render... prevent looping...
12036             return;
12037         }
12038         this._initEventsCalled = true;
12039         
12040         if (!this.store) {
12041             throw "can not find store for combo";
12042         }
12043         
12044         this.store = Roo.factory(this.store, Roo.data);
12045         
12046         // if we are building from html. then this element is so complex, that we can not really
12047         // use the rendered HTML.
12048         // so we have to trash and replace the previous code.
12049         if (Roo.XComponent.build_from_html) {
12050             
12051             // remove this element....
12052             var e = this.el.dom, k=0;
12053             while (e ) { e = e.previousSibling;  ++k;}
12054
12055             this.el.remove();
12056             
12057             this.el=false;
12058             this.rendered = false;
12059             
12060             this.render(this.parent().getChildContainer(true), k);
12061             
12062             
12063             
12064         }
12065         
12066         
12067         /*
12068          * Touch Devices
12069          */
12070         
12071         if(Roo.isTouch && this.mobileTouchView){
12072             this.initTouchView();
12073             return;
12074         }
12075         
12076         if(this.tickable){
12077             this.initTickableEvents();
12078             return;
12079         }
12080         
12081         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12082         
12083         if(this.hiddenName){
12084             
12085             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12086             
12087             this.hiddenField.dom.value =
12088                 this.hiddenValue !== undefined ? this.hiddenValue :
12089                 this.value !== undefined ? this.value : '';
12090
12091             // prevent input submission
12092             this.el.dom.removeAttribute('name');
12093             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12094              
12095              
12096         }
12097         //if(Roo.isGecko){
12098         //    this.el.dom.setAttribute('autocomplete', 'off');
12099         //}
12100         
12101         var cls = 'x-combo-list';
12102         
12103         //this.list = new Roo.Layer({
12104         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12105         //});
12106         
12107         var _this = this;
12108         
12109         (function(){
12110             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12111             _this.list.setWidth(lw);
12112         }).defer(100);
12113         
12114         this.list.on('mouseover', this.onViewOver, this);
12115         this.list.on('mousemove', this.onViewMove, this);
12116         
12117         this.list.on('scroll', this.onViewScroll, this);
12118         
12119         /*
12120         this.list.swallowEvent('mousewheel');
12121         this.assetHeight = 0;
12122
12123         if(this.title){
12124             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12125             this.assetHeight += this.header.getHeight();
12126         }
12127
12128         this.innerList = this.list.createChild({cls:cls+'-inner'});
12129         this.innerList.on('mouseover', this.onViewOver, this);
12130         this.innerList.on('mousemove', this.onViewMove, this);
12131         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12132         
12133         if(this.allowBlank && !this.pageSize && !this.disableClear){
12134             this.footer = this.list.createChild({cls:cls+'-ft'});
12135             this.pageTb = new Roo.Toolbar(this.footer);
12136            
12137         }
12138         if(this.pageSize){
12139             this.footer = this.list.createChild({cls:cls+'-ft'});
12140             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12141                     {pageSize: this.pageSize});
12142             
12143         }
12144         
12145         if (this.pageTb && this.allowBlank && !this.disableClear) {
12146             var _this = this;
12147             this.pageTb.add(new Roo.Toolbar.Fill(), {
12148                 cls: 'x-btn-icon x-btn-clear',
12149                 text: '&#160;',
12150                 handler: function()
12151                 {
12152                     _this.collapse();
12153                     _this.clearValue();
12154                     _this.onSelect(false, -1);
12155                 }
12156             });
12157         }
12158         if (this.footer) {
12159             this.assetHeight += this.footer.getHeight();
12160         }
12161         */
12162             
12163         if(!this.tpl){
12164             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12165         }
12166
12167         this.view = new Roo.View(this.list, this.tpl, {
12168             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12169         });
12170         //this.view.wrapEl.setDisplayed(false);
12171         this.view.on('click', this.onViewClick, this);
12172         
12173         
12174         
12175         this.store.on('beforeload', this.onBeforeLoad, this);
12176         this.store.on('load', this.onLoad, this);
12177         this.store.on('loadexception', this.onLoadException, this);
12178         /*
12179         if(this.resizable){
12180             this.resizer = new Roo.Resizable(this.list,  {
12181                pinned:true, handles:'se'
12182             });
12183             this.resizer.on('resize', function(r, w, h){
12184                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12185                 this.listWidth = w;
12186                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12187                 this.restrictHeight();
12188             }, this);
12189             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12190         }
12191         */
12192         if(!this.editable){
12193             this.editable = true;
12194             this.setEditable(false);
12195         }
12196         
12197         /*
12198         
12199         if (typeof(this.events.add.listeners) != 'undefined') {
12200             
12201             this.addicon = this.wrap.createChild(
12202                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12203        
12204             this.addicon.on('click', function(e) {
12205                 this.fireEvent('add', this);
12206             }, this);
12207         }
12208         if (typeof(this.events.edit.listeners) != 'undefined') {
12209             
12210             this.editicon = this.wrap.createChild(
12211                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12212             if (this.addicon) {
12213                 this.editicon.setStyle('margin-left', '40px');
12214             }
12215             this.editicon.on('click', function(e) {
12216                 
12217                 // we fire even  if inothing is selected..
12218                 this.fireEvent('edit', this, this.lastData );
12219                 
12220             }, this);
12221         }
12222         */
12223         
12224         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12225             "up" : function(e){
12226                 this.inKeyMode = true;
12227                 this.selectPrev();
12228             },
12229
12230             "down" : function(e){
12231                 if(!this.isExpanded()){
12232                     this.onTriggerClick();
12233                 }else{
12234                     this.inKeyMode = true;
12235                     this.selectNext();
12236                 }
12237             },
12238
12239             "enter" : function(e){
12240 //                this.onViewClick();
12241                 //return true;
12242                 this.collapse();
12243                 
12244                 if(this.fireEvent("specialkey", this, e)){
12245                     this.onViewClick(false);
12246                 }
12247                 
12248                 return true;
12249             },
12250
12251             "esc" : function(e){
12252                 this.collapse();
12253             },
12254
12255             "tab" : function(e){
12256                 this.collapse();
12257                 
12258                 if(this.fireEvent("specialkey", this, e)){
12259                     this.onViewClick(false);
12260                 }
12261                 
12262                 return true;
12263             },
12264
12265             scope : this,
12266
12267             doRelay : function(foo, bar, hname){
12268                 if(hname == 'down' || this.scope.isExpanded()){
12269                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12270                 }
12271                 return true;
12272             },
12273
12274             forceKeyDown: true
12275         });
12276         
12277         
12278         this.queryDelay = Math.max(this.queryDelay || 10,
12279                 this.mode == 'local' ? 10 : 250);
12280         
12281         
12282         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12283         
12284         if(this.typeAhead){
12285             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12286         }
12287         if(this.editable !== false){
12288             this.inputEl().on("keyup", this.onKeyUp, this);
12289         }
12290         if(this.forceSelection){
12291             this.inputEl().on('blur', this.doForce, this);
12292         }
12293         
12294         if(this.multiple){
12295             this.choices = this.el.select('ul.select2-choices', true).first();
12296             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12297         }
12298     },
12299     
12300     initTickableEvents: function()
12301     {   
12302         this.createList();
12303         
12304         if(this.hiddenName){
12305             
12306             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12307             
12308             this.hiddenField.dom.value =
12309                 this.hiddenValue !== undefined ? this.hiddenValue :
12310                 this.value !== undefined ? this.value : '';
12311
12312             // prevent input submission
12313             this.el.dom.removeAttribute('name');
12314             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12315              
12316              
12317         }
12318         
12319 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12320         
12321         this.choices = this.el.select('ul.select2-choices', true).first();
12322         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12323         if(this.triggerList){
12324             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12325         }
12326          
12327         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12328         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12329         
12330         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12331         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12332         
12333         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12334         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12335         
12336         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12337         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12338         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12339         
12340         this.okBtn.hide();
12341         this.cancelBtn.hide();
12342         
12343         var _this = this;
12344         
12345         (function(){
12346             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12347             _this.list.setWidth(lw);
12348         }).defer(100);
12349         
12350         this.list.on('mouseover', this.onViewOver, this);
12351         this.list.on('mousemove', this.onViewMove, this);
12352         
12353         this.list.on('scroll', this.onViewScroll, this);
12354         
12355         if(!this.tpl){
12356             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12357         }
12358
12359         this.view = new Roo.View(this.list, this.tpl, {
12360             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12361         });
12362         
12363         //this.view.wrapEl.setDisplayed(false);
12364         this.view.on('click', this.onViewClick, this);
12365         
12366         
12367         
12368         this.store.on('beforeload', this.onBeforeLoad, this);
12369         this.store.on('load', this.onLoad, this);
12370         this.store.on('loadexception', this.onLoadException, this);
12371         
12372         if(this.editable){
12373             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12374                 "up" : function(e){
12375                     this.inKeyMode = true;
12376                     this.selectPrev();
12377                 },
12378
12379                 "down" : function(e){
12380                     this.inKeyMode = true;
12381                     this.selectNext();
12382                 },
12383
12384                 "enter" : function(e){
12385                     if(this.fireEvent("specialkey", this, e)){
12386                         this.onViewClick(false);
12387                     }
12388                     
12389                     return true;
12390                 },
12391
12392                 "esc" : function(e){
12393                     this.onTickableFooterButtonClick(e, false, false);
12394                 },
12395
12396                 "tab" : function(e){
12397                     this.fireEvent("specialkey", this, e);
12398                     
12399                     this.onTickableFooterButtonClick(e, false, false);
12400                     
12401                     return true;
12402                 },
12403
12404                 scope : this,
12405
12406                 doRelay : function(e, fn, key){
12407                     if(this.scope.isExpanded()){
12408                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12409                     }
12410                     return true;
12411                 },
12412
12413                 forceKeyDown: true
12414             });
12415         }
12416         
12417         this.queryDelay = Math.max(this.queryDelay || 10,
12418                 this.mode == 'local' ? 10 : 250);
12419         
12420         
12421         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12422         
12423         if(this.typeAhead){
12424             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12425         }
12426         
12427         if(this.editable !== false){
12428             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12429         }
12430         
12431     },
12432
12433     onDestroy : function(){
12434         if(this.view){
12435             this.view.setStore(null);
12436             this.view.el.removeAllListeners();
12437             this.view.el.remove();
12438             this.view.purgeListeners();
12439         }
12440         if(this.list){
12441             this.list.dom.innerHTML  = '';
12442         }
12443         
12444         if(this.store){
12445             this.store.un('beforeload', this.onBeforeLoad, this);
12446             this.store.un('load', this.onLoad, this);
12447             this.store.un('loadexception', this.onLoadException, this);
12448         }
12449         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12450     },
12451
12452     // private
12453     fireKey : function(e){
12454         if(e.isNavKeyPress() && !this.list.isVisible()){
12455             this.fireEvent("specialkey", this, e);
12456         }
12457     },
12458
12459     // private
12460     onResize: function(w, h){
12461 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12462 //        
12463 //        if(typeof w != 'number'){
12464 //            // we do not handle it!?!?
12465 //            return;
12466 //        }
12467 //        var tw = this.trigger.getWidth();
12468 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12469 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12470 //        var x = w - tw;
12471 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12472 //            
12473 //        //this.trigger.setStyle('left', x+'px');
12474 //        
12475 //        if(this.list && this.listWidth === undefined){
12476 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12477 //            this.list.setWidth(lw);
12478 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12479 //        }
12480         
12481     
12482         
12483     },
12484
12485     /**
12486      * Allow or prevent the user from directly editing the field text.  If false is passed,
12487      * the user will only be able to select from the items defined in the dropdown list.  This method
12488      * is the runtime equivalent of setting the 'editable' config option at config time.
12489      * @param {Boolean} value True to allow the user to directly edit the field text
12490      */
12491     setEditable : function(value){
12492         if(value == this.editable){
12493             return;
12494         }
12495         this.editable = value;
12496         if(!value){
12497             this.inputEl().dom.setAttribute('readOnly', true);
12498             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12499             this.inputEl().addClass('x-combo-noedit');
12500         }else{
12501             this.inputEl().dom.setAttribute('readOnly', false);
12502             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12503             this.inputEl().removeClass('x-combo-noedit');
12504         }
12505     },
12506
12507     // private
12508     
12509     onBeforeLoad : function(combo,opts){
12510         if(!this.hasFocus){
12511             return;
12512         }
12513          if (!opts.add) {
12514             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12515          }
12516         this.restrictHeight();
12517         this.selectedIndex = -1;
12518     },
12519
12520     // private
12521     onLoad : function(){
12522         
12523         this.hasQuery = false;
12524         
12525         if(!this.hasFocus){
12526             return;
12527         }
12528         
12529         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12530             this.loading.hide();
12531         }
12532              
12533         if(this.store.getCount() > 0){
12534             this.expand();
12535             this.restrictHeight();
12536             if(this.lastQuery == this.allQuery){
12537                 if(this.editable && !this.tickable){
12538                     this.inputEl().dom.select();
12539                 }
12540                 
12541                 if(
12542                     !this.selectByValue(this.value, true) &&
12543                     this.autoFocus && 
12544                     (
12545                         !this.store.lastOptions ||
12546                         typeof(this.store.lastOptions.add) == 'undefined' || 
12547                         this.store.lastOptions.add != true
12548                     )
12549                 ){
12550                     this.select(0, true);
12551                 }
12552             }else{
12553                 if(this.autoFocus){
12554                     this.selectNext();
12555                 }
12556                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12557                     this.taTask.delay(this.typeAheadDelay);
12558                 }
12559             }
12560         }else{
12561             this.onEmptyResults();
12562         }
12563         
12564         //this.el.focus();
12565     },
12566     // private
12567     onLoadException : function()
12568     {
12569         this.hasQuery = false;
12570         
12571         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12572             this.loading.hide();
12573         }
12574         
12575         if(this.tickable && this.editable){
12576             return;
12577         }
12578         
12579         this.collapse();
12580         // only causes errors at present
12581         //Roo.log(this.store.reader.jsonData);
12582         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12583             // fixme
12584             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12585         //}
12586         
12587         
12588     },
12589     // private
12590     onTypeAhead : function(){
12591         if(this.store.getCount() > 0){
12592             var r = this.store.getAt(0);
12593             var newValue = r.data[this.displayField];
12594             var len = newValue.length;
12595             var selStart = this.getRawValue().length;
12596             
12597             if(selStart != len){
12598                 this.setRawValue(newValue);
12599                 this.selectText(selStart, newValue.length);
12600             }
12601         }
12602     },
12603
12604     // private
12605     onSelect : function(record, index){
12606         
12607         if(this.fireEvent('beforeselect', this, record, index) !== false){
12608         
12609             this.setFromData(index > -1 ? record.data : false);
12610             
12611             this.collapse();
12612             this.fireEvent('select', this, record, index);
12613         }
12614     },
12615
12616     /**
12617      * Returns the currently selected field value or empty string if no value is set.
12618      * @return {String} value The selected value
12619      */
12620     getValue : function(){
12621         
12622         if(this.multiple){
12623             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12624         }
12625         
12626         if(this.valueField){
12627             return typeof this.value != 'undefined' ? this.value : '';
12628         }else{
12629             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12630         }
12631     },
12632
12633     /**
12634      * Clears any text/value currently set in the field
12635      */
12636     clearValue : function(){
12637         if(this.hiddenField){
12638             this.hiddenField.dom.value = '';
12639         }
12640         this.value = '';
12641         this.setRawValue('');
12642         this.lastSelectionText = '';
12643         this.lastData = false;
12644         
12645         var close = this.closeTriggerEl();
12646         
12647         if(close){
12648             close.hide();
12649         }
12650         
12651     },
12652
12653     /**
12654      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12655      * will be displayed in the field.  If the value does not match the data value of an existing item,
12656      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12657      * Otherwise the field will be blank (although the value will still be set).
12658      * @param {String} value The value to match
12659      */
12660     setValue : function(v){
12661         if(this.multiple){
12662             this.syncValue();
12663             return;
12664         }
12665         
12666         var text = v;
12667         if(this.valueField){
12668             var r = this.findRecord(this.valueField, v);
12669             if(r){
12670                 text = r.data[this.displayField];
12671             }else if(this.valueNotFoundText !== undefined){
12672                 text = this.valueNotFoundText;
12673             }
12674         }
12675         this.lastSelectionText = text;
12676         if(this.hiddenField){
12677             this.hiddenField.dom.value = v;
12678         }
12679         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12680         this.value = v;
12681         
12682         var close = this.closeTriggerEl();
12683         
12684         if(close){
12685             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12686         }
12687     },
12688     /**
12689      * @property {Object} the last set data for the element
12690      */
12691     
12692     lastData : false,
12693     /**
12694      * Sets the value of the field based on a object which is related to the record format for the store.
12695      * @param {Object} value the value to set as. or false on reset?
12696      */
12697     setFromData : function(o){
12698         
12699         if(this.multiple){
12700             this.addItem(o);
12701             return;
12702         }
12703             
12704         var dv = ''; // display value
12705         var vv = ''; // value value..
12706         this.lastData = o;
12707         if (this.displayField) {
12708             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12709         } else {
12710             // this is an error condition!!!
12711             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12712         }
12713         
12714         if(this.valueField){
12715             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12716         }
12717         
12718         var close = this.closeTriggerEl();
12719         
12720         if(close){
12721             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12722         }
12723         
12724         if(this.hiddenField){
12725             this.hiddenField.dom.value = vv;
12726             
12727             this.lastSelectionText = dv;
12728             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12729             this.value = vv;
12730             return;
12731         }
12732         // no hidden field.. - we store the value in 'value', but still display
12733         // display field!!!!
12734         this.lastSelectionText = dv;
12735         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12736         this.value = vv;
12737         
12738         
12739         
12740     },
12741     // private
12742     reset : function(){
12743         // overridden so that last data is reset..
12744         
12745         if(this.multiple){
12746             this.clearItem();
12747             return;
12748         }
12749         
12750         this.setValue(this.originalValue);
12751         this.clearInvalid();
12752         this.lastData = false;
12753         if (this.view) {
12754             this.view.clearSelections();
12755         }
12756     },
12757     // private
12758     findRecord : function(prop, value){
12759         var record;
12760         if(this.store.getCount() > 0){
12761             this.store.each(function(r){
12762                 if(r.data[prop] == value){
12763                     record = r;
12764                     return false;
12765                 }
12766                 return true;
12767             });
12768         }
12769         return record;
12770     },
12771     
12772     getName: function()
12773     {
12774         // returns hidden if it's set..
12775         if (!this.rendered) {return ''};
12776         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12777         
12778     },
12779     // private
12780     onViewMove : function(e, t){
12781         this.inKeyMode = false;
12782     },
12783
12784     // private
12785     onViewOver : function(e, t){
12786         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12787             return;
12788         }
12789         var item = this.view.findItemFromChild(t);
12790         
12791         if(item){
12792             var index = this.view.indexOf(item);
12793             this.select(index, false);
12794         }
12795     },
12796
12797     // private
12798     onViewClick : function(view, doFocus, el, e)
12799     {
12800         var index = this.view.getSelectedIndexes()[0];
12801         
12802         var r = this.store.getAt(index);
12803         
12804         if(this.tickable){
12805             
12806             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12807                 return;
12808             }
12809             
12810             var rm = false;
12811             var _this = this;
12812             
12813             Roo.each(this.tickItems, function(v,k){
12814                 
12815                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12816                     Roo.log(v);
12817                     _this.tickItems.splice(k, 1);
12818                     
12819                     if(typeof(e) == 'undefined' && view == false){
12820                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12821                     }
12822                     
12823                     rm = true;
12824                     return;
12825                 }
12826             });
12827             
12828             if(rm){
12829                 return;
12830             }
12831             
12832             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12833                 this.tickItems.push(r.data);
12834             }
12835             
12836             if(typeof(e) == 'undefined' && view == false){
12837                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12838             }
12839                     
12840             return;
12841         }
12842         
12843         if(r){
12844             this.onSelect(r, index);
12845         }
12846         if(doFocus !== false && !this.blockFocus){
12847             this.inputEl().focus();
12848         }
12849     },
12850
12851     // private
12852     restrictHeight : function(){
12853         //this.innerList.dom.style.height = '';
12854         //var inner = this.innerList.dom;
12855         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12856         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12857         //this.list.beginUpdate();
12858         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12859         this.list.alignTo(this.inputEl(), this.listAlign);
12860         this.list.alignTo(this.inputEl(), this.listAlign);
12861         //this.list.endUpdate();
12862     },
12863
12864     // private
12865     onEmptyResults : function(){
12866         
12867         if(this.tickable && this.editable){
12868             this.restrictHeight();
12869             return;
12870         }
12871         
12872         this.collapse();
12873     },
12874
12875     /**
12876      * Returns true if the dropdown list is expanded, else false.
12877      */
12878     isExpanded : function(){
12879         return this.list.isVisible();
12880     },
12881
12882     /**
12883      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12884      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12885      * @param {String} value The data value of the item to select
12886      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12887      * selected item if it is not currently in view (defaults to true)
12888      * @return {Boolean} True if the value matched an item in the list, else false
12889      */
12890     selectByValue : function(v, scrollIntoView){
12891         if(v !== undefined && v !== null){
12892             var r = this.findRecord(this.valueField || this.displayField, v);
12893             if(r){
12894                 this.select(this.store.indexOf(r), scrollIntoView);
12895                 return true;
12896             }
12897         }
12898         return false;
12899     },
12900
12901     /**
12902      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12903      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12904      * @param {Number} index The zero-based index of the list item to select
12905      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12906      * selected item if it is not currently in view (defaults to true)
12907      */
12908     select : function(index, scrollIntoView){
12909         this.selectedIndex = index;
12910         this.view.select(index);
12911         if(scrollIntoView !== false){
12912             var el = this.view.getNode(index);
12913             /*
12914              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12915              */
12916             if(el){
12917                 this.list.scrollChildIntoView(el, false);
12918             }
12919         }
12920     },
12921
12922     // private
12923     selectNext : function(){
12924         var ct = this.store.getCount();
12925         if(ct > 0){
12926             if(this.selectedIndex == -1){
12927                 this.select(0);
12928             }else if(this.selectedIndex < ct-1){
12929                 this.select(this.selectedIndex+1);
12930             }
12931         }
12932     },
12933
12934     // private
12935     selectPrev : function(){
12936         var ct = this.store.getCount();
12937         if(ct > 0){
12938             if(this.selectedIndex == -1){
12939                 this.select(0);
12940             }else if(this.selectedIndex != 0){
12941                 this.select(this.selectedIndex-1);
12942             }
12943         }
12944     },
12945
12946     // private
12947     onKeyUp : function(e){
12948         if(this.editable !== false && !e.isSpecialKey()){
12949             this.lastKey = e.getKey();
12950             this.dqTask.delay(this.queryDelay);
12951         }
12952     },
12953
12954     // private
12955     validateBlur : function(){
12956         return !this.list || !this.list.isVisible();   
12957     },
12958
12959     // private
12960     initQuery : function(){
12961         
12962         var v = this.getRawValue();
12963         
12964         if(this.tickable && this.editable){
12965             v = this.tickableInputEl().getValue();
12966         }
12967         
12968         this.doQuery(v);
12969     },
12970
12971     // private
12972     doForce : function(){
12973         if(this.inputEl().dom.value.length > 0){
12974             this.inputEl().dom.value =
12975                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12976              
12977         }
12978     },
12979
12980     /**
12981      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12982      * query allowing the query action to be canceled if needed.
12983      * @param {String} query The SQL query to execute
12984      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12985      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12986      * saved in the current store (defaults to false)
12987      */
12988     doQuery : function(q, forceAll){
12989         
12990         if(q === undefined || q === null){
12991             q = '';
12992         }
12993         var qe = {
12994             query: q,
12995             forceAll: forceAll,
12996             combo: this,
12997             cancel:false
12998         };
12999         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13000             return false;
13001         }
13002         q = qe.query;
13003         
13004         forceAll = qe.forceAll;
13005         if(forceAll === true || (q.length >= this.minChars)){
13006             
13007             this.hasQuery = true;
13008             
13009             if(this.lastQuery != q || this.alwaysQuery){
13010                 this.lastQuery = q;
13011                 if(this.mode == 'local'){
13012                     this.selectedIndex = -1;
13013                     if(forceAll){
13014                         this.store.clearFilter();
13015                     }else{
13016                         
13017                         if(this.specialFilter){
13018                             this.fireEvent('specialfilter', this);
13019                             this.onLoad();
13020                             return;
13021                         }
13022                         
13023                         this.store.filter(this.displayField, q);
13024                     }
13025                     
13026                     this.store.fireEvent("datachanged", this.store);
13027                     
13028                     this.onLoad();
13029                     
13030                     
13031                 }else{
13032                     
13033                     this.store.baseParams[this.queryParam] = q;
13034                     
13035                     var options = {params : this.getParams(q)};
13036                     
13037                     if(this.loadNext){
13038                         options.add = true;
13039                         options.params.start = this.page * this.pageSize;
13040                     }
13041                     
13042                     this.store.load(options);
13043                     
13044                     /*
13045                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13046                      *  we should expand the list on onLoad
13047                      *  so command out it
13048                      */
13049 //                    this.expand();
13050                 }
13051             }else{
13052                 this.selectedIndex = -1;
13053                 this.onLoad();   
13054             }
13055         }
13056         
13057         this.loadNext = false;
13058     },
13059     
13060     // private
13061     getParams : function(q){
13062         var p = {};
13063         //p[this.queryParam] = q;
13064         
13065         if(this.pageSize){
13066             p.start = 0;
13067             p.limit = this.pageSize;
13068         }
13069         return p;
13070     },
13071
13072     /**
13073      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13074      */
13075     collapse : function(){
13076         if(!this.isExpanded()){
13077             return;
13078         }
13079         
13080         this.list.hide();
13081         
13082         if(this.tickable){
13083             this.hasFocus = false;
13084             this.okBtn.hide();
13085             this.cancelBtn.hide();
13086             this.trigger.show();
13087             
13088             if(this.editable){
13089                 this.tickableInputEl().dom.value = '';
13090                 this.tickableInputEl().blur();
13091             }
13092             
13093         }
13094         
13095         Roo.get(document).un('mousedown', this.collapseIf, this);
13096         Roo.get(document).un('mousewheel', this.collapseIf, this);
13097         if (!this.editable) {
13098             Roo.get(document).un('keydown', this.listKeyPress, this);
13099         }
13100         this.fireEvent('collapse', this);
13101     },
13102
13103     // private
13104     collapseIf : function(e){
13105         var in_combo  = e.within(this.el);
13106         var in_list =  e.within(this.list);
13107         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13108         
13109         if (in_combo || in_list || is_list) {
13110             //e.stopPropagation();
13111             return;
13112         }
13113         
13114         if(this.tickable){
13115             this.onTickableFooterButtonClick(e, false, false);
13116         }
13117
13118         this.collapse();
13119         
13120     },
13121
13122     /**
13123      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13124      */
13125     expand : function(){
13126        
13127         if(this.isExpanded() || !this.hasFocus){
13128             return;
13129         }
13130         
13131         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13132         this.list.setWidth(lw);
13133         
13134         
13135          Roo.log('expand');
13136         
13137         this.list.show();
13138         
13139         this.restrictHeight();
13140         
13141         if(this.tickable){
13142             
13143             this.tickItems = Roo.apply([], this.item);
13144             
13145             this.okBtn.show();
13146             this.cancelBtn.show();
13147             this.trigger.hide();
13148             
13149             if(this.editable){
13150                 this.tickableInputEl().focus();
13151             }
13152             
13153         }
13154         
13155         Roo.get(document).on('mousedown', this.collapseIf, this);
13156         Roo.get(document).on('mousewheel', this.collapseIf, this);
13157         if (!this.editable) {
13158             Roo.get(document).on('keydown', this.listKeyPress, this);
13159         }
13160         
13161         this.fireEvent('expand', this);
13162     },
13163
13164     // private
13165     // Implements the default empty TriggerField.onTriggerClick function
13166     onTriggerClick : function(e)
13167     {
13168         Roo.log('trigger click');
13169         
13170         if(this.disabled || !this.triggerList){
13171             return;
13172         }
13173         
13174         this.page = 0;
13175         this.loadNext = false;
13176         
13177         if(this.isExpanded()){
13178             this.collapse();
13179             if (!this.blockFocus) {
13180                 this.inputEl().focus();
13181             }
13182             
13183         }else {
13184             this.hasFocus = true;
13185             if(this.triggerAction == 'all') {
13186                 this.doQuery(this.allQuery, true);
13187             } else {
13188                 this.doQuery(this.getRawValue());
13189             }
13190             if (!this.blockFocus) {
13191                 this.inputEl().focus();
13192             }
13193         }
13194     },
13195     
13196     onTickableTriggerClick : function(e)
13197     {
13198         if(this.disabled){
13199             return;
13200         }
13201         
13202         this.page = 0;
13203         this.loadNext = false;
13204         this.hasFocus = true;
13205         
13206         if(this.triggerAction == 'all') {
13207             this.doQuery(this.allQuery, true);
13208         } else {
13209             this.doQuery(this.getRawValue());
13210         }
13211     },
13212     
13213     onSearchFieldClick : function(e)
13214     {
13215         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13216             this.onTickableFooterButtonClick(e, false, false);
13217             return;
13218         }
13219         
13220         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13221             return;
13222         }
13223         
13224         this.page = 0;
13225         this.loadNext = false;
13226         this.hasFocus = true;
13227         
13228         if(this.triggerAction == 'all') {
13229             this.doQuery(this.allQuery, true);
13230         } else {
13231             this.doQuery(this.getRawValue());
13232         }
13233     },
13234     
13235     listKeyPress : function(e)
13236     {
13237         //Roo.log('listkeypress');
13238         // scroll to first matching element based on key pres..
13239         if (e.isSpecialKey()) {
13240             return false;
13241         }
13242         var k = String.fromCharCode(e.getKey()).toUpperCase();
13243         //Roo.log(k);
13244         var match  = false;
13245         var csel = this.view.getSelectedNodes();
13246         var cselitem = false;
13247         if (csel.length) {
13248             var ix = this.view.indexOf(csel[0]);
13249             cselitem  = this.store.getAt(ix);
13250             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13251                 cselitem = false;
13252             }
13253             
13254         }
13255         
13256         this.store.each(function(v) { 
13257             if (cselitem) {
13258                 // start at existing selection.
13259                 if (cselitem.id == v.id) {
13260                     cselitem = false;
13261                 }
13262                 return true;
13263             }
13264                 
13265             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13266                 match = this.store.indexOf(v);
13267                 return false;
13268             }
13269             return true;
13270         }, this);
13271         
13272         if (match === false) {
13273             return true; // no more action?
13274         }
13275         // scroll to?
13276         this.view.select(match);
13277         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13278         sn.scrollIntoView(sn.dom.parentNode, false);
13279     },
13280     
13281     onViewScroll : function(e, t){
13282         
13283         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){
13284             return;
13285         }
13286         
13287         this.hasQuery = true;
13288         
13289         this.loading = this.list.select('.loading', true).first();
13290         
13291         if(this.loading === null){
13292             this.list.createChild({
13293                 tag: 'div',
13294                 cls: 'loading select2-more-results select2-active',
13295                 html: 'Loading more results...'
13296             });
13297             
13298             this.loading = this.list.select('.loading', true).first();
13299             
13300             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13301             
13302             this.loading.hide();
13303         }
13304         
13305         this.loading.show();
13306         
13307         var _combo = this;
13308         
13309         this.page++;
13310         this.loadNext = true;
13311         
13312         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13313         
13314         return;
13315     },
13316     
13317     addItem : function(o)
13318     {   
13319         var dv = ''; // display value
13320         
13321         if (this.displayField) {
13322             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13323         } else {
13324             // this is an error condition!!!
13325             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13326         }
13327         
13328         if(!dv.length){
13329             return;
13330         }
13331         
13332         var choice = this.choices.createChild({
13333             tag: 'li',
13334             cls: 'select2-search-choice',
13335             cn: [
13336                 {
13337                     tag: 'div',
13338                     html: dv
13339                 },
13340                 {
13341                     tag: 'a',
13342                     href: '#',
13343                     cls: 'select2-search-choice-close',
13344                     tabindex: '-1'
13345                 }
13346             ]
13347             
13348         }, this.searchField);
13349         
13350         var close = choice.select('a.select2-search-choice-close', true).first();
13351         
13352         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13353         
13354         this.item.push(o);
13355         
13356         this.lastData = o;
13357         
13358         this.syncValue();
13359         
13360         this.inputEl().dom.value = '';
13361         
13362         this.validate();
13363     },
13364     
13365     onRemoveItem : function(e, _self, o)
13366     {
13367         e.preventDefault();
13368         
13369         this.lastItem = Roo.apply([], this.item);
13370         
13371         var index = this.item.indexOf(o.data) * 1;
13372         
13373         if( index < 0){
13374             Roo.log('not this item?!');
13375             return;
13376         }
13377         
13378         this.item.splice(index, 1);
13379         o.item.remove();
13380         
13381         this.syncValue();
13382         
13383         this.fireEvent('remove', this, e);
13384         
13385         this.validate();
13386         
13387     },
13388     
13389     syncValue : function()
13390     {
13391         if(!this.item.length){
13392             this.clearValue();
13393             return;
13394         }
13395             
13396         var value = [];
13397         var _this = this;
13398         Roo.each(this.item, function(i){
13399             if(_this.valueField){
13400                 value.push(i[_this.valueField]);
13401                 return;
13402             }
13403
13404             value.push(i);
13405         });
13406
13407         this.value = value.join(',');
13408
13409         if(this.hiddenField){
13410             this.hiddenField.dom.value = this.value;
13411         }
13412         
13413         this.store.fireEvent("datachanged", this.store);
13414     },
13415     
13416     clearItem : function()
13417     {
13418         if(!this.multiple){
13419             return;
13420         }
13421         
13422         this.item = [];
13423         
13424         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13425            c.remove();
13426         });
13427         
13428         this.syncValue();
13429         
13430         this.validate();
13431         
13432         if(this.tickable && !Roo.isTouch){
13433             this.view.refresh();
13434         }
13435     },
13436     
13437     inputEl: function ()
13438     {
13439         if(Roo.isTouch && this.mobileTouchView){
13440             return this.el.select('input.form-control',true).first();
13441         }
13442         
13443         if(this.tickable){
13444             return this.searchField;
13445         }
13446         
13447         return this.el.select('input.form-control',true).first();
13448     },
13449     
13450     
13451     onTickableFooterButtonClick : function(e, btn, el)
13452     {
13453         e.preventDefault();
13454         
13455         this.lastItem = Roo.apply([], this.item);
13456         
13457         if(btn && btn.name == 'cancel'){
13458             this.tickItems = Roo.apply([], this.item);
13459             this.collapse();
13460             return;
13461         }
13462         
13463         this.clearItem();
13464         
13465         var _this = this;
13466         
13467         Roo.each(this.tickItems, function(o){
13468             _this.addItem(o);
13469         });
13470         
13471         this.collapse();
13472         
13473     },
13474     
13475     validate : function()
13476     {
13477         var v = this.getRawValue();
13478         
13479         if(this.multiple){
13480             v = this.getValue();
13481         }
13482         
13483         if(this.disabled || this.allowBlank || v.length){
13484             this.markValid();
13485             return true;
13486         }
13487         
13488         this.markInvalid();
13489         return false;
13490     },
13491     
13492     tickableInputEl : function()
13493     {
13494         if(!this.tickable || !this.editable){
13495             return this.inputEl();
13496         }
13497         
13498         return this.inputEl().select('.select2-search-field-input', true).first();
13499     },
13500     
13501     
13502     getAutoCreateTouchView : function()
13503     {
13504         var id = Roo.id();
13505         
13506         var cfg = {
13507             cls: 'form-group' //input-group
13508         };
13509         
13510         var input =  {
13511             tag: 'input',
13512             id : id,
13513             type : this.inputType,
13514             cls : 'form-control x-combo-noedit',
13515             autocomplete: 'new-password',
13516             placeholder : this.placeholder || '',
13517             readonly : true
13518         };
13519         
13520         if (this.name) {
13521             input.name = this.name;
13522         }
13523         
13524         if (this.size) {
13525             input.cls += ' input-' + this.size;
13526         }
13527         
13528         if (this.disabled) {
13529             input.disabled = true;
13530         }
13531         
13532         var inputblock = {
13533             cls : '',
13534             cn : [
13535                 input
13536             ]
13537         };
13538         
13539         if(this.before){
13540             inputblock.cls += ' input-group';
13541             
13542             inputblock.cn.unshift({
13543                 tag :'span',
13544                 cls : 'input-group-addon',
13545                 html : this.before
13546             });
13547         }
13548         
13549         if(this.removable && !this.multiple){
13550             inputblock.cls += ' roo-removable';
13551             
13552             inputblock.cn.push({
13553                 tag: 'button',
13554                 html : 'x',
13555                 cls : 'roo-combo-removable-btn close'
13556             });
13557         }
13558
13559         if(this.hasFeedback && !this.allowBlank){
13560             
13561             inputblock.cls += ' has-feedback';
13562             
13563             inputblock.cn.push({
13564                 tag: 'span',
13565                 cls: 'glyphicon form-control-feedback'
13566             });
13567             
13568         }
13569         
13570         if (this.after) {
13571             
13572             inputblock.cls += (this.before) ? '' : ' input-group';
13573             
13574             inputblock.cn.push({
13575                 tag :'span',
13576                 cls : 'input-group-addon',
13577                 html : this.after
13578             });
13579         }
13580
13581         var box = {
13582             tag: 'div',
13583             cn: [
13584                 {
13585                     tag: 'input',
13586                     type : 'hidden',
13587                     cls: 'form-hidden-field'
13588                 },
13589                 inputblock
13590             ]
13591             
13592         };
13593         
13594         if(this.multiple){
13595             box = {
13596                 tag: 'div',
13597                 cn: [
13598                     {
13599                         tag: 'input',
13600                         type : 'hidden',
13601                         cls: 'form-hidden-field'
13602                     },
13603                     {
13604                         tag: 'ul',
13605                         cls: 'select2-choices',
13606                         cn:[
13607                             {
13608                                 tag: 'li',
13609                                 cls: 'select2-search-field',
13610                                 cn: [
13611
13612                                     inputblock
13613                                 ]
13614                             }
13615                         ]
13616                     }
13617                 ]
13618             }
13619         };
13620         
13621         var combobox = {
13622             cls: 'select2-container input-group',
13623             cn: [
13624                 box
13625             ]
13626         };
13627         
13628         if(this.multiple){
13629             combobox.cls += ' select2-container-multi';
13630         }
13631         
13632         var align = this.labelAlign || this.parentLabelAlign();
13633         
13634         cfg.cn = combobox;
13635         
13636         if(this.fieldLabel.length){
13637             
13638             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13639             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13640             
13641             cfg.cn = [
13642                 {
13643                     tag: 'label',
13644                     cls : 'control-label ' + lw,
13645                     html : this.fieldLabel
13646
13647                 },
13648                 {
13649                     cls : cw, 
13650                     cn: [
13651                         combobox
13652                     ]
13653                 }
13654             ];
13655         }
13656         
13657         var settings = this;
13658         
13659         ['xs','sm','md','lg'].map(function(size){
13660             if (settings[size]) {
13661                 cfg.cls += ' col-' + size + '-' + settings[size];
13662             }
13663         });
13664         
13665         return cfg;
13666     },
13667     
13668     initTouchView : function()
13669     {
13670         this.renderTouchView();
13671         
13672         this.touchViewEl.on('scroll', function(){
13673             this.el.dom.scrollTop = 0;
13674         }, this);
13675         
13676         this.originalValue = this.getValue();
13677         
13678         this.inputEl().on("click", this.showTouchView, this);
13679         
13680         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13681         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13682         
13683         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13684         
13685         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13686         this.store.on('load', this.onTouchViewLoad, this);
13687         this.store.on('loadexception', this.onTouchViewLoadException, this);
13688         
13689         if(this.hiddenName){
13690             
13691             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13692             
13693             this.hiddenField.dom.value =
13694                 this.hiddenValue !== undefined ? this.hiddenValue :
13695                 this.value !== undefined ? this.value : '';
13696         
13697             this.el.dom.removeAttribute('name');
13698             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13699         }
13700         
13701         if(this.multiple){
13702             this.choices = this.el.select('ul.select2-choices', true).first();
13703             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13704         }
13705         
13706         if(this.removable && !this.multiple){
13707             var close = this.closeTriggerEl();
13708             if(close){
13709                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13710                 close.on('click', this.removeBtnClick, this, close);
13711             }
13712         }
13713         /*
13714          * fix the bug in Safari iOS8
13715          */
13716         this.inputEl().on("focus", function(e){
13717             document.activeElement.blur();
13718         }, this);
13719         
13720         return;
13721         
13722         
13723     },
13724     
13725     renderTouchView : function()
13726     {
13727         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13728         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13729         
13730         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13731         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13732         
13733         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13734         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13735         this.touchViewBodyEl.setStyle('overflow', 'auto');
13736         
13737         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13738         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13739         
13740         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13741         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13742         
13743     },
13744     
13745     showTouchView : function()
13746     {
13747         if(this.disabled){
13748             return;
13749         }
13750         
13751         this.touchViewHeaderEl.hide();
13752
13753         if(this.fieldLabel.length){
13754             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13755             this.touchViewHeaderEl.show();
13756         }
13757
13758         this.touchViewEl.show();
13759
13760         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13761         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13762
13763         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13764
13765         if(this.fieldLabel.length){
13766             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13767         }
13768         
13769         this.touchViewBodyEl.setHeight(bodyHeight);
13770
13771         if(this.animate){
13772             var _this = this;
13773             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13774         }else{
13775             this.touchViewEl.addClass('in');
13776         }
13777
13778         this.doTouchViewQuery();
13779         
13780     },
13781     
13782     hideTouchView : function()
13783     {
13784         this.touchViewEl.removeClass('in');
13785
13786         if(this.animate){
13787             var _this = this;
13788             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13789         }else{
13790             this.touchViewEl.setStyle('display', 'none');
13791         }
13792         
13793     },
13794     
13795     setTouchViewValue : function()
13796     {
13797         if(this.multiple){
13798             this.clearItem();
13799         
13800             var _this = this;
13801
13802             Roo.each(this.tickItems, function(o){
13803                 this.addItem(o);
13804             }, this);
13805         }
13806         
13807         this.hideTouchView();
13808     },
13809     
13810     doTouchViewQuery : function()
13811     {
13812         var qe = {
13813             query: '',
13814             forceAll: true,
13815             combo: this,
13816             cancel:false
13817         };
13818         
13819         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13820             return false;
13821         }
13822         
13823         if(!this.alwaysQuery || this.mode == 'local'){
13824             this.onTouchViewLoad();
13825             return;
13826         }
13827         
13828         this.store.load();
13829     },
13830     
13831     onTouchViewBeforeLoad : function(combo,opts)
13832     {
13833         return;
13834     },
13835
13836     // private
13837     onTouchViewLoad : function()
13838     {
13839         if(this.store.getCount() < 1){
13840             this.onTouchViewEmptyResults();
13841             return;
13842         }
13843         
13844         this.clearTouchView();
13845         
13846         var rawValue = this.getRawValue();
13847         
13848         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13849         
13850         this.tickItems = [];
13851         
13852         this.store.data.each(function(d, rowIndex){
13853             var row = this.touchViewListGroup.createChild(template);
13854             
13855             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13856                 var cfg = {
13857                     data : d.data,
13858                     html : d.data[this.displayField]
13859                 };
13860                 
13861                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13862                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13863                 }
13864             }
13865             
13866             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13867                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13868             }
13869             
13870             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13871                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13872                 this.tickItems.push(d.data);
13873             }
13874             
13875             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13876             
13877         }, this);
13878         
13879         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13880         
13881         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13882
13883         if(this.fieldLabel.length){
13884             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13885         }
13886
13887         var listHeight = this.touchViewListGroup.getHeight();
13888         
13889         var _this = this;
13890         
13891         if(firstChecked && listHeight > bodyHeight){
13892             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13893         }
13894         
13895     },
13896     
13897     onTouchViewLoadException : function()
13898     {
13899         this.hideTouchView();
13900     },
13901     
13902     onTouchViewEmptyResults : function()
13903     {
13904         this.clearTouchView();
13905         
13906         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13907         
13908         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13909         
13910     },
13911     
13912     clearTouchView : function()
13913     {
13914         this.touchViewListGroup.dom.innerHTML = '';
13915     },
13916     
13917     onTouchViewClick : function(e, el, o)
13918     {
13919         e.preventDefault();
13920         
13921         var row = o.row;
13922         var rowIndex = o.rowIndex;
13923         
13924         var r = this.store.getAt(rowIndex);
13925         
13926         if(!this.multiple){
13927             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13928                 c.dom.removeAttribute('checked');
13929             }, this);
13930             
13931             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13932         
13933             this.setFromData(r.data);
13934             
13935             var close = this.closeTriggerEl();
13936         
13937             if(close){
13938                 close.show();
13939             }
13940
13941             this.hideTouchView();
13942             
13943             this.fireEvent('select', this, r, rowIndex);
13944             
13945             return;
13946         }
13947         
13948         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13949             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13950             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13951             return;
13952         }
13953         
13954         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13955         this.addItem(r.data);
13956         this.tickItems.push(r.data);
13957         
13958     }
13959     
13960
13961     /** 
13962     * @cfg {Boolean} grow 
13963     * @hide 
13964     */
13965     /** 
13966     * @cfg {Number} growMin 
13967     * @hide 
13968     */
13969     /** 
13970     * @cfg {Number} growMax 
13971     * @hide 
13972     */
13973     /**
13974      * @hide
13975      * @method autoSize
13976      */
13977 });
13978
13979 Roo.apply(Roo.bootstrap.ComboBox,  {
13980     
13981     header : {
13982         tag: 'div',
13983         cls: 'modal-header',
13984         cn: [
13985             {
13986                 tag: 'h4',
13987                 cls: 'modal-title'
13988             }
13989         ]
13990     },
13991     
13992     body : {
13993         tag: 'div',
13994         cls: 'modal-body',
13995         cn: [
13996             {
13997                 tag: 'ul',
13998                 cls: 'list-group'
13999             }
14000         ]
14001     },
14002     
14003     listItemRadio : {
14004         tag: 'li',
14005         cls: 'list-group-item',
14006         cn: [
14007             {
14008                 tag: 'span',
14009                 cls: 'roo-combobox-list-group-item-value'
14010             },
14011             {
14012                 tag: 'div',
14013                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14014                 cn: [
14015                     {
14016                         tag: 'input',
14017                         type: 'radio'
14018                     },
14019                     {
14020                         tag: 'label'
14021                     }
14022                 ]
14023             }
14024         ]
14025     },
14026     
14027     listItemCheckbox : {
14028         tag: 'li',
14029         cls: 'list-group-item',
14030         cn: [
14031             {
14032                 tag: 'span',
14033                 cls: 'roo-combobox-list-group-item-value'
14034             },
14035             {
14036                 tag: 'div',
14037                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14038                 cn: [
14039                     {
14040                         tag: 'input',
14041                         type: 'checkbox'
14042                     },
14043                     {
14044                         tag: 'label'
14045                     }
14046                 ]
14047             }
14048         ]
14049     },
14050     
14051     emptyResult : {
14052         tag: 'div',
14053         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14054     },
14055     
14056     footer : {
14057         tag: 'div',
14058         cls: 'modal-footer',
14059         cn: [
14060             {
14061                 tag: 'div',
14062                 cls: 'row',
14063                 cn: [
14064                     {
14065                         tag: 'div',
14066                         cls: 'col-xs-6 text-left',
14067                         cn: {
14068                             tag: 'button',
14069                             cls: 'btn btn-danger roo-touch-view-cancel',
14070                             html: 'Cancel'
14071                         }
14072                     },
14073                     {
14074                         tag: 'div',
14075                         cls: 'col-xs-6 text-right',
14076                         cn: {
14077                             tag: 'button',
14078                             cls: 'btn btn-success roo-touch-view-ok',
14079                             html: 'OK'
14080                         }
14081                     }
14082                 ]
14083             }
14084         ]
14085         
14086     }
14087 });
14088
14089 Roo.apply(Roo.bootstrap.ComboBox,  {
14090     
14091     touchViewTemplate : {
14092         tag: 'div',
14093         cls: 'modal fade roo-combobox-touch-view',
14094         cn: [
14095             {
14096                 tag: 'div',
14097                 cls: 'modal-dialog',
14098                 style : 'position:fixed', // we have to fix position....
14099                 cn: [
14100                     {
14101                         tag: 'div',
14102                         cls: 'modal-content',
14103                         cn: [
14104                             Roo.bootstrap.ComboBox.header,
14105                             Roo.bootstrap.ComboBox.body,
14106                             Roo.bootstrap.ComboBox.footer
14107                         ]
14108                     }
14109                 ]
14110             }
14111         ]
14112     }
14113 });/*
14114  * Based on:
14115  * Ext JS Library 1.1.1
14116  * Copyright(c) 2006-2007, Ext JS, LLC.
14117  *
14118  * Originally Released Under LGPL - original licence link has changed is not relivant.
14119  *
14120  * Fork - LGPL
14121  * <script type="text/javascript">
14122  */
14123
14124 /**
14125  * @class Roo.View
14126  * @extends Roo.util.Observable
14127  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14128  * This class also supports single and multi selection modes. <br>
14129  * Create a data model bound view:
14130  <pre><code>
14131  var store = new Roo.data.Store(...);
14132
14133  var view = new Roo.View({
14134     el : "my-element",
14135     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14136  
14137     singleSelect: true,
14138     selectedClass: "ydataview-selected",
14139     store: store
14140  });
14141
14142  // listen for node click?
14143  view.on("click", function(vw, index, node, e){
14144  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14145  });
14146
14147  // load XML data
14148  dataModel.load("foobar.xml");
14149  </code></pre>
14150  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14151  * <br><br>
14152  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14153  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14154  * 
14155  * Note: old style constructor is still suported (container, template, config)
14156  * 
14157  * @constructor
14158  * Create a new View
14159  * @param {Object} config The config object
14160  * 
14161  */
14162 Roo.View = function(config, depreciated_tpl, depreciated_config){
14163     
14164     this.parent = false;
14165     
14166     if (typeof(depreciated_tpl) == 'undefined') {
14167         // new way.. - universal constructor.
14168         Roo.apply(this, config);
14169         this.el  = Roo.get(this.el);
14170     } else {
14171         // old format..
14172         this.el  = Roo.get(config);
14173         this.tpl = depreciated_tpl;
14174         Roo.apply(this, depreciated_config);
14175     }
14176     this.wrapEl  = this.el.wrap().wrap();
14177     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14178     
14179     
14180     if(typeof(this.tpl) == "string"){
14181         this.tpl = new Roo.Template(this.tpl);
14182     } else {
14183         // support xtype ctors..
14184         this.tpl = new Roo.factory(this.tpl, Roo);
14185     }
14186     
14187     
14188     this.tpl.compile();
14189     
14190     /** @private */
14191     this.addEvents({
14192         /**
14193          * @event beforeclick
14194          * Fires before a click is processed. Returns false to cancel the default action.
14195          * @param {Roo.View} this
14196          * @param {Number} index The index of the target node
14197          * @param {HTMLElement} node The target node
14198          * @param {Roo.EventObject} e The raw event object
14199          */
14200             "beforeclick" : true,
14201         /**
14202          * @event click
14203          * Fires when a template node is clicked.
14204          * @param {Roo.View} this
14205          * @param {Number} index The index of the target node
14206          * @param {HTMLElement} node The target node
14207          * @param {Roo.EventObject} e The raw event object
14208          */
14209             "click" : true,
14210         /**
14211          * @event dblclick
14212          * Fires when a template node is double clicked.
14213          * @param {Roo.View} this
14214          * @param {Number} index The index of the target node
14215          * @param {HTMLElement} node The target node
14216          * @param {Roo.EventObject} e The raw event object
14217          */
14218             "dblclick" : true,
14219         /**
14220          * @event contextmenu
14221          * Fires when a template node is right clicked.
14222          * @param {Roo.View} this
14223          * @param {Number} index The index of the target node
14224          * @param {HTMLElement} node The target node
14225          * @param {Roo.EventObject} e The raw event object
14226          */
14227             "contextmenu" : true,
14228         /**
14229          * @event selectionchange
14230          * Fires when the selected nodes change.
14231          * @param {Roo.View} this
14232          * @param {Array} selections Array of the selected nodes
14233          */
14234             "selectionchange" : true,
14235     
14236         /**
14237          * @event beforeselect
14238          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14239          * @param {Roo.View} this
14240          * @param {HTMLElement} node The node to be selected
14241          * @param {Array} selections Array of currently selected nodes
14242          */
14243             "beforeselect" : true,
14244         /**
14245          * @event preparedata
14246          * Fires on every row to render, to allow you to change the data.
14247          * @param {Roo.View} this
14248          * @param {Object} data to be rendered (change this)
14249          */
14250           "preparedata" : true
14251           
14252           
14253         });
14254
14255
14256
14257     this.el.on({
14258         "click": this.onClick,
14259         "dblclick": this.onDblClick,
14260         "contextmenu": this.onContextMenu,
14261         scope:this
14262     });
14263
14264     this.selections = [];
14265     this.nodes = [];
14266     this.cmp = new Roo.CompositeElementLite([]);
14267     if(this.store){
14268         this.store = Roo.factory(this.store, Roo.data);
14269         this.setStore(this.store, true);
14270     }
14271     
14272     if ( this.footer && this.footer.xtype) {
14273            
14274          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14275         
14276         this.footer.dataSource = this.store;
14277         this.footer.container = fctr;
14278         this.footer = Roo.factory(this.footer, Roo);
14279         fctr.insertFirst(this.el);
14280         
14281         // this is a bit insane - as the paging toolbar seems to detach the el..
14282 //        dom.parentNode.parentNode.parentNode
14283          // they get detached?
14284     }
14285     
14286     
14287     Roo.View.superclass.constructor.call(this);
14288     
14289     
14290 };
14291
14292 Roo.extend(Roo.View, Roo.util.Observable, {
14293     
14294      /**
14295      * @cfg {Roo.data.Store} store Data store to load data from.
14296      */
14297     store : false,
14298     
14299     /**
14300      * @cfg {String|Roo.Element} el The container element.
14301      */
14302     el : '',
14303     
14304     /**
14305      * @cfg {String|Roo.Template} tpl The template used by this View 
14306      */
14307     tpl : false,
14308     /**
14309      * @cfg {String} dataName the named area of the template to use as the data area
14310      *                          Works with domtemplates roo-name="name"
14311      */
14312     dataName: false,
14313     /**
14314      * @cfg {String} selectedClass The css class to add to selected nodes
14315      */
14316     selectedClass : "x-view-selected",
14317      /**
14318      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14319      */
14320     emptyText : "",
14321     
14322     /**
14323      * @cfg {String} text to display on mask (default Loading)
14324      */
14325     mask : false,
14326     /**
14327      * @cfg {Boolean} multiSelect Allow multiple selection
14328      */
14329     multiSelect : false,
14330     /**
14331      * @cfg {Boolean} singleSelect Allow single selection
14332      */
14333     singleSelect:  false,
14334     
14335     /**
14336      * @cfg {Boolean} toggleSelect - selecting 
14337      */
14338     toggleSelect : false,
14339     
14340     /**
14341      * @cfg {Boolean} tickable - selecting 
14342      */
14343     tickable : false,
14344     
14345     /**
14346      * Returns the element this view is bound to.
14347      * @return {Roo.Element}
14348      */
14349     getEl : function(){
14350         return this.wrapEl;
14351     },
14352     
14353     
14354
14355     /**
14356      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14357      */
14358     refresh : function(){
14359         //Roo.log('refresh');
14360         var t = this.tpl;
14361         
14362         // if we are using something like 'domtemplate', then
14363         // the what gets used is:
14364         // t.applySubtemplate(NAME, data, wrapping data..)
14365         // the outer template then get' applied with
14366         //     the store 'extra data'
14367         // and the body get's added to the
14368         //      roo-name="data" node?
14369         //      <span class='roo-tpl-{name}'></span> ?????
14370         
14371         
14372         
14373         this.clearSelections();
14374         this.el.update("");
14375         var html = [];
14376         var records = this.store.getRange();
14377         if(records.length < 1) {
14378             
14379             // is this valid??  = should it render a template??
14380             
14381             this.el.update(this.emptyText);
14382             return;
14383         }
14384         var el = this.el;
14385         if (this.dataName) {
14386             this.el.update(t.apply(this.store.meta)); //????
14387             el = this.el.child('.roo-tpl-' + this.dataName);
14388         }
14389         
14390         for(var i = 0, len = records.length; i < len; i++){
14391             var data = this.prepareData(records[i].data, i, records[i]);
14392             this.fireEvent("preparedata", this, data, i, records[i]);
14393             
14394             var d = Roo.apply({}, data);
14395             
14396             if(this.tickable){
14397                 Roo.apply(d, {'roo-id' : Roo.id()});
14398                 
14399                 var _this = this;
14400             
14401                 Roo.each(this.parent.item, function(item){
14402                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14403                         return;
14404                     }
14405                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14406                 });
14407             }
14408             
14409             html[html.length] = Roo.util.Format.trim(
14410                 this.dataName ?
14411                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14412                     t.apply(d)
14413             );
14414         }
14415         
14416         
14417         
14418         el.update(html.join(""));
14419         this.nodes = el.dom.childNodes;
14420         this.updateIndexes(0);
14421     },
14422     
14423
14424     /**
14425      * Function to override to reformat the data that is sent to
14426      * the template for each node.
14427      * DEPRICATED - use the preparedata event handler.
14428      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14429      * a JSON object for an UpdateManager bound view).
14430      */
14431     prepareData : function(data, index, record)
14432     {
14433         this.fireEvent("preparedata", this, data, index, record);
14434         return data;
14435     },
14436
14437     onUpdate : function(ds, record){
14438         // Roo.log('on update');   
14439         this.clearSelections();
14440         var index = this.store.indexOf(record);
14441         var n = this.nodes[index];
14442         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14443         n.parentNode.removeChild(n);
14444         this.updateIndexes(index, index);
14445     },
14446
14447     
14448     
14449 // --------- FIXME     
14450     onAdd : function(ds, records, index)
14451     {
14452         //Roo.log(['on Add', ds, records, index] );        
14453         this.clearSelections();
14454         if(this.nodes.length == 0){
14455             this.refresh();
14456             return;
14457         }
14458         var n = this.nodes[index];
14459         for(var i = 0, len = records.length; i < len; i++){
14460             var d = this.prepareData(records[i].data, i, records[i]);
14461             if(n){
14462                 this.tpl.insertBefore(n, d);
14463             }else{
14464                 
14465                 this.tpl.append(this.el, d);
14466             }
14467         }
14468         this.updateIndexes(index);
14469     },
14470
14471     onRemove : function(ds, record, index){
14472        // Roo.log('onRemove');
14473         this.clearSelections();
14474         var el = this.dataName  ?
14475             this.el.child('.roo-tpl-' + this.dataName) :
14476             this.el; 
14477         
14478         el.dom.removeChild(this.nodes[index]);
14479         this.updateIndexes(index);
14480     },
14481
14482     /**
14483      * Refresh an individual node.
14484      * @param {Number} index
14485      */
14486     refreshNode : function(index){
14487         this.onUpdate(this.store, this.store.getAt(index));
14488     },
14489
14490     updateIndexes : function(startIndex, endIndex){
14491         var ns = this.nodes;
14492         startIndex = startIndex || 0;
14493         endIndex = endIndex || ns.length - 1;
14494         for(var i = startIndex; i <= endIndex; i++){
14495             ns[i].nodeIndex = i;
14496         }
14497     },
14498
14499     /**
14500      * Changes the data store this view uses and refresh the view.
14501      * @param {Store} store
14502      */
14503     setStore : function(store, initial){
14504         if(!initial && this.store){
14505             this.store.un("datachanged", this.refresh);
14506             this.store.un("add", this.onAdd);
14507             this.store.un("remove", this.onRemove);
14508             this.store.un("update", this.onUpdate);
14509             this.store.un("clear", this.refresh);
14510             this.store.un("beforeload", this.onBeforeLoad);
14511             this.store.un("load", this.onLoad);
14512             this.store.un("loadexception", this.onLoad);
14513         }
14514         if(store){
14515           
14516             store.on("datachanged", this.refresh, this);
14517             store.on("add", this.onAdd, this);
14518             store.on("remove", this.onRemove, this);
14519             store.on("update", this.onUpdate, this);
14520             store.on("clear", this.refresh, this);
14521             store.on("beforeload", this.onBeforeLoad, this);
14522             store.on("load", this.onLoad, this);
14523             store.on("loadexception", this.onLoad, this);
14524         }
14525         
14526         if(store){
14527             this.refresh();
14528         }
14529     },
14530     /**
14531      * onbeforeLoad - masks the loading area.
14532      *
14533      */
14534     onBeforeLoad : function(store,opts)
14535     {
14536          //Roo.log('onBeforeLoad');   
14537         if (!opts.add) {
14538             this.el.update("");
14539         }
14540         this.el.mask(this.mask ? this.mask : "Loading" ); 
14541     },
14542     onLoad : function ()
14543     {
14544         this.el.unmask();
14545     },
14546     
14547
14548     /**
14549      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14550      * @param {HTMLElement} node
14551      * @return {HTMLElement} The template node
14552      */
14553     findItemFromChild : function(node){
14554         var el = this.dataName  ?
14555             this.el.child('.roo-tpl-' + this.dataName,true) :
14556             this.el.dom; 
14557         
14558         if(!node || node.parentNode == el){
14559                     return node;
14560             }
14561             var p = node.parentNode;
14562             while(p && p != el){
14563             if(p.parentNode == el){
14564                 return p;
14565             }
14566             p = p.parentNode;
14567         }
14568             return null;
14569     },
14570
14571     /** @ignore */
14572     onClick : function(e){
14573         var item = this.findItemFromChild(e.getTarget());
14574         if(item){
14575             var index = this.indexOf(item);
14576             if(this.onItemClick(item, index, e) !== false){
14577                 this.fireEvent("click", this, index, item, e);
14578             }
14579         }else{
14580             this.clearSelections();
14581         }
14582     },
14583
14584     /** @ignore */
14585     onContextMenu : function(e){
14586         var item = this.findItemFromChild(e.getTarget());
14587         if(item){
14588             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14589         }
14590     },
14591
14592     /** @ignore */
14593     onDblClick : function(e){
14594         var item = this.findItemFromChild(e.getTarget());
14595         if(item){
14596             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14597         }
14598     },
14599
14600     onItemClick : function(item, index, e)
14601     {
14602         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14603             return false;
14604         }
14605         if (this.toggleSelect) {
14606             var m = this.isSelected(item) ? 'unselect' : 'select';
14607             //Roo.log(m);
14608             var _t = this;
14609             _t[m](item, true, false);
14610             return true;
14611         }
14612         if(this.multiSelect || this.singleSelect){
14613             if(this.multiSelect && e.shiftKey && this.lastSelection){
14614                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14615             }else{
14616                 this.select(item, this.multiSelect && e.ctrlKey);
14617                 this.lastSelection = item;
14618             }
14619             
14620             if(!this.tickable){
14621                 e.preventDefault();
14622             }
14623             
14624         }
14625         return true;
14626     },
14627
14628     /**
14629      * Get the number of selected nodes.
14630      * @return {Number}
14631      */
14632     getSelectionCount : function(){
14633         return this.selections.length;
14634     },
14635
14636     /**
14637      * Get the currently selected nodes.
14638      * @return {Array} An array of HTMLElements
14639      */
14640     getSelectedNodes : function(){
14641         return this.selections;
14642     },
14643
14644     /**
14645      * Get the indexes of the selected nodes.
14646      * @return {Array}
14647      */
14648     getSelectedIndexes : function(){
14649         var indexes = [], s = this.selections;
14650         for(var i = 0, len = s.length; i < len; i++){
14651             indexes.push(s[i].nodeIndex);
14652         }
14653         return indexes;
14654     },
14655
14656     /**
14657      * Clear all selections
14658      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14659      */
14660     clearSelections : function(suppressEvent){
14661         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14662             this.cmp.elements = this.selections;
14663             this.cmp.removeClass(this.selectedClass);
14664             this.selections = [];
14665             if(!suppressEvent){
14666                 this.fireEvent("selectionchange", this, this.selections);
14667             }
14668         }
14669     },
14670
14671     /**
14672      * Returns true if the passed node is selected
14673      * @param {HTMLElement/Number} node The node or node index
14674      * @return {Boolean}
14675      */
14676     isSelected : function(node){
14677         var s = this.selections;
14678         if(s.length < 1){
14679             return false;
14680         }
14681         node = this.getNode(node);
14682         return s.indexOf(node) !== -1;
14683     },
14684
14685     /**
14686      * Selects nodes.
14687      * @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
14688      * @param {Boolean} keepExisting (optional) true to keep existing selections
14689      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14690      */
14691     select : function(nodeInfo, keepExisting, suppressEvent){
14692         if(nodeInfo instanceof Array){
14693             if(!keepExisting){
14694                 this.clearSelections(true);
14695             }
14696             for(var i = 0, len = nodeInfo.length; i < len; i++){
14697                 this.select(nodeInfo[i], true, true);
14698             }
14699             return;
14700         } 
14701         var node = this.getNode(nodeInfo);
14702         if(!node || this.isSelected(node)){
14703             return; // already selected.
14704         }
14705         if(!keepExisting){
14706             this.clearSelections(true);
14707         }
14708         
14709         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14710             Roo.fly(node).addClass(this.selectedClass);
14711             this.selections.push(node);
14712             if(!suppressEvent){
14713                 this.fireEvent("selectionchange", this, this.selections);
14714             }
14715         }
14716         
14717         
14718     },
14719       /**
14720      * Unselects nodes.
14721      * @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
14722      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14723      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14724      */
14725     unselect : function(nodeInfo, keepExisting, suppressEvent)
14726     {
14727         if(nodeInfo instanceof Array){
14728             Roo.each(this.selections, function(s) {
14729                 this.unselect(s, nodeInfo);
14730             }, this);
14731             return;
14732         }
14733         var node = this.getNode(nodeInfo);
14734         if(!node || !this.isSelected(node)){
14735             //Roo.log("not selected");
14736             return; // not selected.
14737         }
14738         // fireevent???
14739         var ns = [];
14740         Roo.each(this.selections, function(s) {
14741             if (s == node ) {
14742                 Roo.fly(node).removeClass(this.selectedClass);
14743
14744                 return;
14745             }
14746             ns.push(s);
14747         },this);
14748         
14749         this.selections= ns;
14750         this.fireEvent("selectionchange", this, this.selections);
14751     },
14752
14753     /**
14754      * Gets a template node.
14755      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14756      * @return {HTMLElement} The node or null if it wasn't found
14757      */
14758     getNode : function(nodeInfo){
14759         if(typeof nodeInfo == "string"){
14760             return document.getElementById(nodeInfo);
14761         }else if(typeof nodeInfo == "number"){
14762             return this.nodes[nodeInfo];
14763         }
14764         return nodeInfo;
14765     },
14766
14767     /**
14768      * Gets a range template nodes.
14769      * @param {Number} startIndex
14770      * @param {Number} endIndex
14771      * @return {Array} An array of nodes
14772      */
14773     getNodes : function(start, end){
14774         var ns = this.nodes;
14775         start = start || 0;
14776         end = typeof end == "undefined" ? ns.length - 1 : end;
14777         var nodes = [];
14778         if(start <= end){
14779             for(var i = start; i <= end; i++){
14780                 nodes.push(ns[i]);
14781             }
14782         } else{
14783             for(var i = start; i >= end; i--){
14784                 nodes.push(ns[i]);
14785             }
14786         }
14787         return nodes;
14788     },
14789
14790     /**
14791      * Finds the index of the passed node
14792      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14793      * @return {Number} The index of the node or -1
14794      */
14795     indexOf : function(node){
14796         node = this.getNode(node);
14797         if(typeof node.nodeIndex == "number"){
14798             return node.nodeIndex;
14799         }
14800         var ns = this.nodes;
14801         for(var i = 0, len = ns.length; i < len; i++){
14802             if(ns[i] == node){
14803                 return i;
14804             }
14805         }
14806         return -1;
14807     }
14808 });
14809 /*
14810  * - LGPL
14811  *
14812  * based on jquery fullcalendar
14813  * 
14814  */
14815
14816 Roo.bootstrap = Roo.bootstrap || {};
14817 /**
14818  * @class Roo.bootstrap.Calendar
14819  * @extends Roo.bootstrap.Component
14820  * Bootstrap Calendar class
14821  * @cfg {Boolean} loadMask (true|false) default false
14822  * @cfg {Object} header generate the user specific header of the calendar, default false
14823
14824  * @constructor
14825  * Create a new Container
14826  * @param {Object} config The config object
14827  */
14828
14829
14830
14831 Roo.bootstrap.Calendar = function(config){
14832     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14833      this.addEvents({
14834         /**
14835              * @event select
14836              * Fires when a date is selected
14837              * @param {DatePicker} this
14838              * @param {Date} date The selected date
14839              */
14840         'select': true,
14841         /**
14842              * @event monthchange
14843              * Fires when the displayed month changes 
14844              * @param {DatePicker} this
14845              * @param {Date} date The selected month
14846              */
14847         'monthchange': true,
14848         /**
14849              * @event evententer
14850              * Fires when mouse over an event
14851              * @param {Calendar} this
14852              * @param {event} Event
14853              */
14854         'evententer': true,
14855         /**
14856              * @event eventleave
14857              * Fires when the mouse leaves an
14858              * @param {Calendar} this
14859              * @param {event}
14860              */
14861         'eventleave': true,
14862         /**
14863              * @event eventclick
14864              * Fires when the mouse click an
14865              * @param {Calendar} this
14866              * @param {event}
14867              */
14868         'eventclick': true
14869         
14870     });
14871
14872 };
14873
14874 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14875     
14876      /**
14877      * @cfg {Number} startDay
14878      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14879      */
14880     startDay : 0,
14881     
14882     loadMask : false,
14883     
14884     header : false,
14885       
14886     getAutoCreate : function(){
14887         
14888         
14889         var fc_button = function(name, corner, style, content ) {
14890             return Roo.apply({},{
14891                 tag : 'span',
14892                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14893                          (corner.length ?
14894                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14895                             ''
14896                         ),
14897                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14898                 unselectable: 'on'
14899             });
14900         };
14901         
14902         var header = {};
14903         
14904         if(!this.header){
14905             header = {
14906                 tag : 'table',
14907                 cls : 'fc-header',
14908                 style : 'width:100%',
14909                 cn : [
14910                     {
14911                         tag: 'tr',
14912                         cn : [
14913                             {
14914                                 tag : 'td',
14915                                 cls : 'fc-header-left',
14916                                 cn : [
14917                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14918                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14919                                     { tag: 'span', cls: 'fc-header-space' },
14920                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14921
14922
14923                                 ]
14924                             },
14925
14926                             {
14927                                 tag : 'td',
14928                                 cls : 'fc-header-center',
14929                                 cn : [
14930                                     {
14931                                         tag: 'span',
14932                                         cls: 'fc-header-title',
14933                                         cn : {
14934                                             tag: 'H2',
14935                                             html : 'month / year'
14936                                         }
14937                                     }
14938
14939                                 ]
14940                             },
14941                             {
14942                                 tag : 'td',
14943                                 cls : 'fc-header-right',
14944                                 cn : [
14945                               /*      fc_button('month', 'left', '', 'month' ),
14946                                     fc_button('week', '', '', 'week' ),
14947                                     fc_button('day', 'right', '', 'day' )
14948                                 */    
14949
14950                                 ]
14951                             }
14952
14953                         ]
14954                     }
14955                 ]
14956             };
14957         }
14958         
14959         header = this.header;
14960         
14961        
14962         var cal_heads = function() {
14963             var ret = [];
14964             // fixme - handle this.
14965             
14966             for (var i =0; i < Date.dayNames.length; i++) {
14967                 var d = Date.dayNames[i];
14968                 ret.push({
14969                     tag: 'th',
14970                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14971                     html : d.substring(0,3)
14972                 });
14973                 
14974             }
14975             ret[0].cls += ' fc-first';
14976             ret[6].cls += ' fc-last';
14977             return ret;
14978         };
14979         var cal_cell = function(n) {
14980             return  {
14981                 tag: 'td',
14982                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14983                 cn : [
14984                     {
14985                         cn : [
14986                             {
14987                                 cls: 'fc-day-number',
14988                                 html: 'D'
14989                             },
14990                             {
14991                                 cls: 'fc-day-content',
14992                              
14993                                 cn : [
14994                                      {
14995                                         style: 'position: relative;' // height: 17px;
14996                                     }
14997                                 ]
14998                             }
14999                             
15000                             
15001                         ]
15002                     }
15003                 ]
15004                 
15005             }
15006         };
15007         var cal_rows = function() {
15008             
15009             var ret = [];
15010             for (var r = 0; r < 6; r++) {
15011                 var row= {
15012                     tag : 'tr',
15013                     cls : 'fc-week',
15014                     cn : []
15015                 };
15016                 
15017                 for (var i =0; i < Date.dayNames.length; i++) {
15018                     var d = Date.dayNames[i];
15019                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15020
15021                 }
15022                 row.cn[0].cls+=' fc-first';
15023                 row.cn[0].cn[0].style = 'min-height:90px';
15024                 row.cn[6].cls+=' fc-last';
15025                 ret.push(row);
15026                 
15027             }
15028             ret[0].cls += ' fc-first';
15029             ret[4].cls += ' fc-prev-last';
15030             ret[5].cls += ' fc-last';
15031             return ret;
15032             
15033         };
15034         
15035         var cal_table = {
15036             tag: 'table',
15037             cls: 'fc-border-separate',
15038             style : 'width:100%',
15039             cellspacing  : 0,
15040             cn : [
15041                 { 
15042                     tag: 'thead',
15043                     cn : [
15044                         { 
15045                             tag: 'tr',
15046                             cls : 'fc-first fc-last',
15047                             cn : cal_heads()
15048                         }
15049                     ]
15050                 },
15051                 { 
15052                     tag: 'tbody',
15053                     cn : cal_rows()
15054                 }
15055                   
15056             ]
15057         };
15058          
15059          var cfg = {
15060             cls : 'fc fc-ltr',
15061             cn : [
15062                 header,
15063                 {
15064                     cls : 'fc-content',
15065                     style : "position: relative;",
15066                     cn : [
15067                         {
15068                             cls : 'fc-view fc-view-month fc-grid',
15069                             style : 'position: relative',
15070                             unselectable : 'on',
15071                             cn : [
15072                                 {
15073                                     cls : 'fc-event-container',
15074                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15075                                 },
15076                                 cal_table
15077                             ]
15078                         }
15079                     ]
15080     
15081                 }
15082            ] 
15083             
15084         };
15085         
15086          
15087         
15088         return cfg;
15089     },
15090     
15091     
15092     initEvents : function()
15093     {
15094         if(!this.store){
15095             throw "can not find store for calendar";
15096         }
15097         
15098         var mark = {
15099             tag: "div",
15100             cls:"x-dlg-mask",
15101             style: "text-align:center",
15102             cn: [
15103                 {
15104                     tag: "div",
15105                     style: "background-color:white;width:50%;margin:250 auto",
15106                     cn: [
15107                         {
15108                             tag: "img",
15109                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15110                         },
15111                         {
15112                             tag: "span",
15113                             html: "Loading"
15114                         }
15115                         
15116                     ]
15117                 }
15118             ]
15119         };
15120         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15121         
15122         var size = this.el.select('.fc-content', true).first().getSize();
15123         this.maskEl.setSize(size.width, size.height);
15124         this.maskEl.enableDisplayMode("block");
15125         if(!this.loadMask){
15126             this.maskEl.hide();
15127         }
15128         
15129         this.store = Roo.factory(this.store, Roo.data);
15130         this.store.on('load', this.onLoad, this);
15131         this.store.on('beforeload', this.onBeforeLoad, this);
15132         
15133         this.resize();
15134         
15135         this.cells = this.el.select('.fc-day',true);
15136         //Roo.log(this.cells);
15137         this.textNodes = this.el.query('.fc-day-number');
15138         this.cells.addClassOnOver('fc-state-hover');
15139         
15140         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15141         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15142         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15143         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15144         
15145         this.on('monthchange', this.onMonthChange, this);
15146         
15147         this.update(new Date().clearTime());
15148     },
15149     
15150     resize : function() {
15151         var sz  = this.el.getSize();
15152         
15153         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15154         this.el.select('.fc-day-content div',true).setHeight(34);
15155     },
15156     
15157     
15158     // private
15159     showPrevMonth : function(e){
15160         this.update(this.activeDate.add("mo", -1));
15161     },
15162     showToday : function(e){
15163         this.update(new Date().clearTime());
15164     },
15165     // private
15166     showNextMonth : function(e){
15167         this.update(this.activeDate.add("mo", 1));
15168     },
15169
15170     // private
15171     showPrevYear : function(){
15172         this.update(this.activeDate.add("y", -1));
15173     },
15174
15175     // private
15176     showNextYear : function(){
15177         this.update(this.activeDate.add("y", 1));
15178     },
15179
15180     
15181    // private
15182     update : function(date)
15183     {
15184         var vd = this.activeDate;
15185         this.activeDate = date;
15186 //        if(vd && this.el){
15187 //            var t = date.getTime();
15188 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15189 //                Roo.log('using add remove');
15190 //                
15191 //                this.fireEvent('monthchange', this, date);
15192 //                
15193 //                this.cells.removeClass("fc-state-highlight");
15194 //                this.cells.each(function(c){
15195 //                   if(c.dateValue == t){
15196 //                       c.addClass("fc-state-highlight");
15197 //                       setTimeout(function(){
15198 //                            try{c.dom.firstChild.focus();}catch(e){}
15199 //                       }, 50);
15200 //                       return false;
15201 //                   }
15202 //                   return true;
15203 //                });
15204 //                return;
15205 //            }
15206 //        }
15207         
15208         var days = date.getDaysInMonth();
15209         
15210         var firstOfMonth = date.getFirstDateOfMonth();
15211         var startingPos = firstOfMonth.getDay()-this.startDay;
15212         
15213         if(startingPos < this.startDay){
15214             startingPos += 7;
15215         }
15216         
15217         var pm = date.add(Date.MONTH, -1);
15218         var prevStart = pm.getDaysInMonth()-startingPos;
15219 //        
15220         this.cells = this.el.select('.fc-day',true);
15221         this.textNodes = this.el.query('.fc-day-number');
15222         this.cells.addClassOnOver('fc-state-hover');
15223         
15224         var cells = this.cells.elements;
15225         var textEls = this.textNodes;
15226         
15227         Roo.each(cells, function(cell){
15228             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15229         });
15230         
15231         days += startingPos;
15232
15233         // convert everything to numbers so it's fast
15234         var day = 86400000;
15235         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15236         //Roo.log(d);
15237         //Roo.log(pm);
15238         //Roo.log(prevStart);
15239         
15240         var today = new Date().clearTime().getTime();
15241         var sel = date.clearTime().getTime();
15242         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15243         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15244         var ddMatch = this.disabledDatesRE;
15245         var ddText = this.disabledDatesText;
15246         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15247         var ddaysText = this.disabledDaysText;
15248         var format = this.format;
15249         
15250         var setCellClass = function(cal, cell){
15251             cell.row = 0;
15252             cell.events = [];
15253             cell.more = [];
15254             //Roo.log('set Cell Class');
15255             cell.title = "";
15256             var t = d.getTime();
15257             
15258             //Roo.log(d);
15259             
15260             cell.dateValue = t;
15261             if(t == today){
15262                 cell.className += " fc-today";
15263                 cell.className += " fc-state-highlight";
15264                 cell.title = cal.todayText;
15265             }
15266             if(t == sel){
15267                 // disable highlight in other month..
15268                 //cell.className += " fc-state-highlight";
15269                 
15270             }
15271             // disabling
15272             if(t < min) {
15273                 cell.className = " fc-state-disabled";
15274                 cell.title = cal.minText;
15275                 return;
15276             }
15277             if(t > max) {
15278                 cell.className = " fc-state-disabled";
15279                 cell.title = cal.maxText;
15280                 return;
15281             }
15282             if(ddays){
15283                 if(ddays.indexOf(d.getDay()) != -1){
15284                     cell.title = ddaysText;
15285                     cell.className = " fc-state-disabled";
15286                 }
15287             }
15288             if(ddMatch && format){
15289                 var fvalue = d.dateFormat(format);
15290                 if(ddMatch.test(fvalue)){
15291                     cell.title = ddText.replace("%0", fvalue);
15292                     cell.className = " fc-state-disabled";
15293                 }
15294             }
15295             
15296             if (!cell.initialClassName) {
15297                 cell.initialClassName = cell.dom.className;
15298             }
15299             
15300             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15301         };
15302
15303         var i = 0;
15304         
15305         for(; i < startingPos; i++) {
15306             textEls[i].innerHTML = (++prevStart);
15307             d.setDate(d.getDate()+1);
15308             
15309             cells[i].className = "fc-past fc-other-month";
15310             setCellClass(this, cells[i]);
15311         }
15312         
15313         var intDay = 0;
15314         
15315         for(; i < days; i++){
15316             intDay = i - startingPos + 1;
15317             textEls[i].innerHTML = (intDay);
15318             d.setDate(d.getDate()+1);
15319             
15320             cells[i].className = ''; // "x-date-active";
15321             setCellClass(this, cells[i]);
15322         }
15323         var extraDays = 0;
15324         
15325         for(; i < 42; i++) {
15326             textEls[i].innerHTML = (++extraDays);
15327             d.setDate(d.getDate()+1);
15328             
15329             cells[i].className = "fc-future fc-other-month";
15330             setCellClass(this, cells[i]);
15331         }
15332         
15333         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15334         
15335         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15336         
15337         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15338         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15339         
15340         if(totalRows != 6){
15341             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15342             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15343         }
15344         
15345         this.fireEvent('monthchange', this, date);
15346         
15347         
15348         /*
15349         if(!this.internalRender){
15350             var main = this.el.dom.firstChild;
15351             var w = main.offsetWidth;
15352             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15353             Roo.fly(main).setWidth(w);
15354             this.internalRender = true;
15355             // opera does not respect the auto grow header center column
15356             // then, after it gets a width opera refuses to recalculate
15357             // without a second pass
15358             if(Roo.isOpera && !this.secondPass){
15359                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15360                 this.secondPass = true;
15361                 this.update.defer(10, this, [date]);
15362             }
15363         }
15364         */
15365         
15366     },
15367     
15368     findCell : function(dt) {
15369         dt = dt.clearTime().getTime();
15370         var ret = false;
15371         this.cells.each(function(c){
15372             //Roo.log("check " +c.dateValue + '?=' + dt);
15373             if(c.dateValue == dt){
15374                 ret = c;
15375                 return false;
15376             }
15377             return true;
15378         });
15379         
15380         return ret;
15381     },
15382     
15383     findCells : function(ev) {
15384         var s = ev.start.clone().clearTime().getTime();
15385        // Roo.log(s);
15386         var e= ev.end.clone().clearTime().getTime();
15387        // Roo.log(e);
15388         var ret = [];
15389         this.cells.each(function(c){
15390              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15391             
15392             if(c.dateValue > e){
15393                 return ;
15394             }
15395             if(c.dateValue < s){
15396                 return ;
15397             }
15398             ret.push(c);
15399         });
15400         
15401         return ret;    
15402     },
15403     
15404 //    findBestRow: function(cells)
15405 //    {
15406 //        var ret = 0;
15407 //        
15408 //        for (var i =0 ; i < cells.length;i++) {
15409 //            ret  = Math.max(cells[i].rows || 0,ret);
15410 //        }
15411 //        return ret;
15412 //        
15413 //    },
15414     
15415     
15416     addItem : function(ev)
15417     {
15418         // look for vertical location slot in
15419         var cells = this.findCells(ev);
15420         
15421 //        ev.row = this.findBestRow(cells);
15422         
15423         // work out the location.
15424         
15425         var crow = false;
15426         var rows = [];
15427         for(var i =0; i < cells.length; i++) {
15428             
15429             cells[i].row = cells[0].row;
15430             
15431             if(i == 0){
15432                 cells[i].row = cells[i].row + 1;
15433             }
15434             
15435             if (!crow) {
15436                 crow = {
15437                     start : cells[i],
15438                     end :  cells[i]
15439                 };
15440                 continue;
15441             }
15442             if (crow.start.getY() == cells[i].getY()) {
15443                 // on same row.
15444                 crow.end = cells[i];
15445                 continue;
15446             }
15447             // different row.
15448             rows.push(crow);
15449             crow = {
15450                 start: cells[i],
15451                 end : cells[i]
15452             };
15453             
15454         }
15455         
15456         rows.push(crow);
15457         ev.els = [];
15458         ev.rows = rows;
15459         ev.cells = cells;
15460         
15461         cells[0].events.push(ev);
15462         
15463         this.calevents.push(ev);
15464     },
15465     
15466     clearEvents: function() {
15467         
15468         if(!this.calevents){
15469             return;
15470         }
15471         
15472         Roo.each(this.cells.elements, function(c){
15473             c.row = 0;
15474             c.events = [];
15475             c.more = [];
15476         });
15477         
15478         Roo.each(this.calevents, function(e) {
15479             Roo.each(e.els, function(el) {
15480                 el.un('mouseenter' ,this.onEventEnter, this);
15481                 el.un('mouseleave' ,this.onEventLeave, this);
15482                 el.remove();
15483             },this);
15484         },this);
15485         
15486         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15487             e.remove();
15488         });
15489         
15490     },
15491     
15492     renderEvents: function()
15493     {   
15494         var _this = this;
15495         
15496         this.cells.each(function(c) {
15497             
15498             if(c.row < 5){
15499                 return;
15500             }
15501             
15502             var ev = c.events;
15503             
15504             var r = 4;
15505             if(c.row != c.events.length){
15506                 r = 4 - (4 - (c.row - c.events.length));
15507             }
15508             
15509             c.events = ev.slice(0, r);
15510             c.more = ev.slice(r);
15511             
15512             if(c.more.length && c.more.length == 1){
15513                 c.events.push(c.more.pop());
15514             }
15515             
15516             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15517             
15518         });
15519             
15520         this.cells.each(function(c) {
15521             
15522             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15523             
15524             
15525             for (var e = 0; e < c.events.length; e++){
15526                 var ev = c.events[e];
15527                 var rows = ev.rows;
15528                 
15529                 for(var i = 0; i < rows.length; i++) {
15530                 
15531                     // how many rows should it span..
15532
15533                     var  cfg = {
15534                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15535                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15536
15537                         unselectable : "on",
15538                         cn : [
15539                             {
15540                                 cls: 'fc-event-inner',
15541                                 cn : [
15542     //                                {
15543     //                                  tag:'span',
15544     //                                  cls: 'fc-event-time',
15545     //                                  html : cells.length > 1 ? '' : ev.time
15546     //                                },
15547                                     {
15548                                       tag:'span',
15549                                       cls: 'fc-event-title',
15550                                       html : String.format('{0}', ev.title)
15551                                     }
15552
15553
15554                                 ]
15555                             },
15556                             {
15557                                 cls: 'ui-resizable-handle ui-resizable-e',
15558                                 html : '&nbsp;&nbsp;&nbsp'
15559                             }
15560
15561                         ]
15562                     };
15563
15564                     if (i == 0) {
15565                         cfg.cls += ' fc-event-start';
15566                     }
15567                     if ((i+1) == rows.length) {
15568                         cfg.cls += ' fc-event-end';
15569                     }
15570
15571                     var ctr = _this.el.select('.fc-event-container',true).first();
15572                     var cg = ctr.createChild(cfg);
15573
15574                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15575                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15576
15577                     var r = (c.more.length) ? 1 : 0;
15578                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15579                     cg.setWidth(ebox.right - sbox.x -2);
15580
15581                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15582                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15583                     cg.on('click', _this.onEventClick, _this, ev);
15584
15585                     ev.els.push(cg);
15586                     
15587                 }
15588                 
15589             }
15590             
15591             
15592             if(c.more.length){
15593                 var  cfg = {
15594                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15595                     style : 'position: absolute',
15596                     unselectable : "on",
15597                     cn : [
15598                         {
15599                             cls: 'fc-event-inner',
15600                             cn : [
15601                                 {
15602                                   tag:'span',
15603                                   cls: 'fc-event-title',
15604                                   html : 'More'
15605                                 }
15606
15607
15608                             ]
15609                         },
15610                         {
15611                             cls: 'ui-resizable-handle ui-resizable-e',
15612                             html : '&nbsp;&nbsp;&nbsp'
15613                         }
15614
15615                     ]
15616                 };
15617
15618                 var ctr = _this.el.select('.fc-event-container',true).first();
15619                 var cg = ctr.createChild(cfg);
15620
15621                 var sbox = c.select('.fc-day-content',true).first().getBox();
15622                 var ebox = c.select('.fc-day-content',true).first().getBox();
15623                 //Roo.log(cg);
15624                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15625                 cg.setWidth(ebox.right - sbox.x -2);
15626
15627                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15628                 
15629             }
15630             
15631         });
15632         
15633         
15634         
15635     },
15636     
15637     onEventEnter: function (e, el,event,d) {
15638         this.fireEvent('evententer', this, el, event);
15639     },
15640     
15641     onEventLeave: function (e, el,event,d) {
15642         this.fireEvent('eventleave', this, el, event);
15643     },
15644     
15645     onEventClick: function (e, el,event,d) {
15646         this.fireEvent('eventclick', this, el, event);
15647     },
15648     
15649     onMonthChange: function () {
15650         this.store.load();
15651     },
15652     
15653     onMoreEventClick: function(e, el, more)
15654     {
15655         var _this = this;
15656         
15657         this.calpopover.placement = 'right';
15658         this.calpopover.setTitle('More');
15659         
15660         this.calpopover.setContent('');
15661         
15662         var ctr = this.calpopover.el.select('.popover-content', true).first();
15663         
15664         Roo.each(more, function(m){
15665             var cfg = {
15666                 cls : 'fc-event-hori fc-event-draggable',
15667                 html : m.title
15668             };
15669             var cg = ctr.createChild(cfg);
15670             
15671             cg.on('click', _this.onEventClick, _this, m);
15672         });
15673         
15674         this.calpopover.show(el);
15675         
15676         
15677     },
15678     
15679     onLoad: function () 
15680     {   
15681         this.calevents = [];
15682         var cal = this;
15683         
15684         if(this.store.getCount() > 0){
15685             this.store.data.each(function(d){
15686                cal.addItem({
15687                     id : d.data.id,
15688                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15689                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15690                     time : d.data.start_time,
15691                     title : d.data.title,
15692                     description : d.data.description,
15693                     venue : d.data.venue
15694                 });
15695             });
15696         }
15697         
15698         this.renderEvents();
15699         
15700         if(this.calevents.length && this.loadMask){
15701             this.maskEl.hide();
15702         }
15703     },
15704     
15705     onBeforeLoad: function()
15706     {
15707         this.clearEvents();
15708         if(this.loadMask){
15709             this.maskEl.show();
15710         }
15711     }
15712 });
15713
15714  
15715  /*
15716  * - LGPL
15717  *
15718  * element
15719  * 
15720  */
15721
15722 /**
15723  * @class Roo.bootstrap.Popover
15724  * @extends Roo.bootstrap.Component
15725  * Bootstrap Popover class
15726  * @cfg {String} html contents of the popover   (or false to use children..)
15727  * @cfg {String} title of popover (or false to hide)
15728  * @cfg {String} placement how it is placed
15729  * @cfg {String} trigger click || hover (or false to trigger manually)
15730  * @cfg {String} over what (parent or false to trigger manually.)
15731  * @cfg {Number} delay - delay before showing
15732  
15733  * @constructor
15734  * Create a new Popover
15735  * @param {Object} config The config object
15736  */
15737
15738 Roo.bootstrap.Popover = function(config){
15739     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15740     
15741     this.addEvents({
15742         // raw events
15743          /**
15744          * @event show
15745          * After the popover show
15746          * 
15747          * @param {Roo.bootstrap.Popover} this
15748          */
15749         "show" : true,
15750         /**
15751          * @event hide
15752          * After the popover hide
15753          * 
15754          * @param {Roo.bootstrap.Popover} this
15755          */
15756         "hide" : true
15757     });
15758 };
15759
15760 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15761     
15762     title: 'Fill in a title',
15763     html: false,
15764     
15765     placement : 'right',
15766     trigger : 'hover', // hover
15767     
15768     delay : 0,
15769     
15770     over: 'parent',
15771     
15772     can_build_overlaid : false,
15773     
15774     getChildContainer : function()
15775     {
15776         return this.el.select('.popover-content',true).first();
15777     },
15778     
15779     getAutoCreate : function(){
15780          
15781         var cfg = {
15782            cls : 'popover roo-dynamic',
15783            style: 'display:block',
15784            cn : [
15785                 {
15786                     cls : 'arrow'
15787                 },
15788                 {
15789                     cls : 'popover-inner',
15790                     cn : [
15791                         {
15792                             tag: 'h3',
15793                             cls: 'popover-title',
15794                             html : this.title
15795                         },
15796                         {
15797                             cls : 'popover-content',
15798                             html : this.html
15799                         }
15800                     ]
15801                     
15802                 }
15803            ]
15804         };
15805         
15806         return cfg;
15807     },
15808     setTitle: function(str)
15809     {
15810         this.title = str;
15811         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15812     },
15813     setContent: function(str)
15814     {
15815         this.html = str;
15816         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15817     },
15818     // as it get's added to the bottom of the page.
15819     onRender : function(ct, position)
15820     {
15821         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15822         if(!this.el){
15823             var cfg = Roo.apply({},  this.getAutoCreate());
15824             cfg.id = Roo.id();
15825             
15826             if (this.cls) {
15827                 cfg.cls += ' ' + this.cls;
15828             }
15829             if (this.style) {
15830                 cfg.style = this.style;
15831             }
15832             //Roo.log("adding to ");
15833             this.el = Roo.get(document.body).createChild(cfg, position);
15834 //            Roo.log(this.el);
15835         }
15836         this.initEvents();
15837     },
15838     
15839     initEvents : function()
15840     {
15841         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15842         this.el.enableDisplayMode('block');
15843         this.el.hide();
15844         if (this.over === false) {
15845             return; 
15846         }
15847         if (this.triggers === false) {
15848             return;
15849         }
15850         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15851         var triggers = this.trigger ? this.trigger.split(' ') : [];
15852         Roo.each(triggers, function(trigger) {
15853         
15854             if (trigger == 'click') {
15855                 on_el.on('click', this.toggle, this);
15856             } else if (trigger != 'manual') {
15857                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15858                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15859       
15860                 on_el.on(eventIn  ,this.enter, this);
15861                 on_el.on(eventOut, this.leave, this);
15862             }
15863         }, this);
15864         
15865     },
15866     
15867     
15868     // private
15869     timeout : null,
15870     hoverState : null,
15871     
15872     toggle : function () {
15873         this.hoverState == 'in' ? this.leave() : this.enter();
15874     },
15875     
15876     enter : function () {
15877        
15878     
15879         clearTimeout(this.timeout);
15880     
15881         this.hoverState = 'in';
15882     
15883         if (!this.delay || !this.delay.show) {
15884             this.show();
15885             return;
15886         }
15887         var _t = this;
15888         this.timeout = setTimeout(function () {
15889             if (_t.hoverState == 'in') {
15890                 _t.show();
15891             }
15892         }, this.delay.show)
15893     },
15894     leave : function() {
15895         clearTimeout(this.timeout);
15896     
15897         this.hoverState = 'out';
15898     
15899         if (!this.delay || !this.delay.hide) {
15900             this.hide();
15901             return;
15902         }
15903         var _t = this;
15904         this.timeout = setTimeout(function () {
15905             if (_t.hoverState == 'out') {
15906                 _t.hide();
15907             }
15908         }, this.delay.hide)
15909     },
15910     
15911     show : function (on_el)
15912     {
15913         if (!on_el) {
15914             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15915         }
15916         // set content.
15917         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15918         if (this.html !== false) {
15919             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15920         }
15921         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15922         if (!this.title.length) {
15923             this.el.select('.popover-title',true).hide();
15924         }
15925         
15926         var placement = typeof this.placement == 'function' ?
15927             this.placement.call(this, this.el, on_el) :
15928             this.placement;
15929             
15930         var autoToken = /\s?auto?\s?/i;
15931         var autoPlace = autoToken.test(placement);
15932         if (autoPlace) {
15933             placement = placement.replace(autoToken, '') || 'top';
15934         }
15935         
15936         //this.el.detach()
15937         //this.el.setXY([0,0]);
15938         this.el.show();
15939         this.el.dom.style.display='block';
15940         this.el.addClass(placement);
15941         
15942         //this.el.appendTo(on_el);
15943         
15944         var p = this.getPosition();
15945         var box = this.el.getBox();
15946         
15947         if (autoPlace) {
15948             // fixme..
15949         }
15950         var align = Roo.bootstrap.Popover.alignment[placement];
15951         this.el.alignTo(on_el, align[0],align[1]);
15952         //var arrow = this.el.select('.arrow',true).first();
15953         //arrow.set(align[2], 
15954         
15955         this.el.addClass('in');
15956         
15957         
15958         if (this.el.hasClass('fade')) {
15959             // fade it?
15960         }
15961         
15962         this.fireEvent('show', this);
15963         
15964     },
15965     hide : function()
15966     {
15967         this.el.setXY([0,0]);
15968         this.el.removeClass('in');
15969         this.el.hide();
15970         this.hoverState = null;
15971         
15972         this.fireEvent('hide', this);
15973     }
15974     
15975 });
15976
15977 Roo.bootstrap.Popover.alignment = {
15978     'left' : ['r-l', [-10,0], 'right'],
15979     'right' : ['l-r', [10,0], 'left'],
15980     'bottom' : ['t-b', [0,10], 'top'],
15981     'top' : [ 'b-t', [0,-10], 'bottom']
15982 };
15983
15984  /*
15985  * - LGPL
15986  *
15987  * Progress
15988  * 
15989  */
15990
15991 /**
15992  * @class Roo.bootstrap.Progress
15993  * @extends Roo.bootstrap.Component
15994  * Bootstrap Progress class
15995  * @cfg {Boolean} striped striped of the progress bar
15996  * @cfg {Boolean} active animated of the progress bar
15997  * 
15998  * 
15999  * @constructor
16000  * Create a new Progress
16001  * @param {Object} config The config object
16002  */
16003
16004 Roo.bootstrap.Progress = function(config){
16005     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16006 };
16007
16008 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16009     
16010     striped : false,
16011     active: false,
16012     
16013     getAutoCreate : function(){
16014         var cfg = {
16015             tag: 'div',
16016             cls: 'progress'
16017         };
16018         
16019         
16020         if(this.striped){
16021             cfg.cls += ' progress-striped';
16022         }
16023       
16024         if(this.active){
16025             cfg.cls += ' active';
16026         }
16027         
16028         
16029         return cfg;
16030     }
16031    
16032 });
16033
16034  
16035
16036  /*
16037  * - LGPL
16038  *
16039  * ProgressBar
16040  * 
16041  */
16042
16043 /**
16044  * @class Roo.bootstrap.ProgressBar
16045  * @extends Roo.bootstrap.Component
16046  * Bootstrap ProgressBar class
16047  * @cfg {Number} aria_valuenow aria-value now
16048  * @cfg {Number} aria_valuemin aria-value min
16049  * @cfg {Number} aria_valuemax aria-value max
16050  * @cfg {String} label label for the progress bar
16051  * @cfg {String} panel (success | info | warning | danger )
16052  * @cfg {String} role role of the progress bar
16053  * @cfg {String} sr_only text
16054  * 
16055  * 
16056  * @constructor
16057  * Create a new ProgressBar
16058  * @param {Object} config The config object
16059  */
16060
16061 Roo.bootstrap.ProgressBar = function(config){
16062     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16063 };
16064
16065 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16066     
16067     aria_valuenow : 0,
16068     aria_valuemin : 0,
16069     aria_valuemax : 100,
16070     label : false,
16071     panel : false,
16072     role : false,
16073     sr_only: false,
16074     
16075     getAutoCreate : function()
16076     {
16077         
16078         var cfg = {
16079             tag: 'div',
16080             cls: 'progress-bar',
16081             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16082         };
16083         
16084         if(this.sr_only){
16085             cfg.cn = {
16086                 tag: 'span',
16087                 cls: 'sr-only',
16088                 html: this.sr_only
16089             }
16090         }
16091         
16092         if(this.role){
16093             cfg.role = this.role;
16094         }
16095         
16096         if(this.aria_valuenow){
16097             cfg['aria-valuenow'] = this.aria_valuenow;
16098         }
16099         
16100         if(this.aria_valuemin){
16101             cfg['aria-valuemin'] = this.aria_valuemin;
16102         }
16103         
16104         if(this.aria_valuemax){
16105             cfg['aria-valuemax'] = this.aria_valuemax;
16106         }
16107         
16108         if(this.label && !this.sr_only){
16109             cfg.html = this.label;
16110         }
16111         
16112         if(this.panel){
16113             cfg.cls += ' progress-bar-' + this.panel;
16114         }
16115         
16116         return cfg;
16117     },
16118     
16119     update : function(aria_valuenow)
16120     {
16121         this.aria_valuenow = aria_valuenow;
16122         
16123         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16124     }
16125    
16126 });
16127
16128  
16129
16130  /*
16131  * - LGPL
16132  *
16133  * column
16134  * 
16135  */
16136
16137 /**
16138  * @class Roo.bootstrap.TabGroup
16139  * @extends Roo.bootstrap.Column
16140  * Bootstrap Column class
16141  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16142  * @cfg {Boolean} carousel true to make the group behave like a carousel
16143  * @cfg {Boolean} bullets show bullets for the panels
16144  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16145  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16146  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16147  * 
16148  * @constructor
16149  * Create a new TabGroup
16150  * @param {Object} config The config object
16151  */
16152
16153 Roo.bootstrap.TabGroup = function(config){
16154     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16155     if (!this.navId) {
16156         this.navId = Roo.id();
16157     }
16158     this.tabs = [];
16159     Roo.bootstrap.TabGroup.register(this);
16160     
16161 };
16162
16163 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16164     
16165     carousel : false,
16166     transition : false,
16167     bullets : 0,
16168     timer : 0,
16169     autoslide : false,
16170     slideFn : false,
16171     slideOnTouch : false,
16172     
16173     getAutoCreate : function()
16174     {
16175         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16176         
16177         cfg.cls += ' tab-content';
16178         
16179         if (this.carousel) {
16180             cfg.cls += ' carousel slide';
16181             
16182             cfg.cn = [{
16183                cls : 'carousel-inner'
16184             }];
16185         
16186             if(this.bullets  && !Roo.isTouch){
16187                 
16188                 var bullets = {
16189                     cls : 'carousel-bullets',
16190                     cn : []
16191                 };
16192                
16193                 if(this.bullets_cls){
16194                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16195                 }
16196                  /*
16197                 for (var i = 0; i < this.bullets; i++){
16198                     bullets.cn.push({
16199                         cls : 'bullet bullet-' + i
16200                     });
16201                 }
16202                 */
16203                 bullets.cn.push({
16204                     cls : 'clear'
16205                 });
16206                 
16207                 cfg.cn[0].cn = bullets;
16208             }
16209         }
16210         
16211         return cfg;
16212     },
16213     
16214     initEvents:  function()
16215     {
16216         if(Roo.isTouch && this.slideOnTouch){
16217             this.el.on("touchstart", this.onTouchStart, this);
16218         }
16219         
16220         if(this.autoslide){
16221             var _this = this;
16222             
16223             this.slideFn = window.setInterval(function() {
16224                 _this.showPanelNext();
16225             }, this.timer);
16226         }
16227         
16228     },
16229     
16230     onTouchStart : function(e, el, o)
16231     {
16232         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16233             return;
16234         }
16235         
16236         this.showPanelNext();
16237     },
16238     
16239     getChildContainer : function()
16240     {
16241         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16242     },
16243     
16244     /**
16245     * register a Navigation item
16246     * @param {Roo.bootstrap.NavItem} the navitem to add
16247     */
16248     register : function(item)
16249     {
16250         this.tabs.push( item);
16251         item.navId = this.navId; // not really needed..
16252         this.addBullet();
16253     
16254     },
16255     
16256     getActivePanel : function()
16257     {
16258         var r = false;
16259         Roo.each(this.tabs, function(t) {
16260             if (t.active) {
16261                 r = t;
16262                 return false;
16263             }
16264             return null;
16265         });
16266         return r;
16267         
16268     },
16269     getPanelByName : function(n)
16270     {
16271         var r = false;
16272         Roo.each(this.tabs, function(t) {
16273             if (t.tabId == n) {
16274                 r = t;
16275                 return false;
16276             }
16277             return null;
16278         });
16279         return r;
16280     },
16281     indexOfPanel : function(p)
16282     {
16283         var r = false;
16284         Roo.each(this.tabs, function(t,i) {
16285             if (t.tabId == p.tabId) {
16286                 r = i;
16287                 return false;
16288             }
16289             return null;
16290         });
16291         return r;
16292     },
16293     /**
16294      * show a specific panel
16295      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16296      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16297      */
16298     showPanel : function (pan)
16299     {
16300         if(this.transition || typeof(pan) == 'undefined'){
16301             Roo.log("waiting for the transitionend");
16302             return;
16303         }
16304         
16305         if (typeof(pan) == 'number') {
16306             pan = this.tabs[pan];
16307         }
16308         
16309         if (typeof(pan) == 'string') {
16310             pan = this.getPanelByName(pan);
16311         }
16312         
16313         var cur = this.getActivePanel();
16314         
16315         if(!pan || !cur){
16316             Roo.log('pan or acitve pan is undefined');
16317             return false;
16318         }
16319         
16320         if (pan.tabId == this.getActivePanel().tabId) {
16321             return true;
16322         }
16323         
16324         if (false === cur.fireEvent('beforedeactivate')) {
16325             return false;
16326         }
16327         
16328         if(this.bullets > 0 && !Roo.isTouch){
16329             this.setActiveBullet(this.indexOfPanel(pan));
16330         }
16331         
16332         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16333             
16334             this.transition = true;
16335             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16336             var lr = dir == 'next' ? 'left' : 'right';
16337             pan.el.addClass(dir); // or prev
16338             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16339             cur.el.addClass(lr); // or right
16340             pan.el.addClass(lr);
16341             
16342             var _this = this;
16343             cur.el.on('transitionend', function() {
16344                 Roo.log("trans end?");
16345                 
16346                 pan.el.removeClass([lr,dir]);
16347                 pan.setActive(true);
16348                 
16349                 cur.el.removeClass([lr]);
16350                 cur.setActive(false);
16351                 
16352                 _this.transition = false;
16353                 
16354             }, this, { single:  true } );
16355             
16356             return true;
16357         }
16358         
16359         cur.setActive(false);
16360         pan.setActive(true);
16361         
16362         return true;
16363         
16364     },
16365     showPanelNext : function()
16366     {
16367         var i = this.indexOfPanel(this.getActivePanel());
16368         
16369         if (i >= this.tabs.length - 1 && !this.autoslide) {
16370             return;
16371         }
16372         
16373         if (i >= this.tabs.length - 1 && this.autoslide) {
16374             i = -1;
16375         }
16376         
16377         this.showPanel(this.tabs[i+1]);
16378     },
16379     
16380     showPanelPrev : function()
16381     {
16382         var i = this.indexOfPanel(this.getActivePanel());
16383         
16384         if (i  < 1 && !this.autoslide) {
16385             return;
16386         }
16387         
16388         if (i < 1 && this.autoslide) {
16389             i = this.tabs.length;
16390         }
16391         
16392         this.showPanel(this.tabs[i-1]);
16393     },
16394     
16395     
16396     addBullet: function()
16397     {
16398         if(!this.bullets || Roo.isTouch){
16399             return;
16400         }
16401         var ctr = this.el.select('.carousel-bullets',true).first();
16402         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16403         var bullet = ctr.createChild({
16404             cls : 'bullet bullet-' + i
16405         },ctr.dom.lastChild);
16406         
16407         
16408         var _this = this;
16409         
16410         bullet.on('click', (function(e, el, o, ii, t){
16411
16412             e.preventDefault();
16413
16414             this.showPanel(ii);
16415
16416             if(this.autoslide && this.slideFn){
16417                 clearInterval(this.slideFn);
16418                 this.slideFn = window.setInterval(function() {
16419                     _this.showPanelNext();
16420                 }, this.timer);
16421             }
16422
16423         }).createDelegate(this, [i, bullet], true));
16424                 
16425         
16426     },
16427      
16428     setActiveBullet : function(i)
16429     {
16430         if(Roo.isTouch){
16431             return;
16432         }
16433         
16434         Roo.each(this.el.select('.bullet', true).elements, function(el){
16435             el.removeClass('selected');
16436         });
16437
16438         var bullet = this.el.select('.bullet-' + i, true).first();
16439         
16440         if(!bullet){
16441             return;
16442         }
16443         
16444         bullet.addClass('selected');
16445     }
16446     
16447     
16448   
16449 });
16450
16451  
16452
16453  
16454  
16455 Roo.apply(Roo.bootstrap.TabGroup, {
16456     
16457     groups: {},
16458      /**
16459     * register a Navigation Group
16460     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16461     */
16462     register : function(navgrp)
16463     {
16464         this.groups[navgrp.navId] = navgrp;
16465         
16466     },
16467     /**
16468     * fetch a Navigation Group based on the navigation ID
16469     * if one does not exist , it will get created.
16470     * @param {string} the navgroup to add
16471     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16472     */
16473     get: function(navId) {
16474         if (typeof(this.groups[navId]) == 'undefined') {
16475             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16476         }
16477         return this.groups[navId] ;
16478     }
16479     
16480     
16481     
16482 });
16483
16484  /*
16485  * - LGPL
16486  *
16487  * TabPanel
16488  * 
16489  */
16490
16491 /**
16492  * @class Roo.bootstrap.TabPanel
16493  * @extends Roo.bootstrap.Component
16494  * Bootstrap TabPanel class
16495  * @cfg {Boolean} active panel active
16496  * @cfg {String} html panel content
16497  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16498  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16499  * 
16500  * 
16501  * @constructor
16502  * Create a new TabPanel
16503  * @param {Object} config The config object
16504  */
16505
16506 Roo.bootstrap.TabPanel = function(config){
16507     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16508     this.addEvents({
16509         /**
16510              * @event changed
16511              * Fires when the active status changes
16512              * @param {Roo.bootstrap.TabPanel} this
16513              * @param {Boolean} state the new state
16514             
16515          */
16516         'changed': true,
16517         /**
16518              * @event beforedeactivate
16519              * Fires before a tab is de-activated - can be used to do validation on a form.
16520              * @param {Roo.bootstrap.TabPanel} this
16521              * @return {Boolean} false if there is an error
16522             
16523          */
16524         'beforedeactivate': true
16525      });
16526     
16527     this.tabId = this.tabId || Roo.id();
16528   
16529 };
16530
16531 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16532     
16533     active: false,
16534     html: false,
16535     tabId: false,
16536     navId : false,
16537     
16538     getAutoCreate : function(){
16539         var cfg = {
16540             tag: 'div',
16541             // item is needed for carousel - not sure if it has any effect otherwise
16542             cls: 'tab-pane item',
16543             html: this.html || ''
16544         };
16545         
16546         if(this.active){
16547             cfg.cls += ' active';
16548         }
16549         
16550         if(this.tabId){
16551             cfg.tabId = this.tabId;
16552         }
16553         
16554         
16555         return cfg;
16556     },
16557     
16558     initEvents:  function()
16559     {
16560         var p = this.parent();
16561         this.navId = this.navId || p.navId;
16562         
16563         if (typeof(this.navId) != 'undefined') {
16564             // not really needed.. but just in case.. parent should be a NavGroup.
16565             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16566             
16567             tg.register(this);
16568             
16569             var i = tg.tabs.length - 1;
16570             
16571             if(this.active && tg.bullets > 0 && i < tg.bullets){
16572                 tg.setActiveBullet(i);
16573             }
16574         }
16575         
16576     },
16577     
16578     
16579     onRender : function(ct, position)
16580     {
16581        // Roo.log("Call onRender: " + this.xtype);
16582         
16583         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16584         
16585         
16586         
16587         
16588         
16589     },
16590     
16591     setActive: function(state)
16592     {
16593         Roo.log("panel - set active " + this.tabId + "=" + state);
16594         
16595         this.active = state;
16596         if (!state) {
16597             this.el.removeClass('active');
16598             
16599         } else  if (!this.el.hasClass('active')) {
16600             this.el.addClass('active');
16601         }
16602         
16603         this.fireEvent('changed', this, state);
16604     }
16605     
16606     
16607 });
16608  
16609
16610  
16611
16612  /*
16613  * - LGPL
16614  *
16615  * DateField
16616  * 
16617  */
16618
16619 /**
16620  * @class Roo.bootstrap.DateField
16621  * @extends Roo.bootstrap.Input
16622  * Bootstrap DateField class
16623  * @cfg {Number} weekStart default 0
16624  * @cfg {String} viewMode default empty, (months|years)
16625  * @cfg {String} minViewMode default empty, (months|years)
16626  * @cfg {Number} startDate default -Infinity
16627  * @cfg {Number} endDate default Infinity
16628  * @cfg {Boolean} todayHighlight default false
16629  * @cfg {Boolean} todayBtn default false
16630  * @cfg {Boolean} calendarWeeks default false
16631  * @cfg {Object} daysOfWeekDisabled default empty
16632  * @cfg {Boolean} singleMode default false (true | false)
16633  * 
16634  * @cfg {Boolean} keyboardNavigation default true
16635  * @cfg {String} language default en
16636  * 
16637  * @constructor
16638  * Create a new DateField
16639  * @param {Object} config The config object
16640  */
16641
16642 Roo.bootstrap.DateField = function(config){
16643     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16644      this.addEvents({
16645             /**
16646              * @event show
16647              * Fires when this field show.
16648              * @param {Roo.bootstrap.DateField} this
16649              * @param {Mixed} date The date value
16650              */
16651             show : true,
16652             /**
16653              * @event show
16654              * Fires when this field hide.
16655              * @param {Roo.bootstrap.DateField} this
16656              * @param {Mixed} date The date value
16657              */
16658             hide : true,
16659             /**
16660              * @event select
16661              * Fires when select a date.
16662              * @param {Roo.bootstrap.DateField} this
16663              * @param {Mixed} date The date value
16664              */
16665             select : true,
16666             /**
16667              * @event beforeselect
16668              * Fires when before select a date.
16669              * @param {Roo.bootstrap.DateField} this
16670              * @param {Mixed} date The date value
16671              */
16672             beforeselect : true
16673         });
16674 };
16675
16676 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16677     
16678     /**
16679      * @cfg {String} format
16680      * The default date format string which can be overriden for localization support.  The format must be
16681      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16682      */
16683     format : "m/d/y",
16684     /**
16685      * @cfg {String} altFormats
16686      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16687      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16688      */
16689     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16690     
16691     weekStart : 0,
16692     
16693     viewMode : '',
16694     
16695     minViewMode : '',
16696     
16697     todayHighlight : false,
16698     
16699     todayBtn: false,
16700     
16701     language: 'en',
16702     
16703     keyboardNavigation: true,
16704     
16705     calendarWeeks: false,
16706     
16707     startDate: -Infinity,
16708     
16709     endDate: Infinity,
16710     
16711     daysOfWeekDisabled: [],
16712     
16713     _events: [],
16714     
16715     singleMode : false,
16716     
16717     UTCDate: function()
16718     {
16719         return new Date(Date.UTC.apply(Date, arguments));
16720     },
16721     
16722     UTCToday: function()
16723     {
16724         var today = new Date();
16725         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16726     },
16727     
16728     getDate: function() {
16729             var d = this.getUTCDate();
16730             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16731     },
16732     
16733     getUTCDate: function() {
16734             return this.date;
16735     },
16736     
16737     setDate: function(d) {
16738             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16739     },
16740     
16741     setUTCDate: function(d) {
16742             this.date = d;
16743             this.setValue(this.formatDate(this.date));
16744     },
16745         
16746     onRender: function(ct, position)
16747     {
16748         
16749         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16750         
16751         this.language = this.language || 'en';
16752         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16753         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16754         
16755         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16756         this.format = this.format || 'm/d/y';
16757         this.isInline = false;
16758         this.isInput = true;
16759         this.component = this.el.select('.add-on', true).first() || false;
16760         this.component = (this.component && this.component.length === 0) ? false : this.component;
16761         this.hasInput = this.component && this.inputEL().length;
16762         
16763         if (typeof(this.minViewMode === 'string')) {
16764             switch (this.minViewMode) {
16765                 case 'months':
16766                     this.minViewMode = 1;
16767                     break;
16768                 case 'years':
16769                     this.minViewMode = 2;
16770                     break;
16771                 default:
16772                     this.minViewMode = 0;
16773                     break;
16774             }
16775         }
16776         
16777         if (typeof(this.viewMode === 'string')) {
16778             switch (this.viewMode) {
16779                 case 'months':
16780                     this.viewMode = 1;
16781                     break;
16782                 case 'years':
16783                     this.viewMode = 2;
16784                     break;
16785                 default:
16786                     this.viewMode = 0;
16787                     break;
16788             }
16789         }
16790                 
16791         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16792         
16793 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16794         
16795         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16796         
16797         this.picker().on('mousedown', this.onMousedown, this);
16798         this.picker().on('click', this.onClick, this);
16799         
16800         this.picker().addClass('datepicker-dropdown');
16801         
16802         this.startViewMode = this.viewMode;
16803         
16804         if(this.singleMode){
16805             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16806                 v.setVisibilityMode(Roo.Element.DISPLAY);
16807                 v.hide();
16808             });
16809             
16810             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16811                 v.setStyle('width', '189px');
16812             });
16813         }
16814         
16815         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16816             if(!this.calendarWeeks){
16817                 v.remove();
16818                 return;
16819             }
16820             
16821             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16822             v.attr('colspan', function(i, val){
16823                 return parseInt(val) + 1;
16824             });
16825         });
16826                         
16827         
16828         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16829         
16830         this.setStartDate(this.startDate);
16831         this.setEndDate(this.endDate);
16832         
16833         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16834         
16835         this.fillDow();
16836         this.fillMonths();
16837         this.update();
16838         this.showMode();
16839         
16840         if(this.isInline) {
16841             this.show();
16842         }
16843     },
16844     
16845     picker : function()
16846     {
16847         return this.pickerEl;
16848 //        return this.el.select('.datepicker', true).first();
16849     },
16850     
16851     fillDow: function()
16852     {
16853         var dowCnt = this.weekStart;
16854         
16855         var dow = {
16856             tag: 'tr',
16857             cn: [
16858                 
16859             ]
16860         };
16861         
16862         if(this.calendarWeeks){
16863             dow.cn.push({
16864                 tag: 'th',
16865                 cls: 'cw',
16866                 html: '&nbsp;'
16867             })
16868         }
16869         
16870         while (dowCnt < this.weekStart + 7) {
16871             dow.cn.push({
16872                 tag: 'th',
16873                 cls: 'dow',
16874                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16875             });
16876         }
16877         
16878         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16879     },
16880     
16881     fillMonths: function()
16882     {    
16883         var i = 0;
16884         var months = this.picker().select('>.datepicker-months td', true).first();
16885         
16886         months.dom.innerHTML = '';
16887         
16888         while (i < 12) {
16889             var month = {
16890                 tag: 'span',
16891                 cls: 'month',
16892                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16893             };
16894             
16895             months.createChild(month);
16896         }
16897         
16898     },
16899     
16900     update: function()
16901     {
16902         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;
16903         
16904         if (this.date < this.startDate) {
16905             this.viewDate = new Date(this.startDate);
16906         } else if (this.date > this.endDate) {
16907             this.viewDate = new Date(this.endDate);
16908         } else {
16909             this.viewDate = new Date(this.date);
16910         }
16911         
16912         this.fill();
16913     },
16914     
16915     fill: function() 
16916     {
16917         var d = new Date(this.viewDate),
16918                 year = d.getUTCFullYear(),
16919                 month = d.getUTCMonth(),
16920                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16921                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16922                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16923                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16924                 currentDate = this.date && this.date.valueOf(),
16925                 today = this.UTCToday();
16926         
16927         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16928         
16929 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16930         
16931 //        this.picker.select('>tfoot th.today').
16932 //                                              .text(dates[this.language].today)
16933 //                                              .toggle(this.todayBtn !== false);
16934     
16935         this.updateNavArrows();
16936         this.fillMonths();
16937                                                 
16938         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16939         
16940         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16941          
16942         prevMonth.setUTCDate(day);
16943         
16944         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16945         
16946         var nextMonth = new Date(prevMonth);
16947         
16948         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16949         
16950         nextMonth = nextMonth.valueOf();
16951         
16952         var fillMonths = false;
16953         
16954         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16955         
16956         while(prevMonth.valueOf() < nextMonth) {
16957             var clsName = '';
16958             
16959             if (prevMonth.getUTCDay() === this.weekStart) {
16960                 if(fillMonths){
16961                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16962                 }
16963                     
16964                 fillMonths = {
16965                     tag: 'tr',
16966                     cn: []
16967                 };
16968                 
16969                 if(this.calendarWeeks){
16970                     // ISO 8601: First week contains first thursday.
16971                     // ISO also states week starts on Monday, but we can be more abstract here.
16972                     var
16973                     // Start of current week: based on weekstart/current date
16974                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16975                     // Thursday of this week
16976                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16977                     // First Thursday of year, year from thursday
16978                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16979                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16980                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16981                     
16982                     fillMonths.cn.push({
16983                         tag: 'td',
16984                         cls: 'cw',
16985                         html: calWeek
16986                     });
16987                 }
16988             }
16989             
16990             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16991                 clsName += ' old';
16992             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16993                 clsName += ' new';
16994             }
16995             if (this.todayHighlight &&
16996                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16997                 prevMonth.getUTCMonth() == today.getMonth() &&
16998                 prevMonth.getUTCDate() == today.getDate()) {
16999                 clsName += ' today';
17000             }
17001             
17002             if (currentDate && prevMonth.valueOf() === currentDate) {
17003                 clsName += ' active';
17004             }
17005             
17006             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17007                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17008                     clsName += ' disabled';
17009             }
17010             
17011             fillMonths.cn.push({
17012                 tag: 'td',
17013                 cls: 'day ' + clsName,
17014                 html: prevMonth.getDate()
17015             });
17016             
17017             prevMonth.setDate(prevMonth.getDate()+1);
17018         }
17019           
17020         var currentYear = this.date && this.date.getUTCFullYear();
17021         var currentMonth = this.date && this.date.getUTCMonth();
17022         
17023         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17024         
17025         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17026             v.removeClass('active');
17027             
17028             if(currentYear === year && k === currentMonth){
17029                 v.addClass('active');
17030             }
17031             
17032             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17033                 v.addClass('disabled');
17034             }
17035             
17036         });
17037         
17038         
17039         year = parseInt(year/10, 10) * 10;
17040         
17041         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17042         
17043         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17044         
17045         year -= 1;
17046         for (var i = -1; i < 11; i++) {
17047             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17048                 tag: 'span',
17049                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17050                 html: year
17051             });
17052             
17053             year += 1;
17054         }
17055     },
17056     
17057     showMode: function(dir) 
17058     {
17059         if (dir) {
17060             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17061         }
17062         
17063         Roo.each(this.picker().select('>div',true).elements, function(v){
17064             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17065             v.hide();
17066         });
17067         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17068     },
17069     
17070     place: function()
17071     {
17072         if(this.isInline) {
17073             return;
17074         }
17075         
17076         this.picker().removeClass(['bottom', 'top']);
17077         
17078         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17079             /*
17080              * place to the top of element!
17081              *
17082              */
17083             
17084             this.picker().addClass('top');
17085             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17086             
17087             return;
17088         }
17089         
17090         this.picker().addClass('bottom');
17091         
17092         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17093     },
17094     
17095     parseDate : function(value)
17096     {
17097         if(!value || value instanceof Date){
17098             return value;
17099         }
17100         var v = Date.parseDate(value, this.format);
17101         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17102             v = Date.parseDate(value, 'Y-m-d');
17103         }
17104         if(!v && this.altFormats){
17105             if(!this.altFormatsArray){
17106                 this.altFormatsArray = this.altFormats.split("|");
17107             }
17108             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17109                 v = Date.parseDate(value, this.altFormatsArray[i]);
17110             }
17111         }
17112         return v;
17113     },
17114     
17115     formatDate : function(date, fmt)
17116     {   
17117         return (!date || !(date instanceof Date)) ?
17118         date : date.dateFormat(fmt || this.format);
17119     },
17120     
17121     onFocus : function()
17122     {
17123         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17124         this.show();
17125     },
17126     
17127     onBlur : function()
17128     {
17129         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17130         
17131         var d = this.inputEl().getValue();
17132         
17133         this.setValue(d);
17134                 
17135         this.hide();
17136     },
17137     
17138     show : function()
17139     {
17140         this.picker().show();
17141         this.update();
17142         this.place();
17143         
17144         this.fireEvent('show', this, this.date);
17145     },
17146     
17147     hide : function()
17148     {
17149         if(this.isInline) {
17150             return;
17151         }
17152         this.picker().hide();
17153         this.viewMode = this.startViewMode;
17154         this.showMode();
17155         
17156         this.fireEvent('hide', this, this.date);
17157         
17158     },
17159     
17160     onMousedown: function(e)
17161     {
17162         e.stopPropagation();
17163         e.preventDefault();
17164     },
17165     
17166     keyup: function(e)
17167     {
17168         Roo.bootstrap.DateField.superclass.keyup.call(this);
17169         this.update();
17170     },
17171
17172     setValue: function(v)
17173     {
17174         if(this.fireEvent('beforeselect', this, v) !== false){
17175             var d = new Date(this.parseDate(v) ).clearTime();
17176         
17177             if(isNaN(d.getTime())){
17178                 this.date = this.viewDate = '';
17179                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17180                 return;
17181             }
17182
17183             v = this.formatDate(d);
17184
17185             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17186
17187             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17188
17189             this.update();
17190
17191             this.fireEvent('select', this, this.date);
17192         }
17193     },
17194     
17195     getValue: function()
17196     {
17197         return this.formatDate(this.date);
17198     },
17199     
17200     fireKey: function(e)
17201     {
17202         if (!this.picker().isVisible()){
17203             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17204                 this.show();
17205             }
17206             return;
17207         }
17208         
17209         var dateChanged = false,
17210         dir, day, month,
17211         newDate, newViewDate;
17212         
17213         switch(e.keyCode){
17214             case 27: // escape
17215                 this.hide();
17216                 e.preventDefault();
17217                 break;
17218             case 37: // left
17219             case 39: // right
17220                 if (!this.keyboardNavigation) {
17221                     break;
17222                 }
17223                 dir = e.keyCode == 37 ? -1 : 1;
17224                 
17225                 if (e.ctrlKey){
17226                     newDate = this.moveYear(this.date, dir);
17227                     newViewDate = this.moveYear(this.viewDate, dir);
17228                 } else if (e.shiftKey){
17229                     newDate = this.moveMonth(this.date, dir);
17230                     newViewDate = this.moveMonth(this.viewDate, dir);
17231                 } else {
17232                     newDate = new Date(this.date);
17233                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17234                     newViewDate = new Date(this.viewDate);
17235                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17236                 }
17237                 if (this.dateWithinRange(newDate)){
17238                     this.date = newDate;
17239                     this.viewDate = newViewDate;
17240                     this.setValue(this.formatDate(this.date));
17241 //                    this.update();
17242                     e.preventDefault();
17243                     dateChanged = true;
17244                 }
17245                 break;
17246             case 38: // up
17247             case 40: // down
17248                 if (!this.keyboardNavigation) {
17249                     break;
17250                 }
17251                 dir = e.keyCode == 38 ? -1 : 1;
17252                 if (e.ctrlKey){
17253                     newDate = this.moveYear(this.date, dir);
17254                     newViewDate = this.moveYear(this.viewDate, dir);
17255                 } else if (e.shiftKey){
17256                     newDate = this.moveMonth(this.date, dir);
17257                     newViewDate = this.moveMonth(this.viewDate, dir);
17258                 } else {
17259                     newDate = new Date(this.date);
17260                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17261                     newViewDate = new Date(this.viewDate);
17262                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17263                 }
17264                 if (this.dateWithinRange(newDate)){
17265                     this.date = newDate;
17266                     this.viewDate = newViewDate;
17267                     this.setValue(this.formatDate(this.date));
17268 //                    this.update();
17269                     e.preventDefault();
17270                     dateChanged = true;
17271                 }
17272                 break;
17273             case 13: // enter
17274                 this.setValue(this.formatDate(this.date));
17275                 this.hide();
17276                 e.preventDefault();
17277                 break;
17278             case 9: // tab
17279                 this.setValue(this.formatDate(this.date));
17280                 this.hide();
17281                 break;
17282             case 16: // shift
17283             case 17: // ctrl
17284             case 18: // alt
17285                 break;
17286             default :
17287                 this.hide();
17288                 
17289         }
17290     },
17291     
17292     
17293     onClick: function(e) 
17294     {
17295         e.stopPropagation();
17296         e.preventDefault();
17297         
17298         var target = e.getTarget();
17299         
17300         if(target.nodeName.toLowerCase() === 'i'){
17301             target = Roo.get(target).dom.parentNode;
17302         }
17303         
17304         var nodeName = target.nodeName;
17305         var className = target.className;
17306         var html = target.innerHTML;
17307         //Roo.log(nodeName);
17308         
17309         switch(nodeName.toLowerCase()) {
17310             case 'th':
17311                 switch(className) {
17312                     case 'switch':
17313                         this.showMode(1);
17314                         break;
17315                     case 'prev':
17316                     case 'next':
17317                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17318                         switch(this.viewMode){
17319                                 case 0:
17320                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17321                                         break;
17322                                 case 1:
17323                                 case 2:
17324                                         this.viewDate = this.moveYear(this.viewDate, dir);
17325                                         break;
17326                         }
17327                         this.fill();
17328                         break;
17329                     case 'today':
17330                         var date = new Date();
17331                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17332 //                        this.fill()
17333                         this.setValue(this.formatDate(this.date));
17334                         
17335                         this.hide();
17336                         break;
17337                 }
17338                 break;
17339             case 'span':
17340                 if (className.indexOf('disabled') < 0) {
17341                     this.viewDate.setUTCDate(1);
17342                     if (className.indexOf('month') > -1) {
17343                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17344                     } else {
17345                         var year = parseInt(html, 10) || 0;
17346                         this.viewDate.setUTCFullYear(year);
17347                         
17348                     }
17349                     
17350                     if(this.singleMode){
17351                         this.setValue(this.formatDate(this.viewDate));
17352                         this.hide();
17353                         return;
17354                     }
17355                     
17356                     this.showMode(-1);
17357                     this.fill();
17358                 }
17359                 break;
17360                 
17361             case 'td':
17362                 //Roo.log(className);
17363                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17364                     var day = parseInt(html, 10) || 1;
17365                     var year = this.viewDate.getUTCFullYear(),
17366                         month = this.viewDate.getUTCMonth();
17367
17368                     if (className.indexOf('old') > -1) {
17369                         if(month === 0 ){
17370                             month = 11;
17371                             year -= 1;
17372                         }else{
17373                             month -= 1;
17374                         }
17375                     } else if (className.indexOf('new') > -1) {
17376                         if (month == 11) {
17377                             month = 0;
17378                             year += 1;
17379                         } else {
17380                             month += 1;
17381                         }
17382                     }
17383                     //Roo.log([year,month,day]);
17384                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17385                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17386 //                    this.fill();
17387                     //Roo.log(this.formatDate(this.date));
17388                     this.setValue(this.formatDate(this.date));
17389                     this.hide();
17390                 }
17391                 break;
17392         }
17393     },
17394     
17395     setStartDate: function(startDate)
17396     {
17397         this.startDate = startDate || -Infinity;
17398         if (this.startDate !== -Infinity) {
17399             this.startDate = this.parseDate(this.startDate);
17400         }
17401         this.update();
17402         this.updateNavArrows();
17403     },
17404
17405     setEndDate: function(endDate)
17406     {
17407         this.endDate = endDate || Infinity;
17408         if (this.endDate !== Infinity) {
17409             this.endDate = this.parseDate(this.endDate);
17410         }
17411         this.update();
17412         this.updateNavArrows();
17413     },
17414     
17415     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17416     {
17417         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17418         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17419             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17420         }
17421         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17422             return parseInt(d, 10);
17423         });
17424         this.update();
17425         this.updateNavArrows();
17426     },
17427     
17428     updateNavArrows: function() 
17429     {
17430         if(this.singleMode){
17431             return;
17432         }
17433         
17434         var d = new Date(this.viewDate),
17435         year = d.getUTCFullYear(),
17436         month = d.getUTCMonth();
17437         
17438         Roo.each(this.picker().select('.prev', true).elements, function(v){
17439             v.show();
17440             switch (this.viewMode) {
17441                 case 0:
17442
17443                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17444                         v.hide();
17445                     }
17446                     break;
17447                 case 1:
17448                 case 2:
17449                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17450                         v.hide();
17451                     }
17452                     break;
17453             }
17454         });
17455         
17456         Roo.each(this.picker().select('.next', true).elements, function(v){
17457             v.show();
17458             switch (this.viewMode) {
17459                 case 0:
17460
17461                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17462                         v.hide();
17463                     }
17464                     break;
17465                 case 1:
17466                 case 2:
17467                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17468                         v.hide();
17469                     }
17470                     break;
17471             }
17472         })
17473     },
17474     
17475     moveMonth: function(date, dir)
17476     {
17477         if (!dir) {
17478             return date;
17479         }
17480         var new_date = new Date(date.valueOf()),
17481         day = new_date.getUTCDate(),
17482         month = new_date.getUTCMonth(),
17483         mag = Math.abs(dir),
17484         new_month, test;
17485         dir = dir > 0 ? 1 : -1;
17486         if (mag == 1){
17487             test = dir == -1
17488             // If going back one month, make sure month is not current month
17489             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17490             ? function(){
17491                 return new_date.getUTCMonth() == month;
17492             }
17493             // If going forward one month, make sure month is as expected
17494             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17495             : function(){
17496                 return new_date.getUTCMonth() != new_month;
17497             };
17498             new_month = month + dir;
17499             new_date.setUTCMonth(new_month);
17500             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17501             if (new_month < 0 || new_month > 11) {
17502                 new_month = (new_month + 12) % 12;
17503             }
17504         } else {
17505             // For magnitudes >1, move one month at a time...
17506             for (var i=0; i<mag; i++) {
17507                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17508                 new_date = this.moveMonth(new_date, dir);
17509             }
17510             // ...then reset the day, keeping it in the new month
17511             new_month = new_date.getUTCMonth();
17512             new_date.setUTCDate(day);
17513             test = function(){
17514                 return new_month != new_date.getUTCMonth();
17515             };
17516         }
17517         // Common date-resetting loop -- if date is beyond end of month, make it
17518         // end of month
17519         while (test()){
17520             new_date.setUTCDate(--day);
17521             new_date.setUTCMonth(new_month);
17522         }
17523         return new_date;
17524     },
17525
17526     moveYear: function(date, dir)
17527     {
17528         return this.moveMonth(date, dir*12);
17529     },
17530
17531     dateWithinRange: function(date)
17532     {
17533         return date >= this.startDate && date <= this.endDate;
17534     },
17535
17536     
17537     remove: function() 
17538     {
17539         this.picker().remove();
17540     }
17541    
17542 });
17543
17544 Roo.apply(Roo.bootstrap.DateField,  {
17545     
17546     head : {
17547         tag: 'thead',
17548         cn: [
17549         {
17550             tag: 'tr',
17551             cn: [
17552             {
17553                 tag: 'th',
17554                 cls: 'prev',
17555                 html: '<i class="fa fa-arrow-left"/>'
17556             },
17557             {
17558                 tag: 'th',
17559                 cls: 'switch',
17560                 colspan: '5'
17561             },
17562             {
17563                 tag: 'th',
17564                 cls: 'next',
17565                 html: '<i class="fa fa-arrow-right"/>'
17566             }
17567
17568             ]
17569         }
17570         ]
17571     },
17572     
17573     content : {
17574         tag: 'tbody',
17575         cn: [
17576         {
17577             tag: 'tr',
17578             cn: [
17579             {
17580                 tag: 'td',
17581                 colspan: '7'
17582             }
17583             ]
17584         }
17585         ]
17586     },
17587     
17588     footer : {
17589         tag: 'tfoot',
17590         cn: [
17591         {
17592             tag: 'tr',
17593             cn: [
17594             {
17595                 tag: 'th',
17596                 colspan: '7',
17597                 cls: 'today'
17598             }
17599                     
17600             ]
17601         }
17602         ]
17603     },
17604     
17605     dates:{
17606         en: {
17607             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17608             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17609             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17610             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17611             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17612             today: "Today"
17613         }
17614     },
17615     
17616     modes: [
17617     {
17618         clsName: 'days',
17619         navFnc: 'Month',
17620         navStep: 1
17621     },
17622     {
17623         clsName: 'months',
17624         navFnc: 'FullYear',
17625         navStep: 1
17626     },
17627     {
17628         clsName: 'years',
17629         navFnc: 'FullYear',
17630         navStep: 10
17631     }]
17632 });
17633
17634 Roo.apply(Roo.bootstrap.DateField,  {
17635   
17636     template : {
17637         tag: 'div',
17638         cls: 'datepicker dropdown-menu roo-dynamic',
17639         cn: [
17640         {
17641             tag: 'div',
17642             cls: 'datepicker-days',
17643             cn: [
17644             {
17645                 tag: 'table',
17646                 cls: 'table-condensed',
17647                 cn:[
17648                 Roo.bootstrap.DateField.head,
17649                 {
17650                     tag: 'tbody'
17651                 },
17652                 Roo.bootstrap.DateField.footer
17653                 ]
17654             }
17655             ]
17656         },
17657         {
17658             tag: 'div',
17659             cls: 'datepicker-months',
17660             cn: [
17661             {
17662                 tag: 'table',
17663                 cls: 'table-condensed',
17664                 cn:[
17665                 Roo.bootstrap.DateField.head,
17666                 Roo.bootstrap.DateField.content,
17667                 Roo.bootstrap.DateField.footer
17668                 ]
17669             }
17670             ]
17671         },
17672         {
17673             tag: 'div',
17674             cls: 'datepicker-years',
17675             cn: [
17676             {
17677                 tag: 'table',
17678                 cls: 'table-condensed',
17679                 cn:[
17680                 Roo.bootstrap.DateField.head,
17681                 Roo.bootstrap.DateField.content,
17682                 Roo.bootstrap.DateField.footer
17683                 ]
17684             }
17685             ]
17686         }
17687         ]
17688     }
17689 });
17690
17691  
17692
17693  /*
17694  * - LGPL
17695  *
17696  * TimeField
17697  * 
17698  */
17699
17700 /**
17701  * @class Roo.bootstrap.TimeField
17702  * @extends Roo.bootstrap.Input
17703  * Bootstrap DateField class
17704  * 
17705  * 
17706  * @constructor
17707  * Create a new TimeField
17708  * @param {Object} config The config object
17709  */
17710
17711 Roo.bootstrap.TimeField = function(config){
17712     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17713     this.addEvents({
17714             /**
17715              * @event show
17716              * Fires when this field show.
17717              * @param {Roo.bootstrap.DateField} thisthis
17718              * @param {Mixed} date The date value
17719              */
17720             show : true,
17721             /**
17722              * @event show
17723              * Fires when this field hide.
17724              * @param {Roo.bootstrap.DateField} this
17725              * @param {Mixed} date The date value
17726              */
17727             hide : true,
17728             /**
17729              * @event select
17730              * Fires when select a date.
17731              * @param {Roo.bootstrap.DateField} this
17732              * @param {Mixed} date The date value
17733              */
17734             select : true
17735         });
17736 };
17737
17738 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17739     
17740     /**
17741      * @cfg {String} format
17742      * The default time format string which can be overriden for localization support.  The format must be
17743      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17744      */
17745     format : "H:i",
17746        
17747     onRender: function(ct, position)
17748     {
17749         
17750         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17751                 
17752         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17753         
17754         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17755         
17756         this.pop = this.picker().select('>.datepicker-time',true).first();
17757         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17758         
17759         this.picker().on('mousedown', this.onMousedown, this);
17760         this.picker().on('click', this.onClick, this);
17761         
17762         this.picker().addClass('datepicker-dropdown');
17763     
17764         this.fillTime();
17765         this.update();
17766             
17767         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17768         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17769         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17770         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17771         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17772         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17773
17774     },
17775     
17776     fireKey: function(e){
17777         if (!this.picker().isVisible()){
17778             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17779                 this.show();
17780             }
17781             return;
17782         }
17783
17784         e.preventDefault();
17785         
17786         switch(e.keyCode){
17787             case 27: // escape
17788                 this.hide();
17789                 break;
17790             case 37: // left
17791             case 39: // right
17792                 this.onTogglePeriod();
17793                 break;
17794             case 38: // up
17795                 this.onIncrementMinutes();
17796                 break;
17797             case 40: // down
17798                 this.onDecrementMinutes();
17799                 break;
17800             case 13: // enter
17801             case 9: // tab
17802                 this.setTime();
17803                 break;
17804         }
17805     },
17806     
17807     onClick: function(e) {
17808         e.stopPropagation();
17809         e.preventDefault();
17810     },
17811     
17812     picker : function()
17813     {
17814         return this.el.select('.datepicker', true).first();
17815     },
17816     
17817     fillTime: function()
17818     {    
17819         var time = this.pop.select('tbody', true).first();
17820         
17821         time.dom.innerHTML = '';
17822         
17823         time.createChild({
17824             tag: 'tr',
17825             cn: [
17826                 {
17827                     tag: 'td',
17828                     cn: [
17829                         {
17830                             tag: 'a',
17831                             href: '#',
17832                             cls: 'btn',
17833                             cn: [
17834                                 {
17835                                     tag: 'span',
17836                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17837                                 }
17838                             ]
17839                         } 
17840                     ]
17841                 },
17842                 {
17843                     tag: 'td',
17844                     cls: 'separator'
17845                 },
17846                 {
17847                     tag: 'td',
17848                     cn: [
17849                         {
17850                             tag: 'a',
17851                             href: '#',
17852                             cls: 'btn',
17853                             cn: [
17854                                 {
17855                                     tag: 'span',
17856                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17857                                 }
17858                             ]
17859                         }
17860                     ]
17861                 },
17862                 {
17863                     tag: 'td',
17864                     cls: 'separator'
17865                 }
17866             ]
17867         });
17868         
17869         time.createChild({
17870             tag: 'tr',
17871             cn: [
17872                 {
17873                     tag: 'td',
17874                     cn: [
17875                         {
17876                             tag: 'span',
17877                             cls: 'timepicker-hour',
17878                             html: '00'
17879                         }  
17880                     ]
17881                 },
17882                 {
17883                     tag: 'td',
17884                     cls: 'separator',
17885                     html: ':'
17886                 },
17887                 {
17888                     tag: 'td',
17889                     cn: [
17890                         {
17891                             tag: 'span',
17892                             cls: 'timepicker-minute',
17893                             html: '00'
17894                         }  
17895                     ]
17896                 },
17897                 {
17898                     tag: 'td',
17899                     cls: 'separator'
17900                 },
17901                 {
17902                     tag: 'td',
17903                     cn: [
17904                         {
17905                             tag: 'button',
17906                             type: 'button',
17907                             cls: 'btn btn-primary period',
17908                             html: 'AM'
17909                             
17910                         }
17911                     ]
17912                 }
17913             ]
17914         });
17915         
17916         time.createChild({
17917             tag: 'tr',
17918             cn: [
17919                 {
17920                     tag: 'td',
17921                     cn: [
17922                         {
17923                             tag: 'a',
17924                             href: '#',
17925                             cls: 'btn',
17926                             cn: [
17927                                 {
17928                                     tag: 'span',
17929                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17930                                 }
17931                             ]
17932                         }
17933                     ]
17934                 },
17935                 {
17936                     tag: 'td',
17937                     cls: 'separator'
17938                 },
17939                 {
17940                     tag: 'td',
17941                     cn: [
17942                         {
17943                             tag: 'a',
17944                             href: '#',
17945                             cls: 'btn',
17946                             cn: [
17947                                 {
17948                                     tag: 'span',
17949                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17950                                 }
17951                             ]
17952                         }
17953                     ]
17954                 },
17955                 {
17956                     tag: 'td',
17957                     cls: 'separator'
17958                 }
17959             ]
17960         });
17961         
17962     },
17963     
17964     update: function()
17965     {
17966         
17967         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17968         
17969         this.fill();
17970     },
17971     
17972     fill: function() 
17973     {
17974         var hours = this.time.getHours();
17975         var minutes = this.time.getMinutes();
17976         var period = 'AM';
17977         
17978         if(hours > 11){
17979             period = 'PM';
17980         }
17981         
17982         if(hours == 0){
17983             hours = 12;
17984         }
17985         
17986         
17987         if(hours > 12){
17988             hours = hours - 12;
17989         }
17990         
17991         if(hours < 10){
17992             hours = '0' + hours;
17993         }
17994         
17995         if(minutes < 10){
17996             minutes = '0' + minutes;
17997         }
17998         
17999         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18000         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18001         this.pop.select('button', true).first().dom.innerHTML = period;
18002         
18003     },
18004     
18005     place: function()
18006     {   
18007         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18008         
18009         var cls = ['bottom'];
18010         
18011         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18012             cls.pop();
18013             cls.push('top');
18014         }
18015         
18016         cls.push('right');
18017         
18018         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18019             cls.pop();
18020             cls.push('left');
18021         }
18022         
18023         this.picker().addClass(cls.join('-'));
18024         
18025         var _this = this;
18026         
18027         Roo.each(cls, function(c){
18028             if(c == 'bottom'){
18029                 _this.picker().setTop(_this.inputEl().getHeight());
18030                 return;
18031             }
18032             if(c == 'top'){
18033                 _this.picker().setTop(0 - _this.picker().getHeight());
18034                 return;
18035             }
18036             
18037             if(c == 'left'){
18038                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18039                 return;
18040             }
18041             if(c == 'right'){
18042                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18043                 return;
18044             }
18045         });
18046         
18047     },
18048   
18049     onFocus : function()
18050     {
18051         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18052         this.show();
18053     },
18054     
18055     onBlur : function()
18056     {
18057         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18058         this.hide();
18059     },
18060     
18061     show : function()
18062     {
18063         this.picker().show();
18064         this.pop.show();
18065         this.update();
18066         this.place();
18067         
18068         this.fireEvent('show', this, this.date);
18069     },
18070     
18071     hide : function()
18072     {
18073         this.picker().hide();
18074         this.pop.hide();
18075         
18076         this.fireEvent('hide', this, this.date);
18077     },
18078     
18079     setTime : function()
18080     {
18081         this.hide();
18082         this.setValue(this.time.format(this.format));
18083         
18084         this.fireEvent('select', this, this.date);
18085         
18086         
18087     },
18088     
18089     onMousedown: function(e){
18090         e.stopPropagation();
18091         e.preventDefault();
18092     },
18093     
18094     onIncrementHours: function()
18095     {
18096         Roo.log('onIncrementHours');
18097         this.time = this.time.add(Date.HOUR, 1);
18098         this.update();
18099         
18100     },
18101     
18102     onDecrementHours: function()
18103     {
18104         Roo.log('onDecrementHours');
18105         this.time = this.time.add(Date.HOUR, -1);
18106         this.update();
18107     },
18108     
18109     onIncrementMinutes: function()
18110     {
18111         Roo.log('onIncrementMinutes');
18112         this.time = this.time.add(Date.MINUTE, 1);
18113         this.update();
18114     },
18115     
18116     onDecrementMinutes: function()
18117     {
18118         Roo.log('onDecrementMinutes');
18119         this.time = this.time.add(Date.MINUTE, -1);
18120         this.update();
18121     },
18122     
18123     onTogglePeriod: function()
18124     {
18125         Roo.log('onTogglePeriod');
18126         this.time = this.time.add(Date.HOUR, 12);
18127         this.update();
18128     }
18129     
18130    
18131 });
18132
18133 Roo.apply(Roo.bootstrap.TimeField,  {
18134     
18135     content : {
18136         tag: 'tbody',
18137         cn: [
18138             {
18139                 tag: 'tr',
18140                 cn: [
18141                 {
18142                     tag: 'td',
18143                     colspan: '7'
18144                 }
18145                 ]
18146             }
18147         ]
18148     },
18149     
18150     footer : {
18151         tag: 'tfoot',
18152         cn: [
18153             {
18154                 tag: 'tr',
18155                 cn: [
18156                 {
18157                     tag: 'th',
18158                     colspan: '7',
18159                     cls: '',
18160                     cn: [
18161                         {
18162                             tag: 'button',
18163                             cls: 'btn btn-info ok',
18164                             html: 'OK'
18165                         }
18166                     ]
18167                 }
18168
18169                 ]
18170             }
18171         ]
18172     }
18173 });
18174
18175 Roo.apply(Roo.bootstrap.TimeField,  {
18176   
18177     template : {
18178         tag: 'div',
18179         cls: 'datepicker dropdown-menu',
18180         cn: [
18181             {
18182                 tag: 'div',
18183                 cls: 'datepicker-time',
18184                 cn: [
18185                 {
18186                     tag: 'table',
18187                     cls: 'table-condensed',
18188                     cn:[
18189                     Roo.bootstrap.TimeField.content,
18190                     Roo.bootstrap.TimeField.footer
18191                     ]
18192                 }
18193                 ]
18194             }
18195         ]
18196     }
18197 });
18198
18199  
18200
18201  /*
18202  * - LGPL
18203  *
18204  * MonthField
18205  * 
18206  */
18207
18208 /**
18209  * @class Roo.bootstrap.MonthField
18210  * @extends Roo.bootstrap.Input
18211  * Bootstrap MonthField class
18212  * 
18213  * @cfg {String} language default en
18214  * 
18215  * @constructor
18216  * Create a new MonthField
18217  * @param {Object} config The config object
18218  */
18219
18220 Roo.bootstrap.MonthField = function(config){
18221     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18222     
18223     this.addEvents({
18224         /**
18225          * @event show
18226          * Fires when this field show.
18227          * @param {Roo.bootstrap.MonthField} this
18228          * @param {Mixed} date The date value
18229          */
18230         show : true,
18231         /**
18232          * @event show
18233          * Fires when this field hide.
18234          * @param {Roo.bootstrap.MonthField} this
18235          * @param {Mixed} date The date value
18236          */
18237         hide : true,
18238         /**
18239          * @event select
18240          * Fires when select a date.
18241          * @param {Roo.bootstrap.MonthField} this
18242          * @param {String} oldvalue The old value
18243          * @param {String} newvalue The new value
18244          */
18245         select : true
18246     });
18247 };
18248
18249 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18250     
18251     onRender: function(ct, position)
18252     {
18253         
18254         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18255         
18256         this.language = this.language || 'en';
18257         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18258         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18259         
18260         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18261         this.isInline = false;
18262         this.isInput = true;
18263         this.component = this.el.select('.add-on', true).first() || false;
18264         this.component = (this.component && this.component.length === 0) ? false : this.component;
18265         this.hasInput = this.component && this.inputEL().length;
18266         
18267         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18268         
18269         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18270         
18271         this.picker().on('mousedown', this.onMousedown, this);
18272         this.picker().on('click', this.onClick, this);
18273         
18274         this.picker().addClass('datepicker-dropdown');
18275         
18276         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18277             v.setStyle('width', '189px');
18278         });
18279         
18280         this.fillMonths();
18281         
18282         this.update();
18283         
18284         if(this.isInline) {
18285             this.show();
18286         }
18287         
18288     },
18289     
18290     setValue: function(v, suppressEvent)
18291     {   
18292         var o = this.getValue();
18293         
18294         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18295         
18296         this.update();
18297
18298         if(suppressEvent !== true){
18299             this.fireEvent('select', this, o, v);
18300         }
18301         
18302     },
18303     
18304     getValue: function()
18305     {
18306         return this.value;
18307     },
18308     
18309     onClick: function(e) 
18310     {
18311         e.stopPropagation();
18312         e.preventDefault();
18313         
18314         var target = e.getTarget();
18315         
18316         if(target.nodeName.toLowerCase() === 'i'){
18317             target = Roo.get(target).dom.parentNode;
18318         }
18319         
18320         var nodeName = target.nodeName;
18321         var className = target.className;
18322         var html = target.innerHTML;
18323         
18324         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18325             return;
18326         }
18327         
18328         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18329         
18330         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18331         
18332         this.hide();
18333                         
18334     },
18335     
18336     picker : function()
18337     {
18338         return this.pickerEl;
18339     },
18340     
18341     fillMonths: function()
18342     {    
18343         var i = 0;
18344         var months = this.picker().select('>.datepicker-months td', true).first();
18345         
18346         months.dom.innerHTML = '';
18347         
18348         while (i < 12) {
18349             var month = {
18350                 tag: 'span',
18351                 cls: 'month',
18352                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18353             };
18354             
18355             months.createChild(month);
18356         }
18357         
18358     },
18359     
18360     update: function()
18361     {
18362         var _this = this;
18363         
18364         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18365             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18366         }
18367         
18368         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18369             e.removeClass('active');
18370             
18371             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18372                 e.addClass('active');
18373             }
18374         })
18375     },
18376     
18377     place: function()
18378     {
18379         if(this.isInline) {
18380             return;
18381         }
18382         
18383         this.picker().removeClass(['bottom', 'top']);
18384         
18385         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18386             /*
18387              * place to the top of element!
18388              *
18389              */
18390             
18391             this.picker().addClass('top');
18392             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18393             
18394             return;
18395         }
18396         
18397         this.picker().addClass('bottom');
18398         
18399         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18400     },
18401     
18402     onFocus : function()
18403     {
18404         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18405         this.show();
18406     },
18407     
18408     onBlur : function()
18409     {
18410         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18411         
18412         var d = this.inputEl().getValue();
18413         
18414         this.setValue(d);
18415                 
18416         this.hide();
18417     },
18418     
18419     show : function()
18420     {
18421         this.picker().show();
18422         this.picker().select('>.datepicker-months', true).first().show();
18423         this.update();
18424         this.place();
18425         
18426         this.fireEvent('show', this, this.date);
18427     },
18428     
18429     hide : function()
18430     {
18431         if(this.isInline) {
18432             return;
18433         }
18434         this.picker().hide();
18435         this.fireEvent('hide', this, this.date);
18436         
18437     },
18438     
18439     onMousedown: function(e)
18440     {
18441         e.stopPropagation();
18442         e.preventDefault();
18443     },
18444     
18445     keyup: function(e)
18446     {
18447         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18448         this.update();
18449     },
18450
18451     fireKey: function(e)
18452     {
18453         if (!this.picker().isVisible()){
18454             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18455                 this.show();
18456             }
18457             return;
18458         }
18459         
18460         var dir;
18461         
18462         switch(e.keyCode){
18463             case 27: // escape
18464                 this.hide();
18465                 e.preventDefault();
18466                 break;
18467             case 37: // left
18468             case 39: // right
18469                 dir = e.keyCode == 37 ? -1 : 1;
18470                 
18471                 this.vIndex = this.vIndex + dir;
18472                 
18473                 if(this.vIndex < 0){
18474                     this.vIndex = 0;
18475                 }
18476                 
18477                 if(this.vIndex > 11){
18478                     this.vIndex = 11;
18479                 }
18480                 
18481                 if(isNaN(this.vIndex)){
18482                     this.vIndex = 0;
18483                 }
18484                 
18485                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18486                 
18487                 break;
18488             case 38: // up
18489             case 40: // down
18490                 
18491                 dir = e.keyCode == 38 ? -1 : 1;
18492                 
18493                 this.vIndex = this.vIndex + dir * 4;
18494                 
18495                 if(this.vIndex < 0){
18496                     this.vIndex = 0;
18497                 }
18498                 
18499                 if(this.vIndex > 11){
18500                     this.vIndex = 11;
18501                 }
18502                 
18503                 if(isNaN(this.vIndex)){
18504                     this.vIndex = 0;
18505                 }
18506                 
18507                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18508                 break;
18509                 
18510             case 13: // enter
18511                 
18512                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18513                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18514                 }
18515                 
18516                 this.hide();
18517                 e.preventDefault();
18518                 break;
18519             case 9: // tab
18520                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18521                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18522                 }
18523                 this.hide();
18524                 break;
18525             case 16: // shift
18526             case 17: // ctrl
18527             case 18: // alt
18528                 break;
18529             default :
18530                 this.hide();
18531                 
18532         }
18533     },
18534     
18535     remove: function() 
18536     {
18537         this.picker().remove();
18538     }
18539    
18540 });
18541
18542 Roo.apply(Roo.bootstrap.MonthField,  {
18543     
18544     content : {
18545         tag: 'tbody',
18546         cn: [
18547         {
18548             tag: 'tr',
18549             cn: [
18550             {
18551                 tag: 'td',
18552                 colspan: '7'
18553             }
18554             ]
18555         }
18556         ]
18557     },
18558     
18559     dates:{
18560         en: {
18561             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18562             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18563         }
18564     }
18565 });
18566
18567 Roo.apply(Roo.bootstrap.MonthField,  {
18568   
18569     template : {
18570         tag: 'div',
18571         cls: 'datepicker dropdown-menu roo-dynamic',
18572         cn: [
18573             {
18574                 tag: 'div',
18575                 cls: 'datepicker-months',
18576                 cn: [
18577                 {
18578                     tag: 'table',
18579                     cls: 'table-condensed',
18580                     cn:[
18581                         Roo.bootstrap.DateField.content
18582                     ]
18583                 }
18584                 ]
18585             }
18586         ]
18587     }
18588 });
18589
18590  
18591
18592  
18593  /*
18594  * - LGPL
18595  *
18596  * CheckBox
18597  * 
18598  */
18599
18600 /**
18601  * @class Roo.bootstrap.CheckBox
18602  * @extends Roo.bootstrap.Input
18603  * Bootstrap CheckBox class
18604  * 
18605  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18606  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18607  * @cfg {String} boxLabel The text that appears beside the checkbox
18608  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18609  * @cfg {Boolean} checked initnal the element
18610  * @cfg {Boolean} inline inline the element (default false)
18611  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18612  * 
18613  * @constructor
18614  * Create a new CheckBox
18615  * @param {Object} config The config object
18616  */
18617
18618 Roo.bootstrap.CheckBox = function(config){
18619     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18620    
18621     this.addEvents({
18622         /**
18623         * @event check
18624         * Fires when the element is checked or unchecked.
18625         * @param {Roo.bootstrap.CheckBox} this This input
18626         * @param {Boolean} checked The new checked value
18627         */
18628        check : true
18629     });
18630     
18631 };
18632
18633 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18634   
18635     inputType: 'checkbox',
18636     inputValue: 1,
18637     valueOff: 0,
18638     boxLabel: false,
18639     checked: false,
18640     weight : false,
18641     inline: false,
18642     
18643     getAutoCreate : function()
18644     {
18645         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18646         
18647         var id = Roo.id();
18648         
18649         var cfg = {};
18650         
18651         cfg.cls = 'form-group ' + this.inputType; //input-group
18652         
18653         if(this.inline){
18654             cfg.cls += ' ' + this.inputType + '-inline';
18655         }
18656         
18657         var input =  {
18658             tag: 'input',
18659             id : id,
18660             type : this.inputType,
18661             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18662             cls : 'roo-' + this.inputType, //'form-box',
18663             placeholder : this.placeholder || ''
18664             
18665         };
18666         
18667         if (this.weight) { // Validity check?
18668             cfg.cls += " " + this.inputType + "-" + this.weight;
18669         }
18670         
18671         if (this.disabled) {
18672             input.disabled=true;
18673         }
18674         
18675         if(this.checked){
18676             input.checked = this.checked;
18677         }
18678         
18679         if (this.name) {
18680             input.name = this.name;
18681         }
18682         
18683         if (this.size) {
18684             input.cls += ' input-' + this.size;
18685         }
18686         
18687         var settings=this;
18688         
18689         ['xs','sm','md','lg'].map(function(size){
18690             if (settings[size]) {
18691                 cfg.cls += ' col-' + size + '-' + settings[size];
18692             }
18693         });
18694         
18695         var inputblock = input;
18696          
18697         if (this.before || this.after) {
18698             
18699             inputblock = {
18700                 cls : 'input-group',
18701                 cn :  [] 
18702             };
18703             
18704             if (this.before) {
18705                 inputblock.cn.push({
18706                     tag :'span',
18707                     cls : 'input-group-addon',
18708                     html : this.before
18709                 });
18710             }
18711             
18712             inputblock.cn.push(input);
18713             
18714             if (this.after) {
18715                 inputblock.cn.push({
18716                     tag :'span',
18717                     cls : 'input-group-addon',
18718                     html : this.after
18719                 });
18720             }
18721             
18722         }
18723         
18724         if (align ==='left' && this.fieldLabel.length) {
18725 //                Roo.log("left and has label");
18726                 cfg.cn = [
18727                     
18728                     {
18729                         tag: 'label',
18730                         'for' :  id,
18731                         cls : 'control-label col-md-' + this.labelWidth,
18732                         html : this.fieldLabel
18733                         
18734                     },
18735                     {
18736                         cls : "col-md-" + (12 - this.labelWidth), 
18737                         cn: [
18738                             inputblock
18739                         ]
18740                     }
18741                     
18742                 ];
18743         } else if ( this.fieldLabel.length) {
18744 //                Roo.log(" label");
18745                 cfg.cn = [
18746                    
18747                     {
18748                         tag: this.boxLabel ? 'span' : 'label',
18749                         'for': id,
18750                         cls: 'control-label box-input-label',
18751                         //cls : 'input-group-addon',
18752                         html : this.fieldLabel
18753                         
18754                     },
18755                     
18756                     inputblock
18757                     
18758                 ];
18759
18760         } else {
18761             
18762 //                Roo.log(" no label && no align");
18763                 cfg.cn = [  inputblock ] ;
18764                 
18765                 
18766         }
18767         if(this.boxLabel){
18768              var boxLabelCfg = {
18769                 tag: 'label',
18770                 //'for': id, // box label is handled by onclick - so no for...
18771                 cls: 'box-label',
18772                 html: this.boxLabel
18773             };
18774             
18775             if(this.tooltip){
18776                 boxLabelCfg.tooltip = this.tooltip;
18777             }
18778              
18779             cfg.cn.push(boxLabelCfg);
18780         }
18781         
18782         
18783        
18784         return cfg;
18785         
18786     },
18787     
18788     /**
18789      * return the real input element.
18790      */
18791     inputEl: function ()
18792     {
18793         return this.el.select('input.roo-' + this.inputType,true).first();
18794     },
18795     
18796     labelEl: function()
18797     {
18798         return this.el.select('label.control-label',true).first();
18799     },
18800     /* depricated... */
18801     
18802     label: function()
18803     {
18804         return this.labelEl();
18805     },
18806     
18807     initEvents : function()
18808     {
18809 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18810         
18811         this.inputEl().on('click', this.onClick,  this);
18812         
18813         if (this.boxLabel) { 
18814             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18815         }
18816         
18817         this.startValue = this.getValue();
18818         
18819         if(this.groupId){
18820             Roo.bootstrap.CheckBox.register(this);
18821         }
18822     },
18823     
18824     onClick : function()
18825     {   
18826         this.setChecked(!this.checked);
18827     },
18828     
18829     setChecked : function(state,suppressEvent)
18830     {
18831         this.startValue = this.getValue();
18832         
18833         if(this.inputType == 'radio'){
18834             
18835             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18836                 e.dom.checked = false;
18837             });
18838             
18839             this.inputEl().dom.checked = true;
18840             
18841             this.inputEl().dom.value = this.inputValue;
18842             
18843             if(suppressEvent !== true){
18844                 this.fireEvent('check', this, true);
18845             }
18846             
18847             this.validate();
18848             
18849             return;
18850         }
18851         
18852         this.checked = state;
18853         
18854         this.inputEl().dom.checked = state;
18855         
18856         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18857         
18858         if(suppressEvent !== true){
18859             this.fireEvent('check', this, state);
18860         }
18861         
18862         this.validate();
18863     },
18864     
18865     getValue : function()
18866     {
18867         if(this.inputType == 'radio'){
18868             return this.getGroupValue();
18869         }
18870         
18871         return this.inputEl().getValue();
18872         
18873     },
18874     
18875     getGroupValue : function()
18876     {
18877         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18878             return '';
18879         }
18880         
18881         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18882     },
18883     
18884     setValue : function(v,suppressEvent)
18885     {
18886         if(this.inputType == 'radio'){
18887             this.setGroupValue(v, suppressEvent);
18888             return;
18889         }
18890         
18891         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18892         
18893         this.validate();
18894     },
18895     
18896     setGroupValue : function(v, suppressEvent)
18897     {
18898         this.startValue = this.getValue();
18899         
18900         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18901             e.dom.checked = false;
18902             
18903             if(e.dom.value == v){
18904                 e.dom.checked = true;
18905             }
18906         });
18907         
18908         if(suppressEvent !== true){
18909             this.fireEvent('check', this, true);
18910         }
18911
18912         this.validate();
18913         
18914         return;
18915     },
18916     
18917     validate : function()
18918     {
18919         if(
18920                 this.disabled || 
18921                 (this.inputType == 'radio' && this.validateRadio()) ||
18922                 (this.inputType == 'checkbox' && this.validateCheckbox())
18923         ){
18924             this.markValid();
18925             return true;
18926         }
18927         
18928         this.markInvalid();
18929         return false;
18930     },
18931     
18932     validateRadio : function()
18933     {
18934         var valid = false;
18935         
18936         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18937             if(!e.dom.checked){
18938                 return;
18939             }
18940             
18941             valid = true;
18942             
18943             return false;
18944         });
18945         
18946         return valid;
18947     },
18948     
18949     validateCheckbox : function()
18950     {
18951         if(!this.groupId){
18952             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18953         }
18954         
18955         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18956         
18957         if(!group){
18958             return false;
18959         }
18960         
18961         var r = false;
18962         
18963         for(var i in group){
18964             if(r){
18965                 break;
18966             }
18967             
18968             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18969         }
18970         
18971         return r;
18972     },
18973     
18974     /**
18975      * Mark this field as valid
18976      */
18977     markValid : function()
18978     {
18979         if(this.allowBlank){
18980             return;
18981         }
18982         
18983         var _this = this;
18984         
18985         this.fireEvent('valid', this);
18986         
18987         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18988         
18989         if(this.groupId){
18990             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18991         }
18992         
18993         if(label){
18994             label.markValid();
18995         }
18996         
18997         if(this.inputType == 'radio'){
18998             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18999                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19000                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19001             });
19002             
19003             return;
19004         }
19005         
19006         if(!this.groupId){
19007             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19008             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19009             return;
19010         }
19011         
19012         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19013             
19014         if(!group){
19015             return;
19016         }
19017         
19018         for(var i in group){
19019             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19020             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19021         }
19022     },
19023     
19024      /**
19025      * Mark this field as invalid
19026      * @param {String} msg The validation message
19027      */
19028     markInvalid : function(msg)
19029     {
19030         if(this.allowBlank){
19031             return;
19032         }
19033         
19034         var _this = this;
19035         
19036         this.fireEvent('invalid', this, msg);
19037         
19038         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19039         
19040         if(this.groupId){
19041             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19042         }
19043         
19044         if(label){
19045             label.markInvalid();
19046         }
19047             
19048         if(this.inputType == 'radio'){
19049             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19050                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19051                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19052             });
19053             
19054             return;
19055         }
19056         
19057         if(!this.groupId){
19058             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19059             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19060             return;
19061         }
19062         
19063         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19064         
19065         if(!group){
19066             return;
19067         }
19068         
19069         for(var i in group){
19070             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19071             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19072         }
19073         
19074     }
19075     
19076 });
19077
19078 Roo.apply(Roo.bootstrap.CheckBox, {
19079     
19080     groups: {},
19081     
19082      /**
19083     * register a CheckBox Group
19084     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19085     */
19086     register : function(checkbox)
19087     {
19088         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19089             this.groups[checkbox.groupId] = {};
19090         }
19091         
19092         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19093             return;
19094         }
19095         
19096         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19097         
19098     },
19099     /**
19100     * fetch a CheckBox Group based on the group ID
19101     * @param {string} the group ID
19102     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19103     */
19104     get: function(groupId) {
19105         if (typeof(this.groups[groupId]) == 'undefined') {
19106             return false;
19107         }
19108         
19109         return this.groups[groupId] ;
19110     }
19111     
19112     
19113 });
19114 /*
19115  * - LGPL
19116  *
19117  * Radio
19118  *
19119  *
19120  * not inline
19121  *<div class="radio">
19122   <label>
19123     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19124     Option one is this and that&mdash;be sure to include why it's great
19125   </label>
19126 </div>
19127  *
19128  *
19129  *inline
19130  *<span>
19131  *<label class="radio-inline">fieldLabel</label>
19132  *<label class="radio-inline">
19133   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19134 </label>
19135 <span>
19136  * 
19137  * 
19138  */
19139
19140 /**
19141  * @class Roo.bootstrap.Radio
19142  * @extends Roo.bootstrap.CheckBox
19143  * Bootstrap Radio class
19144
19145  * @constructor
19146  * Create a new Radio
19147  * @param {Object} config The config object
19148  */
19149
19150 Roo.bootstrap.Radio = function(config){
19151     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19152    
19153 };
19154
19155 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19156     
19157     inputType: 'radio',
19158     inputValue: '',
19159     valueOff: '',
19160     
19161     getAutoCreate : function()
19162     {
19163         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19164         align = align || 'left'; // default...
19165         
19166         
19167         
19168         var id = Roo.id();
19169         
19170         var cfg = {
19171                 tag : this.inline ? 'span' : 'div',
19172                 cls : '',
19173                 cn : []
19174         };
19175         
19176         var inline = this.inline ? ' radio-inline' : '';
19177         
19178         var lbl = {
19179                 tag: 'label' ,
19180                 // does not need for, as we wrap the input with it..
19181                 'for' : id,
19182                 cls : 'control-label box-label' + inline,
19183                 cn : []
19184         };
19185         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19186         
19187         var fieldLabel = {
19188             tag: 'label' ,
19189             //cls : 'control-label' + inline,
19190             html : this.fieldLabel,
19191             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19192         };
19193         
19194  
19195         
19196         
19197         var input =  {
19198             tag: 'input',
19199             id : id,
19200             type : this.inputType,
19201             //value : (!this.checked) ? this.valueOff : this.inputValue,
19202             value : this.inputValue,
19203             cls : 'roo-radio',
19204             placeholder : this.placeholder || '' // ?? needed????
19205             
19206         };
19207         if (this.weight) { // Validity check?
19208             input.cls += " radio-" + this.weight;
19209         }
19210         if (this.disabled) {
19211             input.disabled=true;
19212         }
19213         
19214         if(this.checked){
19215             input.checked = this.checked;
19216         }
19217         
19218         if (this.name) {
19219             input.name = this.name;
19220         }
19221         
19222         if (this.size) {
19223             input.cls += ' input-' + this.size;
19224         }
19225         
19226         //?? can span's inline have a width??
19227         
19228         var settings=this;
19229         ['xs','sm','md','lg'].map(function(size){
19230             if (settings[size]) {
19231                 cfg.cls += ' col-' + size + '-' + settings[size];
19232             }
19233         });
19234         
19235         var inputblock = input;
19236         
19237         if (this.before || this.after) {
19238             
19239             inputblock = {
19240                 cls : 'input-group',
19241                 tag : 'span',
19242                 cn :  [] 
19243             };
19244             if (this.before) {
19245                 inputblock.cn.push({
19246                     tag :'span',
19247                     cls : 'input-group-addon',
19248                     html : this.before
19249                 });
19250             }
19251             inputblock.cn.push(input);
19252             if (this.after) {
19253                 inputblock.cn.push({
19254                     tag :'span',
19255                     cls : 'input-group-addon',
19256                     html : this.after
19257                 });
19258             }
19259             
19260         };
19261         
19262         
19263         if (this.fieldLabel && this.fieldLabel.length) {
19264             cfg.cn.push(fieldLabel);
19265         }
19266        
19267         // normal bootstrap puts the input inside the label.
19268         // however with our styled version - it has to go after the input.
19269        
19270         //lbl.cn.push(inputblock);
19271         
19272         var lblwrap =  {
19273             tag: 'span',
19274             cls: 'radio' + inline,
19275             cn: [
19276                 inputblock,
19277                 lbl
19278             ]
19279         };
19280         
19281         cfg.cn.push( lblwrap);
19282         
19283         if(this.boxLabel){
19284             lbl.cn.push({
19285                 tag: 'span',
19286                 html: this.boxLabel
19287             })
19288         }
19289          
19290         
19291         return cfg;
19292         
19293     },
19294     
19295     initEvents : function()
19296     {
19297 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19298         
19299         this.inputEl().on('click', this.onClick,  this);
19300         if (this.boxLabel) {
19301             //Roo.log('find label');
19302             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19303         }
19304         
19305     },
19306     
19307     inputEl: function ()
19308     {
19309         return this.el.select('input.roo-radio',true).first();
19310     },
19311     onClick : function()
19312     {   
19313         Roo.log("click");
19314         this.setChecked(true);
19315     },
19316     
19317     setChecked : function(state,suppressEvent)
19318     {
19319         if(state){
19320             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19321                 v.dom.checked = false;
19322             });
19323         }
19324         Roo.log(this.inputEl().dom);
19325         this.checked = state;
19326         this.inputEl().dom.checked = state;
19327         
19328         if(suppressEvent !== true){
19329             this.fireEvent('check', this, state);
19330         }
19331         
19332         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19333         
19334     },
19335     
19336     getGroupValue : function()
19337     {
19338         var value = '';
19339         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19340             if(v.dom.checked == true){
19341                 value = v.dom.value;
19342             }
19343         });
19344         
19345         return value;
19346     },
19347     
19348     /**
19349      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19350      * @return {Mixed} value The field value
19351      */
19352     getValue : function(){
19353         return this.getGroupValue();
19354     }
19355     
19356 });
19357
19358  
19359 //<script type="text/javascript">
19360
19361 /*
19362  * Based  Ext JS Library 1.1.1
19363  * Copyright(c) 2006-2007, Ext JS, LLC.
19364  * LGPL
19365  *
19366  */
19367  
19368 /**
19369  * @class Roo.HtmlEditorCore
19370  * @extends Roo.Component
19371  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19372  *
19373  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19374  */
19375
19376 Roo.HtmlEditorCore = function(config){
19377     
19378     
19379     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19380     
19381     
19382     this.addEvents({
19383         /**
19384          * @event initialize
19385          * Fires when the editor is fully initialized (including the iframe)
19386          * @param {Roo.HtmlEditorCore} this
19387          */
19388         initialize: true,
19389         /**
19390          * @event activate
19391          * Fires when the editor is first receives the focus. Any insertion must wait
19392          * until after this event.
19393          * @param {Roo.HtmlEditorCore} this
19394          */
19395         activate: true,
19396          /**
19397          * @event beforesync
19398          * Fires before the textarea is updated with content from the editor iframe. Return false
19399          * to cancel the sync.
19400          * @param {Roo.HtmlEditorCore} this
19401          * @param {String} html
19402          */
19403         beforesync: true,
19404          /**
19405          * @event beforepush
19406          * Fires before the iframe editor is updated with content from the textarea. Return false
19407          * to cancel the push.
19408          * @param {Roo.HtmlEditorCore} this
19409          * @param {String} html
19410          */
19411         beforepush: true,
19412          /**
19413          * @event sync
19414          * Fires when the textarea is updated with content from the editor iframe.
19415          * @param {Roo.HtmlEditorCore} this
19416          * @param {String} html
19417          */
19418         sync: true,
19419          /**
19420          * @event push
19421          * Fires when the iframe editor is updated with content from the textarea.
19422          * @param {Roo.HtmlEditorCore} this
19423          * @param {String} html
19424          */
19425         push: true,
19426         
19427         /**
19428          * @event editorevent
19429          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19430          * @param {Roo.HtmlEditorCore} this
19431          */
19432         editorevent: true
19433         
19434     });
19435     
19436     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19437     
19438     // defaults : white / black...
19439     this.applyBlacklists();
19440     
19441     
19442     
19443 };
19444
19445
19446 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19447
19448
19449      /**
19450      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19451      */
19452     
19453     owner : false,
19454     
19455      /**
19456      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19457      *                        Roo.resizable.
19458      */
19459     resizable : false,
19460      /**
19461      * @cfg {Number} height (in pixels)
19462      */   
19463     height: 300,
19464    /**
19465      * @cfg {Number} width (in pixels)
19466      */   
19467     width: 500,
19468     
19469     /**
19470      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19471      * 
19472      */
19473     stylesheets: false,
19474     
19475     // id of frame..
19476     frameId: false,
19477     
19478     // private properties
19479     validationEvent : false,
19480     deferHeight: true,
19481     initialized : false,
19482     activated : false,
19483     sourceEditMode : false,
19484     onFocus : Roo.emptyFn,
19485     iframePad:3,
19486     hideMode:'offsets',
19487     
19488     clearUp: true,
19489     
19490     // blacklist + whitelisted elements..
19491     black: false,
19492     white: false,
19493      
19494     
19495
19496     /**
19497      * Protected method that will not generally be called directly. It
19498      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19499      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19500      */
19501     getDocMarkup : function(){
19502         // body styles..
19503         var st = '';
19504         
19505         // inherit styels from page...?? 
19506         if (this.stylesheets === false) {
19507             
19508             Roo.get(document.head).select('style').each(function(node) {
19509                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19510             });
19511             
19512             Roo.get(document.head).select('link').each(function(node) { 
19513                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19514             });
19515             
19516         } else if (!this.stylesheets.length) {
19517                 // simple..
19518                 st = '<style type="text/css">' +
19519                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19520                    '</style>';
19521         } else { 
19522             
19523         }
19524         
19525         st +=  '<style type="text/css">' +
19526             'IMG { cursor: pointer } ' +
19527         '</style>';
19528
19529         
19530         return '<html><head>' + st  +
19531             //<style type="text/css">' +
19532             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19533             //'</style>' +
19534             ' </head><body class="roo-htmleditor-body"></body></html>';
19535     },
19536
19537     // private
19538     onRender : function(ct, position)
19539     {
19540         var _t = this;
19541         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19542         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19543         
19544         
19545         this.el.dom.style.border = '0 none';
19546         this.el.dom.setAttribute('tabIndex', -1);
19547         this.el.addClass('x-hidden hide');
19548         
19549         
19550         
19551         if(Roo.isIE){ // fix IE 1px bogus margin
19552             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19553         }
19554        
19555         
19556         this.frameId = Roo.id();
19557         
19558          
19559         
19560         var iframe = this.owner.wrap.createChild({
19561             tag: 'iframe',
19562             cls: 'form-control', // bootstrap..
19563             id: this.frameId,
19564             name: this.frameId,
19565             frameBorder : 'no',
19566             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19567         }, this.el
19568         );
19569         
19570         
19571         this.iframe = iframe.dom;
19572
19573          this.assignDocWin();
19574         
19575         this.doc.designMode = 'on';
19576        
19577         this.doc.open();
19578         this.doc.write(this.getDocMarkup());
19579         this.doc.close();
19580
19581         
19582         var task = { // must defer to wait for browser to be ready
19583             run : function(){
19584                 //console.log("run task?" + this.doc.readyState);
19585                 this.assignDocWin();
19586                 if(this.doc.body || this.doc.readyState == 'complete'){
19587                     try {
19588                         this.doc.designMode="on";
19589                     } catch (e) {
19590                         return;
19591                     }
19592                     Roo.TaskMgr.stop(task);
19593                     this.initEditor.defer(10, this);
19594                 }
19595             },
19596             interval : 10,
19597             duration: 10000,
19598             scope: this
19599         };
19600         Roo.TaskMgr.start(task);
19601
19602     },
19603
19604     // private
19605     onResize : function(w, h)
19606     {
19607          Roo.log('resize: ' +w + ',' + h );
19608         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19609         if(!this.iframe){
19610             return;
19611         }
19612         if(typeof w == 'number'){
19613             
19614             this.iframe.style.width = w + 'px';
19615         }
19616         if(typeof h == 'number'){
19617             
19618             this.iframe.style.height = h + 'px';
19619             if(this.doc){
19620                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19621             }
19622         }
19623         
19624     },
19625
19626     /**
19627      * Toggles the editor between standard and source edit mode.
19628      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19629      */
19630     toggleSourceEdit : function(sourceEditMode){
19631         
19632         this.sourceEditMode = sourceEditMode === true;
19633         
19634         if(this.sourceEditMode){
19635  
19636             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19637             
19638         }else{
19639             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19640             //this.iframe.className = '';
19641             this.deferFocus();
19642         }
19643         //this.setSize(this.owner.wrap.getSize());
19644         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19645     },
19646
19647     
19648   
19649
19650     /**
19651      * Protected method that will not generally be called directly. If you need/want
19652      * custom HTML cleanup, this is the method you should override.
19653      * @param {String} html The HTML to be cleaned
19654      * return {String} The cleaned HTML
19655      */
19656     cleanHtml : function(html){
19657         html = String(html);
19658         if(html.length > 5){
19659             if(Roo.isSafari){ // strip safari nonsense
19660                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19661             }
19662         }
19663         if(html == '&nbsp;'){
19664             html = '';
19665         }
19666         return html;
19667     },
19668
19669     /**
19670      * HTML Editor -> Textarea
19671      * Protected method that will not generally be called directly. Syncs the contents
19672      * of the editor iframe with the textarea.
19673      */
19674     syncValue : function(){
19675         if(this.initialized){
19676             var bd = (this.doc.body || this.doc.documentElement);
19677             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19678             var html = bd.innerHTML;
19679             if(Roo.isSafari){
19680                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19681                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19682                 if(m && m[1]){
19683                     html = '<div style="'+m[0]+'">' + html + '</div>';
19684                 }
19685             }
19686             html = this.cleanHtml(html);
19687             // fix up the special chars.. normaly like back quotes in word...
19688             // however we do not want to do this with chinese..
19689             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19690                 var cc = b.charCodeAt();
19691                 if (
19692                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19693                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19694                     (cc >= 0xf900 && cc < 0xfb00 )
19695                 ) {
19696                         return b;
19697                 }
19698                 return "&#"+cc+";" 
19699             });
19700             if(this.owner.fireEvent('beforesync', this, html) !== false){
19701                 this.el.dom.value = html;
19702                 this.owner.fireEvent('sync', this, html);
19703             }
19704         }
19705     },
19706
19707     /**
19708      * Protected method that will not generally be called directly. Pushes the value of the textarea
19709      * into the iframe editor.
19710      */
19711     pushValue : function(){
19712         if(this.initialized){
19713             var v = this.el.dom.value.trim();
19714             
19715 //            if(v.length < 1){
19716 //                v = '&#160;';
19717 //            }
19718             
19719             if(this.owner.fireEvent('beforepush', this, v) !== false){
19720                 var d = (this.doc.body || this.doc.documentElement);
19721                 d.innerHTML = v;
19722                 this.cleanUpPaste();
19723                 this.el.dom.value = d.innerHTML;
19724                 this.owner.fireEvent('push', this, v);
19725             }
19726         }
19727     },
19728
19729     // private
19730     deferFocus : function(){
19731         this.focus.defer(10, this);
19732     },
19733
19734     // doc'ed in Field
19735     focus : function(){
19736         if(this.win && !this.sourceEditMode){
19737             this.win.focus();
19738         }else{
19739             this.el.focus();
19740         }
19741     },
19742     
19743     assignDocWin: function()
19744     {
19745         var iframe = this.iframe;
19746         
19747          if(Roo.isIE){
19748             this.doc = iframe.contentWindow.document;
19749             this.win = iframe.contentWindow;
19750         } else {
19751 //            if (!Roo.get(this.frameId)) {
19752 //                return;
19753 //            }
19754 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19755 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19756             
19757             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19758                 return;
19759             }
19760             
19761             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19762             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19763         }
19764     },
19765     
19766     // private
19767     initEditor : function(){
19768         //console.log("INIT EDITOR");
19769         this.assignDocWin();
19770         
19771         
19772         
19773         this.doc.designMode="on";
19774         this.doc.open();
19775         this.doc.write(this.getDocMarkup());
19776         this.doc.close();
19777         
19778         var dbody = (this.doc.body || this.doc.documentElement);
19779         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19780         // this copies styles from the containing element into thsi one..
19781         // not sure why we need all of this..
19782         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19783         
19784         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19785         //ss['background-attachment'] = 'fixed'; // w3c
19786         dbody.bgProperties = 'fixed'; // ie
19787         //Roo.DomHelper.applyStyles(dbody, ss);
19788         Roo.EventManager.on(this.doc, {
19789             //'mousedown': this.onEditorEvent,
19790             'mouseup': this.onEditorEvent,
19791             'dblclick': this.onEditorEvent,
19792             'click': this.onEditorEvent,
19793             'keyup': this.onEditorEvent,
19794             buffer:100,
19795             scope: this
19796         });
19797         if(Roo.isGecko){
19798             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19799         }
19800         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19801             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19802         }
19803         this.initialized = true;
19804
19805         this.owner.fireEvent('initialize', this);
19806         this.pushValue();
19807     },
19808
19809     // private
19810     onDestroy : function(){
19811         
19812         
19813         
19814         if(this.rendered){
19815             
19816             //for (var i =0; i < this.toolbars.length;i++) {
19817             //    // fixme - ask toolbars for heights?
19818             //    this.toolbars[i].onDestroy();
19819            // }
19820             
19821             //this.wrap.dom.innerHTML = '';
19822             //this.wrap.remove();
19823         }
19824     },
19825
19826     // private
19827     onFirstFocus : function(){
19828         
19829         this.assignDocWin();
19830         
19831         
19832         this.activated = true;
19833          
19834     
19835         if(Roo.isGecko){ // prevent silly gecko errors
19836             this.win.focus();
19837             var s = this.win.getSelection();
19838             if(!s.focusNode || s.focusNode.nodeType != 3){
19839                 var r = s.getRangeAt(0);
19840                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19841                 r.collapse(true);
19842                 this.deferFocus();
19843             }
19844             try{
19845                 this.execCmd('useCSS', true);
19846                 this.execCmd('styleWithCSS', false);
19847             }catch(e){}
19848         }
19849         this.owner.fireEvent('activate', this);
19850     },
19851
19852     // private
19853     adjustFont: function(btn){
19854         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19855         //if(Roo.isSafari){ // safari
19856         //    adjust *= 2;
19857        // }
19858         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19859         if(Roo.isSafari){ // safari
19860             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19861             v =  (v < 10) ? 10 : v;
19862             v =  (v > 48) ? 48 : v;
19863             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19864             
19865         }
19866         
19867         
19868         v = Math.max(1, v+adjust);
19869         
19870         this.execCmd('FontSize', v  );
19871     },
19872
19873     onEditorEvent : function(e)
19874     {
19875         this.owner.fireEvent('editorevent', this, e);
19876       //  this.updateToolbar();
19877         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19878     },
19879
19880     insertTag : function(tg)
19881     {
19882         // could be a bit smarter... -> wrap the current selected tRoo..
19883         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19884             
19885             range = this.createRange(this.getSelection());
19886             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19887             wrappingNode.appendChild(range.extractContents());
19888             range.insertNode(wrappingNode);
19889
19890             return;
19891             
19892             
19893             
19894         }
19895         this.execCmd("formatblock",   tg);
19896         
19897     },
19898     
19899     insertText : function(txt)
19900     {
19901         
19902         
19903         var range = this.createRange();
19904         range.deleteContents();
19905                //alert(Sender.getAttribute('label'));
19906                
19907         range.insertNode(this.doc.createTextNode(txt));
19908     } ,
19909     
19910      
19911
19912     /**
19913      * Executes a Midas editor command on the editor document and performs necessary focus and
19914      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19915      * @param {String} cmd The Midas command
19916      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19917      */
19918     relayCmd : function(cmd, value){
19919         this.win.focus();
19920         this.execCmd(cmd, value);
19921         this.owner.fireEvent('editorevent', this);
19922         //this.updateToolbar();
19923         this.owner.deferFocus();
19924     },
19925
19926     /**
19927      * Executes a Midas editor command directly on the editor document.
19928      * For visual commands, you should use {@link #relayCmd} instead.
19929      * <b>This should only be called after the editor is initialized.</b>
19930      * @param {String} cmd The Midas command
19931      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19932      */
19933     execCmd : function(cmd, value){
19934         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19935         this.syncValue();
19936     },
19937  
19938  
19939    
19940     /**
19941      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19942      * to insert tRoo.
19943      * @param {String} text | dom node.. 
19944      */
19945     insertAtCursor : function(text)
19946     {
19947         
19948         
19949         
19950         if(!this.activated){
19951             return;
19952         }
19953         /*
19954         if(Roo.isIE){
19955             this.win.focus();
19956             var r = this.doc.selection.createRange();
19957             if(r){
19958                 r.collapse(true);
19959                 r.pasteHTML(text);
19960                 this.syncValue();
19961                 this.deferFocus();
19962             
19963             }
19964             return;
19965         }
19966         */
19967         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19968             this.win.focus();
19969             
19970             
19971             // from jquery ui (MIT licenced)
19972             var range, node;
19973             var win = this.win;
19974             
19975             if (win.getSelection && win.getSelection().getRangeAt) {
19976                 range = win.getSelection().getRangeAt(0);
19977                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19978                 range.insertNode(node);
19979             } else if (win.document.selection && win.document.selection.createRange) {
19980                 // no firefox support
19981                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19982                 win.document.selection.createRange().pasteHTML(txt);
19983             } else {
19984                 // no firefox support
19985                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19986                 this.execCmd('InsertHTML', txt);
19987             } 
19988             
19989             this.syncValue();
19990             
19991             this.deferFocus();
19992         }
19993     },
19994  // private
19995     mozKeyPress : function(e){
19996         if(e.ctrlKey){
19997             var c = e.getCharCode(), cmd;
19998           
19999             if(c > 0){
20000                 c = String.fromCharCode(c).toLowerCase();
20001                 switch(c){
20002                     case 'b':
20003                         cmd = 'bold';
20004                         break;
20005                     case 'i':
20006                         cmd = 'italic';
20007                         break;
20008                     
20009                     case 'u':
20010                         cmd = 'underline';
20011                         break;
20012                     
20013                     case 'v':
20014                         this.cleanUpPaste.defer(100, this);
20015                         return;
20016                         
20017                 }
20018                 if(cmd){
20019                     this.win.focus();
20020                     this.execCmd(cmd);
20021                     this.deferFocus();
20022                     e.preventDefault();
20023                 }
20024                 
20025             }
20026         }
20027     },
20028
20029     // private
20030     fixKeys : function(){ // load time branching for fastest keydown performance
20031         if(Roo.isIE){
20032             return function(e){
20033                 var k = e.getKey(), r;
20034                 if(k == e.TAB){
20035                     e.stopEvent();
20036                     r = this.doc.selection.createRange();
20037                     if(r){
20038                         r.collapse(true);
20039                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20040                         this.deferFocus();
20041                     }
20042                     return;
20043                 }
20044                 
20045                 if(k == e.ENTER){
20046                     r = this.doc.selection.createRange();
20047                     if(r){
20048                         var target = r.parentElement();
20049                         if(!target || target.tagName.toLowerCase() != 'li'){
20050                             e.stopEvent();
20051                             r.pasteHTML('<br />');
20052                             r.collapse(false);
20053                             r.select();
20054                         }
20055                     }
20056                 }
20057                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20058                     this.cleanUpPaste.defer(100, this);
20059                     return;
20060                 }
20061                 
20062                 
20063             };
20064         }else if(Roo.isOpera){
20065             return function(e){
20066                 var k = e.getKey();
20067                 if(k == e.TAB){
20068                     e.stopEvent();
20069                     this.win.focus();
20070                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20071                     this.deferFocus();
20072                 }
20073                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20074                     this.cleanUpPaste.defer(100, this);
20075                     return;
20076                 }
20077                 
20078             };
20079         }else if(Roo.isSafari){
20080             return function(e){
20081                 var k = e.getKey();
20082                 
20083                 if(k == e.TAB){
20084                     e.stopEvent();
20085                     this.execCmd('InsertText','\t');
20086                     this.deferFocus();
20087                     return;
20088                 }
20089                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20090                     this.cleanUpPaste.defer(100, this);
20091                     return;
20092                 }
20093                 
20094              };
20095         }
20096     }(),
20097     
20098     getAllAncestors: function()
20099     {
20100         var p = this.getSelectedNode();
20101         var a = [];
20102         if (!p) {
20103             a.push(p); // push blank onto stack..
20104             p = this.getParentElement();
20105         }
20106         
20107         
20108         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20109             a.push(p);
20110             p = p.parentNode;
20111         }
20112         a.push(this.doc.body);
20113         return a;
20114     },
20115     lastSel : false,
20116     lastSelNode : false,
20117     
20118     
20119     getSelection : function() 
20120     {
20121         this.assignDocWin();
20122         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20123     },
20124     
20125     getSelectedNode: function() 
20126     {
20127         // this may only work on Gecko!!!
20128         
20129         // should we cache this!!!!
20130         
20131         
20132         
20133          
20134         var range = this.createRange(this.getSelection()).cloneRange();
20135         
20136         if (Roo.isIE) {
20137             var parent = range.parentElement();
20138             while (true) {
20139                 var testRange = range.duplicate();
20140                 testRange.moveToElementText(parent);
20141                 if (testRange.inRange(range)) {
20142                     break;
20143                 }
20144                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20145                     break;
20146                 }
20147                 parent = parent.parentElement;
20148             }
20149             return parent;
20150         }
20151         
20152         // is ancestor a text element.
20153         var ac =  range.commonAncestorContainer;
20154         if (ac.nodeType == 3) {
20155             ac = ac.parentNode;
20156         }
20157         
20158         var ar = ac.childNodes;
20159          
20160         var nodes = [];
20161         var other_nodes = [];
20162         var has_other_nodes = false;
20163         for (var i=0;i<ar.length;i++) {
20164             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20165                 continue;
20166             }
20167             // fullly contained node.
20168             
20169             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20170                 nodes.push(ar[i]);
20171                 continue;
20172             }
20173             
20174             // probably selected..
20175             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20176                 other_nodes.push(ar[i]);
20177                 continue;
20178             }
20179             // outer..
20180             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20181                 continue;
20182             }
20183             
20184             
20185             has_other_nodes = true;
20186         }
20187         if (!nodes.length && other_nodes.length) {
20188             nodes= other_nodes;
20189         }
20190         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20191             return false;
20192         }
20193         
20194         return nodes[0];
20195     },
20196     createRange: function(sel)
20197     {
20198         // this has strange effects when using with 
20199         // top toolbar - not sure if it's a great idea.
20200         //this.editor.contentWindow.focus();
20201         if (typeof sel != "undefined") {
20202             try {
20203                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20204             } catch(e) {
20205                 return this.doc.createRange();
20206             }
20207         } else {
20208             return this.doc.createRange();
20209         }
20210     },
20211     getParentElement: function()
20212     {
20213         
20214         this.assignDocWin();
20215         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20216         
20217         var range = this.createRange(sel);
20218          
20219         try {
20220             var p = range.commonAncestorContainer;
20221             while (p.nodeType == 3) { // text node
20222                 p = p.parentNode;
20223             }
20224             return p;
20225         } catch (e) {
20226             return null;
20227         }
20228     
20229     },
20230     /***
20231      *
20232      * Range intersection.. the hard stuff...
20233      *  '-1' = before
20234      *  '0' = hits..
20235      *  '1' = after.
20236      *         [ -- selected range --- ]
20237      *   [fail]                        [fail]
20238      *
20239      *    basically..
20240      *      if end is before start or  hits it. fail.
20241      *      if start is after end or hits it fail.
20242      *
20243      *   if either hits (but other is outside. - then it's not 
20244      *   
20245      *    
20246      **/
20247     
20248     
20249     // @see http://www.thismuchiknow.co.uk/?p=64.
20250     rangeIntersectsNode : function(range, node)
20251     {
20252         var nodeRange = node.ownerDocument.createRange();
20253         try {
20254             nodeRange.selectNode(node);
20255         } catch (e) {
20256             nodeRange.selectNodeContents(node);
20257         }
20258     
20259         var rangeStartRange = range.cloneRange();
20260         rangeStartRange.collapse(true);
20261     
20262         var rangeEndRange = range.cloneRange();
20263         rangeEndRange.collapse(false);
20264     
20265         var nodeStartRange = nodeRange.cloneRange();
20266         nodeStartRange.collapse(true);
20267     
20268         var nodeEndRange = nodeRange.cloneRange();
20269         nodeEndRange.collapse(false);
20270     
20271         return rangeStartRange.compareBoundaryPoints(
20272                  Range.START_TO_START, nodeEndRange) == -1 &&
20273                rangeEndRange.compareBoundaryPoints(
20274                  Range.START_TO_START, nodeStartRange) == 1;
20275         
20276          
20277     },
20278     rangeCompareNode : function(range, node)
20279     {
20280         var nodeRange = node.ownerDocument.createRange();
20281         try {
20282             nodeRange.selectNode(node);
20283         } catch (e) {
20284             nodeRange.selectNodeContents(node);
20285         }
20286         
20287         
20288         range.collapse(true);
20289     
20290         nodeRange.collapse(true);
20291      
20292         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20293         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20294          
20295         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20296         
20297         var nodeIsBefore   =  ss == 1;
20298         var nodeIsAfter    = ee == -1;
20299         
20300         if (nodeIsBefore && nodeIsAfter) {
20301             return 0; // outer
20302         }
20303         if (!nodeIsBefore && nodeIsAfter) {
20304             return 1; //right trailed.
20305         }
20306         
20307         if (nodeIsBefore && !nodeIsAfter) {
20308             return 2;  // left trailed.
20309         }
20310         // fully contined.
20311         return 3;
20312     },
20313
20314     // private? - in a new class?
20315     cleanUpPaste :  function()
20316     {
20317         // cleans up the whole document..
20318         Roo.log('cleanuppaste');
20319         
20320         this.cleanUpChildren(this.doc.body);
20321         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20322         if (clean != this.doc.body.innerHTML) {
20323             this.doc.body.innerHTML = clean;
20324         }
20325         
20326     },
20327     
20328     cleanWordChars : function(input) {// change the chars to hex code
20329         var he = Roo.HtmlEditorCore;
20330         
20331         var output = input;
20332         Roo.each(he.swapCodes, function(sw) { 
20333             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20334             
20335             output = output.replace(swapper, sw[1]);
20336         });
20337         
20338         return output;
20339     },
20340     
20341     
20342     cleanUpChildren : function (n)
20343     {
20344         if (!n.childNodes.length) {
20345             return;
20346         }
20347         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20348            this.cleanUpChild(n.childNodes[i]);
20349         }
20350     },
20351     
20352     
20353         
20354     
20355     cleanUpChild : function (node)
20356     {
20357         var ed = this;
20358         //console.log(node);
20359         if (node.nodeName == "#text") {
20360             // clean up silly Windows -- stuff?
20361             return; 
20362         }
20363         if (node.nodeName == "#comment") {
20364             node.parentNode.removeChild(node);
20365             // clean up silly Windows -- stuff?
20366             return; 
20367         }
20368         var lcname = node.tagName.toLowerCase();
20369         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20370         // whitelist of tags..
20371         
20372         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20373             // remove node.
20374             node.parentNode.removeChild(node);
20375             return;
20376             
20377         }
20378         
20379         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20380         
20381         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20382         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20383         
20384         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20385         //    remove_keep_children = true;
20386         //}
20387         
20388         if (remove_keep_children) {
20389             this.cleanUpChildren(node);
20390             // inserts everything just before this node...
20391             while (node.childNodes.length) {
20392                 var cn = node.childNodes[0];
20393                 node.removeChild(cn);
20394                 node.parentNode.insertBefore(cn, node);
20395             }
20396             node.parentNode.removeChild(node);
20397             return;
20398         }
20399         
20400         if (!node.attributes || !node.attributes.length) {
20401             this.cleanUpChildren(node);
20402             return;
20403         }
20404         
20405         function cleanAttr(n,v)
20406         {
20407             
20408             if (v.match(/^\./) || v.match(/^\//)) {
20409                 return;
20410             }
20411             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20412                 return;
20413             }
20414             if (v.match(/^#/)) {
20415                 return;
20416             }
20417 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20418             node.removeAttribute(n);
20419             
20420         }
20421         
20422         var cwhite = this.cwhite;
20423         var cblack = this.cblack;
20424             
20425         function cleanStyle(n,v)
20426         {
20427             if (v.match(/expression/)) { //XSS?? should we even bother..
20428                 node.removeAttribute(n);
20429                 return;
20430             }
20431             
20432             var parts = v.split(/;/);
20433             var clean = [];
20434             
20435             Roo.each(parts, function(p) {
20436                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20437                 if (!p.length) {
20438                     return true;
20439                 }
20440                 var l = p.split(':').shift().replace(/\s+/g,'');
20441                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20442                 
20443                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20444 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20445                     //node.removeAttribute(n);
20446                     return true;
20447                 }
20448                 //Roo.log()
20449                 // only allow 'c whitelisted system attributes'
20450                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20451 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20452                     //node.removeAttribute(n);
20453                     return true;
20454                 }
20455                 
20456                 
20457                  
20458                 
20459                 clean.push(p);
20460                 return true;
20461             });
20462             if (clean.length) { 
20463                 node.setAttribute(n, clean.join(';'));
20464             } else {
20465                 node.removeAttribute(n);
20466             }
20467             
20468         }
20469         
20470         
20471         for (var i = node.attributes.length-1; i > -1 ; i--) {
20472             var a = node.attributes[i];
20473             //console.log(a);
20474             
20475             if (a.name.toLowerCase().substr(0,2)=='on')  {
20476                 node.removeAttribute(a.name);
20477                 continue;
20478             }
20479             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20480                 node.removeAttribute(a.name);
20481                 continue;
20482             }
20483             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20484                 cleanAttr(a.name,a.value); // fixme..
20485                 continue;
20486             }
20487             if (a.name == 'style') {
20488                 cleanStyle(a.name,a.value);
20489                 continue;
20490             }
20491             /// clean up MS crap..
20492             // tecnically this should be a list of valid class'es..
20493             
20494             
20495             if (a.name == 'class') {
20496                 if (a.value.match(/^Mso/)) {
20497                     node.className = '';
20498                 }
20499                 
20500                 if (a.value.match(/body/)) {
20501                     node.className = '';
20502                 }
20503                 continue;
20504             }
20505             
20506             // style cleanup!?
20507             // class cleanup?
20508             
20509         }
20510         
20511         
20512         this.cleanUpChildren(node);
20513         
20514         
20515     },
20516     
20517     /**
20518      * Clean up MS wordisms...
20519      */
20520     cleanWord : function(node)
20521     {
20522         
20523         
20524         if (!node) {
20525             this.cleanWord(this.doc.body);
20526             return;
20527         }
20528         if (node.nodeName == "#text") {
20529             // clean up silly Windows -- stuff?
20530             return; 
20531         }
20532         if (node.nodeName == "#comment") {
20533             node.parentNode.removeChild(node);
20534             // clean up silly Windows -- stuff?
20535             return; 
20536         }
20537         
20538         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20539             node.parentNode.removeChild(node);
20540             return;
20541         }
20542         
20543         // remove - but keep children..
20544         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20545             while (node.childNodes.length) {
20546                 var cn = node.childNodes[0];
20547                 node.removeChild(cn);
20548                 node.parentNode.insertBefore(cn, node);
20549             }
20550             node.parentNode.removeChild(node);
20551             this.iterateChildren(node, this.cleanWord);
20552             return;
20553         }
20554         // clean styles
20555         if (node.className.length) {
20556             
20557             var cn = node.className.split(/\W+/);
20558             var cna = [];
20559             Roo.each(cn, function(cls) {
20560                 if (cls.match(/Mso[a-zA-Z]+/)) {
20561                     return;
20562                 }
20563                 cna.push(cls);
20564             });
20565             node.className = cna.length ? cna.join(' ') : '';
20566             if (!cna.length) {
20567                 node.removeAttribute("class");
20568             }
20569         }
20570         
20571         if (node.hasAttribute("lang")) {
20572             node.removeAttribute("lang");
20573         }
20574         
20575         if (node.hasAttribute("style")) {
20576             
20577             var styles = node.getAttribute("style").split(";");
20578             var nstyle = [];
20579             Roo.each(styles, function(s) {
20580                 if (!s.match(/:/)) {
20581                     return;
20582                 }
20583                 var kv = s.split(":");
20584                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20585                     return;
20586                 }
20587                 // what ever is left... we allow.
20588                 nstyle.push(s);
20589             });
20590             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20591             if (!nstyle.length) {
20592                 node.removeAttribute('style');
20593             }
20594         }
20595         this.iterateChildren(node, this.cleanWord);
20596         
20597         
20598         
20599     },
20600     /**
20601      * iterateChildren of a Node, calling fn each time, using this as the scole..
20602      * @param {DomNode} node node to iterate children of.
20603      * @param {Function} fn method of this class to call on each item.
20604      */
20605     iterateChildren : function(node, fn)
20606     {
20607         if (!node.childNodes.length) {
20608                 return;
20609         }
20610         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20611            fn.call(this, node.childNodes[i])
20612         }
20613     },
20614     
20615     
20616     /**
20617      * cleanTableWidths.
20618      *
20619      * Quite often pasting from word etc.. results in tables with column and widths.
20620      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20621      *
20622      */
20623     cleanTableWidths : function(node)
20624     {
20625          
20626          
20627         if (!node) {
20628             this.cleanTableWidths(this.doc.body);
20629             return;
20630         }
20631         
20632         // ignore list...
20633         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20634             return; 
20635         }
20636         Roo.log(node.tagName);
20637         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20638             this.iterateChildren(node, this.cleanTableWidths);
20639             return;
20640         }
20641         if (node.hasAttribute('width')) {
20642             node.removeAttribute('width');
20643         }
20644         
20645          
20646         if (node.hasAttribute("style")) {
20647             // pretty basic...
20648             
20649             var styles = node.getAttribute("style").split(";");
20650             var nstyle = [];
20651             Roo.each(styles, function(s) {
20652                 if (!s.match(/:/)) {
20653                     return;
20654                 }
20655                 var kv = s.split(":");
20656                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20657                     return;
20658                 }
20659                 // what ever is left... we allow.
20660                 nstyle.push(s);
20661             });
20662             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20663             if (!nstyle.length) {
20664                 node.removeAttribute('style');
20665             }
20666         }
20667         
20668         this.iterateChildren(node, this.cleanTableWidths);
20669         
20670         
20671     },
20672     
20673     
20674     
20675     
20676     domToHTML : function(currentElement, depth, nopadtext) {
20677         
20678         depth = depth || 0;
20679         nopadtext = nopadtext || false;
20680     
20681         if (!currentElement) {
20682             return this.domToHTML(this.doc.body);
20683         }
20684         
20685         //Roo.log(currentElement);
20686         var j;
20687         var allText = false;
20688         var nodeName = currentElement.nodeName;
20689         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20690         
20691         if  (nodeName == '#text') {
20692             
20693             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20694         }
20695         
20696         
20697         var ret = '';
20698         if (nodeName != 'BODY') {
20699              
20700             var i = 0;
20701             // Prints the node tagName, such as <A>, <IMG>, etc
20702             if (tagName) {
20703                 var attr = [];
20704                 for(i = 0; i < currentElement.attributes.length;i++) {
20705                     // quoting?
20706                     var aname = currentElement.attributes.item(i).name;
20707                     if (!currentElement.attributes.item(i).value.length) {
20708                         continue;
20709                     }
20710                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20711                 }
20712                 
20713                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20714             } 
20715             else {
20716                 
20717                 // eack
20718             }
20719         } else {
20720             tagName = false;
20721         }
20722         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20723             return ret;
20724         }
20725         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20726             nopadtext = true;
20727         }
20728         
20729         
20730         // Traverse the tree
20731         i = 0;
20732         var currentElementChild = currentElement.childNodes.item(i);
20733         var allText = true;
20734         var innerHTML  = '';
20735         lastnode = '';
20736         while (currentElementChild) {
20737             // Formatting code (indent the tree so it looks nice on the screen)
20738             var nopad = nopadtext;
20739             if (lastnode == 'SPAN') {
20740                 nopad  = true;
20741             }
20742             // text
20743             if  (currentElementChild.nodeName == '#text') {
20744                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20745                 toadd = nopadtext ? toadd : toadd.trim();
20746                 if (!nopad && toadd.length > 80) {
20747                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20748                 }
20749                 innerHTML  += toadd;
20750                 
20751                 i++;
20752                 currentElementChild = currentElement.childNodes.item(i);
20753                 lastNode = '';
20754                 continue;
20755             }
20756             allText = false;
20757             
20758             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20759                 
20760             // Recursively traverse the tree structure of the child node
20761             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20762             lastnode = currentElementChild.nodeName;
20763             i++;
20764             currentElementChild=currentElement.childNodes.item(i);
20765         }
20766         
20767         ret += innerHTML;
20768         
20769         if (!allText) {
20770                 // The remaining code is mostly for formatting the tree
20771             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20772         }
20773         
20774         
20775         if (tagName) {
20776             ret+= "</"+tagName+">";
20777         }
20778         return ret;
20779         
20780     },
20781         
20782     applyBlacklists : function()
20783     {
20784         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20785         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20786         
20787         this.white = [];
20788         this.black = [];
20789         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20790             if (b.indexOf(tag) > -1) {
20791                 return;
20792             }
20793             this.white.push(tag);
20794             
20795         }, this);
20796         
20797         Roo.each(w, function(tag) {
20798             if (b.indexOf(tag) > -1) {
20799                 return;
20800             }
20801             if (this.white.indexOf(tag) > -1) {
20802                 return;
20803             }
20804             this.white.push(tag);
20805             
20806         }, this);
20807         
20808         
20809         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20810             if (w.indexOf(tag) > -1) {
20811                 return;
20812             }
20813             this.black.push(tag);
20814             
20815         }, this);
20816         
20817         Roo.each(b, function(tag) {
20818             if (w.indexOf(tag) > -1) {
20819                 return;
20820             }
20821             if (this.black.indexOf(tag) > -1) {
20822                 return;
20823             }
20824             this.black.push(tag);
20825             
20826         }, this);
20827         
20828         
20829         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20830         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20831         
20832         this.cwhite = [];
20833         this.cblack = [];
20834         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20835             if (b.indexOf(tag) > -1) {
20836                 return;
20837             }
20838             this.cwhite.push(tag);
20839             
20840         }, this);
20841         
20842         Roo.each(w, function(tag) {
20843             if (b.indexOf(tag) > -1) {
20844                 return;
20845             }
20846             if (this.cwhite.indexOf(tag) > -1) {
20847                 return;
20848             }
20849             this.cwhite.push(tag);
20850             
20851         }, this);
20852         
20853         
20854         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20855             if (w.indexOf(tag) > -1) {
20856                 return;
20857             }
20858             this.cblack.push(tag);
20859             
20860         }, this);
20861         
20862         Roo.each(b, function(tag) {
20863             if (w.indexOf(tag) > -1) {
20864                 return;
20865             }
20866             if (this.cblack.indexOf(tag) > -1) {
20867                 return;
20868             }
20869             this.cblack.push(tag);
20870             
20871         }, this);
20872     },
20873     
20874     setStylesheets : function(stylesheets)
20875     {
20876         if(typeof(stylesheets) == 'string'){
20877             Roo.get(this.iframe.contentDocument.head).createChild({
20878                 tag : 'link',
20879                 rel : 'stylesheet',
20880                 type : 'text/css',
20881                 href : stylesheets
20882             });
20883             
20884             return;
20885         }
20886         var _this = this;
20887      
20888         Roo.each(stylesheets, function(s) {
20889             if(!s.length){
20890                 return;
20891             }
20892             
20893             Roo.get(_this.iframe.contentDocument.head).createChild({
20894                 tag : 'link',
20895                 rel : 'stylesheet',
20896                 type : 'text/css',
20897                 href : s
20898             });
20899         });
20900
20901         
20902     },
20903     
20904     removeStylesheets : function()
20905     {
20906         var _this = this;
20907         
20908         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20909             s.remove();
20910         });
20911     }
20912     
20913     // hide stuff that is not compatible
20914     /**
20915      * @event blur
20916      * @hide
20917      */
20918     /**
20919      * @event change
20920      * @hide
20921      */
20922     /**
20923      * @event focus
20924      * @hide
20925      */
20926     /**
20927      * @event specialkey
20928      * @hide
20929      */
20930     /**
20931      * @cfg {String} fieldClass @hide
20932      */
20933     /**
20934      * @cfg {String} focusClass @hide
20935      */
20936     /**
20937      * @cfg {String} autoCreate @hide
20938      */
20939     /**
20940      * @cfg {String} inputType @hide
20941      */
20942     /**
20943      * @cfg {String} invalidClass @hide
20944      */
20945     /**
20946      * @cfg {String} invalidText @hide
20947      */
20948     /**
20949      * @cfg {String} msgFx @hide
20950      */
20951     /**
20952      * @cfg {String} validateOnBlur @hide
20953      */
20954 });
20955
20956 Roo.HtmlEditorCore.white = [
20957         'area', 'br', 'img', 'input', 'hr', 'wbr',
20958         
20959        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20960        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20961        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20962        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20963        'table',   'ul',         'xmp', 
20964        
20965        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20966       'thead',   'tr', 
20967      
20968       'dir', 'menu', 'ol', 'ul', 'dl',
20969        
20970       'embed',  'object'
20971 ];
20972
20973
20974 Roo.HtmlEditorCore.black = [
20975     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20976         'applet', // 
20977         'base',   'basefont', 'bgsound', 'blink',  'body', 
20978         'frame',  'frameset', 'head',    'html',   'ilayer', 
20979         'iframe', 'layer',  'link',     'meta',    'object',   
20980         'script', 'style' ,'title',  'xml' // clean later..
20981 ];
20982 Roo.HtmlEditorCore.clean = [
20983     'script', 'style', 'title', 'xml'
20984 ];
20985 Roo.HtmlEditorCore.remove = [
20986     'font'
20987 ];
20988 // attributes..
20989
20990 Roo.HtmlEditorCore.ablack = [
20991     'on'
20992 ];
20993     
20994 Roo.HtmlEditorCore.aclean = [ 
20995     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20996 ];
20997
20998 // protocols..
20999 Roo.HtmlEditorCore.pwhite= [
21000         'http',  'https',  'mailto'
21001 ];
21002
21003 // white listed style attributes.
21004 Roo.HtmlEditorCore.cwhite= [
21005       //  'text-align', /// default is to allow most things..
21006       
21007          
21008 //        'font-size'//??
21009 ];
21010
21011 // black listed style attributes.
21012 Roo.HtmlEditorCore.cblack= [
21013       //  'font-size' -- this can be set by the project 
21014 ];
21015
21016
21017 Roo.HtmlEditorCore.swapCodes   =[ 
21018     [    8211, "--" ], 
21019     [    8212, "--" ], 
21020     [    8216,  "'" ],  
21021     [    8217, "'" ],  
21022     [    8220, '"' ],  
21023     [    8221, '"' ],  
21024     [    8226, "*" ],  
21025     [    8230, "..." ]
21026 ]; 
21027
21028     /*
21029  * - LGPL
21030  *
21031  * HtmlEditor
21032  * 
21033  */
21034
21035 /**
21036  * @class Roo.bootstrap.HtmlEditor
21037  * @extends Roo.bootstrap.TextArea
21038  * Bootstrap HtmlEditor class
21039
21040  * @constructor
21041  * Create a new HtmlEditor
21042  * @param {Object} config The config object
21043  */
21044
21045 Roo.bootstrap.HtmlEditor = function(config){
21046     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21047     if (!this.toolbars) {
21048         this.toolbars = [];
21049     }
21050     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21051     this.addEvents({
21052             /**
21053              * @event initialize
21054              * Fires when the editor is fully initialized (including the iframe)
21055              * @param {HtmlEditor} this
21056              */
21057             initialize: true,
21058             /**
21059              * @event activate
21060              * Fires when the editor is first receives the focus. Any insertion must wait
21061              * until after this event.
21062              * @param {HtmlEditor} this
21063              */
21064             activate: true,
21065              /**
21066              * @event beforesync
21067              * Fires before the textarea is updated with content from the editor iframe. Return false
21068              * to cancel the sync.
21069              * @param {HtmlEditor} this
21070              * @param {String} html
21071              */
21072             beforesync: true,
21073              /**
21074              * @event beforepush
21075              * Fires before the iframe editor is updated with content from the textarea. Return false
21076              * to cancel the push.
21077              * @param {HtmlEditor} this
21078              * @param {String} html
21079              */
21080             beforepush: true,
21081              /**
21082              * @event sync
21083              * Fires when the textarea is updated with content from the editor iframe.
21084              * @param {HtmlEditor} this
21085              * @param {String} html
21086              */
21087             sync: true,
21088              /**
21089              * @event push
21090              * Fires when the iframe editor is updated with content from the textarea.
21091              * @param {HtmlEditor} this
21092              * @param {String} html
21093              */
21094             push: true,
21095              /**
21096              * @event editmodechange
21097              * Fires when the editor switches edit modes
21098              * @param {HtmlEditor} this
21099              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21100              */
21101             editmodechange: true,
21102             /**
21103              * @event editorevent
21104              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21105              * @param {HtmlEditor} this
21106              */
21107             editorevent: true,
21108             /**
21109              * @event firstfocus
21110              * Fires when on first focus - needed by toolbars..
21111              * @param {HtmlEditor} this
21112              */
21113             firstfocus: true,
21114             /**
21115              * @event autosave
21116              * Auto save the htmlEditor value as a file into Events
21117              * @param {HtmlEditor} this
21118              */
21119             autosave: true,
21120             /**
21121              * @event savedpreview
21122              * preview the saved version of htmlEditor
21123              * @param {HtmlEditor} this
21124              */
21125             savedpreview: true
21126         });
21127 };
21128
21129
21130 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21131     
21132     
21133       /**
21134      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21135      */
21136     toolbars : false,
21137    
21138      /**
21139      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21140      *                        Roo.resizable.
21141      */
21142     resizable : false,
21143      /**
21144      * @cfg {Number} height (in pixels)
21145      */   
21146     height: 300,
21147    /**
21148      * @cfg {Number} width (in pixels)
21149      */   
21150     width: false,
21151     
21152     /**
21153      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21154      * 
21155      */
21156     stylesheets: false,
21157     
21158     // id of frame..
21159     frameId: false,
21160     
21161     // private properties
21162     validationEvent : false,
21163     deferHeight: true,
21164     initialized : false,
21165     activated : false,
21166     
21167     onFocus : Roo.emptyFn,
21168     iframePad:3,
21169     hideMode:'offsets',
21170     
21171     
21172     tbContainer : false,
21173     
21174     toolbarContainer :function() {
21175         return this.wrap.select('.x-html-editor-tb',true).first();
21176     },
21177
21178     /**
21179      * Protected method that will not generally be called directly. It
21180      * is called when the editor creates its toolbar. Override this method if you need to
21181      * add custom toolbar buttons.
21182      * @param {HtmlEditor} editor
21183      */
21184     createToolbar : function(){
21185         
21186         Roo.log("create toolbars");
21187         
21188         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21189         this.toolbars[0].render(this.toolbarContainer());
21190         
21191         return;
21192         
21193 //        if (!editor.toolbars || !editor.toolbars.length) {
21194 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21195 //        }
21196 //        
21197 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21198 //            editor.toolbars[i] = Roo.factory(
21199 //                    typeof(editor.toolbars[i]) == 'string' ?
21200 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21201 //                Roo.bootstrap.HtmlEditor);
21202 //            editor.toolbars[i].init(editor);
21203 //        }
21204     },
21205
21206      
21207     // private
21208     onRender : function(ct, position)
21209     {
21210        // Roo.log("Call onRender: " + this.xtype);
21211         var _t = this;
21212         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21213       
21214         this.wrap = this.inputEl().wrap({
21215             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21216         });
21217         
21218         this.editorcore.onRender(ct, position);
21219          
21220         if (this.resizable) {
21221             this.resizeEl = new Roo.Resizable(this.wrap, {
21222                 pinned : true,
21223                 wrap: true,
21224                 dynamic : true,
21225                 minHeight : this.height,
21226                 height: this.height,
21227                 handles : this.resizable,
21228                 width: this.width,
21229                 listeners : {
21230                     resize : function(r, w, h) {
21231                         _t.onResize(w,h); // -something
21232                     }
21233                 }
21234             });
21235             
21236         }
21237         this.createToolbar(this);
21238        
21239         
21240         if(!this.width && this.resizable){
21241             this.setSize(this.wrap.getSize());
21242         }
21243         if (this.resizeEl) {
21244             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21245             // should trigger onReize..
21246         }
21247         
21248     },
21249
21250     // private
21251     onResize : function(w, h)
21252     {
21253         Roo.log('resize: ' +w + ',' + h );
21254         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21255         var ew = false;
21256         var eh = false;
21257         
21258         if(this.inputEl() ){
21259             if(typeof w == 'number'){
21260                 var aw = w - this.wrap.getFrameWidth('lr');
21261                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21262                 ew = aw;
21263             }
21264             if(typeof h == 'number'){
21265                  var tbh = -11;  // fixme it needs to tool bar size!
21266                 for (var i =0; i < this.toolbars.length;i++) {
21267                     // fixme - ask toolbars for heights?
21268                     tbh += this.toolbars[i].el.getHeight();
21269                     //if (this.toolbars[i].footer) {
21270                     //    tbh += this.toolbars[i].footer.el.getHeight();
21271                     //}
21272                 }
21273               
21274                 
21275                 
21276                 
21277                 
21278                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21279                 ah -= 5; // knock a few pixes off for look..
21280                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21281                 var eh = ah;
21282             }
21283         }
21284         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21285         this.editorcore.onResize(ew,eh);
21286         
21287     },
21288
21289     /**
21290      * Toggles the editor between standard and source edit mode.
21291      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21292      */
21293     toggleSourceEdit : function(sourceEditMode)
21294     {
21295         this.editorcore.toggleSourceEdit(sourceEditMode);
21296         
21297         if(this.editorcore.sourceEditMode){
21298             Roo.log('editor - showing textarea');
21299             
21300 //            Roo.log('in');
21301 //            Roo.log(this.syncValue());
21302             this.syncValue();
21303             this.inputEl().removeClass(['hide', 'x-hidden']);
21304             this.inputEl().dom.removeAttribute('tabIndex');
21305             this.inputEl().focus();
21306         }else{
21307             Roo.log('editor - hiding textarea');
21308 //            Roo.log('out')
21309 //            Roo.log(this.pushValue()); 
21310             this.pushValue();
21311             
21312             this.inputEl().addClass(['hide', 'x-hidden']);
21313             this.inputEl().dom.setAttribute('tabIndex', -1);
21314             //this.deferFocus();
21315         }
21316          
21317         if(this.resizable){
21318             this.setSize(this.wrap.getSize());
21319         }
21320         
21321         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21322     },
21323  
21324     // private (for BoxComponent)
21325     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21326
21327     // private (for BoxComponent)
21328     getResizeEl : function(){
21329         return this.wrap;
21330     },
21331
21332     // private (for BoxComponent)
21333     getPositionEl : function(){
21334         return this.wrap;
21335     },
21336
21337     // private
21338     initEvents : function(){
21339         this.originalValue = this.getValue();
21340     },
21341
21342 //    /**
21343 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21344 //     * @method
21345 //     */
21346 //    markInvalid : Roo.emptyFn,
21347 //    /**
21348 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21349 //     * @method
21350 //     */
21351 //    clearInvalid : Roo.emptyFn,
21352
21353     setValue : function(v){
21354         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21355         this.editorcore.pushValue();
21356     },
21357
21358      
21359     // private
21360     deferFocus : function(){
21361         this.focus.defer(10, this);
21362     },
21363
21364     // doc'ed in Field
21365     focus : function(){
21366         this.editorcore.focus();
21367         
21368     },
21369       
21370
21371     // private
21372     onDestroy : function(){
21373         
21374         
21375         
21376         if(this.rendered){
21377             
21378             for (var i =0; i < this.toolbars.length;i++) {
21379                 // fixme - ask toolbars for heights?
21380                 this.toolbars[i].onDestroy();
21381             }
21382             
21383             this.wrap.dom.innerHTML = '';
21384             this.wrap.remove();
21385         }
21386     },
21387
21388     // private
21389     onFirstFocus : function(){
21390         //Roo.log("onFirstFocus");
21391         this.editorcore.onFirstFocus();
21392          for (var i =0; i < this.toolbars.length;i++) {
21393             this.toolbars[i].onFirstFocus();
21394         }
21395         
21396     },
21397     
21398     // private
21399     syncValue : function()
21400     {   
21401         this.editorcore.syncValue();
21402     },
21403     
21404     pushValue : function()
21405     {   
21406         this.editorcore.pushValue();
21407     }
21408      
21409     
21410     // hide stuff that is not compatible
21411     /**
21412      * @event blur
21413      * @hide
21414      */
21415     /**
21416      * @event change
21417      * @hide
21418      */
21419     /**
21420      * @event focus
21421      * @hide
21422      */
21423     /**
21424      * @event specialkey
21425      * @hide
21426      */
21427     /**
21428      * @cfg {String} fieldClass @hide
21429      */
21430     /**
21431      * @cfg {String} focusClass @hide
21432      */
21433     /**
21434      * @cfg {String} autoCreate @hide
21435      */
21436     /**
21437      * @cfg {String} inputType @hide
21438      */
21439     /**
21440      * @cfg {String} invalidClass @hide
21441      */
21442     /**
21443      * @cfg {String} invalidText @hide
21444      */
21445     /**
21446      * @cfg {String} msgFx @hide
21447      */
21448     /**
21449      * @cfg {String} validateOnBlur @hide
21450      */
21451 });
21452  
21453     
21454    
21455    
21456    
21457       
21458 Roo.namespace('Roo.bootstrap.htmleditor');
21459 /**
21460  * @class Roo.bootstrap.HtmlEditorToolbar1
21461  * Basic Toolbar
21462  * 
21463  * Usage:
21464  *
21465  new Roo.bootstrap.HtmlEditor({
21466     ....
21467     toolbars : [
21468         new Roo.bootstrap.HtmlEditorToolbar1({
21469             disable : { fonts: 1 , format: 1, ..., ... , ...],
21470             btns : [ .... ]
21471         })
21472     }
21473      
21474  * 
21475  * @cfg {Object} disable List of elements to disable..
21476  * @cfg {Array} btns List of additional buttons.
21477  * 
21478  * 
21479  * NEEDS Extra CSS? 
21480  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21481  */
21482  
21483 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21484 {
21485     
21486     Roo.apply(this, config);
21487     
21488     // default disabled, based on 'good practice'..
21489     this.disable = this.disable || {};
21490     Roo.applyIf(this.disable, {
21491         fontSize : true,
21492         colors : true,
21493         specialElements : true
21494     });
21495     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21496     
21497     this.editor = config.editor;
21498     this.editorcore = config.editor.editorcore;
21499     
21500     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21501     
21502     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21503     // dont call parent... till later.
21504 }
21505 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21506      
21507     bar : true,
21508     
21509     editor : false,
21510     editorcore : false,
21511     
21512     
21513     formats : [
21514         "p" ,  
21515         "h1","h2","h3","h4","h5","h6", 
21516         "pre", "code", 
21517         "abbr", "acronym", "address", "cite", "samp", "var",
21518         'div','span'
21519     ],
21520     
21521     onRender : function(ct, position)
21522     {
21523        // Roo.log("Call onRender: " + this.xtype);
21524         
21525        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21526        Roo.log(this.el);
21527        this.el.dom.style.marginBottom = '0';
21528        var _this = this;
21529        var editorcore = this.editorcore;
21530        var editor= this.editor;
21531        
21532        var children = [];
21533        var btn = function(id,cmd , toggle, handler){
21534        
21535             var  event = toggle ? 'toggle' : 'click';
21536        
21537             var a = {
21538                 size : 'sm',
21539                 xtype: 'Button',
21540                 xns: Roo.bootstrap,
21541                 glyphicon : id,
21542                 cmd : id || cmd,
21543                 enableToggle:toggle !== false,
21544                 //html : 'submit'
21545                 pressed : toggle ? false : null,
21546                 listeners : {}
21547             };
21548             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21549                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21550             };
21551             children.push(a);
21552             return a;
21553        }
21554         
21555         var style = {
21556                 xtype: 'Button',
21557                 size : 'sm',
21558                 xns: Roo.bootstrap,
21559                 glyphicon : 'font',
21560                 //html : 'submit'
21561                 menu : {
21562                     xtype: 'Menu',
21563                     xns: Roo.bootstrap,
21564                     items:  []
21565                 }
21566         };
21567         Roo.each(this.formats, function(f) {
21568             style.menu.items.push({
21569                 xtype :'MenuItem',
21570                 xns: Roo.bootstrap,
21571                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21572                 tagname : f,
21573                 listeners : {
21574                     click : function()
21575                     {
21576                         editorcore.insertTag(this.tagname);
21577                         editor.focus();
21578                     }
21579                 }
21580                 
21581             });
21582         });
21583          children.push(style);   
21584             
21585             
21586         btn('bold',false,true);
21587         btn('italic',false,true);
21588         btn('align-left', 'justifyleft',true);
21589         btn('align-center', 'justifycenter',true);
21590         btn('align-right' , 'justifyright',true);
21591         btn('link', false, false, function(btn) {
21592             //Roo.log("create link?");
21593             var url = prompt(this.createLinkText, this.defaultLinkValue);
21594             if(url && url != 'http:/'+'/'){
21595                 this.editorcore.relayCmd('createlink', url);
21596             }
21597         }),
21598         btn('list','insertunorderedlist',true);
21599         btn('pencil', false,true, function(btn){
21600                 Roo.log(this);
21601                 
21602                 this.toggleSourceEdit(btn.pressed);
21603         });
21604         /*
21605         var cog = {
21606                 xtype: 'Button',
21607                 size : 'sm',
21608                 xns: Roo.bootstrap,
21609                 glyphicon : 'cog',
21610                 //html : 'submit'
21611                 menu : {
21612                     xtype: 'Menu',
21613                     xns: Roo.bootstrap,
21614                     items:  []
21615                 }
21616         };
21617         
21618         cog.menu.items.push({
21619             xtype :'MenuItem',
21620             xns: Roo.bootstrap,
21621             html : Clean styles,
21622             tagname : f,
21623             listeners : {
21624                 click : function()
21625                 {
21626                     editorcore.insertTag(this.tagname);
21627                     editor.focus();
21628                 }
21629             }
21630             
21631         });
21632        */
21633         
21634          
21635        this.xtype = 'NavSimplebar';
21636         
21637         for(var i=0;i< children.length;i++) {
21638             
21639             this.buttons.add(this.addxtypeChild(children[i]));
21640             
21641         }
21642         
21643         editor.on('editorevent', this.updateToolbar, this);
21644     },
21645     onBtnClick : function(id)
21646     {
21647        this.editorcore.relayCmd(id);
21648        this.editorcore.focus();
21649     },
21650     
21651     /**
21652      * Protected method that will not generally be called directly. It triggers
21653      * a toolbar update by reading the markup state of the current selection in the editor.
21654      */
21655     updateToolbar: function(){
21656
21657         if(!this.editorcore.activated){
21658             this.editor.onFirstFocus(); // is this neeed?
21659             return;
21660         }
21661
21662         var btns = this.buttons; 
21663         var doc = this.editorcore.doc;
21664         btns.get('bold').setActive(doc.queryCommandState('bold'));
21665         btns.get('italic').setActive(doc.queryCommandState('italic'));
21666         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21667         
21668         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21669         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21670         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21671         
21672         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21673         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21674          /*
21675         
21676         var ans = this.editorcore.getAllAncestors();
21677         if (this.formatCombo) {
21678             
21679             
21680             var store = this.formatCombo.store;
21681             this.formatCombo.setValue("");
21682             for (var i =0; i < ans.length;i++) {
21683                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21684                     // select it..
21685                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21686                     break;
21687                 }
21688             }
21689         }
21690         
21691         
21692         
21693         // hides menus... - so this cant be on a menu...
21694         Roo.bootstrap.MenuMgr.hideAll();
21695         */
21696         Roo.bootstrap.MenuMgr.hideAll();
21697         //this.editorsyncValue();
21698     },
21699     onFirstFocus: function() {
21700         this.buttons.each(function(item){
21701            item.enable();
21702         });
21703     },
21704     toggleSourceEdit : function(sourceEditMode){
21705         
21706           
21707         if(sourceEditMode){
21708             Roo.log("disabling buttons");
21709            this.buttons.each( function(item){
21710                 if(item.cmd != 'pencil'){
21711                     item.disable();
21712                 }
21713             });
21714           
21715         }else{
21716             Roo.log("enabling buttons");
21717             if(this.editorcore.initialized){
21718                 this.buttons.each( function(item){
21719                     item.enable();
21720                 });
21721             }
21722             
21723         }
21724         Roo.log("calling toggole on editor");
21725         // tell the editor that it's been pressed..
21726         this.editor.toggleSourceEdit(sourceEditMode);
21727        
21728     }
21729 });
21730
21731
21732
21733
21734
21735 /**
21736  * @class Roo.bootstrap.Table.AbstractSelectionModel
21737  * @extends Roo.util.Observable
21738  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21739  * implemented by descendant classes.  This class should not be directly instantiated.
21740  * @constructor
21741  */
21742 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21743     this.locked = false;
21744     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21745 };
21746
21747
21748 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21749     /** @ignore Called by the grid automatically. Do not call directly. */
21750     init : function(grid){
21751         this.grid = grid;
21752         this.initEvents();
21753     },
21754
21755     /**
21756      * Locks the selections.
21757      */
21758     lock : function(){
21759         this.locked = true;
21760     },
21761
21762     /**
21763      * Unlocks the selections.
21764      */
21765     unlock : function(){
21766         this.locked = false;
21767     },
21768
21769     /**
21770      * Returns true if the selections are locked.
21771      * @return {Boolean}
21772      */
21773     isLocked : function(){
21774         return this.locked;
21775     }
21776 });
21777 /**
21778  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21779  * @class Roo.bootstrap.Table.RowSelectionModel
21780  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21781  * It supports multiple selections and keyboard selection/navigation. 
21782  * @constructor
21783  * @param {Object} config
21784  */
21785
21786 Roo.bootstrap.Table.RowSelectionModel = function(config){
21787     Roo.apply(this, config);
21788     this.selections = new Roo.util.MixedCollection(false, function(o){
21789         return o.id;
21790     });
21791
21792     this.last = false;
21793     this.lastActive = false;
21794
21795     this.addEvents({
21796         /**
21797              * @event selectionchange
21798              * Fires when the selection changes
21799              * @param {SelectionModel} this
21800              */
21801             "selectionchange" : true,
21802         /**
21803              * @event afterselectionchange
21804              * Fires after the selection changes (eg. by key press or clicking)
21805              * @param {SelectionModel} this
21806              */
21807             "afterselectionchange" : true,
21808         /**
21809              * @event beforerowselect
21810              * Fires when a row is selected being selected, return false to cancel.
21811              * @param {SelectionModel} this
21812              * @param {Number} rowIndex The selected index
21813              * @param {Boolean} keepExisting False if other selections will be cleared
21814              */
21815             "beforerowselect" : true,
21816         /**
21817              * @event rowselect
21818              * Fires when a row is selected.
21819              * @param {SelectionModel} this
21820              * @param {Number} rowIndex The selected index
21821              * @param {Roo.data.Record} r The record
21822              */
21823             "rowselect" : true,
21824         /**
21825              * @event rowdeselect
21826              * Fires when a row is deselected.
21827              * @param {SelectionModel} this
21828              * @param {Number} rowIndex The selected index
21829              */
21830         "rowdeselect" : true
21831     });
21832     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21833     this.locked = false;
21834 };
21835
21836 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21837     /**
21838      * @cfg {Boolean} singleSelect
21839      * True to allow selection of only one row at a time (defaults to false)
21840      */
21841     singleSelect : false,
21842
21843     // private
21844     initEvents : function(){
21845
21846         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21847             this.grid.on("mousedown", this.handleMouseDown, this);
21848         }else{ // allow click to work like normal
21849             this.grid.on("rowclick", this.handleDragableRowClick, this);
21850         }
21851
21852         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21853             "up" : function(e){
21854                 if(!e.shiftKey){
21855                     this.selectPrevious(e.shiftKey);
21856                 }else if(this.last !== false && this.lastActive !== false){
21857                     var last = this.last;
21858                     this.selectRange(this.last,  this.lastActive-1);
21859                     this.grid.getView().focusRow(this.lastActive);
21860                     if(last !== false){
21861                         this.last = last;
21862                     }
21863                 }else{
21864                     this.selectFirstRow();
21865                 }
21866                 this.fireEvent("afterselectionchange", this);
21867             },
21868             "down" : function(e){
21869                 if(!e.shiftKey){
21870                     this.selectNext(e.shiftKey);
21871                 }else if(this.last !== false && this.lastActive !== false){
21872                     var last = this.last;
21873                     this.selectRange(this.last,  this.lastActive+1);
21874                     this.grid.getView().focusRow(this.lastActive);
21875                     if(last !== false){
21876                         this.last = last;
21877                     }
21878                 }else{
21879                     this.selectFirstRow();
21880                 }
21881                 this.fireEvent("afterselectionchange", this);
21882             },
21883             scope: this
21884         });
21885
21886         var view = this.grid.view;
21887         view.on("refresh", this.onRefresh, this);
21888         view.on("rowupdated", this.onRowUpdated, this);
21889         view.on("rowremoved", this.onRemove, this);
21890     },
21891
21892     // private
21893     onRefresh : function(){
21894         var ds = this.grid.dataSource, i, v = this.grid.view;
21895         var s = this.selections;
21896         s.each(function(r){
21897             if((i = ds.indexOfId(r.id)) != -1){
21898                 v.onRowSelect(i);
21899             }else{
21900                 s.remove(r);
21901             }
21902         });
21903     },
21904
21905     // private
21906     onRemove : function(v, index, r){
21907         this.selections.remove(r);
21908     },
21909
21910     // private
21911     onRowUpdated : function(v, index, r){
21912         if(this.isSelected(r)){
21913             v.onRowSelect(index);
21914         }
21915     },
21916
21917     /**
21918      * Select records.
21919      * @param {Array} records The records to select
21920      * @param {Boolean} keepExisting (optional) True to keep existing selections
21921      */
21922     selectRecords : function(records, keepExisting){
21923         if(!keepExisting){
21924             this.clearSelections();
21925         }
21926         var ds = this.grid.dataSource;
21927         for(var i = 0, len = records.length; i < len; i++){
21928             this.selectRow(ds.indexOf(records[i]), true);
21929         }
21930     },
21931
21932     /**
21933      * Gets the number of selected rows.
21934      * @return {Number}
21935      */
21936     getCount : function(){
21937         return this.selections.length;
21938     },
21939
21940     /**
21941      * Selects the first row in the grid.
21942      */
21943     selectFirstRow : function(){
21944         this.selectRow(0);
21945     },
21946
21947     /**
21948      * Select the last row.
21949      * @param {Boolean} keepExisting (optional) True to keep existing selections
21950      */
21951     selectLastRow : function(keepExisting){
21952         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21953     },
21954
21955     /**
21956      * Selects the row immediately following the last selected row.
21957      * @param {Boolean} keepExisting (optional) True to keep existing selections
21958      */
21959     selectNext : function(keepExisting){
21960         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21961             this.selectRow(this.last+1, keepExisting);
21962             this.grid.getView().focusRow(this.last);
21963         }
21964     },
21965
21966     /**
21967      * Selects the row that precedes the last selected row.
21968      * @param {Boolean} keepExisting (optional) True to keep existing selections
21969      */
21970     selectPrevious : function(keepExisting){
21971         if(this.last){
21972             this.selectRow(this.last-1, keepExisting);
21973             this.grid.getView().focusRow(this.last);
21974         }
21975     },
21976
21977     /**
21978      * Returns the selected records
21979      * @return {Array} Array of selected records
21980      */
21981     getSelections : function(){
21982         return [].concat(this.selections.items);
21983     },
21984
21985     /**
21986      * Returns the first selected record.
21987      * @return {Record}
21988      */
21989     getSelected : function(){
21990         return this.selections.itemAt(0);
21991     },
21992
21993
21994     /**
21995      * Clears all selections.
21996      */
21997     clearSelections : function(fast){
21998         if(this.locked) {
21999             return;
22000         }
22001         if(fast !== true){
22002             var ds = this.grid.dataSource;
22003             var s = this.selections;
22004             s.each(function(r){
22005                 this.deselectRow(ds.indexOfId(r.id));
22006             }, this);
22007             s.clear();
22008         }else{
22009             this.selections.clear();
22010         }
22011         this.last = false;
22012     },
22013
22014
22015     /**
22016      * Selects all rows.
22017      */
22018     selectAll : function(){
22019         if(this.locked) {
22020             return;
22021         }
22022         this.selections.clear();
22023         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22024             this.selectRow(i, true);
22025         }
22026     },
22027
22028     /**
22029      * Returns True if there is a selection.
22030      * @return {Boolean}
22031      */
22032     hasSelection : function(){
22033         return this.selections.length > 0;
22034     },
22035
22036     /**
22037      * Returns True if the specified row is selected.
22038      * @param {Number/Record} record The record or index of the record to check
22039      * @return {Boolean}
22040      */
22041     isSelected : function(index){
22042         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22043         return (r && this.selections.key(r.id) ? true : false);
22044     },
22045
22046     /**
22047      * Returns True if the specified record id is selected.
22048      * @param {String} id The id of record to check
22049      * @return {Boolean}
22050      */
22051     isIdSelected : function(id){
22052         return (this.selections.key(id) ? true : false);
22053     },
22054
22055     // private
22056     handleMouseDown : function(e, t){
22057         var view = this.grid.getView(), rowIndex;
22058         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22059             return;
22060         };
22061         if(e.shiftKey && this.last !== false){
22062             var last = this.last;
22063             this.selectRange(last, rowIndex, e.ctrlKey);
22064             this.last = last; // reset the last
22065             view.focusRow(rowIndex);
22066         }else{
22067             var isSelected = this.isSelected(rowIndex);
22068             if(e.button !== 0 && isSelected){
22069                 view.focusRow(rowIndex);
22070             }else if(e.ctrlKey && isSelected){
22071                 this.deselectRow(rowIndex);
22072             }else if(!isSelected){
22073                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22074                 view.focusRow(rowIndex);
22075             }
22076         }
22077         this.fireEvent("afterselectionchange", this);
22078     },
22079     // private
22080     handleDragableRowClick :  function(grid, rowIndex, e) 
22081     {
22082         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22083             this.selectRow(rowIndex, false);
22084             grid.view.focusRow(rowIndex);
22085              this.fireEvent("afterselectionchange", this);
22086         }
22087     },
22088     
22089     /**
22090      * Selects multiple rows.
22091      * @param {Array} rows Array of the indexes of the row to select
22092      * @param {Boolean} keepExisting (optional) True to keep existing selections
22093      */
22094     selectRows : function(rows, keepExisting){
22095         if(!keepExisting){
22096             this.clearSelections();
22097         }
22098         for(var i = 0, len = rows.length; i < len; i++){
22099             this.selectRow(rows[i], true);
22100         }
22101     },
22102
22103     /**
22104      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22105      * @param {Number} startRow The index of the first row in the range
22106      * @param {Number} endRow The index of the last row in the range
22107      * @param {Boolean} keepExisting (optional) True to retain existing selections
22108      */
22109     selectRange : function(startRow, endRow, keepExisting){
22110         if(this.locked) {
22111             return;
22112         }
22113         if(!keepExisting){
22114             this.clearSelections();
22115         }
22116         if(startRow <= endRow){
22117             for(var i = startRow; i <= endRow; i++){
22118                 this.selectRow(i, true);
22119             }
22120         }else{
22121             for(var i = startRow; i >= endRow; i--){
22122                 this.selectRow(i, true);
22123             }
22124         }
22125     },
22126
22127     /**
22128      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22129      * @param {Number} startRow The index of the first row in the range
22130      * @param {Number} endRow The index of the last row in the range
22131      */
22132     deselectRange : function(startRow, endRow, preventViewNotify){
22133         if(this.locked) {
22134             return;
22135         }
22136         for(var i = startRow; i <= endRow; i++){
22137             this.deselectRow(i, preventViewNotify);
22138         }
22139     },
22140
22141     /**
22142      * Selects a row.
22143      * @param {Number} row The index of the row to select
22144      * @param {Boolean} keepExisting (optional) True to keep existing selections
22145      */
22146     selectRow : function(index, keepExisting, preventViewNotify){
22147         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22148             return;
22149         }
22150         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22151             if(!keepExisting || this.singleSelect){
22152                 this.clearSelections();
22153             }
22154             var r = this.grid.dataSource.getAt(index);
22155             this.selections.add(r);
22156             this.last = this.lastActive = index;
22157             if(!preventViewNotify){
22158                 this.grid.getView().onRowSelect(index);
22159             }
22160             this.fireEvent("rowselect", this, index, r);
22161             this.fireEvent("selectionchange", this);
22162         }
22163     },
22164
22165     /**
22166      * Deselects a row.
22167      * @param {Number} row The index of the row to deselect
22168      */
22169     deselectRow : function(index, preventViewNotify){
22170         if(this.locked) {
22171             return;
22172         }
22173         if(this.last == index){
22174             this.last = false;
22175         }
22176         if(this.lastActive == index){
22177             this.lastActive = false;
22178         }
22179         var r = this.grid.dataSource.getAt(index);
22180         this.selections.remove(r);
22181         if(!preventViewNotify){
22182             this.grid.getView().onRowDeselect(index);
22183         }
22184         this.fireEvent("rowdeselect", this, index);
22185         this.fireEvent("selectionchange", this);
22186     },
22187
22188     // private
22189     restoreLast : function(){
22190         if(this._last){
22191             this.last = this._last;
22192         }
22193     },
22194
22195     // private
22196     acceptsNav : function(row, col, cm){
22197         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22198     },
22199
22200     // private
22201     onEditorKey : function(field, e){
22202         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22203         if(k == e.TAB){
22204             e.stopEvent();
22205             ed.completeEdit();
22206             if(e.shiftKey){
22207                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22208             }else{
22209                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22210             }
22211         }else if(k == e.ENTER && !e.ctrlKey){
22212             e.stopEvent();
22213             ed.completeEdit();
22214             if(e.shiftKey){
22215                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22216             }else{
22217                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22218             }
22219         }else if(k == e.ESC){
22220             ed.cancelEdit();
22221         }
22222         if(newCell){
22223             g.startEditing(newCell[0], newCell[1]);
22224         }
22225     }
22226 });/*
22227  * Based on:
22228  * Ext JS Library 1.1.1
22229  * Copyright(c) 2006-2007, Ext JS, LLC.
22230  *
22231  * Originally Released Under LGPL - original licence link has changed is not relivant.
22232  *
22233  * Fork - LGPL
22234  * <script type="text/javascript">
22235  */
22236  
22237 /**
22238  * @class Roo.bootstrap.PagingToolbar
22239  * @extends Roo.bootstrap.NavSimplebar
22240  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22241  * @constructor
22242  * Create a new PagingToolbar
22243  * @param {Object} config The config object
22244  * @param {Roo.data.Store} store
22245  */
22246 Roo.bootstrap.PagingToolbar = function(config)
22247 {
22248     // old args format still supported... - xtype is prefered..
22249         // created from xtype...
22250     
22251     this.ds = config.dataSource;
22252     
22253     if (config.store && !this.ds) {
22254         this.store= Roo.factory(config.store, Roo.data);
22255         this.ds = this.store;
22256         this.ds.xmodule = this.xmodule || false;
22257     }
22258     
22259     this.toolbarItems = [];
22260     if (config.items) {
22261         this.toolbarItems = config.items;
22262     }
22263     
22264     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22265     
22266     this.cursor = 0;
22267     
22268     if (this.ds) { 
22269         this.bind(this.ds);
22270     }
22271     
22272     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22273     
22274 };
22275
22276 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22277     /**
22278      * @cfg {Roo.data.Store} dataSource
22279      * The underlying data store providing the paged data
22280      */
22281     /**
22282      * @cfg {String/HTMLElement/Element} container
22283      * container The id or element that will contain the toolbar
22284      */
22285     /**
22286      * @cfg {Boolean} displayInfo
22287      * True to display the displayMsg (defaults to false)
22288      */
22289     /**
22290      * @cfg {Number} pageSize
22291      * The number of records to display per page (defaults to 20)
22292      */
22293     pageSize: 20,
22294     /**
22295      * @cfg {String} displayMsg
22296      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22297      */
22298     displayMsg : 'Displaying {0} - {1} of {2}',
22299     /**
22300      * @cfg {String} emptyMsg
22301      * The message to display when no records are found (defaults to "No data to display")
22302      */
22303     emptyMsg : 'No data to display',
22304     /**
22305      * Customizable piece of the default paging text (defaults to "Page")
22306      * @type String
22307      */
22308     beforePageText : "Page",
22309     /**
22310      * Customizable piece of the default paging text (defaults to "of %0")
22311      * @type String
22312      */
22313     afterPageText : "of {0}",
22314     /**
22315      * Customizable piece of the default paging text (defaults to "First Page")
22316      * @type String
22317      */
22318     firstText : "First Page",
22319     /**
22320      * Customizable piece of the default paging text (defaults to "Previous Page")
22321      * @type String
22322      */
22323     prevText : "Previous Page",
22324     /**
22325      * Customizable piece of the default paging text (defaults to "Next Page")
22326      * @type String
22327      */
22328     nextText : "Next Page",
22329     /**
22330      * Customizable piece of the default paging text (defaults to "Last Page")
22331      * @type String
22332      */
22333     lastText : "Last Page",
22334     /**
22335      * Customizable piece of the default paging text (defaults to "Refresh")
22336      * @type String
22337      */
22338     refreshText : "Refresh",
22339
22340     buttons : false,
22341     // private
22342     onRender : function(ct, position) 
22343     {
22344         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22345         this.navgroup.parentId = this.id;
22346         this.navgroup.onRender(this.el, null);
22347         // add the buttons to the navgroup
22348         
22349         if(this.displayInfo){
22350             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22351             this.displayEl = this.el.select('.x-paging-info', true).first();
22352 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22353 //            this.displayEl = navel.el.select('span',true).first();
22354         }
22355         
22356         var _this = this;
22357         
22358         if(this.buttons){
22359             Roo.each(_this.buttons, function(e){ // this might need to use render????
22360                Roo.factory(e).onRender(_this.el, null);
22361             });
22362         }
22363             
22364         Roo.each(_this.toolbarItems, function(e) {
22365             _this.navgroup.addItem(e);
22366         });
22367         
22368         
22369         this.first = this.navgroup.addItem({
22370             tooltip: this.firstText,
22371             cls: "prev",
22372             icon : 'fa fa-backward',
22373             disabled: true,
22374             preventDefault: true,
22375             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22376         });
22377         
22378         this.prev =  this.navgroup.addItem({
22379             tooltip: this.prevText,
22380             cls: "prev",
22381             icon : 'fa fa-step-backward',
22382             disabled: true,
22383             preventDefault: true,
22384             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22385         });
22386     //this.addSeparator();
22387         
22388         
22389         var field = this.navgroup.addItem( {
22390             tagtype : 'span',
22391             cls : 'x-paging-position',
22392             
22393             html : this.beforePageText  +
22394                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22395                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22396          } ); //?? escaped?
22397         
22398         this.field = field.el.select('input', true).first();
22399         this.field.on("keydown", this.onPagingKeydown, this);
22400         this.field.on("focus", function(){this.dom.select();});
22401     
22402     
22403         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22404         //this.field.setHeight(18);
22405         //this.addSeparator();
22406         this.next = this.navgroup.addItem({
22407             tooltip: this.nextText,
22408             cls: "next",
22409             html : ' <i class="fa fa-step-forward">',
22410             disabled: true,
22411             preventDefault: true,
22412             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22413         });
22414         this.last = this.navgroup.addItem({
22415             tooltip: this.lastText,
22416             icon : 'fa fa-forward',
22417             cls: "next",
22418             disabled: true,
22419             preventDefault: true,
22420             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22421         });
22422     //this.addSeparator();
22423         this.loading = this.navgroup.addItem({
22424             tooltip: this.refreshText,
22425             icon: 'fa fa-refresh',
22426             preventDefault: true,
22427             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22428         });
22429         
22430     },
22431
22432     // private
22433     updateInfo : function(){
22434         if(this.displayEl){
22435             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22436             var msg = count == 0 ?
22437                 this.emptyMsg :
22438                 String.format(
22439                     this.displayMsg,
22440                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22441                 );
22442             this.displayEl.update(msg);
22443         }
22444     },
22445
22446     // private
22447     onLoad : function(ds, r, o){
22448        this.cursor = o.params ? o.params.start : 0;
22449        var d = this.getPageData(),
22450             ap = d.activePage,
22451             ps = d.pages;
22452         
22453        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22454        this.field.dom.value = ap;
22455        this.first.setDisabled(ap == 1);
22456        this.prev.setDisabled(ap == 1);
22457        this.next.setDisabled(ap == ps);
22458        this.last.setDisabled(ap == ps);
22459        this.loading.enable();
22460        this.updateInfo();
22461     },
22462
22463     // private
22464     getPageData : function(){
22465         var total = this.ds.getTotalCount();
22466         return {
22467             total : total,
22468             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22469             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22470         };
22471     },
22472
22473     // private
22474     onLoadError : function(){
22475         this.loading.enable();
22476     },
22477
22478     // private
22479     onPagingKeydown : function(e){
22480         var k = e.getKey();
22481         var d = this.getPageData();
22482         if(k == e.RETURN){
22483             var v = this.field.dom.value, pageNum;
22484             if(!v || isNaN(pageNum = parseInt(v, 10))){
22485                 this.field.dom.value = d.activePage;
22486                 return;
22487             }
22488             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22489             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22490             e.stopEvent();
22491         }
22492         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))
22493         {
22494           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22495           this.field.dom.value = pageNum;
22496           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22497           e.stopEvent();
22498         }
22499         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22500         {
22501           var v = this.field.dom.value, pageNum; 
22502           var increment = (e.shiftKey) ? 10 : 1;
22503           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22504                 increment *= -1;
22505           }
22506           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22507             this.field.dom.value = d.activePage;
22508             return;
22509           }
22510           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22511           {
22512             this.field.dom.value = parseInt(v, 10) + increment;
22513             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22514             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22515           }
22516           e.stopEvent();
22517         }
22518     },
22519
22520     // private
22521     beforeLoad : function(){
22522         if(this.loading){
22523             this.loading.disable();
22524         }
22525     },
22526
22527     // private
22528     onClick : function(which){
22529         
22530         var ds = this.ds;
22531         if (!ds) {
22532             return;
22533         }
22534         
22535         switch(which){
22536             case "first":
22537                 ds.load({params:{start: 0, limit: this.pageSize}});
22538             break;
22539             case "prev":
22540                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22541             break;
22542             case "next":
22543                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22544             break;
22545             case "last":
22546                 var total = ds.getTotalCount();
22547                 var extra = total % this.pageSize;
22548                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22549                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22550             break;
22551             case "refresh":
22552                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22553             break;
22554         }
22555     },
22556
22557     /**
22558      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22559      * @param {Roo.data.Store} store The data store to unbind
22560      */
22561     unbind : function(ds){
22562         ds.un("beforeload", this.beforeLoad, this);
22563         ds.un("load", this.onLoad, this);
22564         ds.un("loadexception", this.onLoadError, this);
22565         ds.un("remove", this.updateInfo, this);
22566         ds.un("add", this.updateInfo, this);
22567         this.ds = undefined;
22568     },
22569
22570     /**
22571      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22572      * @param {Roo.data.Store} store The data store to bind
22573      */
22574     bind : function(ds){
22575         ds.on("beforeload", this.beforeLoad, this);
22576         ds.on("load", this.onLoad, this);
22577         ds.on("loadexception", this.onLoadError, this);
22578         ds.on("remove", this.updateInfo, this);
22579         ds.on("add", this.updateInfo, this);
22580         this.ds = ds;
22581     }
22582 });/*
22583  * - LGPL
22584  *
22585  * element
22586  * 
22587  */
22588
22589 /**
22590  * @class Roo.bootstrap.MessageBar
22591  * @extends Roo.bootstrap.Component
22592  * Bootstrap MessageBar class
22593  * @cfg {String} html contents of the MessageBar
22594  * @cfg {String} weight (info | success | warning | danger) default info
22595  * @cfg {String} beforeClass insert the bar before the given class
22596  * @cfg {Boolean} closable (true | false) default false
22597  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22598  * 
22599  * @constructor
22600  * Create a new Element
22601  * @param {Object} config The config object
22602  */
22603
22604 Roo.bootstrap.MessageBar = function(config){
22605     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22606 };
22607
22608 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22609     
22610     html: '',
22611     weight: 'info',
22612     closable: false,
22613     fixed: false,
22614     beforeClass: 'bootstrap-sticky-wrap',
22615     
22616     getAutoCreate : function(){
22617         
22618         var cfg = {
22619             tag: 'div',
22620             cls: 'alert alert-dismissable alert-' + this.weight,
22621             cn: [
22622                 {
22623                     tag: 'span',
22624                     cls: 'message',
22625                     html: this.html || ''
22626                 }
22627             ]
22628         };
22629         
22630         if(this.fixed){
22631             cfg.cls += ' alert-messages-fixed';
22632         }
22633         
22634         if(this.closable){
22635             cfg.cn.push({
22636                 tag: 'button',
22637                 cls: 'close',
22638                 html: 'x'
22639             });
22640         }
22641         
22642         return cfg;
22643     },
22644     
22645     onRender : function(ct, position)
22646     {
22647         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22648         
22649         if(!this.el){
22650             var cfg = Roo.apply({},  this.getAutoCreate());
22651             cfg.id = Roo.id();
22652             
22653             if (this.cls) {
22654                 cfg.cls += ' ' + this.cls;
22655             }
22656             if (this.style) {
22657                 cfg.style = this.style;
22658             }
22659             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22660             
22661             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22662         }
22663         
22664         this.el.select('>button.close').on('click', this.hide, this);
22665         
22666     },
22667     
22668     show : function()
22669     {
22670         if (!this.rendered) {
22671             this.render();
22672         }
22673         
22674         this.el.show();
22675         
22676         this.fireEvent('show', this);
22677         
22678     },
22679     
22680     hide : function()
22681     {
22682         if (!this.rendered) {
22683             this.render();
22684         }
22685         
22686         this.el.hide();
22687         
22688         this.fireEvent('hide', this);
22689     },
22690     
22691     update : function()
22692     {
22693 //        var e = this.el.dom.firstChild;
22694 //        
22695 //        if(this.closable){
22696 //            e = e.nextSibling;
22697 //        }
22698 //        
22699 //        e.data = this.html || '';
22700
22701         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22702     }
22703    
22704 });
22705
22706  
22707
22708      /*
22709  * - LGPL
22710  *
22711  * Graph
22712  * 
22713  */
22714
22715
22716 /**
22717  * @class Roo.bootstrap.Graph
22718  * @extends Roo.bootstrap.Component
22719  * Bootstrap Graph class
22720 > Prameters
22721  -sm {number} sm 4
22722  -md {number} md 5
22723  @cfg {String} graphtype  bar | vbar | pie
22724  @cfg {number} g_x coodinator | centre x (pie)
22725  @cfg {number} g_y coodinator | centre y (pie)
22726  @cfg {number} g_r radius (pie)
22727  @cfg {number} g_height height of the chart (respected by all elements in the set)
22728  @cfg {number} g_width width of the chart (respected by all elements in the set)
22729  @cfg {Object} title The title of the chart
22730     
22731  -{Array}  values
22732  -opts (object) options for the chart 
22733      o {
22734      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22735      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22736      o vgutter (number)
22737      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.
22738      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22739      o to
22740      o stretch (boolean)
22741      o }
22742  -opts (object) options for the pie
22743      o{
22744      o cut
22745      o startAngle (number)
22746      o endAngle (number)
22747      } 
22748  *
22749  * @constructor
22750  * Create a new Input
22751  * @param {Object} config The config object
22752  */
22753
22754 Roo.bootstrap.Graph = function(config){
22755     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22756     
22757     this.addEvents({
22758         // img events
22759         /**
22760          * @event click
22761          * The img click event for the img.
22762          * @param {Roo.EventObject} e
22763          */
22764         "click" : true
22765     });
22766 };
22767
22768 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22769     
22770     sm: 4,
22771     md: 5,
22772     graphtype: 'bar',
22773     g_height: 250,
22774     g_width: 400,
22775     g_x: 50,
22776     g_y: 50,
22777     g_r: 30,
22778     opts:{
22779         //g_colors: this.colors,
22780         g_type: 'soft',
22781         g_gutter: '20%'
22782
22783     },
22784     title : false,
22785
22786     getAutoCreate : function(){
22787         
22788         var cfg = {
22789             tag: 'div',
22790             html : null
22791         };
22792         
22793         
22794         return  cfg;
22795     },
22796
22797     onRender : function(ct,position){
22798         
22799         
22800         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22801         
22802         if (typeof(Raphael) == 'undefined') {
22803             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
22804             return;
22805         }
22806         
22807         this.raphael = Raphael(this.el.dom);
22808         
22809                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22810                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22811                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22812                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22813                 /*
22814                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22815                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22816                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22817                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22818                 
22819                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22820                 r.barchart(330, 10, 300, 220, data1);
22821                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22822                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22823                 */
22824                 
22825                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22826                 // r.barchart(30, 30, 560, 250,  xdata, {
22827                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22828                 //     axis : "0 0 1 1",
22829                 //     axisxlabels :  xdata
22830                 //     //yvalues : cols,
22831                    
22832                 // });
22833 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22834 //        
22835 //        this.load(null,xdata,{
22836 //                axis : "0 0 1 1",
22837 //                axisxlabels :  xdata
22838 //                });
22839
22840     },
22841
22842     load : function(graphtype,xdata,opts)
22843     {
22844         this.raphael.clear();
22845         if(!graphtype) {
22846             graphtype = this.graphtype;
22847         }
22848         if(!opts){
22849             opts = this.opts;
22850         }
22851         var r = this.raphael,
22852             fin = function () {
22853                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22854             },
22855             fout = function () {
22856                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22857             },
22858             pfin = function() {
22859                 this.sector.stop();
22860                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22861
22862                 if (this.label) {
22863                     this.label[0].stop();
22864                     this.label[0].attr({ r: 7.5 });
22865                     this.label[1].attr({ "font-weight": 800 });
22866                 }
22867             },
22868             pfout = function() {
22869                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22870
22871                 if (this.label) {
22872                     this.label[0].animate({ r: 5 }, 500, "bounce");
22873                     this.label[1].attr({ "font-weight": 400 });
22874                 }
22875             };
22876
22877         switch(graphtype){
22878             case 'bar':
22879                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22880                 break;
22881             case 'hbar':
22882                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22883                 break;
22884             case 'pie':
22885 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22886 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22887 //            
22888                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22889                 
22890                 break;
22891
22892         }
22893         
22894         if(this.title){
22895             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22896         }
22897         
22898     },
22899     
22900     setTitle: function(o)
22901     {
22902         this.title = o;
22903     },
22904     
22905     initEvents: function() {
22906         
22907         if(!this.href){
22908             this.el.on('click', this.onClick, this);
22909         }
22910     },
22911     
22912     onClick : function(e)
22913     {
22914         Roo.log('img onclick');
22915         this.fireEvent('click', this, e);
22916     }
22917    
22918 });
22919
22920  
22921 /*
22922  * - LGPL
22923  *
22924  * numberBox
22925  * 
22926  */
22927 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22928
22929 /**
22930  * @class Roo.bootstrap.dash.NumberBox
22931  * @extends Roo.bootstrap.Component
22932  * Bootstrap NumberBox class
22933  * @cfg {String} headline Box headline
22934  * @cfg {String} content Box content
22935  * @cfg {String} icon Box icon
22936  * @cfg {String} footer Footer text
22937  * @cfg {String} fhref Footer href
22938  * 
22939  * @constructor
22940  * Create a new NumberBox
22941  * @param {Object} config The config object
22942  */
22943
22944
22945 Roo.bootstrap.dash.NumberBox = function(config){
22946     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22947     
22948 };
22949
22950 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22951     
22952     headline : '',
22953     content : '',
22954     icon : '',
22955     footer : '',
22956     fhref : '',
22957     ficon : '',
22958     
22959     getAutoCreate : function(){
22960         
22961         var cfg = {
22962             tag : 'div',
22963             cls : 'small-box ',
22964             cn : [
22965                 {
22966                     tag : 'div',
22967                     cls : 'inner',
22968                     cn :[
22969                         {
22970                             tag : 'h3',
22971                             cls : 'roo-headline',
22972                             html : this.headline
22973                         },
22974                         {
22975                             tag : 'p',
22976                             cls : 'roo-content',
22977                             html : this.content
22978                         }
22979                     ]
22980                 }
22981             ]
22982         };
22983         
22984         if(this.icon){
22985             cfg.cn.push({
22986                 tag : 'div',
22987                 cls : 'icon',
22988                 cn :[
22989                     {
22990                         tag : 'i',
22991                         cls : 'ion ' + this.icon
22992                     }
22993                 ]
22994             });
22995         }
22996         
22997         if(this.footer){
22998             var footer = {
22999                 tag : 'a',
23000                 cls : 'small-box-footer',
23001                 href : this.fhref || '#',
23002                 html : this.footer
23003             };
23004             
23005             cfg.cn.push(footer);
23006             
23007         }
23008         
23009         return  cfg;
23010     },
23011
23012     onRender : function(ct,position){
23013         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23014
23015
23016        
23017                 
23018     },
23019
23020     setHeadline: function (value)
23021     {
23022         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23023     },
23024     
23025     setFooter: function (value, href)
23026     {
23027         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23028         
23029         if(href){
23030             this.el.select('a.small-box-footer',true).first().attr('href', href);
23031         }
23032         
23033     },
23034
23035     setContent: function (value)
23036     {
23037         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23038     },
23039
23040     initEvents: function() 
23041     {   
23042         
23043     }
23044     
23045 });
23046
23047  
23048 /*
23049  * - LGPL
23050  *
23051  * TabBox
23052  * 
23053  */
23054 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23055
23056 /**
23057  * @class Roo.bootstrap.dash.TabBox
23058  * @extends Roo.bootstrap.Component
23059  * Bootstrap TabBox class
23060  * @cfg {String} title Title of the TabBox
23061  * @cfg {String} icon Icon of the TabBox
23062  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23063  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23064  * 
23065  * @constructor
23066  * Create a new TabBox
23067  * @param {Object} config The config object
23068  */
23069
23070
23071 Roo.bootstrap.dash.TabBox = function(config){
23072     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23073     this.addEvents({
23074         // raw events
23075         /**
23076          * @event addpane
23077          * When a pane is added
23078          * @param {Roo.bootstrap.dash.TabPane} pane
23079          */
23080         "addpane" : true,
23081         /**
23082          * @event activatepane
23083          * When a pane is activated
23084          * @param {Roo.bootstrap.dash.TabPane} pane
23085          */
23086         "activatepane" : true
23087         
23088          
23089     });
23090     
23091     this.panes = [];
23092 };
23093
23094 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23095
23096     title : '',
23097     icon : false,
23098     showtabs : true,
23099     tabScrollable : false,
23100     
23101     getChildContainer : function()
23102     {
23103         return this.el.select('.tab-content', true).first();
23104     },
23105     
23106     getAutoCreate : function(){
23107         
23108         var header = {
23109             tag: 'li',
23110             cls: 'pull-left header',
23111             html: this.title,
23112             cn : []
23113         };
23114         
23115         if(this.icon){
23116             header.cn.push({
23117                 tag: 'i',
23118                 cls: 'fa ' + this.icon
23119             });
23120         }
23121         
23122         var h = {
23123             tag: 'ul',
23124             cls: 'nav nav-tabs pull-right',
23125             cn: [
23126                 header
23127             ]
23128         };
23129         
23130         if(this.tabScrollable){
23131             h = {
23132                 tag: 'div',
23133                 cls: 'tab-header',
23134                 cn: [
23135                     {
23136                         tag: 'ul',
23137                         cls: 'nav nav-tabs pull-right',
23138                         cn: [
23139                             header
23140                         ]
23141                     }
23142                 ]
23143             };
23144         }
23145         
23146         var cfg = {
23147             tag: 'div',
23148             cls: 'nav-tabs-custom',
23149             cn: [
23150                 h,
23151                 {
23152                     tag: 'div',
23153                     cls: 'tab-content no-padding',
23154                     cn: []
23155                 }
23156             ]
23157         };
23158
23159         return  cfg;
23160     },
23161     initEvents : function()
23162     {
23163         //Roo.log('add add pane handler');
23164         this.on('addpane', this.onAddPane, this);
23165     },
23166      /**
23167      * Updates the box title
23168      * @param {String} html to set the title to.
23169      */
23170     setTitle : function(value)
23171     {
23172         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23173     },
23174     onAddPane : function(pane)
23175     {
23176         this.panes.push(pane);
23177         //Roo.log('addpane');
23178         //Roo.log(pane);
23179         // tabs are rendere left to right..
23180         if(!this.showtabs){
23181             return;
23182         }
23183         
23184         var ctr = this.el.select('.nav-tabs', true).first();
23185          
23186          
23187         var existing = ctr.select('.nav-tab',true);
23188         var qty = existing.getCount();;
23189         
23190         
23191         var tab = ctr.createChild({
23192             tag : 'li',
23193             cls : 'nav-tab' + (qty ? '' : ' active'),
23194             cn : [
23195                 {
23196                     tag : 'a',
23197                     href:'#',
23198                     html : pane.title
23199                 }
23200             ]
23201         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23202         pane.tab = tab;
23203         
23204         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23205         if (!qty) {
23206             pane.el.addClass('active');
23207         }
23208         
23209                 
23210     },
23211     onTabClick : function(ev,un,ob,pane)
23212     {
23213         //Roo.log('tab - prev default');
23214         ev.preventDefault();
23215         
23216         
23217         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23218         pane.tab.addClass('active');
23219         //Roo.log(pane.title);
23220         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23221         // technically we should have a deactivate event.. but maybe add later.
23222         // and it should not de-activate the selected tab...
23223         this.fireEvent('activatepane', pane);
23224         pane.el.addClass('active');
23225         pane.fireEvent('activate');
23226         
23227         
23228     },
23229     
23230     getActivePane : function()
23231     {
23232         var r = false;
23233         Roo.each(this.panes, function(p) {
23234             if(p.el.hasClass('active')){
23235                 r = p;
23236                 return false;
23237             }
23238             
23239             return;
23240         });
23241         
23242         return r;
23243     }
23244     
23245     
23246 });
23247
23248  
23249 /*
23250  * - LGPL
23251  *
23252  * Tab pane
23253  * 
23254  */
23255 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23256 /**
23257  * @class Roo.bootstrap.TabPane
23258  * @extends Roo.bootstrap.Component
23259  * Bootstrap TabPane class
23260  * @cfg {Boolean} active (false | true) Default false
23261  * @cfg {String} title title of panel
23262
23263  * 
23264  * @constructor
23265  * Create a new TabPane
23266  * @param {Object} config The config object
23267  */
23268
23269 Roo.bootstrap.dash.TabPane = function(config){
23270     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23271     
23272     this.addEvents({
23273         // raw events
23274         /**
23275          * @event activate
23276          * When a pane is activated
23277          * @param {Roo.bootstrap.dash.TabPane} pane
23278          */
23279         "activate" : true
23280          
23281     });
23282 };
23283
23284 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23285     
23286     active : false,
23287     title : '',
23288     
23289     // the tabBox that this is attached to.
23290     tab : false,
23291      
23292     getAutoCreate : function() 
23293     {
23294         var cfg = {
23295             tag: 'div',
23296             cls: 'tab-pane'
23297         };
23298         
23299         if(this.active){
23300             cfg.cls += ' active';
23301         }
23302         
23303         return cfg;
23304     },
23305     initEvents  : function()
23306     {
23307         //Roo.log('trigger add pane handler');
23308         this.parent().fireEvent('addpane', this)
23309     },
23310     
23311      /**
23312      * Updates the tab title 
23313      * @param {String} html to set the title to.
23314      */
23315     setTitle: function(str)
23316     {
23317         if (!this.tab) {
23318             return;
23319         }
23320         this.title = str;
23321         this.tab.select('a', true).first().dom.innerHTML = str;
23322         
23323     }
23324     
23325     
23326     
23327 });
23328
23329  
23330
23331
23332  /*
23333  * - LGPL
23334  *
23335  * menu
23336  * 
23337  */
23338 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23339
23340 /**
23341  * @class Roo.bootstrap.menu.Menu
23342  * @extends Roo.bootstrap.Component
23343  * Bootstrap Menu class - container for Menu
23344  * @cfg {String} html Text of the menu
23345  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23346  * @cfg {String} icon Font awesome icon
23347  * @cfg {String} pos Menu align to (top | bottom) default bottom
23348  * 
23349  * 
23350  * @constructor
23351  * Create a new Menu
23352  * @param {Object} config The config object
23353  */
23354
23355
23356 Roo.bootstrap.menu.Menu = function(config){
23357     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23358     
23359     this.addEvents({
23360         /**
23361          * @event beforeshow
23362          * Fires before this menu is displayed
23363          * @param {Roo.bootstrap.menu.Menu} this
23364          */
23365         beforeshow : true,
23366         /**
23367          * @event beforehide
23368          * Fires before this menu is hidden
23369          * @param {Roo.bootstrap.menu.Menu} this
23370          */
23371         beforehide : true,
23372         /**
23373          * @event show
23374          * Fires after this menu is displayed
23375          * @param {Roo.bootstrap.menu.Menu} this
23376          */
23377         show : true,
23378         /**
23379          * @event hide
23380          * Fires after this menu is hidden
23381          * @param {Roo.bootstrap.menu.Menu} this
23382          */
23383         hide : true,
23384         /**
23385          * @event click
23386          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23387          * @param {Roo.bootstrap.menu.Menu} this
23388          * @param {Roo.EventObject} e
23389          */
23390         click : true
23391     });
23392     
23393 };
23394
23395 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23396     
23397     submenu : false,
23398     html : '',
23399     weight : 'default',
23400     icon : false,
23401     pos : 'bottom',
23402     
23403     
23404     getChildContainer : function() {
23405         if(this.isSubMenu){
23406             return this.el;
23407         }
23408         
23409         return this.el.select('ul.dropdown-menu', true).first();  
23410     },
23411     
23412     getAutoCreate : function()
23413     {
23414         var text = [
23415             {
23416                 tag : 'span',
23417                 cls : 'roo-menu-text',
23418                 html : this.html
23419             }
23420         ];
23421         
23422         if(this.icon){
23423             text.unshift({
23424                 tag : 'i',
23425                 cls : 'fa ' + this.icon
23426             })
23427         }
23428         
23429         
23430         var cfg = {
23431             tag : 'div',
23432             cls : 'btn-group',
23433             cn : [
23434                 {
23435                     tag : 'button',
23436                     cls : 'dropdown-button btn btn-' + this.weight,
23437                     cn : text
23438                 },
23439                 {
23440                     tag : 'button',
23441                     cls : 'dropdown-toggle btn btn-' + this.weight,
23442                     cn : [
23443                         {
23444                             tag : 'span',
23445                             cls : 'caret'
23446                         }
23447                     ]
23448                 },
23449                 {
23450                     tag : 'ul',
23451                     cls : 'dropdown-menu'
23452                 }
23453             ]
23454             
23455         };
23456         
23457         if(this.pos == 'top'){
23458             cfg.cls += ' dropup';
23459         }
23460         
23461         if(this.isSubMenu){
23462             cfg = {
23463                 tag : 'ul',
23464                 cls : 'dropdown-menu'
23465             }
23466         }
23467         
23468         return cfg;
23469     },
23470     
23471     onRender : function(ct, position)
23472     {
23473         this.isSubMenu = ct.hasClass('dropdown-submenu');
23474         
23475         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23476     },
23477     
23478     initEvents : function() 
23479     {
23480         if(this.isSubMenu){
23481             return;
23482         }
23483         
23484         this.hidden = true;
23485         
23486         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23487         this.triggerEl.on('click', this.onTriggerPress, this);
23488         
23489         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23490         this.buttonEl.on('click', this.onClick, this);
23491         
23492     },
23493     
23494     list : function()
23495     {
23496         if(this.isSubMenu){
23497             return this.el;
23498         }
23499         
23500         return this.el.select('ul.dropdown-menu', true).first();
23501     },
23502     
23503     onClick : function(e)
23504     {
23505         this.fireEvent("click", this, e);
23506     },
23507     
23508     onTriggerPress  : function(e)
23509     {   
23510         if (this.isVisible()) {
23511             this.hide();
23512         } else {
23513             this.show();
23514         }
23515     },
23516     
23517     isVisible : function(){
23518         return !this.hidden;
23519     },
23520     
23521     show : function()
23522     {
23523         this.fireEvent("beforeshow", this);
23524         
23525         this.hidden = false;
23526         this.el.addClass('open');
23527         
23528         Roo.get(document).on("mouseup", this.onMouseUp, this);
23529         
23530         this.fireEvent("show", this);
23531         
23532         
23533     },
23534     
23535     hide : function()
23536     {
23537         this.fireEvent("beforehide", this);
23538         
23539         this.hidden = true;
23540         this.el.removeClass('open');
23541         
23542         Roo.get(document).un("mouseup", this.onMouseUp);
23543         
23544         this.fireEvent("hide", this);
23545     },
23546     
23547     onMouseUp : function()
23548     {
23549         this.hide();
23550     }
23551     
23552 });
23553
23554  
23555  /*
23556  * - LGPL
23557  *
23558  * menu item
23559  * 
23560  */
23561 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23562
23563 /**
23564  * @class Roo.bootstrap.menu.Item
23565  * @extends Roo.bootstrap.Component
23566  * Bootstrap MenuItem class
23567  * @cfg {Boolean} submenu (true | false) default false
23568  * @cfg {String} html text of the item
23569  * @cfg {String} href the link
23570  * @cfg {Boolean} disable (true | false) default false
23571  * @cfg {Boolean} preventDefault (true | false) default true
23572  * @cfg {String} icon Font awesome icon
23573  * @cfg {String} pos Submenu align to (left | right) default right 
23574  * 
23575  * 
23576  * @constructor
23577  * Create a new Item
23578  * @param {Object} config The config object
23579  */
23580
23581
23582 Roo.bootstrap.menu.Item = function(config){
23583     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23584     this.addEvents({
23585         /**
23586          * @event mouseover
23587          * Fires when the mouse is hovering over this menu
23588          * @param {Roo.bootstrap.menu.Item} this
23589          * @param {Roo.EventObject} e
23590          */
23591         mouseover : true,
23592         /**
23593          * @event mouseout
23594          * Fires when the mouse exits this menu
23595          * @param {Roo.bootstrap.menu.Item} this
23596          * @param {Roo.EventObject} e
23597          */
23598         mouseout : true,
23599         // raw events
23600         /**
23601          * @event click
23602          * The raw click event for the entire grid.
23603          * @param {Roo.EventObject} e
23604          */
23605         click : true
23606     });
23607 };
23608
23609 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23610     
23611     submenu : false,
23612     href : '',
23613     html : '',
23614     preventDefault: true,
23615     disable : false,
23616     icon : false,
23617     pos : 'right',
23618     
23619     getAutoCreate : function()
23620     {
23621         var text = [
23622             {
23623                 tag : 'span',
23624                 cls : 'roo-menu-item-text',
23625                 html : this.html
23626             }
23627         ];
23628         
23629         if(this.icon){
23630             text.unshift({
23631                 tag : 'i',
23632                 cls : 'fa ' + this.icon
23633             })
23634         }
23635         
23636         var cfg = {
23637             tag : 'li',
23638             cn : [
23639                 {
23640                     tag : 'a',
23641                     href : this.href || '#',
23642                     cn : text
23643                 }
23644             ]
23645         };
23646         
23647         if(this.disable){
23648             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23649         }
23650         
23651         if(this.submenu){
23652             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23653             
23654             if(this.pos == 'left'){
23655                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23656             }
23657         }
23658         
23659         return cfg;
23660     },
23661     
23662     initEvents : function() 
23663     {
23664         this.el.on('mouseover', this.onMouseOver, this);
23665         this.el.on('mouseout', this.onMouseOut, this);
23666         
23667         this.el.select('a', true).first().on('click', this.onClick, this);
23668         
23669     },
23670     
23671     onClick : function(e)
23672     {
23673         if(this.preventDefault){
23674             e.preventDefault();
23675         }
23676         
23677         this.fireEvent("click", this, e);
23678     },
23679     
23680     onMouseOver : function(e)
23681     {
23682         if(this.submenu && this.pos == 'left'){
23683             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23684         }
23685         
23686         this.fireEvent("mouseover", this, e);
23687     },
23688     
23689     onMouseOut : function(e)
23690     {
23691         this.fireEvent("mouseout", this, e);
23692     }
23693 });
23694
23695  
23696
23697  /*
23698  * - LGPL
23699  *
23700  * menu separator
23701  * 
23702  */
23703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23704
23705 /**
23706  * @class Roo.bootstrap.menu.Separator
23707  * @extends Roo.bootstrap.Component
23708  * Bootstrap Separator class
23709  * 
23710  * @constructor
23711  * Create a new Separator
23712  * @param {Object} config The config object
23713  */
23714
23715
23716 Roo.bootstrap.menu.Separator = function(config){
23717     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23718 };
23719
23720 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23721     
23722     getAutoCreate : function(){
23723         var cfg = {
23724             tag : 'li',
23725             cls: 'divider'
23726         };
23727         
23728         return cfg;
23729     }
23730    
23731 });
23732
23733  
23734
23735  /*
23736  * - LGPL
23737  *
23738  * Tooltip
23739  * 
23740  */
23741
23742 /**
23743  * @class Roo.bootstrap.Tooltip
23744  * Bootstrap Tooltip class
23745  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23746  * to determine which dom element triggers the tooltip.
23747  * 
23748  * It needs to add support for additional attributes like tooltip-position
23749  * 
23750  * @constructor
23751  * Create a new Toolti
23752  * @param {Object} config The config object
23753  */
23754
23755 Roo.bootstrap.Tooltip = function(config){
23756     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23757 };
23758
23759 Roo.apply(Roo.bootstrap.Tooltip, {
23760     /**
23761      * @function init initialize tooltip monitoring.
23762      * @static
23763      */
23764     currentEl : false,
23765     currentTip : false,
23766     currentRegion : false,
23767     
23768     //  init : delay?
23769     
23770     init : function()
23771     {
23772         Roo.get(document).on('mouseover', this.enter ,this);
23773         Roo.get(document).on('mouseout', this.leave, this);
23774          
23775         
23776         this.currentTip = new Roo.bootstrap.Tooltip();
23777     },
23778     
23779     enter : function(ev)
23780     {
23781         var dom = ev.getTarget();
23782         
23783         //Roo.log(['enter',dom]);
23784         var el = Roo.fly(dom);
23785         if (this.currentEl) {
23786             //Roo.log(dom);
23787             //Roo.log(this.currentEl);
23788             //Roo.log(this.currentEl.contains(dom));
23789             if (this.currentEl == el) {
23790                 return;
23791             }
23792             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23793                 return;
23794             }
23795
23796         }
23797         
23798         if (this.currentTip.el) {
23799             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23800         }    
23801         //Roo.log(ev);
23802         var bindEl = el;
23803         
23804         // you can not look for children, as if el is the body.. then everythign is the child..
23805         if (!el.attr('tooltip')) { //
23806             if (!el.select("[tooltip]").elements.length) {
23807                 return;
23808             }
23809             // is the mouse over this child...?
23810             bindEl = el.select("[tooltip]").first();
23811             var xy = ev.getXY();
23812             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23813                 //Roo.log("not in region.");
23814                 return;
23815             }
23816             //Roo.log("child element over..");
23817             
23818         }
23819         this.currentEl = bindEl;
23820         this.currentTip.bind(bindEl);
23821         this.currentRegion = Roo.lib.Region.getRegion(dom);
23822         this.currentTip.enter();
23823         
23824     },
23825     leave : function(ev)
23826     {
23827         var dom = ev.getTarget();
23828         //Roo.log(['leave',dom]);
23829         if (!this.currentEl) {
23830             return;
23831         }
23832         
23833         
23834         if (dom != this.currentEl.dom) {
23835             return;
23836         }
23837         var xy = ev.getXY();
23838         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23839             return;
23840         }
23841         // only activate leave if mouse cursor is outside... bounding box..
23842         
23843         
23844         
23845         
23846         if (this.currentTip) {
23847             this.currentTip.leave();
23848         }
23849         //Roo.log('clear currentEl');
23850         this.currentEl = false;
23851         
23852         
23853     },
23854     alignment : {
23855         'left' : ['r-l', [-2,0], 'right'],
23856         'right' : ['l-r', [2,0], 'left'],
23857         'bottom' : ['t-b', [0,2], 'top'],
23858         'top' : [ 'b-t', [0,-2], 'bottom']
23859     }
23860     
23861 });
23862
23863
23864 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23865     
23866     
23867     bindEl : false,
23868     
23869     delay : null, // can be { show : 300 , hide: 500}
23870     
23871     timeout : null,
23872     
23873     hoverState : null, //???
23874     
23875     placement : 'bottom', 
23876     
23877     getAutoCreate : function(){
23878     
23879         var cfg = {
23880            cls : 'tooltip',
23881            role : 'tooltip',
23882            cn : [
23883                 {
23884                     cls : 'tooltip-arrow'
23885                 },
23886                 {
23887                     cls : 'tooltip-inner'
23888                 }
23889            ]
23890         };
23891         
23892         return cfg;
23893     },
23894     bind : function(el)
23895     {
23896         this.bindEl = el;
23897     },
23898       
23899     
23900     enter : function () {
23901        
23902         if (this.timeout != null) {
23903             clearTimeout(this.timeout);
23904         }
23905         
23906         this.hoverState = 'in';
23907          //Roo.log("enter - show");
23908         if (!this.delay || !this.delay.show) {
23909             this.show();
23910             return;
23911         }
23912         var _t = this;
23913         this.timeout = setTimeout(function () {
23914             if (_t.hoverState == 'in') {
23915                 _t.show();
23916             }
23917         }, this.delay.show);
23918     },
23919     leave : function()
23920     {
23921         clearTimeout(this.timeout);
23922     
23923         this.hoverState = 'out';
23924          if (!this.delay || !this.delay.hide) {
23925             this.hide();
23926             return;
23927         }
23928        
23929         var _t = this;
23930         this.timeout = setTimeout(function () {
23931             //Roo.log("leave - timeout");
23932             
23933             if (_t.hoverState == 'out') {
23934                 _t.hide();
23935                 Roo.bootstrap.Tooltip.currentEl = false;
23936             }
23937         }, delay);
23938     },
23939     
23940     show : function ()
23941     {
23942         if (!this.el) {
23943             this.render(document.body);
23944         }
23945         // set content.
23946         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23947         
23948         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23949         
23950         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23951         
23952         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23953         
23954         var placement = typeof this.placement == 'function' ?
23955             this.placement.call(this, this.el, on_el) :
23956             this.placement;
23957             
23958         var autoToken = /\s?auto?\s?/i;
23959         var autoPlace = autoToken.test(placement);
23960         if (autoPlace) {
23961             placement = placement.replace(autoToken, '') || 'top';
23962         }
23963         
23964         //this.el.detach()
23965         //this.el.setXY([0,0]);
23966         this.el.show();
23967         //this.el.dom.style.display='block';
23968         
23969         //this.el.appendTo(on_el);
23970         
23971         var p = this.getPosition();
23972         var box = this.el.getBox();
23973         
23974         if (autoPlace) {
23975             // fixme..
23976         }
23977         
23978         var align = Roo.bootstrap.Tooltip.alignment[placement];
23979         
23980         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23981         
23982         if(placement == 'top' || placement == 'bottom'){
23983             if(xy[0] < 0){
23984                 placement = 'right';
23985             }
23986             
23987             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23988                 placement = 'left';
23989             }
23990         }
23991         
23992         align = Roo.bootstrap.Tooltip.alignment[placement];
23993         
23994         this.el.alignTo(this.bindEl, align[0],align[1]);
23995         //var arrow = this.el.select('.arrow',true).first();
23996         //arrow.set(align[2], 
23997         
23998         this.el.addClass(placement);
23999         
24000         this.el.addClass('in fade');
24001         
24002         this.hoverState = null;
24003         
24004         if (this.el.hasClass('fade')) {
24005             // fade it?
24006         }
24007         
24008     },
24009     hide : function()
24010     {
24011          
24012         if (!this.el) {
24013             return;
24014         }
24015         //this.el.setXY([0,0]);
24016         this.el.removeClass('in');
24017         //this.el.hide();
24018         
24019     }
24020     
24021 });
24022  
24023
24024  /*
24025  * - LGPL
24026  *
24027  * Location Picker
24028  * 
24029  */
24030
24031 /**
24032  * @class Roo.bootstrap.LocationPicker
24033  * @extends Roo.bootstrap.Component
24034  * Bootstrap LocationPicker class
24035  * @cfg {Number} latitude Position when init default 0
24036  * @cfg {Number} longitude Position when init default 0
24037  * @cfg {Number} zoom default 15
24038  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24039  * @cfg {Boolean} mapTypeControl default false
24040  * @cfg {Boolean} disableDoubleClickZoom default false
24041  * @cfg {Boolean} scrollwheel default true
24042  * @cfg {Boolean} streetViewControl default false
24043  * @cfg {Number} radius default 0
24044  * @cfg {String} locationName
24045  * @cfg {Boolean} draggable default true
24046  * @cfg {Boolean} enableAutocomplete default false
24047  * @cfg {Boolean} enableReverseGeocode default true
24048  * @cfg {String} markerTitle
24049  * 
24050  * @constructor
24051  * Create a new LocationPicker
24052  * @param {Object} config The config object
24053  */
24054
24055
24056 Roo.bootstrap.LocationPicker = function(config){
24057     
24058     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24059     
24060     this.addEvents({
24061         /**
24062          * @event initial
24063          * Fires when the picker initialized.
24064          * @param {Roo.bootstrap.LocationPicker} this
24065          * @param {Google Location} location
24066          */
24067         initial : true,
24068         /**
24069          * @event positionchanged
24070          * Fires when the picker position changed.
24071          * @param {Roo.bootstrap.LocationPicker} this
24072          * @param {Google Location} location
24073          */
24074         positionchanged : true,
24075         /**
24076          * @event resize
24077          * Fires when the map resize.
24078          * @param {Roo.bootstrap.LocationPicker} this
24079          */
24080         resize : true,
24081         /**
24082          * @event show
24083          * Fires when the map show.
24084          * @param {Roo.bootstrap.LocationPicker} this
24085          */
24086         show : true,
24087         /**
24088          * @event hide
24089          * Fires when the map hide.
24090          * @param {Roo.bootstrap.LocationPicker} this
24091          */
24092         hide : true,
24093         /**
24094          * @event mapClick
24095          * Fires when click the map.
24096          * @param {Roo.bootstrap.LocationPicker} this
24097          * @param {Map event} e
24098          */
24099         mapClick : true,
24100         /**
24101          * @event mapRightClick
24102          * Fires when right click the map.
24103          * @param {Roo.bootstrap.LocationPicker} this
24104          * @param {Map event} e
24105          */
24106         mapRightClick : true,
24107         /**
24108          * @event markerClick
24109          * Fires when click the marker.
24110          * @param {Roo.bootstrap.LocationPicker} this
24111          * @param {Map event} e
24112          */
24113         markerClick : true,
24114         /**
24115          * @event markerRightClick
24116          * Fires when right click the marker.
24117          * @param {Roo.bootstrap.LocationPicker} this
24118          * @param {Map event} e
24119          */
24120         markerRightClick : true,
24121         /**
24122          * @event OverlayViewDraw
24123          * Fires when OverlayView Draw
24124          * @param {Roo.bootstrap.LocationPicker} this
24125          */
24126         OverlayViewDraw : true,
24127         /**
24128          * @event OverlayViewOnAdd
24129          * Fires when OverlayView Draw
24130          * @param {Roo.bootstrap.LocationPicker} this
24131          */
24132         OverlayViewOnAdd : true,
24133         /**
24134          * @event OverlayViewOnRemove
24135          * Fires when OverlayView Draw
24136          * @param {Roo.bootstrap.LocationPicker} this
24137          */
24138         OverlayViewOnRemove : true,
24139         /**
24140          * @event OverlayViewShow
24141          * Fires when OverlayView Draw
24142          * @param {Roo.bootstrap.LocationPicker} this
24143          * @param {Pixel} cpx
24144          */
24145         OverlayViewShow : true,
24146         /**
24147          * @event OverlayViewHide
24148          * Fires when OverlayView Draw
24149          * @param {Roo.bootstrap.LocationPicker} this
24150          */
24151         OverlayViewHide : true,
24152         /**
24153          * @event loadexception
24154          * Fires when load google lib failed.
24155          * @param {Roo.bootstrap.LocationPicker} this
24156          */
24157         loadexception : true
24158     });
24159         
24160 };
24161
24162 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24163     
24164     gMapContext: false,
24165     
24166     latitude: 0,
24167     longitude: 0,
24168     zoom: 15,
24169     mapTypeId: false,
24170     mapTypeControl: false,
24171     disableDoubleClickZoom: false,
24172     scrollwheel: true,
24173     streetViewControl: false,
24174     radius: 0,
24175     locationName: '',
24176     draggable: true,
24177     enableAutocomplete: false,
24178     enableReverseGeocode: true,
24179     markerTitle: '',
24180     
24181     getAutoCreate: function()
24182     {
24183
24184         var cfg = {
24185             tag: 'div',
24186             cls: 'roo-location-picker'
24187         };
24188         
24189         return cfg
24190     },
24191     
24192     initEvents: function(ct, position)
24193     {       
24194         if(!this.el.getWidth() || this.isApplied()){
24195             return;
24196         }
24197         
24198         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24199         
24200         this.initial();
24201     },
24202     
24203     initial: function()
24204     {
24205         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24206             this.fireEvent('loadexception', this);
24207             return;
24208         }
24209         
24210         if(!this.mapTypeId){
24211             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24212         }
24213         
24214         this.gMapContext = this.GMapContext();
24215         
24216         this.initOverlayView();
24217         
24218         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24219         
24220         var _this = this;
24221                 
24222         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24223             _this.setPosition(_this.gMapContext.marker.position);
24224         });
24225         
24226         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24227             _this.fireEvent('mapClick', this, event);
24228             
24229         });
24230
24231         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24232             _this.fireEvent('mapRightClick', this, event);
24233             
24234         });
24235         
24236         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24237             _this.fireEvent('markerClick', this, event);
24238             
24239         });
24240
24241         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24242             _this.fireEvent('markerRightClick', this, event);
24243             
24244         });
24245         
24246         this.setPosition(this.gMapContext.location);
24247         
24248         this.fireEvent('initial', this, this.gMapContext.location);
24249     },
24250     
24251     initOverlayView: function()
24252     {
24253         var _this = this;
24254         
24255         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24256             
24257             draw: function()
24258             {
24259                 _this.fireEvent('OverlayViewDraw', _this);
24260             },
24261             
24262             onAdd: function()
24263             {
24264                 _this.fireEvent('OverlayViewOnAdd', _this);
24265             },
24266             
24267             onRemove: function()
24268             {
24269                 _this.fireEvent('OverlayViewOnRemove', _this);
24270             },
24271             
24272             show: function(cpx)
24273             {
24274                 _this.fireEvent('OverlayViewShow', _this, cpx);
24275             },
24276             
24277             hide: function()
24278             {
24279                 _this.fireEvent('OverlayViewHide', _this);
24280             }
24281             
24282         });
24283     },
24284     
24285     fromLatLngToContainerPixel: function(event)
24286     {
24287         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24288     },
24289     
24290     isApplied: function() 
24291     {
24292         return this.getGmapContext() == false ? false : true;
24293     },
24294     
24295     getGmapContext: function() 
24296     {
24297         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24298     },
24299     
24300     GMapContext: function() 
24301     {
24302         var position = new google.maps.LatLng(this.latitude, this.longitude);
24303         
24304         var _map = new google.maps.Map(this.el.dom, {
24305             center: position,
24306             zoom: this.zoom,
24307             mapTypeId: this.mapTypeId,
24308             mapTypeControl: this.mapTypeControl,
24309             disableDoubleClickZoom: this.disableDoubleClickZoom,
24310             scrollwheel: this.scrollwheel,
24311             streetViewControl: this.streetViewControl,
24312             locationName: this.locationName,
24313             draggable: this.draggable,
24314             enableAutocomplete: this.enableAutocomplete,
24315             enableReverseGeocode: this.enableReverseGeocode
24316         });
24317         
24318         var _marker = new google.maps.Marker({
24319             position: position,
24320             map: _map,
24321             title: this.markerTitle,
24322             draggable: this.draggable
24323         });
24324         
24325         return {
24326             map: _map,
24327             marker: _marker,
24328             circle: null,
24329             location: position,
24330             radius: this.radius,
24331             locationName: this.locationName,
24332             addressComponents: {
24333                 formatted_address: null,
24334                 addressLine1: null,
24335                 addressLine2: null,
24336                 streetName: null,
24337                 streetNumber: null,
24338                 city: null,
24339                 district: null,
24340                 state: null,
24341                 stateOrProvince: null
24342             },
24343             settings: this,
24344             domContainer: this.el.dom,
24345             geodecoder: new google.maps.Geocoder()
24346         };
24347     },
24348     
24349     drawCircle: function(center, radius, options) 
24350     {
24351         if (this.gMapContext.circle != null) {
24352             this.gMapContext.circle.setMap(null);
24353         }
24354         if (radius > 0) {
24355             radius *= 1;
24356             options = Roo.apply({}, options, {
24357                 strokeColor: "#0000FF",
24358                 strokeOpacity: .35,
24359                 strokeWeight: 2,
24360                 fillColor: "#0000FF",
24361                 fillOpacity: .2
24362             });
24363             
24364             options.map = this.gMapContext.map;
24365             options.radius = radius;
24366             options.center = center;
24367             this.gMapContext.circle = new google.maps.Circle(options);
24368             return this.gMapContext.circle;
24369         }
24370         
24371         return null;
24372     },
24373     
24374     setPosition: function(location) 
24375     {
24376         this.gMapContext.location = location;
24377         this.gMapContext.marker.setPosition(location);
24378         this.gMapContext.map.panTo(location);
24379         this.drawCircle(location, this.gMapContext.radius, {});
24380         
24381         var _this = this;
24382         
24383         if (this.gMapContext.settings.enableReverseGeocode) {
24384             this.gMapContext.geodecoder.geocode({
24385                 latLng: this.gMapContext.location
24386             }, function(results, status) {
24387                 
24388                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24389                     _this.gMapContext.locationName = results[0].formatted_address;
24390                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24391                     
24392                     _this.fireEvent('positionchanged', this, location);
24393                 }
24394             });
24395             
24396             return;
24397         }
24398         
24399         this.fireEvent('positionchanged', this, location);
24400     },
24401     
24402     resize: function()
24403     {
24404         google.maps.event.trigger(this.gMapContext.map, "resize");
24405         
24406         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24407         
24408         this.fireEvent('resize', this);
24409     },
24410     
24411     setPositionByLatLng: function(latitude, longitude)
24412     {
24413         this.setPosition(new google.maps.LatLng(latitude, longitude));
24414     },
24415     
24416     getCurrentPosition: function() 
24417     {
24418         return {
24419             latitude: this.gMapContext.location.lat(),
24420             longitude: this.gMapContext.location.lng()
24421         };
24422     },
24423     
24424     getAddressName: function() 
24425     {
24426         return this.gMapContext.locationName;
24427     },
24428     
24429     getAddressComponents: function() 
24430     {
24431         return this.gMapContext.addressComponents;
24432     },
24433     
24434     address_component_from_google_geocode: function(address_components) 
24435     {
24436         var result = {};
24437         
24438         for (var i = 0; i < address_components.length; i++) {
24439             var component = address_components[i];
24440             if (component.types.indexOf("postal_code") >= 0) {
24441                 result.postalCode = component.short_name;
24442             } else if (component.types.indexOf("street_number") >= 0) {
24443                 result.streetNumber = component.short_name;
24444             } else if (component.types.indexOf("route") >= 0) {
24445                 result.streetName = component.short_name;
24446             } else if (component.types.indexOf("neighborhood") >= 0) {
24447                 result.city = component.short_name;
24448             } else if (component.types.indexOf("locality") >= 0) {
24449                 result.city = component.short_name;
24450             } else if (component.types.indexOf("sublocality") >= 0) {
24451                 result.district = component.short_name;
24452             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24453                 result.stateOrProvince = component.short_name;
24454             } else if (component.types.indexOf("country") >= 0) {
24455                 result.country = component.short_name;
24456             }
24457         }
24458         
24459         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24460         result.addressLine2 = "";
24461         return result;
24462     },
24463     
24464     setZoomLevel: function(zoom)
24465     {
24466         this.gMapContext.map.setZoom(zoom);
24467     },
24468     
24469     show: function()
24470     {
24471         if(!this.el){
24472             return;
24473         }
24474         
24475         this.el.show();
24476         
24477         this.resize();
24478         
24479         this.fireEvent('show', this);
24480     },
24481     
24482     hide: function()
24483     {
24484         if(!this.el){
24485             return;
24486         }
24487         
24488         this.el.hide();
24489         
24490         this.fireEvent('hide', this);
24491     }
24492     
24493 });
24494
24495 Roo.apply(Roo.bootstrap.LocationPicker, {
24496     
24497     OverlayView : function(map, options)
24498     {
24499         options = options || {};
24500         
24501         this.setMap(map);
24502     }
24503     
24504     
24505 });/*
24506  * - LGPL
24507  *
24508  * Alert
24509  * 
24510  */
24511
24512 /**
24513  * @class Roo.bootstrap.Alert
24514  * @extends Roo.bootstrap.Component
24515  * Bootstrap Alert class
24516  * @cfg {String} title The title of alert
24517  * @cfg {String} html The content of alert
24518  * @cfg {String} weight (  success | info | warning | danger )
24519  * @cfg {String} faicon font-awesomeicon
24520  * 
24521  * @constructor
24522  * Create a new alert
24523  * @param {Object} config The config object
24524  */
24525
24526
24527 Roo.bootstrap.Alert = function(config){
24528     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24529     
24530 };
24531
24532 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24533     
24534     title: '',
24535     html: '',
24536     weight: false,
24537     faicon: false,
24538     
24539     getAutoCreate : function()
24540     {
24541         
24542         var cfg = {
24543             tag : 'div',
24544             cls : 'alert',
24545             cn : [
24546                 {
24547                     tag : 'i',
24548                     cls : 'roo-alert-icon'
24549                     
24550                 },
24551                 {
24552                     tag : 'b',
24553                     cls : 'roo-alert-title',
24554                     html : this.title
24555                 },
24556                 {
24557                     tag : 'span',
24558                     cls : 'roo-alert-text',
24559                     html : this.html
24560                 }
24561             ]
24562         };
24563         
24564         if(this.faicon){
24565             cfg.cn[0].cls += ' fa ' + this.faicon;
24566         }
24567         
24568         if(this.weight){
24569             cfg.cls += ' alert-' + this.weight;
24570         }
24571         
24572         return cfg;
24573     },
24574     
24575     initEvents: function() 
24576     {
24577         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24578     },
24579     
24580     setTitle : function(str)
24581     {
24582         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24583     },
24584     
24585     setText : function(str)
24586     {
24587         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24588     },
24589     
24590     setWeight : function(weight)
24591     {
24592         if(this.weight){
24593             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24594         }
24595         
24596         this.weight = weight;
24597         
24598         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24599     },
24600     
24601     setIcon : function(icon)
24602     {
24603         if(this.faicon){
24604             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24605         }
24606         
24607         this.faicon = icon;
24608         
24609         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24610     },
24611     
24612     hide: function() 
24613     {
24614         this.el.hide();   
24615     },
24616     
24617     show: function() 
24618     {  
24619         this.el.show();   
24620     }
24621     
24622 });
24623
24624  
24625 /*
24626 * Licence: LGPL
24627 */
24628
24629 /**
24630  * @class Roo.bootstrap.UploadCropbox
24631  * @extends Roo.bootstrap.Component
24632  * Bootstrap UploadCropbox class
24633  * @cfg {String} emptyText show when image has been loaded
24634  * @cfg {String} rotateNotify show when image too small to rotate
24635  * @cfg {Number} errorTimeout default 3000
24636  * @cfg {Number} minWidth default 300
24637  * @cfg {Number} minHeight default 300
24638  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24639  * @cfg {Boolean} isDocument (true|false) default false
24640  * @cfg {String} url action url
24641  * @cfg {String} paramName default 'imageUpload'
24642  * @cfg {String} method default POST
24643  * @cfg {Boolean} loadMask (true|false) default true
24644  * @cfg {Boolean} loadingText default 'Loading...'
24645  * 
24646  * @constructor
24647  * Create a new UploadCropbox
24648  * @param {Object} config The config object
24649  */
24650
24651 Roo.bootstrap.UploadCropbox = function(config){
24652     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24653     
24654     this.addEvents({
24655         /**
24656          * @event beforeselectfile
24657          * Fire before select file
24658          * @param {Roo.bootstrap.UploadCropbox} this
24659          */
24660         "beforeselectfile" : true,
24661         /**
24662          * @event initial
24663          * Fire after initEvent
24664          * @param {Roo.bootstrap.UploadCropbox} this
24665          */
24666         "initial" : true,
24667         /**
24668          * @event crop
24669          * Fire after initEvent
24670          * @param {Roo.bootstrap.UploadCropbox} this
24671          * @param {String} data
24672          */
24673         "crop" : true,
24674         /**
24675          * @event prepare
24676          * Fire when preparing the file data
24677          * @param {Roo.bootstrap.UploadCropbox} this
24678          * @param {Object} file
24679          */
24680         "prepare" : true,
24681         /**
24682          * @event exception
24683          * Fire when get exception
24684          * @param {Roo.bootstrap.UploadCropbox} this
24685          * @param {XMLHttpRequest} xhr
24686          */
24687         "exception" : true,
24688         /**
24689          * @event beforeloadcanvas
24690          * Fire before load the canvas
24691          * @param {Roo.bootstrap.UploadCropbox} this
24692          * @param {String} src
24693          */
24694         "beforeloadcanvas" : true,
24695         /**
24696          * @event trash
24697          * Fire when trash image
24698          * @param {Roo.bootstrap.UploadCropbox} this
24699          */
24700         "trash" : true,
24701         /**
24702          * @event download
24703          * Fire when download the image
24704          * @param {Roo.bootstrap.UploadCropbox} this
24705          */
24706         "download" : true,
24707         /**
24708          * @event footerbuttonclick
24709          * Fire when footerbuttonclick
24710          * @param {Roo.bootstrap.UploadCropbox} this
24711          * @param {String} type
24712          */
24713         "footerbuttonclick" : true,
24714         /**
24715          * @event resize
24716          * Fire when resize
24717          * @param {Roo.bootstrap.UploadCropbox} this
24718          */
24719         "resize" : true,
24720         /**
24721          * @event rotate
24722          * Fire when rotate the image
24723          * @param {Roo.bootstrap.UploadCropbox} this
24724          * @param {String} pos
24725          */
24726         "rotate" : true,
24727         /**
24728          * @event inspect
24729          * Fire when inspect the file
24730          * @param {Roo.bootstrap.UploadCropbox} this
24731          * @param {Object} file
24732          */
24733         "inspect" : true,
24734         /**
24735          * @event upload
24736          * Fire when xhr upload the file
24737          * @param {Roo.bootstrap.UploadCropbox} this
24738          * @param {Object} data
24739          */
24740         "upload" : true,
24741         /**
24742          * @event arrange
24743          * Fire when arrange the file data
24744          * @param {Roo.bootstrap.UploadCropbox} this
24745          * @param {Object} formData
24746          */
24747         "arrange" : true
24748     });
24749     
24750     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24751 };
24752
24753 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24754     
24755     emptyText : 'Click to upload image',
24756     rotateNotify : 'Image is too small to rotate',
24757     errorTimeout : 3000,
24758     scale : 0,
24759     baseScale : 1,
24760     rotate : 0,
24761     dragable : false,
24762     pinching : false,
24763     mouseX : 0,
24764     mouseY : 0,
24765     cropData : false,
24766     minWidth : 300,
24767     minHeight : 300,
24768     file : false,
24769     exif : {},
24770     baseRotate : 1,
24771     cropType : 'image/jpeg',
24772     buttons : false,
24773     canvasLoaded : false,
24774     isDocument : false,
24775     method : 'POST',
24776     paramName : 'imageUpload',
24777     loadMask : true,
24778     loadingText : 'Loading...',
24779     maskEl : false,
24780     
24781     getAutoCreate : function()
24782     {
24783         var cfg = {
24784             tag : 'div',
24785             cls : 'roo-upload-cropbox',
24786             cn : [
24787                 {
24788                     tag : 'input',
24789                     cls : 'roo-upload-cropbox-selector',
24790                     type : 'file'
24791                 },
24792                 {
24793                     tag : 'div',
24794                     cls : 'roo-upload-cropbox-body',
24795                     style : 'cursor:pointer',
24796                     cn : [
24797                         {
24798                             tag : 'div',
24799                             cls : 'roo-upload-cropbox-preview'
24800                         },
24801                         {
24802                             tag : 'div',
24803                             cls : 'roo-upload-cropbox-thumb'
24804                         },
24805                         {
24806                             tag : 'div',
24807                             cls : 'roo-upload-cropbox-empty-notify',
24808                             html : this.emptyText
24809                         },
24810                         {
24811                             tag : 'div',
24812                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24813                             html : this.rotateNotify
24814                         }
24815                     ]
24816                 },
24817                 {
24818                     tag : 'div',
24819                     cls : 'roo-upload-cropbox-footer',
24820                     cn : {
24821                         tag : 'div',
24822                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24823                         cn : []
24824                     }
24825                 }
24826             ]
24827         };
24828         
24829         return cfg;
24830     },
24831     
24832     onRender : function(ct, position)
24833     {
24834         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24835         
24836         if (this.buttons.length) {
24837             
24838             Roo.each(this.buttons, function(bb) {
24839                 
24840                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24841                 
24842                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24843                 
24844             }, this);
24845         }
24846         
24847         if(this.loadMask){
24848             this.maskEl = this.el;
24849         }
24850     },
24851     
24852     initEvents : function()
24853     {
24854         this.urlAPI = (window.createObjectURL && window) || 
24855                                 (window.URL && URL.revokeObjectURL && URL) || 
24856                                 (window.webkitURL && webkitURL);
24857                         
24858         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24859         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24860         
24861         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24862         this.selectorEl.hide();
24863         
24864         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24865         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24866         
24867         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24868         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24869         this.thumbEl.hide();
24870         
24871         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24872         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24873         
24874         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24875         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24876         this.errorEl.hide();
24877         
24878         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24879         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24880         this.footerEl.hide();
24881         
24882         this.setThumbBoxSize();
24883         
24884         this.bind();
24885         
24886         this.resize();
24887         
24888         this.fireEvent('initial', this);
24889     },
24890
24891     bind : function()
24892     {
24893         var _this = this;
24894         
24895         window.addEventListener("resize", function() { _this.resize(); } );
24896         
24897         this.bodyEl.on('click', this.beforeSelectFile, this);
24898         
24899         if(Roo.isTouch){
24900             this.bodyEl.on('touchstart', this.onTouchStart, this);
24901             this.bodyEl.on('touchmove', this.onTouchMove, this);
24902             this.bodyEl.on('touchend', this.onTouchEnd, this);
24903         }
24904         
24905         if(!Roo.isTouch){
24906             this.bodyEl.on('mousedown', this.onMouseDown, this);
24907             this.bodyEl.on('mousemove', this.onMouseMove, this);
24908             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24909             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24910             Roo.get(document).on('mouseup', this.onMouseUp, this);
24911         }
24912         
24913         this.selectorEl.on('change', this.onFileSelected, this);
24914     },
24915     
24916     reset : function()
24917     {    
24918         this.scale = 0;
24919         this.baseScale = 1;
24920         this.rotate = 0;
24921         this.baseRotate = 1;
24922         this.dragable = false;
24923         this.pinching = false;
24924         this.mouseX = 0;
24925         this.mouseY = 0;
24926         this.cropData = false;
24927         this.notifyEl.dom.innerHTML = this.emptyText;
24928         
24929         this.selectorEl.dom.value = '';
24930         
24931     },
24932     
24933     resize : function()
24934     {
24935         if(this.fireEvent('resize', this) != false){
24936             this.setThumbBoxPosition();
24937             this.setCanvasPosition();
24938         }
24939     },
24940     
24941     onFooterButtonClick : function(e, el, o, type)
24942     {
24943         switch (type) {
24944             case 'rotate-left' :
24945                 this.onRotateLeft(e);
24946                 break;
24947             case 'rotate-right' :
24948                 this.onRotateRight(e);
24949                 break;
24950             case 'picture' :
24951                 this.beforeSelectFile(e);
24952                 break;
24953             case 'trash' :
24954                 this.trash(e);
24955                 break;
24956             case 'crop' :
24957                 this.crop(e);
24958                 break;
24959             case 'download' :
24960                 this.download(e);
24961                 break;
24962             default :
24963                 break;
24964         }
24965         
24966         this.fireEvent('footerbuttonclick', this, type);
24967     },
24968     
24969     beforeSelectFile : function(e)
24970     {
24971         e.preventDefault();
24972         
24973         if(this.fireEvent('beforeselectfile', this) != false){
24974             this.selectorEl.dom.click();
24975         }
24976     },
24977     
24978     onFileSelected : function(e)
24979     {
24980         e.preventDefault();
24981         
24982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24983             return;
24984         }
24985         
24986         var file = this.selectorEl.dom.files[0];
24987         
24988         if(this.fireEvent('inspect', this, file) != false){
24989             this.prepare(file);
24990         }
24991         
24992     },
24993     
24994     trash : function(e)
24995     {
24996         this.fireEvent('trash', this);
24997     },
24998     
24999     download : function(e)
25000     {
25001         this.fireEvent('download', this);
25002     },
25003     
25004     loadCanvas : function(src)
25005     {   
25006         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25007             
25008             this.reset();
25009             
25010             this.imageEl = document.createElement('img');
25011             
25012             var _this = this;
25013             
25014             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25015             
25016             this.imageEl.src = src;
25017         }
25018     },
25019     
25020     onLoadCanvas : function()
25021     {   
25022         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25023         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25024         
25025         this.bodyEl.un('click', this.beforeSelectFile, this);
25026         
25027         this.notifyEl.hide();
25028         this.thumbEl.show();
25029         this.footerEl.show();
25030         
25031         this.baseRotateLevel();
25032         
25033         if(this.isDocument){
25034             this.setThumbBoxSize();
25035         }
25036         
25037         this.setThumbBoxPosition();
25038         
25039         this.baseScaleLevel();
25040         
25041         this.draw();
25042         
25043         this.resize();
25044         
25045         this.canvasLoaded = true;
25046         
25047         if(this.loadMask){
25048             this.maskEl.unmask();
25049         }
25050         
25051     },
25052     
25053     setCanvasPosition : function()
25054     {   
25055         if(!this.canvasEl){
25056             return;
25057         }
25058         
25059         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25060         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25061         
25062         this.previewEl.setLeft(pw);
25063         this.previewEl.setTop(ph);
25064         
25065     },
25066     
25067     onMouseDown : function(e)
25068     {   
25069         e.stopEvent();
25070         
25071         this.dragable = true;
25072         this.pinching = false;
25073         
25074         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25075             this.dragable = false;
25076             return;
25077         }
25078         
25079         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25080         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25081         
25082     },
25083     
25084     onMouseMove : function(e)
25085     {   
25086         e.stopEvent();
25087         
25088         if(!this.canvasLoaded){
25089             return;
25090         }
25091         
25092         if (!this.dragable){
25093             return;
25094         }
25095         
25096         var minX = Math.ceil(this.thumbEl.getLeft(true));
25097         var minY = Math.ceil(this.thumbEl.getTop(true));
25098         
25099         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25100         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25101         
25102         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25103         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25104         
25105         x = x - this.mouseX;
25106         y = y - this.mouseY;
25107         
25108         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25109         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25110         
25111         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25112         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25113         
25114         this.previewEl.setLeft(bgX);
25115         this.previewEl.setTop(bgY);
25116         
25117         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25118         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25119     },
25120     
25121     onMouseUp : function(e)
25122     {   
25123         e.stopEvent();
25124         
25125         this.dragable = false;
25126     },
25127     
25128     onMouseWheel : function(e)
25129     {   
25130         e.stopEvent();
25131         
25132         this.startScale = this.scale;
25133         
25134         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25135         
25136         if(!this.zoomable()){
25137             this.scale = this.startScale;
25138             return;
25139         }
25140         
25141         this.draw();
25142         
25143         return;
25144     },
25145     
25146     zoomable : function()
25147     {
25148         var minScale = this.thumbEl.getWidth() / this.minWidth;
25149         
25150         if(this.minWidth < this.minHeight){
25151             minScale = this.thumbEl.getHeight() / this.minHeight;
25152         }
25153         
25154         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25155         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25156         
25157         if(
25158                 this.isDocument &&
25159                 (this.rotate == 0 || this.rotate == 180) && 
25160                 (
25161                     width > this.imageEl.OriginWidth || 
25162                     height > this.imageEl.OriginHeight ||
25163                     (width < this.minWidth && height < this.minHeight)
25164                 )
25165         ){
25166             return false;
25167         }
25168         
25169         if(
25170                 this.isDocument &&
25171                 (this.rotate == 90 || this.rotate == 270) && 
25172                 (
25173                     width > this.imageEl.OriginWidth || 
25174                     height > this.imageEl.OriginHeight ||
25175                     (width < this.minHeight && height < this.minWidth)
25176                 )
25177         ){
25178             return false;
25179         }
25180         
25181         if(
25182                 !this.isDocument &&
25183                 (this.rotate == 0 || this.rotate == 180) && 
25184                 (
25185                     width < this.minWidth || 
25186                     width > this.imageEl.OriginWidth || 
25187                     height < this.minHeight || 
25188                     height > this.imageEl.OriginHeight
25189                 )
25190         ){
25191             return false;
25192         }
25193         
25194         if(
25195                 !this.isDocument &&
25196                 (this.rotate == 90 || this.rotate == 270) && 
25197                 (
25198                     width < this.minHeight || 
25199                     width > this.imageEl.OriginWidth || 
25200                     height < this.minWidth || 
25201                     height > this.imageEl.OriginHeight
25202                 )
25203         ){
25204             return false;
25205         }
25206         
25207         return true;
25208         
25209     },
25210     
25211     onRotateLeft : function(e)
25212     {   
25213         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25214             
25215             var minScale = this.thumbEl.getWidth() / this.minWidth;
25216             
25217             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25218             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25219             
25220             this.startScale = this.scale;
25221             
25222             while (this.getScaleLevel() < minScale){
25223             
25224                 this.scale = this.scale + 1;
25225                 
25226                 if(!this.zoomable()){
25227                     break;
25228                 }
25229                 
25230                 if(
25231                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25232                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25233                 ){
25234                     continue;
25235                 }
25236                 
25237                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25238
25239                 this.draw();
25240                 
25241                 return;
25242             }
25243             
25244             this.scale = this.startScale;
25245             
25246             this.onRotateFail();
25247             
25248             return false;
25249         }
25250         
25251         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25252
25253         if(this.isDocument){
25254             this.setThumbBoxSize();
25255             this.setThumbBoxPosition();
25256             this.setCanvasPosition();
25257         }
25258         
25259         this.draw();
25260         
25261         this.fireEvent('rotate', this, 'left');
25262         
25263     },
25264     
25265     onRotateRight : function(e)
25266     {
25267         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25268             
25269             var minScale = this.thumbEl.getWidth() / this.minWidth;
25270         
25271             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25272             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25273             
25274             this.startScale = this.scale;
25275             
25276             while (this.getScaleLevel() < minScale){
25277             
25278                 this.scale = this.scale + 1;
25279                 
25280                 if(!this.zoomable()){
25281                     break;
25282                 }
25283                 
25284                 if(
25285                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25286                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25287                 ){
25288                     continue;
25289                 }
25290                 
25291                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25292
25293                 this.draw();
25294                 
25295                 return;
25296             }
25297             
25298             this.scale = this.startScale;
25299             
25300             this.onRotateFail();
25301             
25302             return false;
25303         }
25304         
25305         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25306
25307         if(this.isDocument){
25308             this.setThumbBoxSize();
25309             this.setThumbBoxPosition();
25310             this.setCanvasPosition();
25311         }
25312         
25313         this.draw();
25314         
25315         this.fireEvent('rotate', this, 'right');
25316     },
25317     
25318     onRotateFail : function()
25319     {
25320         this.errorEl.show(true);
25321         
25322         var _this = this;
25323         
25324         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25325     },
25326     
25327     draw : function()
25328     {
25329         this.previewEl.dom.innerHTML = '';
25330         
25331         var canvasEl = document.createElement("canvas");
25332         
25333         var contextEl = canvasEl.getContext("2d");
25334         
25335         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25336         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25337         var center = this.imageEl.OriginWidth / 2;
25338         
25339         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25340             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25341             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25342             center = this.imageEl.OriginHeight / 2;
25343         }
25344         
25345         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25346         
25347         contextEl.translate(center, center);
25348         contextEl.rotate(this.rotate * Math.PI / 180);
25349
25350         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25351         
25352         this.canvasEl = document.createElement("canvas");
25353         
25354         this.contextEl = this.canvasEl.getContext("2d");
25355         
25356         switch (this.rotate) {
25357             case 0 :
25358                 
25359                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25360                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25361                 
25362                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25363                 
25364                 break;
25365             case 90 : 
25366                 
25367                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25368                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25369                 
25370                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25371                     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);
25372                     break;
25373                 }
25374                 
25375                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25376                 
25377                 break;
25378             case 180 :
25379                 
25380                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25381                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25382                 
25383                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25384                     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);
25385                     break;
25386                 }
25387                 
25388                 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);
25389                 
25390                 break;
25391             case 270 :
25392                 
25393                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25394                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25395         
25396                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25397                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25398                     break;
25399                 }
25400                 
25401                 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);
25402                 
25403                 break;
25404             default : 
25405                 break;
25406         }
25407         
25408         this.previewEl.appendChild(this.canvasEl);
25409         
25410         this.setCanvasPosition();
25411     },
25412     
25413     crop : function()
25414     {
25415         if(!this.canvasLoaded){
25416             return;
25417         }
25418         
25419         var imageCanvas = document.createElement("canvas");
25420         
25421         var imageContext = imageCanvas.getContext("2d");
25422         
25423         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25424         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25425         
25426         var center = imageCanvas.width / 2;
25427         
25428         imageContext.translate(center, center);
25429         
25430         imageContext.rotate(this.rotate * Math.PI / 180);
25431         
25432         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25433         
25434         var canvas = document.createElement("canvas");
25435         
25436         var context = canvas.getContext("2d");
25437                 
25438         canvas.width = this.minWidth;
25439         canvas.height = this.minHeight;
25440
25441         switch (this.rotate) {
25442             case 0 :
25443                 
25444                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25445                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25446                 
25447                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25448                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25449                 
25450                 var targetWidth = this.minWidth - 2 * x;
25451                 var targetHeight = this.minHeight - 2 * y;
25452                 
25453                 var scale = 1;
25454                 
25455                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25456                     scale = targetWidth / width;
25457                 }
25458                 
25459                 if(x > 0 && y == 0){
25460                     scale = targetHeight / height;
25461                 }
25462                 
25463                 if(x > 0 && y > 0){
25464                     scale = targetWidth / width;
25465                     
25466                     if(width < height){
25467                         scale = targetHeight / height;
25468                     }
25469                 }
25470                 
25471                 context.scale(scale, scale);
25472                 
25473                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25474                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25475
25476                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25477                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25478
25479                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25480                 
25481                 break;
25482             case 90 : 
25483                 
25484                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25485                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25486                 
25487                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25488                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25489                 
25490                 var targetWidth = this.minWidth - 2 * x;
25491                 var targetHeight = this.minHeight - 2 * y;
25492                 
25493                 var scale = 1;
25494                 
25495                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25496                     scale = targetWidth / width;
25497                 }
25498                 
25499                 if(x > 0 && y == 0){
25500                     scale = targetHeight / height;
25501                 }
25502                 
25503                 if(x > 0 && y > 0){
25504                     scale = targetWidth / width;
25505                     
25506                     if(width < height){
25507                         scale = targetHeight / height;
25508                     }
25509                 }
25510                 
25511                 context.scale(scale, scale);
25512                 
25513                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25514                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25515
25516                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25517                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25518                 
25519                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25520                 
25521                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25522                 
25523                 break;
25524             case 180 :
25525                 
25526                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25527                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25528                 
25529                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25530                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25531                 
25532                 var targetWidth = this.minWidth - 2 * x;
25533                 var targetHeight = this.minHeight - 2 * y;
25534                 
25535                 var scale = 1;
25536                 
25537                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25538                     scale = targetWidth / width;
25539                 }
25540                 
25541                 if(x > 0 && y == 0){
25542                     scale = targetHeight / height;
25543                 }
25544                 
25545                 if(x > 0 && y > 0){
25546                     scale = targetWidth / width;
25547                     
25548                     if(width < height){
25549                         scale = targetHeight / height;
25550                     }
25551                 }
25552                 
25553                 context.scale(scale, scale);
25554                 
25555                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25556                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25557
25558                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25559                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25560
25561                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25562                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25563                 
25564                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25565                 
25566                 break;
25567             case 270 :
25568                 
25569                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25570                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25571                 
25572                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25573                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25574                 
25575                 var targetWidth = this.minWidth - 2 * x;
25576                 var targetHeight = this.minHeight - 2 * y;
25577                 
25578                 var scale = 1;
25579                 
25580                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25581                     scale = targetWidth / width;
25582                 }
25583                 
25584                 if(x > 0 && y == 0){
25585                     scale = targetHeight / height;
25586                 }
25587                 
25588                 if(x > 0 && y > 0){
25589                     scale = targetWidth / width;
25590                     
25591                     if(width < height){
25592                         scale = targetHeight / height;
25593                     }
25594                 }
25595                 
25596                 context.scale(scale, scale);
25597                 
25598                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25599                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25600
25601                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25602                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25603                 
25604                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25605                 
25606                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25607                 
25608                 break;
25609             default : 
25610                 break;
25611         }
25612         
25613         this.cropData = canvas.toDataURL(this.cropType);
25614         
25615         if(this.fireEvent('crop', this, this.cropData) !== false){
25616             this.process(this.file, this.cropData);
25617         }
25618         
25619         return;
25620         
25621     },
25622     
25623     setThumbBoxSize : function()
25624     {
25625         var width, height;
25626         
25627         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25628             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25629             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25630             
25631             this.minWidth = width;
25632             this.minHeight = height;
25633             
25634             if(this.rotate == 90 || this.rotate == 270){
25635                 this.minWidth = height;
25636                 this.minHeight = width;
25637             }
25638         }
25639         
25640         height = 300;
25641         width = Math.ceil(this.minWidth * height / this.minHeight);
25642         
25643         if(this.minWidth > this.minHeight){
25644             width = 300;
25645             height = Math.ceil(this.minHeight * width / this.minWidth);
25646         }
25647         
25648         this.thumbEl.setStyle({
25649             width : width + 'px',
25650             height : height + 'px'
25651         });
25652
25653         return;
25654             
25655     },
25656     
25657     setThumbBoxPosition : function()
25658     {
25659         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25660         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25661         
25662         this.thumbEl.setLeft(x);
25663         this.thumbEl.setTop(y);
25664         
25665     },
25666     
25667     baseRotateLevel : function()
25668     {
25669         this.baseRotate = 1;
25670         
25671         if(
25672                 typeof(this.exif) != 'undefined' &&
25673                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25674                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25675         ){
25676             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25677         }
25678         
25679         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25680         
25681     },
25682     
25683     baseScaleLevel : function()
25684     {
25685         var width, height;
25686         
25687         if(this.isDocument){
25688             
25689             if(this.baseRotate == 6 || this.baseRotate == 8){
25690             
25691                 height = this.thumbEl.getHeight();
25692                 this.baseScale = height / this.imageEl.OriginWidth;
25693
25694                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25695                     width = this.thumbEl.getWidth();
25696                     this.baseScale = width / this.imageEl.OriginHeight;
25697                 }
25698
25699                 return;
25700             }
25701
25702             height = this.thumbEl.getHeight();
25703             this.baseScale = height / this.imageEl.OriginHeight;
25704
25705             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25706                 width = this.thumbEl.getWidth();
25707                 this.baseScale = width / this.imageEl.OriginWidth;
25708             }
25709
25710             return;
25711         }
25712         
25713         if(this.baseRotate == 6 || this.baseRotate == 8){
25714             
25715             width = this.thumbEl.getHeight();
25716             this.baseScale = width / this.imageEl.OriginHeight;
25717             
25718             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25719                 height = this.thumbEl.getWidth();
25720                 this.baseScale = height / this.imageEl.OriginHeight;
25721             }
25722             
25723             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25724                 height = this.thumbEl.getWidth();
25725                 this.baseScale = height / this.imageEl.OriginHeight;
25726                 
25727                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25728                     width = this.thumbEl.getHeight();
25729                     this.baseScale = width / this.imageEl.OriginWidth;
25730                 }
25731             }
25732             
25733             return;
25734         }
25735         
25736         width = this.thumbEl.getWidth();
25737         this.baseScale = width / this.imageEl.OriginWidth;
25738         
25739         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25740             height = this.thumbEl.getHeight();
25741             this.baseScale = height / this.imageEl.OriginHeight;
25742         }
25743         
25744         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25745             
25746             height = this.thumbEl.getHeight();
25747             this.baseScale = height / this.imageEl.OriginHeight;
25748             
25749             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25750                 width = this.thumbEl.getWidth();
25751                 this.baseScale = width / this.imageEl.OriginWidth;
25752             }
25753             
25754         }
25755         
25756         return;
25757     },
25758     
25759     getScaleLevel : function()
25760     {
25761         return this.baseScale * Math.pow(1.1, this.scale);
25762     },
25763     
25764     onTouchStart : function(e)
25765     {
25766         if(!this.canvasLoaded){
25767             this.beforeSelectFile(e);
25768             return;
25769         }
25770         
25771         var touches = e.browserEvent.touches;
25772         
25773         if(!touches){
25774             return;
25775         }
25776         
25777         if(touches.length == 1){
25778             this.onMouseDown(e);
25779             return;
25780         }
25781         
25782         if(touches.length != 2){
25783             return;
25784         }
25785         
25786         var coords = [];
25787         
25788         for(var i = 0, finger; finger = touches[i]; i++){
25789             coords.push(finger.pageX, finger.pageY);
25790         }
25791         
25792         var x = Math.pow(coords[0] - coords[2], 2);
25793         var y = Math.pow(coords[1] - coords[3], 2);
25794         
25795         this.startDistance = Math.sqrt(x + y);
25796         
25797         this.startScale = this.scale;
25798         
25799         this.pinching = true;
25800         this.dragable = false;
25801         
25802     },
25803     
25804     onTouchMove : function(e)
25805     {
25806         if(!this.pinching && !this.dragable){
25807             return;
25808         }
25809         
25810         var touches = e.browserEvent.touches;
25811         
25812         if(!touches){
25813             return;
25814         }
25815         
25816         if(this.dragable){
25817             this.onMouseMove(e);
25818             return;
25819         }
25820         
25821         var coords = [];
25822         
25823         for(var i = 0, finger; finger = touches[i]; i++){
25824             coords.push(finger.pageX, finger.pageY);
25825         }
25826         
25827         var x = Math.pow(coords[0] - coords[2], 2);
25828         var y = Math.pow(coords[1] - coords[3], 2);
25829         
25830         this.endDistance = Math.sqrt(x + y);
25831         
25832         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25833         
25834         if(!this.zoomable()){
25835             this.scale = this.startScale;
25836             return;
25837         }
25838         
25839         this.draw();
25840         
25841     },
25842     
25843     onTouchEnd : function(e)
25844     {
25845         this.pinching = false;
25846         this.dragable = false;
25847         
25848     },
25849     
25850     process : function(file, crop)
25851     {
25852         if(this.loadMask){
25853             this.maskEl.mask(this.loadingText);
25854         }
25855         
25856         this.xhr = new XMLHttpRequest();
25857         
25858         file.xhr = this.xhr;
25859
25860         this.xhr.open(this.method, this.url, true);
25861         
25862         var headers = {
25863             "Accept": "application/json",
25864             "Cache-Control": "no-cache",
25865             "X-Requested-With": "XMLHttpRequest"
25866         };
25867         
25868         for (var headerName in headers) {
25869             var headerValue = headers[headerName];
25870             if (headerValue) {
25871                 this.xhr.setRequestHeader(headerName, headerValue);
25872             }
25873         }
25874         
25875         var _this = this;
25876         
25877         this.xhr.onload = function()
25878         {
25879             _this.xhrOnLoad(_this.xhr);
25880         }
25881         
25882         this.xhr.onerror = function()
25883         {
25884             _this.xhrOnError(_this.xhr);
25885         }
25886         
25887         var formData = new FormData();
25888
25889         formData.append('returnHTML', 'NO');
25890         
25891         if(crop){
25892             formData.append('crop', crop);
25893         }
25894         
25895         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25896             formData.append(this.paramName, file, file.name);
25897         }
25898         
25899         if(typeof(file.filename) != 'undefined'){
25900             formData.append('filename', file.filename);
25901         }
25902         
25903         if(typeof(file.mimetype) != 'undefined'){
25904             formData.append('mimetype', file.mimetype);
25905         }
25906         
25907         if(this.fireEvent('arrange', this, formData) != false){
25908             this.xhr.send(formData);
25909         };
25910     },
25911     
25912     xhrOnLoad : function(xhr)
25913     {
25914         if(this.loadMask){
25915             this.maskEl.unmask();
25916         }
25917         
25918         if (xhr.readyState !== 4) {
25919             this.fireEvent('exception', this, xhr);
25920             return;
25921         }
25922
25923         var response = Roo.decode(xhr.responseText);
25924         
25925         if(!response.success){
25926             this.fireEvent('exception', this, xhr);
25927             return;
25928         }
25929         
25930         var response = Roo.decode(xhr.responseText);
25931         
25932         this.fireEvent('upload', this, response);
25933         
25934     },
25935     
25936     xhrOnError : function()
25937     {
25938         if(this.loadMask){
25939             this.maskEl.unmask();
25940         }
25941         
25942         Roo.log('xhr on error');
25943         
25944         var response = Roo.decode(xhr.responseText);
25945           
25946         Roo.log(response);
25947         
25948     },
25949     
25950     prepare : function(file)
25951     {   
25952         if(this.loadMask){
25953             this.maskEl.mask(this.loadingText);
25954         }
25955         
25956         this.file = false;
25957         this.exif = {};
25958         
25959         if(typeof(file) === 'string'){
25960             this.loadCanvas(file);
25961             return;
25962         }
25963         
25964         if(!file || !this.urlAPI){
25965             return;
25966         }
25967         
25968         this.file = file;
25969         this.cropType = file.type;
25970         
25971         var _this = this;
25972         
25973         if(this.fireEvent('prepare', this, this.file) != false){
25974             
25975             var reader = new FileReader();
25976             
25977             reader.onload = function (e) {
25978                 if (e.target.error) {
25979                     Roo.log(e.target.error);
25980                     return;
25981                 }
25982                 
25983                 var buffer = e.target.result,
25984                     dataView = new DataView(buffer),
25985                     offset = 2,
25986                     maxOffset = dataView.byteLength - 4,
25987                     markerBytes,
25988                     markerLength;
25989                 
25990                 if (dataView.getUint16(0) === 0xffd8) {
25991                     while (offset < maxOffset) {
25992                         markerBytes = dataView.getUint16(offset);
25993                         
25994                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25995                             markerLength = dataView.getUint16(offset + 2) + 2;
25996                             if (offset + markerLength > dataView.byteLength) {
25997                                 Roo.log('Invalid meta data: Invalid segment size.');
25998                                 break;
25999                             }
26000                             
26001                             if(markerBytes == 0xffe1){
26002                                 _this.parseExifData(
26003                                     dataView,
26004                                     offset,
26005                                     markerLength
26006                                 );
26007                             }
26008                             
26009                             offset += markerLength;
26010                             
26011                             continue;
26012                         }
26013                         
26014                         break;
26015                     }
26016                     
26017                 }
26018                 
26019                 var url = _this.urlAPI.createObjectURL(_this.file);
26020                 
26021                 _this.loadCanvas(url);
26022                 
26023                 return;
26024             }
26025             
26026             reader.readAsArrayBuffer(this.file);
26027             
26028         }
26029         
26030     },
26031     
26032     parseExifData : function(dataView, offset, length)
26033     {
26034         var tiffOffset = offset + 10,
26035             littleEndian,
26036             dirOffset;
26037     
26038         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26039             // No Exif data, might be XMP data instead
26040             return;
26041         }
26042         
26043         // Check for the ASCII code for "Exif" (0x45786966):
26044         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26045             // No Exif data, might be XMP data instead
26046             return;
26047         }
26048         if (tiffOffset + 8 > dataView.byteLength) {
26049             Roo.log('Invalid Exif data: Invalid segment size.');
26050             return;
26051         }
26052         // Check for the two null bytes:
26053         if (dataView.getUint16(offset + 8) !== 0x0000) {
26054             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26055             return;
26056         }
26057         // Check the byte alignment:
26058         switch (dataView.getUint16(tiffOffset)) {
26059         case 0x4949:
26060             littleEndian = true;
26061             break;
26062         case 0x4D4D:
26063             littleEndian = false;
26064             break;
26065         default:
26066             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26067             return;
26068         }
26069         // Check for the TIFF tag marker (0x002A):
26070         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26071             Roo.log('Invalid Exif data: Missing TIFF marker.');
26072             return;
26073         }
26074         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26075         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26076         
26077         this.parseExifTags(
26078             dataView,
26079             tiffOffset,
26080             tiffOffset + dirOffset,
26081             littleEndian
26082         );
26083     },
26084     
26085     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26086     {
26087         var tagsNumber,
26088             dirEndOffset,
26089             i;
26090         if (dirOffset + 6 > dataView.byteLength) {
26091             Roo.log('Invalid Exif data: Invalid directory offset.');
26092             return;
26093         }
26094         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26095         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26096         if (dirEndOffset + 4 > dataView.byteLength) {
26097             Roo.log('Invalid Exif data: Invalid directory size.');
26098             return;
26099         }
26100         for (i = 0; i < tagsNumber; i += 1) {
26101             this.parseExifTag(
26102                 dataView,
26103                 tiffOffset,
26104                 dirOffset + 2 + 12 * i, // tag offset
26105                 littleEndian
26106             );
26107         }
26108         // Return the offset to the next directory:
26109         return dataView.getUint32(dirEndOffset, littleEndian);
26110     },
26111     
26112     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26113     {
26114         var tag = dataView.getUint16(offset, littleEndian);
26115         
26116         this.exif[tag] = this.getExifValue(
26117             dataView,
26118             tiffOffset,
26119             offset,
26120             dataView.getUint16(offset + 2, littleEndian), // tag type
26121             dataView.getUint32(offset + 4, littleEndian), // tag length
26122             littleEndian
26123         );
26124     },
26125     
26126     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26127     {
26128         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26129             tagSize,
26130             dataOffset,
26131             values,
26132             i,
26133             str,
26134             c;
26135     
26136         if (!tagType) {
26137             Roo.log('Invalid Exif data: Invalid tag type.');
26138             return;
26139         }
26140         
26141         tagSize = tagType.size * length;
26142         // Determine if the value is contained in the dataOffset bytes,
26143         // or if the value at the dataOffset is a pointer to the actual data:
26144         dataOffset = tagSize > 4 ?
26145                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26146         if (dataOffset + tagSize > dataView.byteLength) {
26147             Roo.log('Invalid Exif data: Invalid data offset.');
26148             return;
26149         }
26150         if (length === 1) {
26151             return tagType.getValue(dataView, dataOffset, littleEndian);
26152         }
26153         values = [];
26154         for (i = 0; i < length; i += 1) {
26155             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26156         }
26157         
26158         if (tagType.ascii) {
26159             str = '';
26160             // Concatenate the chars:
26161             for (i = 0; i < values.length; i += 1) {
26162                 c = values[i];
26163                 // Ignore the terminating NULL byte(s):
26164                 if (c === '\u0000') {
26165                     break;
26166                 }
26167                 str += c;
26168             }
26169             return str;
26170         }
26171         return values;
26172     }
26173     
26174 });
26175
26176 Roo.apply(Roo.bootstrap.UploadCropbox, {
26177     tags : {
26178         'Orientation': 0x0112
26179     },
26180     
26181     Orientation: {
26182             1: 0, //'top-left',
26183 //            2: 'top-right',
26184             3: 180, //'bottom-right',
26185 //            4: 'bottom-left',
26186 //            5: 'left-top',
26187             6: 90, //'right-top',
26188 //            7: 'right-bottom',
26189             8: 270 //'left-bottom'
26190     },
26191     
26192     exifTagTypes : {
26193         // byte, 8-bit unsigned int:
26194         1: {
26195             getValue: function (dataView, dataOffset) {
26196                 return dataView.getUint8(dataOffset);
26197             },
26198             size: 1
26199         },
26200         // ascii, 8-bit byte:
26201         2: {
26202             getValue: function (dataView, dataOffset) {
26203                 return String.fromCharCode(dataView.getUint8(dataOffset));
26204             },
26205             size: 1,
26206             ascii: true
26207         },
26208         // short, 16 bit int:
26209         3: {
26210             getValue: function (dataView, dataOffset, littleEndian) {
26211                 return dataView.getUint16(dataOffset, littleEndian);
26212             },
26213             size: 2
26214         },
26215         // long, 32 bit int:
26216         4: {
26217             getValue: function (dataView, dataOffset, littleEndian) {
26218                 return dataView.getUint32(dataOffset, littleEndian);
26219             },
26220             size: 4
26221         },
26222         // rational = two long values, first is numerator, second is denominator:
26223         5: {
26224             getValue: function (dataView, dataOffset, littleEndian) {
26225                 return dataView.getUint32(dataOffset, littleEndian) /
26226                     dataView.getUint32(dataOffset + 4, littleEndian);
26227             },
26228             size: 8
26229         },
26230         // slong, 32 bit signed int:
26231         9: {
26232             getValue: function (dataView, dataOffset, littleEndian) {
26233                 return dataView.getInt32(dataOffset, littleEndian);
26234             },
26235             size: 4
26236         },
26237         // srational, two slongs, first is numerator, second is denominator:
26238         10: {
26239             getValue: function (dataView, dataOffset, littleEndian) {
26240                 return dataView.getInt32(dataOffset, littleEndian) /
26241                     dataView.getInt32(dataOffset + 4, littleEndian);
26242             },
26243             size: 8
26244         }
26245     },
26246     
26247     footer : {
26248         STANDARD : [
26249             {
26250                 tag : 'div',
26251                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26252                 action : 'rotate-left',
26253                 cn : [
26254                     {
26255                         tag : 'button',
26256                         cls : 'btn btn-default',
26257                         html : '<i class="fa fa-undo"></i>'
26258                     }
26259                 ]
26260             },
26261             {
26262                 tag : 'div',
26263                 cls : 'btn-group roo-upload-cropbox-picture',
26264                 action : 'picture',
26265                 cn : [
26266                     {
26267                         tag : 'button',
26268                         cls : 'btn btn-default',
26269                         html : '<i class="fa fa-picture-o"></i>'
26270                     }
26271                 ]
26272             },
26273             {
26274                 tag : 'div',
26275                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26276                 action : 'rotate-right',
26277                 cn : [
26278                     {
26279                         tag : 'button',
26280                         cls : 'btn btn-default',
26281                         html : '<i class="fa fa-repeat"></i>'
26282                     }
26283                 ]
26284             }
26285         ],
26286         DOCUMENT : [
26287             {
26288                 tag : 'div',
26289                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26290                 action : 'rotate-left',
26291                 cn : [
26292                     {
26293                         tag : 'button',
26294                         cls : 'btn btn-default',
26295                         html : '<i class="fa fa-undo"></i>'
26296                     }
26297                 ]
26298             },
26299             {
26300                 tag : 'div',
26301                 cls : 'btn-group roo-upload-cropbox-download',
26302                 action : 'download',
26303                 cn : [
26304                     {
26305                         tag : 'button',
26306                         cls : 'btn btn-default',
26307                         html : '<i class="fa fa-download"></i>'
26308                     }
26309                 ]
26310             },
26311             {
26312                 tag : 'div',
26313                 cls : 'btn-group roo-upload-cropbox-crop',
26314                 action : 'crop',
26315                 cn : [
26316                     {
26317                         tag : 'button',
26318                         cls : 'btn btn-default',
26319                         html : '<i class="fa fa-crop"></i>'
26320                     }
26321                 ]
26322             },
26323             {
26324                 tag : 'div',
26325                 cls : 'btn-group roo-upload-cropbox-trash',
26326                 action : 'trash',
26327                 cn : [
26328                     {
26329                         tag : 'button',
26330                         cls : 'btn btn-default',
26331                         html : '<i class="fa fa-trash"></i>'
26332                     }
26333                 ]
26334             },
26335             {
26336                 tag : 'div',
26337                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26338                 action : 'rotate-right',
26339                 cn : [
26340                     {
26341                         tag : 'button',
26342                         cls : 'btn btn-default',
26343                         html : '<i class="fa fa-repeat"></i>'
26344                     }
26345                 ]
26346             }
26347         ],
26348         ROTATOR : [
26349             {
26350                 tag : 'div',
26351                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26352                 action : 'rotate-left',
26353                 cn : [
26354                     {
26355                         tag : 'button',
26356                         cls : 'btn btn-default',
26357                         html : '<i class="fa fa-undo"></i>'
26358                     }
26359                 ]
26360             },
26361             {
26362                 tag : 'div',
26363                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26364                 action : 'rotate-right',
26365                 cn : [
26366                     {
26367                         tag : 'button',
26368                         cls : 'btn btn-default',
26369                         html : '<i class="fa fa-repeat"></i>'
26370                     }
26371                 ]
26372             }
26373         ]
26374     }
26375 });
26376
26377 /*
26378 * Licence: LGPL
26379 */
26380
26381 /**
26382  * @class Roo.bootstrap.DocumentManager
26383  * @extends Roo.bootstrap.Component
26384  * Bootstrap DocumentManager class
26385  * @cfg {String} paramName default 'imageUpload'
26386  * @cfg {String} method default POST
26387  * @cfg {String} url action url
26388  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26389  * @cfg {Boolean} multiple multiple upload default true
26390  * @cfg {Number} thumbSize default 300
26391  * @cfg {String} fieldLabel
26392  * @cfg {Number} labelWidth default 4
26393  * @cfg {String} labelAlign (left|top) default left
26394  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26395  * 
26396  * @constructor
26397  * Create a new DocumentManager
26398  * @param {Object} config The config object
26399  */
26400
26401 Roo.bootstrap.DocumentManager = function(config){
26402     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26403     
26404     this.addEvents({
26405         /**
26406          * @event initial
26407          * Fire when initial the DocumentManager
26408          * @param {Roo.bootstrap.DocumentManager} this
26409          */
26410         "initial" : true,
26411         /**
26412          * @event inspect
26413          * inspect selected file
26414          * @param {Roo.bootstrap.DocumentManager} this
26415          * @param {File} file
26416          */
26417         "inspect" : true,
26418         /**
26419          * @event exception
26420          * Fire when xhr load exception
26421          * @param {Roo.bootstrap.DocumentManager} this
26422          * @param {XMLHttpRequest} xhr
26423          */
26424         "exception" : true,
26425         /**
26426          * @event prepare
26427          * prepare the form data
26428          * @param {Roo.bootstrap.DocumentManager} this
26429          * @param {Object} formData
26430          */
26431         "prepare" : true,
26432         /**
26433          * @event remove
26434          * Fire when remove the file
26435          * @param {Roo.bootstrap.DocumentManager} this
26436          * @param {Object} file
26437          */
26438         "remove" : true,
26439         /**
26440          * @event refresh
26441          * Fire after refresh the file
26442          * @param {Roo.bootstrap.DocumentManager} this
26443          */
26444         "refresh" : true,
26445         /**
26446          * @event click
26447          * Fire after click the image
26448          * @param {Roo.bootstrap.DocumentManager} this
26449          * @param {Object} file
26450          */
26451         "click" : true,
26452         /**
26453          * @event edit
26454          * Fire when upload a image and editable set to true
26455          * @param {Roo.bootstrap.DocumentManager} this
26456          * @param {Object} file
26457          */
26458         "edit" : true,
26459         /**
26460          * @event beforeselectfile
26461          * Fire before select file
26462          * @param {Roo.bootstrap.DocumentManager} this
26463          */
26464         "beforeselectfile" : true,
26465         /**
26466          * @event process
26467          * Fire before process file
26468          * @param {Roo.bootstrap.DocumentManager} this
26469          * @param {Object} file
26470          */
26471         "process" : true
26472         
26473     });
26474 };
26475
26476 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26477     
26478     boxes : 0,
26479     inputName : '',
26480     thumbSize : 300,
26481     multiple : true,
26482     files : [],
26483     method : 'POST',
26484     url : '',
26485     paramName : 'imageUpload',
26486     fieldLabel : '',
26487     labelWidth : 4,
26488     labelAlign : 'left',
26489     editable : true,
26490     delegates : [],
26491     
26492     
26493     xhr : false, 
26494     
26495     getAutoCreate : function()
26496     {   
26497         var managerWidget = {
26498             tag : 'div',
26499             cls : 'roo-document-manager',
26500             cn : [
26501                 {
26502                     tag : 'input',
26503                     cls : 'roo-document-manager-selector',
26504                     type : 'file'
26505                 },
26506                 {
26507                     tag : 'div',
26508                     cls : 'roo-document-manager-uploader',
26509                     cn : [
26510                         {
26511                             tag : 'div',
26512                             cls : 'roo-document-manager-upload-btn',
26513                             html : '<i class="fa fa-plus"></i>'
26514                         }
26515                     ]
26516                     
26517                 }
26518             ]
26519         };
26520         
26521         var content = [
26522             {
26523                 tag : 'div',
26524                 cls : 'column col-md-12',
26525                 cn : managerWidget
26526             }
26527         ];
26528         
26529         if(this.fieldLabel.length){
26530             
26531             content = [
26532                 {
26533                     tag : 'div',
26534                     cls : 'column col-md-12',
26535                     html : this.fieldLabel
26536                 },
26537                 {
26538                     tag : 'div',
26539                     cls : 'column col-md-12',
26540                     cn : managerWidget
26541                 }
26542             ];
26543
26544             if(this.labelAlign == 'left'){
26545                 content = [
26546                     {
26547                         tag : 'div',
26548                         cls : 'column col-md-' + this.labelWidth,
26549                         html : this.fieldLabel
26550                     },
26551                     {
26552                         tag : 'div',
26553                         cls : 'column col-md-' + (12 - this.labelWidth),
26554                         cn : managerWidget
26555                     }
26556                 ];
26557                 
26558             }
26559         }
26560         
26561         var cfg = {
26562             tag : 'div',
26563             cls : 'row clearfix',
26564             cn : content
26565         };
26566         
26567         return cfg;
26568         
26569     },
26570     
26571     initEvents : function()
26572     {
26573         this.managerEl = this.el.select('.roo-document-manager', true).first();
26574         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26575         
26576         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26577         this.selectorEl.hide();
26578         
26579         if(this.multiple){
26580             this.selectorEl.attr('multiple', 'multiple');
26581         }
26582         
26583         this.selectorEl.on('change', this.onFileSelected, this);
26584         
26585         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26586         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26587         
26588         this.uploader.on('click', this.onUploaderClick, this);
26589         
26590         this.renderProgressDialog();
26591         
26592         var _this = this;
26593         
26594         window.addEventListener("resize", function() { _this.refresh(); } );
26595         
26596         this.fireEvent('initial', this);
26597     },
26598     
26599     renderProgressDialog : function()
26600     {
26601         var _this = this;
26602         
26603         this.progressDialog = new Roo.bootstrap.Modal({
26604             cls : 'roo-document-manager-progress-dialog',
26605             allow_close : false,
26606             title : '',
26607             buttons : [
26608                 {
26609                     name  :'cancel',
26610                     weight : 'danger',
26611                     html : 'Cancel'
26612                 }
26613             ], 
26614             listeners : { 
26615                 btnclick : function() {
26616                     _this.uploadCancel();
26617                     this.hide();
26618                 }
26619             }
26620         });
26621          
26622         this.progressDialog.render(Roo.get(document.body));
26623          
26624         this.progress = new Roo.bootstrap.Progress({
26625             cls : 'roo-document-manager-progress',
26626             active : true,
26627             striped : true
26628         });
26629         
26630         this.progress.render(this.progressDialog.getChildContainer());
26631         
26632         this.progressBar = new Roo.bootstrap.ProgressBar({
26633             cls : 'roo-document-manager-progress-bar',
26634             aria_valuenow : 0,
26635             aria_valuemin : 0,
26636             aria_valuemax : 12,
26637             panel : 'success'
26638         });
26639         
26640         this.progressBar.render(this.progress.getChildContainer());
26641     },
26642     
26643     onUploaderClick : function(e)
26644     {
26645         e.preventDefault();
26646      
26647         if(this.fireEvent('beforeselectfile', this) != false){
26648             this.selectorEl.dom.click();
26649         }
26650         
26651     },
26652     
26653     onFileSelected : function(e)
26654     {
26655         e.preventDefault();
26656         
26657         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26658             return;
26659         }
26660         
26661         Roo.each(this.selectorEl.dom.files, function(file){
26662             if(this.fireEvent('inspect', this, file) != false){
26663                 this.files.push(file);
26664             }
26665         }, this);
26666         
26667         this.queue();
26668         
26669     },
26670     
26671     queue : function()
26672     {
26673         this.selectorEl.dom.value = '';
26674         
26675         if(!this.files.length){
26676             return;
26677         }
26678         
26679         if(this.boxes > 0 && this.files.length > this.boxes){
26680             this.files = this.files.slice(0, this.boxes);
26681         }
26682         
26683         this.uploader.show();
26684         
26685         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26686             this.uploader.hide();
26687         }
26688         
26689         var _this = this;
26690         
26691         var files = [];
26692         
26693         var docs = [];
26694         
26695         Roo.each(this.files, function(file){
26696             
26697             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26698                 var f = this.renderPreview(file);
26699                 files.push(f);
26700                 return;
26701             }
26702             
26703             if(file.type.indexOf('image') != -1){
26704                 this.delegates.push(
26705                     (function(){
26706                         _this.process(file);
26707                     }).createDelegate(this)
26708                 );
26709         
26710                 return;
26711             }
26712             
26713             docs.push(
26714                 (function(){
26715                     _this.process(file);
26716                 }).createDelegate(this)
26717             );
26718             
26719         }, this);
26720         
26721         this.files = files;
26722         
26723         this.delegates = this.delegates.concat(docs);
26724         
26725         if(!this.delegates.length){
26726             this.refresh();
26727             return;
26728         }
26729         
26730         this.progressBar.aria_valuemax = this.delegates.length;
26731         
26732         this.arrange();
26733         
26734         return;
26735     },
26736     
26737     arrange : function()
26738     {
26739         if(!this.delegates.length){
26740             this.progressDialog.hide();
26741             this.refresh();
26742             return;
26743         }
26744         
26745         var delegate = this.delegates.shift();
26746         
26747         this.progressDialog.show();
26748         
26749         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26750         
26751         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26752         
26753         delegate();
26754     },
26755     
26756     refresh : function()
26757     {
26758         this.uploader.show();
26759         
26760         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26761             this.uploader.hide();
26762         }
26763         
26764         Roo.isTouch ? this.closable(false) : this.closable(true);
26765         
26766         this.fireEvent('refresh', this);
26767     },
26768     
26769     onRemove : function(e, el, o)
26770     {
26771         e.preventDefault();
26772         
26773         this.fireEvent('remove', this, o);
26774         
26775     },
26776     
26777     remove : function(o)
26778     {
26779         var files = [];
26780         
26781         Roo.each(this.files, function(file){
26782             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26783                 files.push(file);
26784                 return;
26785             }
26786
26787             o.target.remove();
26788
26789         }, this);
26790         
26791         this.files = files;
26792         
26793         this.refresh();
26794     },
26795     
26796     clear : function()
26797     {
26798         Roo.each(this.files, function(file){
26799             if(!file.target){
26800                 return;
26801             }
26802             
26803             file.target.remove();
26804
26805         }, this);
26806         
26807         this.files = [];
26808         
26809         this.refresh();
26810     },
26811     
26812     onClick : function(e, el, o)
26813     {
26814         e.preventDefault();
26815         
26816         this.fireEvent('click', this, o);
26817         
26818     },
26819     
26820     closable : function(closable)
26821     {
26822         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26823             
26824             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26825             
26826             if(closable){
26827                 el.show();
26828                 return;
26829             }
26830             
26831             el.hide();
26832             
26833         }, this);
26834     },
26835     
26836     xhrOnLoad : function(xhr)
26837     {
26838         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26839             el.remove();
26840         }, this);
26841         
26842         if (xhr.readyState !== 4) {
26843             this.arrange();
26844             this.fireEvent('exception', this, xhr);
26845             return;
26846         }
26847
26848         var response = Roo.decode(xhr.responseText);
26849         
26850         if(!response.success){
26851             this.arrange();
26852             this.fireEvent('exception', this, xhr);
26853             return;
26854         }
26855         
26856         var file = this.renderPreview(response.data);
26857         
26858         this.files.push(file);
26859         
26860         this.arrange();
26861         
26862     },
26863     
26864     xhrOnError : function(xhr)
26865     {
26866         Roo.log('xhr on error');
26867         
26868         var response = Roo.decode(xhr.responseText);
26869           
26870         Roo.log(response);
26871         
26872         this.arrange();
26873     },
26874     
26875     process : function(file)
26876     {
26877         if(this.fireEvent('process', this, file) !== false){
26878             if(this.editable && file.type.indexOf('image') != -1){
26879                 this.fireEvent('edit', this, file);
26880                 return;
26881             }
26882
26883             this.uploadStart(file, false);
26884
26885             return;
26886         }
26887         
26888     },
26889     
26890     uploadStart : function(file, crop)
26891     {
26892         this.xhr = new XMLHttpRequest();
26893         
26894         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26895             this.arrange();
26896             return;
26897         }
26898         
26899         file.xhr = this.xhr;
26900             
26901         this.managerEl.createChild({
26902             tag : 'div',
26903             cls : 'roo-document-manager-loading',
26904             cn : [
26905                 {
26906                     tag : 'div',
26907                     tooltip : file.name,
26908                     cls : 'roo-document-manager-thumb',
26909                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26910                 }
26911             ]
26912
26913         });
26914
26915         this.xhr.open(this.method, this.url, true);
26916         
26917         var headers = {
26918             "Accept": "application/json",
26919             "Cache-Control": "no-cache",
26920             "X-Requested-With": "XMLHttpRequest"
26921         };
26922         
26923         for (var headerName in headers) {
26924             var headerValue = headers[headerName];
26925             if (headerValue) {
26926                 this.xhr.setRequestHeader(headerName, headerValue);
26927             }
26928         }
26929         
26930         var _this = this;
26931         
26932         this.xhr.onload = function()
26933         {
26934             _this.xhrOnLoad(_this.xhr);
26935         }
26936         
26937         this.xhr.onerror = function()
26938         {
26939             _this.xhrOnError(_this.xhr);
26940         }
26941         
26942         var formData = new FormData();
26943
26944         formData.append('returnHTML', 'NO');
26945         
26946         if(crop){
26947             formData.append('crop', crop);
26948         }
26949         
26950         formData.append(this.paramName, file, file.name);
26951         
26952         if(this.fireEvent('prepare', this, formData) != false){
26953             this.xhr.send(formData);
26954         };
26955     },
26956     
26957     uploadCancel : function()
26958     {
26959         if (this.xhr) {
26960             this.xhr.abort();
26961         }
26962         
26963         
26964         this.delegates = [];
26965         
26966         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26967             el.remove();
26968         }, this);
26969         
26970         this.arrange();
26971     },
26972     
26973     renderPreview : function(file)
26974     {
26975         if(typeof(file.target) != 'undefined' && file.target){
26976             return file;
26977         }
26978         
26979         var previewEl = this.managerEl.createChild({
26980             tag : 'div',
26981             cls : 'roo-document-manager-preview',
26982             cn : [
26983                 {
26984                     tag : 'div',
26985                     tooltip : file.filename,
26986                     cls : 'roo-document-manager-thumb',
26987                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26988                 },
26989                 {
26990                     tag : 'button',
26991                     cls : 'close',
26992                     html : '<i class="fa fa-times-circle"></i>'
26993                 }
26994             ]
26995         });
26996
26997         var close = previewEl.select('button.close', true).first();
26998
26999         close.on('click', this.onRemove, this, file);
27000
27001         file.target = previewEl;
27002
27003         var image = previewEl.select('img', true).first();
27004         
27005         var _this = this;
27006         
27007         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27008         
27009         image.on('click', this.onClick, this, file);
27010         
27011         return file;
27012         
27013     },
27014     
27015     onPreviewLoad : function(file, image)
27016     {
27017         if(typeof(file.target) == 'undefined' || !file.target){
27018             return;
27019         }
27020         
27021         var width = image.dom.naturalWidth || image.dom.width;
27022         var height = image.dom.naturalHeight || image.dom.height;
27023         
27024         if(width > height){
27025             file.target.addClass('wide');
27026             return;
27027         }
27028         
27029         file.target.addClass('tall');
27030         return;
27031         
27032     },
27033     
27034     uploadFromSource : function(file, crop)
27035     {
27036         this.xhr = new XMLHttpRequest();
27037         
27038         this.managerEl.createChild({
27039             tag : 'div',
27040             cls : 'roo-document-manager-loading',
27041             cn : [
27042                 {
27043                     tag : 'div',
27044                     tooltip : file.name,
27045                     cls : 'roo-document-manager-thumb',
27046                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27047                 }
27048             ]
27049
27050         });
27051
27052         this.xhr.open(this.method, this.url, true);
27053         
27054         var headers = {
27055             "Accept": "application/json",
27056             "Cache-Control": "no-cache",
27057             "X-Requested-With": "XMLHttpRequest"
27058         };
27059         
27060         for (var headerName in headers) {
27061             var headerValue = headers[headerName];
27062             if (headerValue) {
27063                 this.xhr.setRequestHeader(headerName, headerValue);
27064             }
27065         }
27066         
27067         var _this = this;
27068         
27069         this.xhr.onload = function()
27070         {
27071             _this.xhrOnLoad(_this.xhr);
27072         }
27073         
27074         this.xhr.onerror = function()
27075         {
27076             _this.xhrOnError(_this.xhr);
27077         }
27078         
27079         var formData = new FormData();
27080
27081         formData.append('returnHTML', 'NO');
27082         
27083         formData.append('crop', crop);
27084         
27085         if(typeof(file.filename) != 'undefined'){
27086             formData.append('filename', file.filename);
27087         }
27088         
27089         if(typeof(file.mimetype) != 'undefined'){
27090             formData.append('mimetype', file.mimetype);
27091         }
27092         
27093         if(this.fireEvent('prepare', this, formData) != false){
27094             this.xhr.send(formData);
27095         };
27096     }
27097 });
27098
27099 /*
27100 * Licence: LGPL
27101 */
27102
27103 /**
27104  * @class Roo.bootstrap.DocumentViewer
27105  * @extends Roo.bootstrap.Component
27106  * Bootstrap DocumentViewer class
27107  * 
27108  * @constructor
27109  * Create a new DocumentViewer
27110  * @param {Object} config The config object
27111  */
27112
27113 Roo.bootstrap.DocumentViewer = function(config){
27114     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27115     
27116     this.addEvents({
27117         /**
27118          * @event initial
27119          * Fire after initEvent
27120          * @param {Roo.bootstrap.DocumentViewer} this
27121          */
27122         "initial" : true,
27123         /**
27124          * @event click
27125          * Fire after click
27126          * @param {Roo.bootstrap.DocumentViewer} this
27127          */
27128         "click" : true,
27129         /**
27130          * @event trash
27131          * Fire after trash button
27132          * @param {Roo.bootstrap.DocumentViewer} this
27133          */
27134         "trash" : true
27135         
27136     });
27137 };
27138
27139 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27140     
27141     getAutoCreate : function()
27142     {
27143         var cfg = {
27144             tag : 'div',
27145             cls : 'roo-document-viewer',
27146             cn : [
27147                 {
27148                     tag : 'div',
27149                     cls : 'roo-document-viewer-body',
27150                     cn : [
27151                         {
27152                             tag : 'div',
27153                             cls : 'roo-document-viewer-thumb',
27154                             cn : [
27155                                 {
27156                                     tag : 'img',
27157                                     cls : 'roo-document-viewer-image'
27158                                 }
27159                             ]
27160                         }
27161                     ]
27162                 },
27163                 {
27164                     tag : 'div',
27165                     cls : 'roo-document-viewer-footer',
27166                     cn : {
27167                         tag : 'div',
27168                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27169                         cn : [
27170                             {
27171                                 tag : 'div',
27172                                 cls : 'btn-group',
27173                                 cn : [
27174                                     {
27175                                         tag : 'button',
27176                                         cls : 'btn btn-default roo-document-viewer-trash',
27177                                         html : '<i class="fa fa-trash"></i>'
27178                                     }
27179                                 ]
27180                             }
27181                         ]
27182                     }
27183                 }
27184             ]
27185         };
27186         
27187         return cfg;
27188     },
27189     
27190     initEvents : function()
27191     {
27192         
27193         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27194         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27195         
27196         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27197         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27198         
27199         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27200         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27201         
27202         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27203         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27204         
27205         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27206         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27207         
27208         this.bodyEl.on('click', this.onClick, this);
27209         
27210         this.trashBtn.on('click', this.onTrash, this);
27211         
27212     },
27213     
27214     initial : function()
27215     {
27216 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27217         
27218         
27219         this.fireEvent('initial', this);
27220         
27221     },
27222     
27223     onClick : function(e)
27224     {
27225         e.preventDefault();
27226         
27227         this.fireEvent('click', this);
27228     },
27229     
27230     onTrash : function(e)
27231     {
27232         e.preventDefault();
27233         
27234         this.fireEvent('trash', this);
27235     }
27236     
27237 });
27238 /*
27239  * - LGPL
27240  *
27241  * nav progress bar
27242  * 
27243  */
27244
27245 /**
27246  * @class Roo.bootstrap.NavProgressBar
27247  * @extends Roo.bootstrap.Component
27248  * Bootstrap NavProgressBar class
27249  * 
27250  * @constructor
27251  * Create a new nav progress bar
27252  * @param {Object} config The config object
27253  */
27254
27255 Roo.bootstrap.NavProgressBar = function(config){
27256     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27257
27258     this.bullets = this.bullets || [];
27259    
27260 //    Roo.bootstrap.NavProgressBar.register(this);
27261      this.addEvents({
27262         /**
27263              * @event changed
27264              * Fires when the active item changes
27265              * @param {Roo.bootstrap.NavProgressBar} this
27266              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27267              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27268          */
27269         'changed': true
27270      });
27271     
27272 };
27273
27274 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27275     
27276     bullets : [],
27277     barItems : [],
27278     
27279     getAutoCreate : function()
27280     {
27281         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27282         
27283         cfg = {
27284             tag : 'div',
27285             cls : 'roo-navigation-bar-group',
27286             cn : [
27287                 {
27288                     tag : 'div',
27289                     cls : 'roo-navigation-top-bar'
27290                 },
27291                 {
27292                     tag : 'div',
27293                     cls : 'roo-navigation-bullets-bar',
27294                     cn : [
27295                         {
27296                             tag : 'ul',
27297                             cls : 'roo-navigation-bar'
27298                         }
27299                     ]
27300                 },
27301                 
27302                 {
27303                     tag : 'div',
27304                     cls : 'roo-navigation-bottom-bar'
27305                 }
27306             ]
27307             
27308         };
27309         
27310         return cfg;
27311         
27312     },
27313     
27314     initEvents: function() 
27315     {
27316         
27317     },
27318     
27319     onRender : function(ct, position) 
27320     {
27321         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27322         
27323         if(this.bullets.length){
27324             Roo.each(this.bullets, function(b){
27325                this.addItem(b);
27326             }, this);
27327         }
27328         
27329         this.format();
27330         
27331     },
27332     
27333     addItem : function(cfg)
27334     {
27335         var item = new Roo.bootstrap.NavProgressItem(cfg);
27336         
27337         item.parentId = this.id;
27338         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27339         
27340         if(cfg.html){
27341             var top = new Roo.bootstrap.Element({
27342                 tag : 'div',
27343                 cls : 'roo-navigation-bar-text'
27344             });
27345             
27346             var bottom = new Roo.bootstrap.Element({
27347                 tag : 'div',
27348                 cls : 'roo-navigation-bar-text'
27349             });
27350             
27351             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27352             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27353             
27354             var topText = new Roo.bootstrap.Element({
27355                 tag : 'span',
27356                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27357             });
27358             
27359             var bottomText = new Roo.bootstrap.Element({
27360                 tag : 'span',
27361                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27362             });
27363             
27364             topText.onRender(top.el, null);
27365             bottomText.onRender(bottom.el, null);
27366             
27367             item.topEl = top;
27368             item.bottomEl = bottom;
27369         }
27370         
27371         this.barItems.push(item);
27372         
27373         return item;
27374     },
27375     
27376     getActive : function()
27377     {
27378         var active = false;
27379         
27380         Roo.each(this.barItems, function(v){
27381             
27382             if (!v.isActive()) {
27383                 return;
27384             }
27385             
27386             active = v;
27387             return false;
27388             
27389         });
27390         
27391         return active;
27392     },
27393     
27394     setActiveItem : function(item)
27395     {
27396         var prev = false;
27397         
27398         Roo.each(this.barItems, function(v){
27399             if (v.rid == item.rid) {
27400                 return ;
27401             }
27402             
27403             if (v.isActive()) {
27404                 v.setActive(false);
27405                 prev = v;
27406             }
27407         });
27408
27409         item.setActive(true);
27410         
27411         this.fireEvent('changed', this, item, prev);
27412     },
27413     
27414     getBarItem: function(rid)
27415     {
27416         var ret = false;
27417         
27418         Roo.each(this.barItems, function(e) {
27419             if (e.rid != rid) {
27420                 return;
27421             }
27422             
27423             ret =  e;
27424             return false;
27425         });
27426         
27427         return ret;
27428     },
27429     
27430     indexOfItem : function(item)
27431     {
27432         var index = false;
27433         
27434         Roo.each(this.barItems, function(v, i){
27435             
27436             if (v.rid != item.rid) {
27437                 return;
27438             }
27439             
27440             index = i;
27441             return false
27442         });
27443         
27444         return index;
27445     },
27446     
27447     setActiveNext : function()
27448     {
27449         var i = this.indexOfItem(this.getActive());
27450         
27451         if (i > this.barItems.length) {
27452             return;
27453         }
27454         
27455         this.setActiveItem(this.barItems[i+1]);
27456     },
27457     
27458     setActivePrev : function()
27459     {
27460         var i = this.indexOfItem(this.getActive());
27461         
27462         if (i  < 1) {
27463             return;
27464         }
27465         
27466         this.setActiveItem(this.barItems[i-1]);
27467     },
27468     
27469     format : function()
27470     {
27471         if(!this.barItems.length){
27472             return;
27473         }
27474      
27475         var width = 100 / this.barItems.length;
27476         
27477         Roo.each(this.barItems, function(i){
27478             i.el.setStyle('width', width + '%');
27479             i.topEl.el.setStyle('width', width + '%');
27480             i.bottomEl.el.setStyle('width', width + '%');
27481         }, this);
27482         
27483     }
27484     
27485 });
27486 /*
27487  * - LGPL
27488  *
27489  * Nav Progress Item
27490  * 
27491  */
27492
27493 /**
27494  * @class Roo.bootstrap.NavProgressItem
27495  * @extends Roo.bootstrap.Component
27496  * Bootstrap NavProgressItem class
27497  * @cfg {String} rid the reference id
27498  * @cfg {Boolean} active (true|false) Is item active default false
27499  * @cfg {Boolean} disabled (true|false) Is item active default false
27500  * @cfg {String} html
27501  * @cfg {String} position (top|bottom) text position default bottom
27502  * @cfg {String} icon show icon instead of number
27503  * 
27504  * @constructor
27505  * Create a new NavProgressItem
27506  * @param {Object} config The config object
27507  */
27508 Roo.bootstrap.NavProgressItem = function(config){
27509     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27510     this.addEvents({
27511         // raw events
27512         /**
27513          * @event click
27514          * The raw click event for the entire grid.
27515          * @param {Roo.bootstrap.NavProgressItem} this
27516          * @param {Roo.EventObject} e
27517          */
27518         "click" : true
27519     });
27520    
27521 };
27522
27523 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27524     
27525     rid : '',
27526     active : false,
27527     disabled : false,
27528     html : '',
27529     position : 'bottom',
27530     icon : false,
27531     
27532     getAutoCreate : function()
27533     {
27534         var iconCls = 'roo-navigation-bar-item-icon';
27535         
27536         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27537         
27538         var cfg = {
27539             tag: 'li',
27540             cls: 'roo-navigation-bar-item',
27541             cn : [
27542                 {
27543                     tag : 'i',
27544                     cls : iconCls
27545                 }
27546             ]
27547         };
27548         
27549         if(this.active){
27550             cfg.cls += ' active';
27551         }
27552         if(this.disabled){
27553             cfg.cls += ' disabled';
27554         }
27555         
27556         return cfg;
27557     },
27558     
27559     disable : function()
27560     {
27561         this.setDisabled(true);
27562     },
27563     
27564     enable : function()
27565     {
27566         this.setDisabled(false);
27567     },
27568     
27569     initEvents: function() 
27570     {
27571         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27572         
27573         this.iconEl.on('click', this.onClick, this);
27574     },
27575     
27576     onClick : function(e)
27577     {
27578         e.preventDefault();
27579         
27580         if(this.disabled){
27581             return;
27582         }
27583         
27584         if(this.fireEvent('click', this, e) === false){
27585             return;
27586         };
27587         
27588         this.parent().setActiveItem(this);
27589     },
27590     
27591     isActive: function () 
27592     {
27593         return this.active;
27594     },
27595     
27596     setActive : function(state)
27597     {
27598         if(this.active == state){
27599             return;
27600         }
27601         
27602         this.active = state;
27603         
27604         if (state) {
27605             this.el.addClass('active');
27606             return;
27607         }
27608         
27609         this.el.removeClass('active');
27610         
27611         return;
27612     },
27613     
27614     setDisabled : function(state)
27615     {
27616         if(this.disabled == state){
27617             return;
27618         }
27619         
27620         this.disabled = state;
27621         
27622         if (state) {
27623             this.el.addClass('disabled');
27624             return;
27625         }
27626         
27627         this.el.removeClass('disabled');
27628     },
27629     
27630     tooltipEl : function()
27631     {
27632         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27633     }
27634 });
27635  
27636
27637  /*
27638  * - LGPL
27639  *
27640  * FieldLabel
27641  * 
27642  */
27643
27644 /**
27645  * @class Roo.bootstrap.FieldLabel
27646  * @extends Roo.bootstrap.Component
27647  * Bootstrap FieldLabel class
27648  * @cfg {String} html contents of the element
27649  * @cfg {String} tag tag of the element default label
27650  * @cfg {String} cls class of the element
27651  * @cfg {String} target label target 
27652  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27653  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27654  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27655  * @cfg {String} iconTooltip default "This field is required"
27656  * 
27657  * @constructor
27658  * Create a new FieldLabel
27659  * @param {Object} config The config object
27660  */
27661
27662 Roo.bootstrap.FieldLabel = function(config){
27663     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27664     
27665     this.addEvents({
27666             /**
27667              * @event invalid
27668              * Fires after the field has been marked as invalid.
27669              * @param {Roo.form.FieldLabel} this
27670              * @param {String} msg The validation message
27671              */
27672             invalid : true,
27673             /**
27674              * @event valid
27675              * Fires after the field has been validated with no errors.
27676              * @param {Roo.form.FieldLabel} this
27677              */
27678             valid : true
27679         });
27680 };
27681
27682 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27683     
27684     tag: 'label',
27685     cls: '',
27686     html: '',
27687     target: '',
27688     allowBlank : true,
27689     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27690     validClass : 'text-success fa fa-lg fa-check',
27691     iconTooltip : 'This field is required',
27692     
27693     getAutoCreate : function(){
27694         
27695         var cfg = {
27696             tag : this.tag,
27697             cls : 'roo-bootstrap-field-label ' + this.cls,
27698             for : this.target,
27699             cn : [
27700                 {
27701                     tag : 'i',
27702                     cls : '',
27703                     tooltip : this.iconTooltip
27704                 },
27705                 {
27706                     tag : 'span',
27707                     html : this.html
27708                 }
27709             ] 
27710         };
27711         
27712         return cfg;
27713     },
27714     
27715     initEvents: function() 
27716     {
27717         Roo.bootstrap.Element.superclass.initEvents.call(this);
27718         
27719         this.iconEl = this.el.select('i', true).first();
27720         
27721         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27722         
27723         Roo.bootstrap.FieldLabel.register(this);
27724     },
27725     
27726     /**
27727      * Mark this field as valid
27728      */
27729     markValid : function()
27730     {
27731         this.iconEl.show();
27732         
27733         this.iconEl.removeClass(this.invalidClass);
27734         
27735         this.iconEl.addClass(this.validClass);
27736         
27737         this.fireEvent('valid', this);
27738     },
27739     
27740     /**
27741      * Mark this field as invalid
27742      * @param {String} msg The validation message
27743      */
27744     markInvalid : function(msg)
27745     {
27746         this.iconEl.show();
27747         
27748         this.iconEl.removeClass(this.validClass);
27749         
27750         this.iconEl.addClass(this.invalidClass);
27751         
27752         this.fireEvent('invalid', this, msg);
27753     }
27754     
27755    
27756 });
27757
27758 Roo.apply(Roo.bootstrap.FieldLabel, {
27759     
27760     groups: {},
27761     
27762      /**
27763     * register a FieldLabel Group
27764     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27765     */
27766     register : function(label)
27767     {
27768         if(this.groups.hasOwnProperty(label.target)){
27769             return;
27770         }
27771      
27772         this.groups[label.target] = label;
27773         
27774     },
27775     /**
27776     * fetch a FieldLabel Group based on the target
27777     * @param {string} target
27778     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27779     */
27780     get: function(target) {
27781         if (typeof(this.groups[target]) == 'undefined') {
27782             return false;
27783         }
27784         
27785         return this.groups[target] ;
27786     }
27787 });
27788
27789  
27790
27791  /*
27792  * - LGPL
27793  *
27794  * page DateSplitField.
27795  * 
27796  */
27797
27798
27799 /**
27800  * @class Roo.bootstrap.DateSplitField
27801  * @extends Roo.bootstrap.Component
27802  * Bootstrap DateSplitField class
27803  * @cfg {string} fieldLabel - the label associated
27804  * @cfg {Number} labelWidth set the width of label (0-12)
27805  * @cfg {String} labelAlign (top|left)
27806  * @cfg {Boolean} dayAllowBlank (true|false) default false
27807  * @cfg {Boolean} monthAllowBlank (true|false) default false
27808  * @cfg {Boolean} yearAllowBlank (true|false) default false
27809  * @cfg {string} dayPlaceholder 
27810  * @cfg {string} monthPlaceholder
27811  * @cfg {string} yearPlaceholder
27812  * @cfg {string} dayFormat default 'd'
27813  * @cfg {string} monthFormat default 'm'
27814  * @cfg {string} yearFormat default 'Y'
27815
27816  *     
27817  * @constructor
27818  * Create a new DateSplitField
27819  * @param {Object} config The config object
27820  */
27821
27822 Roo.bootstrap.DateSplitField = function(config){
27823     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27824     
27825     this.addEvents({
27826         // raw events
27827          /**
27828          * @event years
27829          * getting the data of years
27830          * @param {Roo.bootstrap.DateSplitField} this
27831          * @param {Object} years
27832          */
27833         "years" : true,
27834         /**
27835          * @event days
27836          * getting the data of days
27837          * @param {Roo.bootstrap.DateSplitField} this
27838          * @param {Object} days
27839          */
27840         "days" : true,
27841         /**
27842          * @event invalid
27843          * Fires after the field has been marked as invalid.
27844          * @param {Roo.form.Field} this
27845          * @param {String} msg The validation message
27846          */
27847         invalid : true,
27848        /**
27849          * @event valid
27850          * Fires after the field has been validated with no errors.
27851          * @param {Roo.form.Field} this
27852          */
27853         valid : true
27854     });
27855 };
27856
27857 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27858     
27859     fieldLabel : '',
27860     labelAlign : 'top',
27861     labelWidth : 3,
27862     dayAllowBlank : false,
27863     monthAllowBlank : false,
27864     yearAllowBlank : false,
27865     dayPlaceholder : '',
27866     monthPlaceholder : '',
27867     yearPlaceholder : '',
27868     dayFormat : 'd',
27869     monthFormat : 'm',
27870     yearFormat : 'Y',
27871     isFormField : true,
27872     
27873     getAutoCreate : function()
27874     {
27875         var cfg = {
27876             tag : 'div',
27877             cls : 'row roo-date-split-field-group',
27878             cn : [
27879                 {
27880                     tag : 'input',
27881                     type : 'hidden',
27882                     cls : 'form-hidden-field roo-date-split-field-group-value',
27883                     name : this.name
27884                 }
27885             ]
27886         };
27887         
27888         if(this.fieldLabel){
27889             cfg.cn.push({
27890                 tag : 'div',
27891                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27892                 cn : [
27893                     {
27894                         tag : 'label',
27895                         html : this.fieldLabel
27896                     }
27897                 ]
27898             });
27899         }
27900         
27901         Roo.each(['day', 'month', 'year'], function(t){
27902             cfg.cn.push({
27903                 tag : 'div',
27904                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27905             });
27906         }, this);
27907         
27908         return cfg;
27909     },
27910     
27911     inputEl: function ()
27912     {
27913         return this.el.select('.roo-date-split-field-group-value', true).first();
27914     },
27915     
27916     onRender : function(ct, position) 
27917     {
27918         var _this = this;
27919         
27920         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27921         
27922         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27923         
27924         this.dayField = new Roo.bootstrap.ComboBox({
27925             allowBlank : this.dayAllowBlank,
27926             alwaysQuery : true,
27927             displayField : 'value',
27928             editable : false,
27929             fieldLabel : '',
27930             forceSelection : true,
27931             mode : 'local',
27932             placeholder : this.dayPlaceholder,
27933             selectOnFocus : true,
27934             tpl : '<div class="select2-result"><b>{value}</b></div>',
27935             triggerAction : 'all',
27936             typeAhead : true,
27937             valueField : 'value',
27938             store : new Roo.data.SimpleStore({
27939                 data : (function() {    
27940                     var days = [];
27941                     _this.fireEvent('days', _this, days);
27942                     return days;
27943                 })(),
27944                 fields : [ 'value' ]
27945             }),
27946             listeners : {
27947                 select : function (_self, record, index)
27948                 {
27949                     _this.setValue(_this.getValue());
27950                 }
27951             }
27952         });
27953
27954         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27955         
27956         this.monthField = new Roo.bootstrap.MonthField({
27957             after : '<i class=\"fa fa-calendar\"></i>',
27958             allowBlank : this.monthAllowBlank,
27959             placeholder : this.monthPlaceholder,
27960             readOnly : true,
27961             listeners : {
27962                 render : function (_self)
27963                 {
27964                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27965                         e.preventDefault();
27966                         _self.focus();
27967                     });
27968                 },
27969                 select : function (_self, oldvalue, newvalue)
27970                 {
27971                     _this.setValue(_this.getValue());
27972                 }
27973             }
27974         });
27975         
27976         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27977         
27978         this.yearField = new Roo.bootstrap.ComboBox({
27979             allowBlank : this.yearAllowBlank,
27980             alwaysQuery : true,
27981             displayField : 'value',
27982             editable : false,
27983             fieldLabel : '',
27984             forceSelection : true,
27985             mode : 'local',
27986             placeholder : this.yearPlaceholder,
27987             selectOnFocus : true,
27988             tpl : '<div class="select2-result"><b>{value}</b></div>',
27989             triggerAction : 'all',
27990             typeAhead : true,
27991             valueField : 'value',
27992             store : new Roo.data.SimpleStore({
27993                 data : (function() {
27994                     var years = [];
27995                     _this.fireEvent('years', _this, years);
27996                     return years;
27997                 })(),
27998                 fields : [ 'value' ]
27999             }),
28000             listeners : {
28001                 select : function (_self, record, index)
28002                 {
28003                     _this.setValue(_this.getValue());
28004                 }
28005             }
28006         });
28007
28008         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28009     },
28010     
28011     setValue : function(v, format)
28012     {
28013         this.inputEl.dom.value = v;
28014         
28015         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28016         
28017         var d = Date.parseDate(v, f);
28018         
28019         if(!d){
28020             this.validate();
28021             return;
28022         }
28023         
28024         this.setDay(d.format(this.dayFormat));
28025         this.setMonth(d.format(this.monthFormat));
28026         this.setYear(d.format(this.yearFormat));
28027         
28028         this.validate();
28029         
28030         return;
28031     },
28032     
28033     setDay : function(v)
28034     {
28035         this.dayField.setValue(v);
28036         this.inputEl.dom.value = this.getValue();
28037         this.validate();
28038         return;
28039     },
28040     
28041     setMonth : function(v)
28042     {
28043         this.monthField.setValue(v, true);
28044         this.inputEl.dom.value = this.getValue();
28045         this.validate();
28046         return;
28047     },
28048     
28049     setYear : function(v)
28050     {
28051         this.yearField.setValue(v);
28052         this.inputEl.dom.value = this.getValue();
28053         this.validate();
28054         return;
28055     },
28056     
28057     getDay : function()
28058     {
28059         return this.dayField.getValue();
28060     },
28061     
28062     getMonth : function()
28063     {
28064         return this.monthField.getValue();
28065     },
28066     
28067     getYear : function()
28068     {
28069         return this.yearField.getValue();
28070     },
28071     
28072     getValue : function()
28073     {
28074         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28075         
28076         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28077         
28078         return date;
28079     },
28080     
28081     reset : function()
28082     {
28083         this.setDay('');
28084         this.setMonth('');
28085         this.setYear('');
28086         this.inputEl.dom.value = '';
28087         this.validate();
28088         return;
28089     },
28090     
28091     validate : function()
28092     {
28093         var d = this.dayField.validate();
28094         var m = this.monthField.validate();
28095         var y = this.yearField.validate();
28096         
28097         var valid = true;
28098         
28099         if(
28100                 (!this.dayAllowBlank && !d) ||
28101                 (!this.monthAllowBlank && !m) ||
28102                 (!this.yearAllowBlank && !y)
28103         ){
28104             valid = false;
28105         }
28106         
28107         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28108             return valid;
28109         }
28110         
28111         if(valid){
28112             this.markValid();
28113             return valid;
28114         }
28115         
28116         this.markInvalid();
28117         
28118         return valid;
28119     },
28120     
28121     markValid : function()
28122     {
28123         
28124         var label = this.el.select('label', true).first();
28125         var icon = this.el.select('i.fa-star', true).first();
28126
28127         if(label && icon){
28128             icon.remove();
28129         }
28130         
28131         this.fireEvent('valid', this);
28132     },
28133     
28134      /**
28135      * Mark this field as invalid
28136      * @param {String} msg The validation message
28137      */
28138     markInvalid : function(msg)
28139     {
28140         
28141         var label = this.el.select('label', true).first();
28142         var icon = this.el.select('i.fa-star', true).first();
28143
28144         if(label && !icon){
28145             this.el.select('.roo-date-split-field-label', true).createChild({
28146                 tag : 'i',
28147                 cls : 'text-danger fa fa-lg fa-star',
28148                 tooltip : 'This field is required',
28149                 style : 'margin-right:5px;'
28150             }, label, true);
28151         }
28152         
28153         this.fireEvent('invalid', this, msg);
28154     },
28155     
28156     clearInvalid : function()
28157     {
28158         var label = this.el.select('label', true).first();
28159         var icon = this.el.select('i.fa-star', true).first();
28160
28161         if(label && icon){
28162             icon.remove();
28163         }
28164         
28165         this.fireEvent('valid', this);
28166     },
28167     
28168     getName: function()
28169     {
28170         return this.name;
28171     }
28172     
28173 });
28174
28175  /**
28176  *
28177  * This is based on 
28178  * http://masonry.desandro.com
28179  *
28180  * The idea is to render all the bricks based on vertical width...
28181  *
28182  * The original code extends 'outlayer' - we might need to use that....
28183  * 
28184  */
28185
28186
28187 /**
28188  * @class Roo.bootstrap.LayoutMasonry
28189  * @extends Roo.bootstrap.Component
28190  * Bootstrap Layout Masonry class
28191  * 
28192  * @constructor
28193  * Create a new Element
28194  * @param {Object} config The config object
28195  */
28196
28197 Roo.bootstrap.LayoutMasonry = function(config){
28198     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28199 };
28200
28201 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28202     
28203       /**
28204      * @cfg {Boolean} isFitWidth  - resize the width..
28205      */   
28206     isFitWidth : false,  // options..
28207     /**
28208      * @cfg {Boolean} isOriginLeft = left align?
28209      */   
28210     isOriginLeft : true,
28211     /**
28212      * @cfg {Boolean} isOriginTop = top align?
28213      */   
28214     isOriginTop : false,
28215     /**
28216      * @cfg {Boolean} isLayoutInstant = no animation?
28217      */   
28218     isLayoutInstant : false, // needed?
28219     /**
28220      * @cfg {Boolean} isResizingContainer = not sure if this is used..
28221      */   
28222     isResizingContainer : true,
28223     /**
28224      * @cfg {Number} columnWidth  width of the columns 
28225      */   
28226     
28227     columnWidth : 0,
28228     /**
28229      * @cfg {Number} padHeight padding below box..
28230      */   
28231     
28232     padHeight : 10, 
28233     
28234     /**
28235      * @cfg {Boolean} isAutoInitial defalut true
28236      */   
28237     
28238     isAutoInitial : true, 
28239     
28240     // private?
28241     gutter : 0,
28242     
28243     containerWidth: 0,
28244     initialColumnWidth : 0,
28245     currentSize : null,
28246     
28247     colYs : null, // array.
28248     maxY : 0,
28249     padWidth: 10,
28250     
28251     
28252     tag: 'div',
28253     cls: '',
28254     bricks: null, //CompositeElement
28255     cols : 0, // array?
28256     // element : null, // wrapped now this.el
28257     _isLayoutInited : null, 
28258     
28259     
28260     getAutoCreate : function(){
28261         
28262         var cfg = {
28263             tag: this.tag,
28264             cls: 'blog-masonary-wrapper ' + this.cls,
28265             cn : {
28266                 cls : 'mas-boxes masonary'
28267             }
28268         };
28269         
28270         
28271         return cfg;
28272     },
28273     getChildContainer: function( )
28274     {
28275         if (this.boxesEl) {
28276             return this.boxesEl;
28277         }
28278         this.boxesEl = this.el.select('.mas-boxes').first();
28279         
28280         return this.boxesEl;
28281     },
28282     
28283     
28284     initEvents : function()
28285     {
28286         var _this = this;
28287         
28288         if(this.isAutoInitial){
28289             Roo.log('hook children rendered');
28290             this.on('childrenrendered', function() {
28291                 Roo.log('children rendered');
28292                 _this.initial();
28293             } ,this);
28294         }
28295         
28296     },
28297     
28298     initial : function()
28299     {
28300         this.reloadItems();
28301
28302         this.currentSize = this.el.getBox(true);
28303
28304         /// was window resize... - let's see if this works..
28305         Roo.EventManager.onWindowResize(this.resize, this); 
28306
28307         if(!this.isAutoInitial){
28308             this.layout();
28309             return;
28310         }
28311         
28312         this.layout.defer(500,this);
28313     },
28314     
28315     reloadItems: function()
28316     {
28317         this.bricks = this.el.select('.masonry-brick', true);
28318         
28319         this.bricks.each(function(b) {
28320             //Roo.log(b.getSize());
28321             if (!b.attr('originalwidth')) {
28322                 b.attr('originalwidth',  b.getSize().width);
28323             }
28324             
28325         });
28326         
28327         Roo.log(this.bricks.elements.length);
28328     },
28329     
28330    
28331     resize : function()
28332     {
28333         Roo.log('resize');
28334         var cs = this.el.getBox(true);
28335         
28336         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28337             Roo.log("no change in with or X");
28338             return;
28339         }
28340         this.currentSize = cs;
28341         this.layout();
28342     },
28343     layout : function()
28344     {
28345          Roo.log('layout');
28346         this._resetLayout();
28347         //this._manageStamps();
28348       
28349         // don't animate first layout
28350         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28351         this.layoutItems( isInstant );
28352       
28353         // flag for initalized
28354         this._isLayoutInited = true;
28355     },
28356     
28357     layoutItems : function( isInstant )
28358     {
28359         //var items = this._getItemsForLayout( this.items );
28360         // original code supports filtering layout items.. we just ignore it..
28361         
28362         this._layoutItems( this.bricks , isInstant );
28363       
28364         this._postLayout();
28365     },
28366     _layoutItems : function ( items , isInstant)
28367     {
28368        //this.fireEvent( 'layout', this, items );
28369     
28370
28371         if ( !items || !items.elements.length ) {
28372           // no items, emit event with empty array
28373             return;
28374         }
28375
28376         var queue = [];
28377         items.each(function(item) {
28378             Roo.log("layout item");
28379             Roo.log(item);
28380             // get x/y object from method
28381             var position = this._getItemLayoutPosition( item );
28382             // enqueue
28383             position.item = item;
28384             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
28385             queue.push( position );
28386         }, this);
28387       
28388         this._processLayoutQueue( queue );
28389     },
28390     /** Sets position of item in DOM
28391     * @param {Element} item
28392     * @param {Number} x - horizontal position
28393     * @param {Number} y - vertical position
28394     * @param {Boolean} isInstant - disables transitions
28395     */
28396     _processLayoutQueue : function( queue )
28397     {
28398         for ( var i=0, len = queue.length; i < len; i++ ) {
28399             var obj = queue[i];
28400             obj.item.position('absolute');
28401             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
28402         }
28403     },
28404       
28405     
28406     /**
28407     * Any logic you want to do after each layout,
28408     * i.e. size the container
28409     */
28410     _postLayout : function()
28411     {
28412         this.resizeContainer();
28413     },
28414     
28415     resizeContainer : function()
28416     {
28417         if ( !this.isResizingContainer ) {
28418             return;
28419         }
28420         var size = this._getContainerSize();
28421         if ( size ) {
28422             this.el.setSize(size.width,size.height);
28423             this.boxesEl.setSize(size.width,size.height);
28424         }
28425     },
28426     
28427     
28428     
28429     _resetLayout : function()
28430     {
28431         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
28432         this.colWidth = this.el.getWidth();
28433         //this.gutter = this.el.getWidth(); 
28434         
28435         this.measureColumns();
28436
28437         // reset column Y
28438         var i = this.cols;
28439         this.colYs = [];
28440         while (i--) {
28441             this.colYs.push( 0 );
28442         }
28443     
28444         this.maxY = 0;
28445     },
28446
28447     measureColumns : function()
28448     {
28449         this.getContainerWidth();
28450       // if columnWidth is 0, default to outerWidth of first item
28451         if ( !this.columnWidth ) {
28452             var firstItem = this.bricks.first();
28453             Roo.log(firstItem);
28454             this.columnWidth  = this.containerWidth;
28455             if (firstItem && firstItem.attr('originalwidth') ) {
28456                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
28457             }
28458             // columnWidth fall back to item of first element
28459             Roo.log("set column width?");
28460                         this.initialColumnWidth = this.columnWidth  ;
28461
28462             // if first elem has no width, default to size of container
28463             
28464         }
28465         
28466         
28467         if (this.initialColumnWidth) {
28468             this.columnWidth = this.initialColumnWidth;
28469         }
28470         
28471         
28472             
28473         // column width is fixed at the top - however if container width get's smaller we should
28474         // reduce it...
28475         
28476         // this bit calcs how man columns..
28477             
28478         var columnWidth = this.columnWidth += this.gutter;
28479       
28480         // calculate columns
28481         var containerWidth = this.containerWidth + this.gutter;
28482         
28483         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
28484         // fix rounding errors, typically with gutters
28485         var excess = columnWidth - containerWidth % columnWidth;
28486         
28487         
28488         // if overshoot is less than a pixel, round up, otherwise floor it
28489         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
28490         cols = Math[ mathMethod ]( cols );
28491         this.cols = Math.max( cols, 1 );
28492         
28493         
28494          // padding positioning..
28495         var totalColWidth = this.cols * this.columnWidth;
28496         var padavail = this.containerWidth - totalColWidth;
28497         // so for 2 columns - we need 3 'pads'
28498         
28499         var padNeeded = (1+this.cols) * this.padWidth;
28500         
28501         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
28502         
28503         this.columnWidth += padExtra
28504         //this.padWidth = Math.floor(padavail /  ( this.cols));
28505         
28506         // adjust colum width so that padding is fixed??
28507         
28508         // we have 3 columns ... total = width * 3
28509         // we have X left over... that should be used by 
28510         
28511         //if (this.expandC) {
28512             
28513         //}
28514         
28515         
28516         
28517     },
28518     
28519     getContainerWidth : function()
28520     {
28521        /* // container is parent if fit width
28522         var container = this.isFitWidth ? this.element.parentNode : this.element;
28523         // check that this.size and size are there
28524         // IE8 triggers resize on body size change, so they might not be
28525         
28526         var size = getSize( container );  //FIXME
28527         this.containerWidth = size && size.innerWidth; //FIXME
28528         */
28529          
28530         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28531         
28532     },
28533     
28534     _getItemLayoutPosition : function( item )  // what is item?
28535     {
28536         // we resize the item to our columnWidth..
28537       
28538         item.setWidth(this.columnWidth);
28539         item.autoBoxAdjust  = false;
28540         
28541         var sz = item.getSize();
28542  
28543         // how many columns does this brick span
28544         var remainder = this.containerWidth % this.columnWidth;
28545         
28546         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
28547         // round if off by 1 pixel, otherwise use ceil
28548         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
28549         colSpan = Math.min( colSpan, this.cols );
28550         
28551         // normally this should be '1' as we dont' currently allow multi width columns..
28552         
28553         var colGroup = this._getColGroup( colSpan );
28554         // get the minimum Y value from the columns
28555         var minimumY = Math.min.apply( Math, colGroup );
28556         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
28557         
28558         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
28559          
28560         // position the brick
28561         var position = {
28562             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
28563             y: this.currentSize.y + minimumY + this.padHeight
28564         };
28565         
28566         Roo.log(position);
28567         // apply setHeight to necessary columns
28568         var setHeight = minimumY + sz.height + this.padHeight;
28569         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
28570         
28571         var setSpan = this.cols + 1 - colGroup.length;
28572         for ( var i = 0; i < setSpan; i++ ) {
28573           this.colYs[ shortColIndex + i ] = setHeight ;
28574         }
28575       
28576         return position;
28577     },
28578     
28579     /**
28580      * @param {Number} colSpan - number of columns the element spans
28581      * @returns {Array} colGroup
28582      */
28583     _getColGroup : function( colSpan )
28584     {
28585         if ( colSpan < 2 ) {
28586           // if brick spans only one column, use all the column Ys
28587           return this.colYs;
28588         }
28589       
28590         var colGroup = [];
28591         // how many different places could this brick fit horizontally
28592         var groupCount = this.cols + 1 - colSpan;
28593         // for each group potential horizontal position
28594         for ( var i = 0; i < groupCount; i++ ) {
28595           // make an array of colY values for that one group
28596           var groupColYs = this.colYs.slice( i, i + colSpan );
28597           // and get the max value of the array
28598           colGroup[i] = Math.max.apply( Math, groupColYs );
28599         }
28600         return colGroup;
28601     },
28602     /*
28603     _manageStamp : function( stamp )
28604     {
28605         var stampSize =  stamp.getSize();
28606         var offset = stamp.getBox();
28607         // get the columns that this stamp affects
28608         var firstX = this.isOriginLeft ? offset.x : offset.right;
28609         var lastX = firstX + stampSize.width;
28610         var firstCol = Math.floor( firstX / this.columnWidth );
28611         firstCol = Math.max( 0, firstCol );
28612         
28613         var lastCol = Math.floor( lastX / this.columnWidth );
28614         // lastCol should not go over if multiple of columnWidth #425
28615         lastCol -= lastX % this.columnWidth ? 0 : 1;
28616         lastCol = Math.min( this.cols - 1, lastCol );
28617         
28618         // set colYs to bottom of the stamp
28619         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
28620             stampSize.height;
28621             
28622         for ( var i = firstCol; i <= lastCol; i++ ) {
28623           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
28624         }
28625     },
28626     */
28627     
28628     _getContainerSize : function()
28629     {
28630         this.maxY = Math.max.apply( Math, this.colYs );
28631         var size = {
28632             height: this.maxY
28633         };
28634       
28635         if ( this.isFitWidth ) {
28636             size.width = this._getContainerFitWidth();
28637         }
28638       
28639         return size;
28640     },
28641     
28642     _getContainerFitWidth : function()
28643     {
28644         var unusedCols = 0;
28645         // count unused columns
28646         var i = this.cols;
28647         while ( --i ) {
28648           if ( this.colYs[i] !== 0 ) {
28649             break;
28650           }
28651           unusedCols++;
28652         }
28653         // fit container to columns that have been used
28654         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
28655     },
28656     
28657     needsResizeLayout : function()
28658     {
28659         var previousWidth = this.containerWidth;
28660         this.getContainerWidth();
28661         return previousWidth !== this.containerWidth;
28662     }
28663  
28664 });
28665
28666  
28667
28668