4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
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];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
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);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @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)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
1549 * @class Roo.bootstrap.Link
1550 * @extends Roo.bootstrap.Component
1551 * Bootstrap Link Class
1552 * @cfg {String} alt image alternative text
1553 * @cfg {String} href a tag href
1554 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555 * @cfg {String} html the content of the link.
1556 * @cfg {String} anchor name for the anchor link
1557 * @cfg {String} fa - favicon
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
1653 * @class Roo.bootstrap.Header
1654 * @extends Roo.bootstrap.Component
1655 * Bootstrap Header class
1656 * @cfg {String} html content of header
1657 * @cfg {Number} level (1|2|3|4|5|6) default 1
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
1705 * @class Roo.bootstrap.MenuMgr
1706 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
1754 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755 m.parentMenu.activeChild = m;
1756 }else if(last && last.isVisible()){
1757 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
1821 menus[menu.id] = menu;
1822 menu.on("beforehide", onBeforeHide);
1823 menu.on("hide", onHide);
1824 menu.on("beforeshow", onBeforeShow);
1825 menu.on("show", onShow);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
1837 * Returns a {@link Roo.menu.Menu} object
1838 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839 * be used to generate and return a new Menu instance.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
1847 /*else if(typeof menu.length == 'number'){ // array of menu items?
1848 return new Roo.bootstrap.Menu({items:menu});
1849 }else{ // otherwise, must be a config
1850 return new Roo.bootstrap.Menu(menu);
1857 unregister : function(menu){
1858 delete menus[menu.id];
1859 menu.un("beforehide", onBeforeHide);
1860 menu.un("hide", onHide);
1861 menu.un("beforeshow", onBeforeShow);
1862 menu.un("show", onShow);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
1899 * @class Roo.bootstrap.Menu
1900 * @extends Roo.bootstrap.Component
1901 * Bootstrap Menu class - container for MenuItems
1902 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903 * @cfg {bool} hidden if the menu should be hidden when rendered.
1904 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1905 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1909 * @param {Object} config The config object
1913 Roo.bootstrap.Menu = function(config){
1914 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915 if (this.registerMenu && this.type != 'treeview') {
1916 Roo.bootstrap.MenuMgr.register(this);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
1945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * @param {Roo.EventObject} e
1953 * Fires when the mouse is hovering over this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when the mouse exits this menu
1962 * @param {Roo.menu.Menu} this
1963 * @param {Roo.EventObject} e
1964 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * Fires when a menu item contained in this menu is clicked
1970 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971 * @param {Roo.EventObject} e
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
2081 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2082 if(t == this.activeItem && t.shouldDeactivate(e)){
2083 this.activeItem.deactivate();
2084 delete this.activeItem;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
2135 * Displays this menu relative to another element
2136 * @param {String/HTMLElement/Roo.Element} element The element to align to
2137 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138 * the element (defaults to this.defaultAlign)
2139 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2150 * Displays this menu at a specific xy position
2151 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
2195 * Hides this menu and optionally all parent menus
2196 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2198 hide : function(deep)
2201 this.hideMenuItems();
2202 if(this.el && this.isVisible()){
2203 this.fireEvent("beforehide", this);
2204 if(this.activeItem){
2205 this.activeItem.deactivate();
2206 this.activeItem = null;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
2237 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238 var pel = Roo.get(e.getTarget());
2239 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240 Roo.log('is treeview or dropdown?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
2305 * @class Roo.bootstrap.MenuItem
2306 * @extends Roo.bootstrap.Component
2307 * Bootstrap MenuItem class
2308 * @cfg {String} html the menu label
2309 * @cfg {String} href the link
2310 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312 * @cfg {Boolean} active used on sidebars to highlight active itesm
2313 * @cfg {String} fa favicon to show on left of menu item.
2314 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
2469 * @class Roo.bootstrap.Modal
2470 * @extends Roo.bootstrap.Component
2471 * Bootstrap Modal class
2472 * @cfg {String} title Title of dialog
2473 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2475 * @cfg {Boolean} specificTitle default false
2476 * @cfg {Array} buttons Array of buttons or standard button set..
2477 * @cfg {String} buttonPosition (left|right|center) default right
2478 * @cfg {Boolean} animate default true
2479 * @cfg {Boolean} allow_close default true
2480 * @cfg {Boolean} fitwindow default false
2481 * @cfg {String} size (sm|lg) default empty
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.headerEl = this.el.select('.modal-header',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2574 this.footerEl = this.el.select('.modal-footer',true).first();
2576 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577 this.maskEl.enableDisplayMode("block");
2579 //this.el.addClass("x-dlg-modal");
2581 if (this.buttons.length) {
2582 Roo.each(this.buttons, function(bb) {
2583 var b = Roo.apply({}, bb);
2584 b.xns = b.xns || Roo.bootstrap;
2585 b.xtype = b.xtype || 'Button';
2586 if (typeof(b.listeners) == 'undefined') {
2587 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2590 var btn = Roo.factory(b);
2592 btn.render(this.el.select('.modal-footer div').first());
2596 // render the children.
2599 if(typeof(this.items) != 'undefined'){
2600 var items = this.items;
2603 for(var i =0;i < items.length;i++) {
2604 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2608 this.items = nitems;
2610 // where are these used - they used to be body/close/footer
2614 //this.el.addClass([this.fieldClass, this.cls]);
2618 getAutoCreate : function(){
2623 html : this.html || ''
2628 cls : 'modal-title',
2632 if(this.specificTitle){
2638 if (this.allow_close) {
2650 if(this.size.length){
2651 size = 'modal-' + this.size;
2656 style : 'display: none',
2659 cls: "modal-dialog " + size,
2662 cls : "modal-content",
2665 cls : 'modal-header',
2670 cls : 'modal-footer',
2674 cls: 'btn-' + this.buttonPosition
2691 modal.cls += ' fade';
2697 getChildContainer : function() {
2702 getButtonContainer : function() {
2703 return this.el.select('.modal-footer div',true).first();
2706 initEvents : function()
2708 if (this.allow_close) {
2709 this.closeEl.on('click', this.hide, this);
2711 Roo.EventManager.onWindowResize(this.resize, this, true);
2718 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2719 if (this.fitwindow) {
2720 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2726 setSize : function(w,h)
2736 if (!this.rendered) {
2740 this.el.setStyle('display', 'block');
2742 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2745 this.el.addClass('in');
2748 this.el.addClass('in');
2752 // not sure how we can show data in here..
2754 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2757 Roo.get(document.body).addClass("x-body-masked");
2759 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2760 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2765 this.fireEvent('show', this);
2767 // set zindex here - otherwise it appears to be ignored...
2768 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2771 this.items.forEach( function(e) {
2772 e.layout ? e.layout() : false;
2780 if(this.fireEvent("beforehide", this) !== false){
2782 Roo.get(document.body).removeClass("x-body-masked");
2783 this.el.removeClass('in');
2784 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2786 if(this.animate){ // why
2788 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2790 this.el.setStyle('display', 'none');
2792 this.fireEvent('hide', this);
2796 addButton : function(str, cb)
2800 var b = Roo.apply({}, { html : str } );
2801 b.xns = b.xns || Roo.bootstrap;
2802 b.xtype = b.xtype || 'Button';
2803 if (typeof(b.listeners) == 'undefined') {
2804 b.listeners = { click : cb.createDelegate(this) };
2807 var btn = Roo.factory(b);
2809 btn.render(this.el.select('.modal-footer div').first());
2815 setDefaultButton : function(btn)
2817 //this.el.select('.modal-footer').()
2821 resizeTo: function(w,h)
2825 this.dialogEl.setWidth(w);
2826 if (this.diff === false) {
2827 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2830 this.bodyEl.setHeight(h-this.diff);
2834 setContentSize : function(w, h)
2838 onButtonClick: function(btn,e)
2841 this.fireEvent('btnclick', btn.name, e);
2844 * Set the title of the Dialog
2845 * @param {String} str new Title
2847 setTitle: function(str) {
2848 this.titleEl.dom.innerHTML = str;
2851 * Set the body of the Dialog
2852 * @param {String} str new Title
2854 setBody: function(str) {
2855 this.bodyEl.dom.innerHTML = str;
2858 * Set the body of the Dialog using the template
2859 * @param {Obj} data - apply this data to the template and replace the body contents.
2861 applyBody: function(obj)
2864 Roo.log("Error - using apply Body without a template");
2867 this.tmpl.overwrite(this.bodyEl, obj);
2873 Roo.apply(Roo.bootstrap.Modal, {
2875 * Button config that displays a single OK button
2884 * Button config that displays Yes and No buttons
2900 * Button config that displays OK and Cancel buttons
2915 * Button config that displays Yes, No and Cancel buttons
2939 * messagebox - can be used as a replace
2943 * @class Roo.MessageBox
2944 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2953 // process text value...
2957 // Show a dialog using config options:
2959 title:'Save Changes?',
2960 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961 buttons: Roo.Msg.YESNOCANCEL,
2968 Roo.bootstrap.MessageBox = function(){
2969 var dlg, opt, mask, waitTimer;
2970 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971 var buttons, activeTextEl, bwidth;
2975 var handleButton = function(button){
2977 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2981 var handleHide = function(){
2983 dlg.el.removeClass(opt.cls);
2986 // Roo.TaskMgr.stop(waitTimer);
2987 // waitTimer = null;
2992 var updateButtons = function(b){
2995 buttons["ok"].hide();
2996 buttons["cancel"].hide();
2997 buttons["yes"].hide();
2998 buttons["no"].hide();
2999 //dlg.footer.dom.style.display = 'none';
3002 dlg.footerEl.dom.style.display = '';
3003 for(var k in buttons){
3004 if(typeof buttons[k] != "function"){
3007 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008 width += buttons[k].el.getWidth()+15;
3018 var handleEsc = function(d, k, e){
3019 if(opt && opt.closable !== false){
3029 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030 * @return {Roo.BasicDialog} The BasicDialog element
3032 getDialog : function(){
3034 dlg = new Roo.bootstrap.Modal( {
3037 //constraintoviewport:false,
3039 //collapsible : false,
3044 //buttonAlign:"center",
3045 closeClick : function(){
3046 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3049 handleButton("cancel");
3054 dlg.on("hide", handleHide);
3056 //dlg.addKeyListener(27, handleEsc);
3058 this.buttons = buttons;
3059 var bt = this.buttonText;
3060 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3065 bodyEl = dlg.bodyEl.createChild({
3067 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068 '<textarea class="roo-mb-textarea"></textarea>' +
3069 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3071 msgEl = bodyEl.dom.firstChild;
3072 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073 textboxEl.enableDisplayMode();
3074 textboxEl.addKeyListener([10,13], function(){
3075 if(dlg.isVisible() && opt && opt.buttons){
3078 }else if(opt.buttons.yes){
3079 handleButton("yes");
3083 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084 textareaEl.enableDisplayMode();
3085 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086 progressEl.enableDisplayMode();
3088 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089 //var pf = progressEl.dom.firstChild;
3091 //pp = Roo.get(pf.firstChild);
3092 //pp.setHeight(pf.offsetHeight);
3100 * Updates the message box body text
3101 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102 * the XHTML-compliant non-breaking space character '&#160;')
3103 * @return {Roo.MessageBox} This message box
3105 updateText : function(text)
3107 if(!dlg.isVisible() && !opt.width){
3108 dlg.dialogEl.setWidth(this.maxWidth);
3109 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3111 msgEl.innerHTML = text || ' ';
3113 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3116 Math.min(opt.width || cw , this.maxWidth),
3117 Math.max(opt.minWidth || this.minWidth, bwidth)
3120 activeTextEl.setWidth(w);
3122 if(dlg.isVisible()){
3123 dlg.fixedcenter = false;
3125 // to big, make it scroll. = But as usual stupid IE does not support
3128 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3132 bodyEl.dom.style.height = '';
3133 bodyEl.dom.style.overflowY = '';
3136 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3138 bodyEl.dom.style.overflowX = '';
3141 dlg.setContentSize(w, bodyEl.getHeight());
3142 if(dlg.isVisible()){
3143 dlg.fixedcenter = true;
3149 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3150 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153 * @return {Roo.MessageBox} This message box
3155 updateProgress : function(value, text){
3157 this.updateText(text);
3159 if (pp) { // weird bug on my firefox - for some reason this is not defined
3160 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3166 * Returns true if the message box is currently displayed
3167 * @return {Boolean} True if the message box is visible, else false
3169 isVisible : function(){
3170 return dlg && dlg.isVisible();
3174 * Hides the message box if it is displayed
3177 if(this.isVisible()){
3183 * Displays a new message box, or reinitializes an existing message box, based on the config options
3184 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185 * The following config object properties are supported:
3187 Property Type Description
3188 ---------- --------------- ------------------------------------------------------------------------------------
3189 animEl String/Element An id or Element from which the message box should animate as it opens and
3190 closes (defaults to undefined)
3191 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable Boolean False to hide the top-right close button (defaults to true). Note that
3194 progress and wait dialogs will ignore this property and always hide the
3195 close button as they can only be closed programmatically.
3196 cls String A custom CSS class to apply to the message box element
3197 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3198 displayed (defaults to 75)
3199 fn Function A callback function to execute after closing the dialog. The arguments to the
3200 function will be btn (the name of the button that was clicked, if applicable,
3201 e.g. "ok"), and text (the value of the active text field, if applicable).
3202 Progress and wait dialogs will ignore this option since they do not respond to
3203 user actions and can only be closed programmatically, so any required function
3204 should be called by the same code after it closes the dialog.
3205 icon String A CSS class that provides a background image to be used as an icon for
3206 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3208 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3209 modal Boolean False to allow user interaction with the page while the message box is
3210 displayed (defaults to true)
3211 msg String A string that will replace the existing message box body text (defaults
3212 to the XHTML-compliant non-breaking space character ' ')
3213 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3214 progress Boolean True to display a progress bar (defaults to false)
3215 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3218 title String The title text
3219 value String The string value to set into the active textbox element if displayed
3220 wait Boolean True to display a progress bar (defaults to false)
3221 width Number The width of the dialog in pixels
3228 msg: 'Please enter your address:',
3230 buttons: Roo.MessageBox.OKCANCEL,
3233 animEl: 'addAddressBtn'
3236 * @param {Object} config Configuration options
3237 * @return {Roo.MessageBox} This message box
3239 show : function(options)
3242 // this causes nightmares if you show one dialog after another
3243 // especially on callbacks..
3245 if(this.isVisible()){
3248 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3250 Roo.log("New Dialog Message:" + options.msg )
3251 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3255 var d = this.getDialog();
3257 d.setTitle(opt.title || " ");
3258 d.closeEl.setDisplayed(opt.closable !== false);
3259 activeTextEl = textboxEl;
3260 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3265 textareaEl.setHeight(typeof opt.multiline == "number" ?
3266 opt.multiline : this.defaultTextHeight);
3267 activeTextEl = textareaEl;
3276 progressEl.setDisplayed(opt.progress === true);
3277 this.updateProgress(0);
3278 activeTextEl.dom.value = opt.value || "";
3280 dlg.setDefaultButton(activeTextEl);
3282 var bs = opt.buttons;
3286 }else if(bs && bs.yes){
3287 db = buttons["yes"];
3289 dlg.setDefaultButton(db);
3291 bwidth = updateButtons(opt.buttons);
3292 this.updateText(opt.msg);
3294 d.el.addClass(opt.cls);
3296 d.proxyDrag = opt.proxyDrag === true;
3297 d.modal = opt.modal !== false;
3298 d.mask = opt.modal !== false ? mask : false;
3300 // force it to the end of the z-index stack so it gets a cursor in FF
3301 document.body.appendChild(dlg.el.dom);
3302 d.animateTarget = null;
3303 d.show(options.animEl);
3309 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3310 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311 * and closing the message box when the process is complete.
3312 * @param {String} title The title bar text
3313 * @param {String} msg The message box body text
3314 * @return {Roo.MessageBox} This message box
3316 progress : function(title, msg){
3323 minWidth: this.minProgressWidth,
3330 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331 * If a callback function is passed it will be called after the user clicks the button, and the
3332 * id of the button that was clicked will be passed as the only parameter to the callback
3333 * (could also be the top-right close button).
3334 * @param {String} title The title bar text
3335 * @param {String} msg The message box body text
3336 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337 * @param {Object} scope (optional) The scope of the callback function
3338 * @return {Roo.MessageBox} This message box
3340 alert : function(title, msg, fn, scope)
3355 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3356 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357 * You are responsible for closing the message box when the process is complete.
3358 * @param {String} msg The message box body text
3359 * @param {String} title (optional) The title bar text
3360 * @return {Roo.MessageBox} This message box
3362 wait : function(msg, title){
3373 waitTimer = Roo.TaskMgr.start({
3375 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3383 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386 * @param {String} title The title bar text
3387 * @param {String} msg The message box body text
3388 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389 * @param {Object} scope (optional) The scope of the callback function
3390 * @return {Roo.MessageBox} This message box
3392 confirm : function(title, msg, fn, scope){
3396 buttons: this.YESNO,
3405 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3407 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408 * (could also be the top-right close button) and the text that was entered will be passed as the two
3409 * parameters to the callback.
3410 * @param {String} title The title bar text
3411 * @param {String} msg The message box body text
3412 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413 * @param {Object} scope (optional) The scope of the callback function
3414 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416 * @return {Roo.MessageBox} This message box
3418 prompt : function(title, msg, fn, scope, multiline){
3422 buttons: this.OKCANCEL,
3427 multiline: multiline,
3434 * Button config that displays a single OK button
3439 * Button config that displays Yes and No buttons
3442 YESNO : {yes:true, no:true},
3444 * Button config that displays OK and Cancel buttons
3447 OKCANCEL : {ok:true, cancel:true},
3449 * Button config that displays Yes, No and Cancel buttons
3452 YESNOCANCEL : {yes:true, no:true, cancel:true},
3455 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3458 defaultTextHeight : 75,
3460 * The maximum width in pixels of the message box (defaults to 600)
3465 * The minimum width in pixels of the message box (defaults to 100)
3470 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3471 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3474 minProgressWidth : 250,
3476 * An object containing the default button text strings that can be overriden for localized language support.
3477 * Supported properties are: ok, cancel, yes and no.
3478 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3491 * Shorthand for {@link Roo.MessageBox}
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3503 * @class Roo.bootstrap.Navbar
3504 * @extends Roo.bootstrap.Component
3505 * Bootstrap Navbar class
3508 * Create a new Navbar
3509 * @param {Object} config The config object
3513 Roo.bootstrap.Navbar = function(config){
3514 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3518 * @event beforetoggle
3519 * Fire before toggle the menu
3520 * @param {Roo.EventObject} e
3522 "beforetoggle" : true
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3535 getAutoCreate : function(){
3538 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3542 initEvents :function ()
3544 //Roo.log(this.el.select('.navbar-toggle',true));
3545 this.el.select('.navbar-toggle',true).on('click', function() {
3546 if(this.fireEvent('beforetoggle', this) !== false){
3547 this.el.select('.navbar-collapse',true).toggleClass('in');
3557 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3559 var size = this.el.getSize();
3560 this.maskEl.setSize(size.width, size.height);
3561 this.maskEl.enableDisplayMode("block");
3570 getChildContainer : function()
3572 if (this.el.select('.collapse').getCount()) {
3573 return this.el.select('.collapse',true).first();
3606 * @class Roo.bootstrap.NavSimplebar
3607 * @extends Roo.bootstrap.Navbar
3608 * Bootstrap Sidebar class
3610 * @cfg {Boolean} inverse is inverted color
3612 * @cfg {String} type (nav | pills | tabs)
3613 * @cfg {Boolean} arrangement stacked | justified
3614 * @cfg {String} align (left | right) alignment
3616 * @cfg {Boolean} main (true|false) main nav bar? default false
3617 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3619 * @cfg {String} tag (header|footer|nav|div) default is nav
3625 * Create a new Sidebar
3626 * @param {Object} config The config object
3630 Roo.bootstrap.NavSimplebar = function(config){
3631 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3650 getAutoCreate : function(){
3654 tag : this.tag || 'div',
3667 this.type = this.type || 'nav';
3668 if (['tabs','pills'].indexOf(this.type)!==-1) {
3669 cfg.cn[0].cls += ' nav-' + this.type
3673 if (this.type!=='nav') {
3674 Roo.log('nav type must be nav/tabs/pills')
3676 cfg.cn[0].cls += ' navbar-nav'
3682 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683 cfg.cn[0].cls += ' nav-' + this.arrangement;
3687 if (this.align === 'right') {
3688 cfg.cn[0].cls += ' navbar-right';
3692 cfg.cls += ' navbar-inverse';
3719 * @class Roo.bootstrap.NavHeaderbar
3720 * @extends Roo.bootstrap.NavSimplebar
3721 * Bootstrap Sidebar class
3723 * @cfg {String} brand what is brand
3724 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725 * @cfg {String} brand_href href of the brand
3726 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3727 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3732 * Create a new Sidebar
3733 * @param {Object} config The config object
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3749 desktopCenter : false,
3752 getAutoCreate : function(){
3755 tag: this.nav || 'nav',
3762 if (this.desktopCenter) {
3763 cn.push({cls : 'container', cn : []});
3770 cls: 'navbar-header',
3775 cls: 'navbar-toggle',
3776 'data-toggle': 'collapse',
3781 html: 'Toggle navigation'
3803 cls: 'collapse navbar-collapse',
3807 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3809 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810 cfg.cls += ' navbar-' + this.position;
3812 // tag can override this..
3814 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3817 if (this.brand !== '') {
3820 href: this.brand_href ? this.brand_href : '#',
3821 cls: 'navbar-brand',
3829 cfg.cls += ' main-nav';
3837 getHeaderChildContainer : function()
3839 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840 return this.el.select('.navbar-header',true).first();
3843 return this.getChildContainer();
3847 initEvents : function()
3849 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3851 if (this.autohide) {
3856 Roo.get(document).on('scroll',function(e) {
3857 var ns = Roo.get(document).getScroll().top;
3858 var os = prevScroll;
3862 ft.removeClass('slideDown');
3863 ft.addClass('slideUp');
3866 ft.removeClass('slideUp');
3867 ft.addClass('slideDown');
3888 * @class Roo.bootstrap.NavSidebar
3889 * @extends Roo.bootstrap.Navbar
3890 * Bootstrap Sidebar class
3893 * Create a new Sidebar
3894 * @param {Object} config The config object
3898 Roo.bootstrap.NavSidebar = function(config){
3899 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3904 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3906 getAutoCreate : function(){
3911 cls: 'sidebar sidebar-nav'
3933 * @class Roo.bootstrap.NavGroup
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap NavGroup class
3936 * @cfg {String} align (left|right)
3937 * @cfg {Boolean} inverse
3938 * @cfg {String} type (nav|pills|tab) default nav
3939 * @cfg {String} navId - reference Id for navbar.
3943 * Create a new nav group
3944 * @param {Object} config The config object
3947 Roo.bootstrap.NavGroup = function(config){
3948 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3951 Roo.bootstrap.NavGroup.register(this);
3955 * Fires when the active item changes
3956 * @param {Roo.bootstrap.NavGroup} this
3957 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3976 getAutoCreate : function()
3978 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3985 if (['tabs','pills'].indexOf(this.type)!==-1) {
3986 cfg.cls += ' nav-' + this.type
3988 if (this.type!=='nav') {
3989 Roo.log('nav type must be nav/tabs/pills')
3991 cfg.cls += ' navbar-nav'
3994 if (this.parent().sidebar) {
3997 cls: 'dashboard-menu sidebar-menu'
4003 if (this.form === true) {
4009 if (this.align === 'right') {
4010 cfg.cls += ' navbar-right';
4012 cfg.cls += ' navbar-left';
4016 if (this.align === 'right') {
4017 cfg.cls += ' navbar-right';
4021 cfg.cls += ' navbar-inverse';
4029 * sets the active Navigation item
4030 * @param {Roo.bootstrap.NavItem} the new current navitem
4032 setActiveItem : function(item)
4035 Roo.each(this.navItems, function(v){
4040 v.setActive(false, true);
4047 item.setActive(true, true);
4048 this.fireEvent('changed', this, item, prev);
4053 * gets the active Navigation item
4054 * @return {Roo.bootstrap.NavItem} the current navitem
4056 getActive : function()
4060 Roo.each(this.navItems, function(v){
4071 indexOfNav : function()
4075 Roo.each(this.navItems, function(v,i){
4086 * adds a Navigation item
4087 * @param {Roo.bootstrap.NavItem} the navitem to add
4089 addItem : function(cfg)
4091 var cn = new Roo.bootstrap.NavItem(cfg);
4093 cn.parentId = this.id;
4094 cn.onRender(this.el, null);
4098 * register a Navigation item
4099 * @param {Roo.bootstrap.NavItem} the navitem to add
4101 register : function(item)
4103 this.navItems.push( item);
4104 item.navId = this.navId;
4109 * clear all the Navigation item
4112 clearAll : function()
4115 this.el.dom.innerHTML = '';
4118 getNavItem: function(tabId)
4121 Roo.each(this.navItems, function(e) {
4122 if (e.tabId == tabId) {
4132 setActiveNext : function()
4134 var i = this.indexOfNav(this.getActive());
4135 if (i > this.navItems.length) {
4138 this.setActiveItem(this.navItems[i+1]);
4140 setActivePrev : function()
4142 var i = this.indexOfNav(this.getActive());
4146 this.setActiveItem(this.navItems[i-1]);
4148 clearWasActive : function(except) {
4149 Roo.each(this.navItems, function(e) {
4150 if (e.tabId != except.tabId && e.was_active) {
4151 e.was_active = false;
4158 getWasActive : function ()
4161 Roo.each(this.navItems, function(e) {
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4180 * register a Navigation Group
4181 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4183 register : function(navgrp)
4185 this.groups[navgrp.navId] = navgrp;
4189 * fetch a Navigation Group based on the navigation ID
4190 * @param {string} the navgroup to add
4191 * @returns {Roo.bootstrap.NavGroup} the navgroup
4193 get: function(navId) {
4194 if (typeof(this.groups[navId]) == 'undefined') {
4196 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4198 return this.groups[navId] ;
4213 * @class Roo.bootstrap.NavItem
4214 * @extends Roo.bootstrap.Component
4215 * Bootstrap Navbar.NavItem class
4216 * @cfg {String} href link to
4217 * @cfg {String} html content of button
4218 * @cfg {String} badge text inside badge
4219 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220 * @cfg {String} glyphicon name of glyphicon
4221 * @cfg {String} icon name of font awesome icon
4222 * @cfg {Boolean} active Is item active
4223 * @cfg {Boolean} disabled Is item disabled
4225 * @cfg {Boolean} preventDefault (true | false) default false
4226 * @cfg {String} tabId the tab that this item activates.
4227 * @cfg {String} tagtype (a|span) render as a href or span?
4228 * @cfg {Boolean} animateRef (true|false) link to element default false
4231 * Create a new Navbar Item
4232 * @param {Object} config The config object
4234 Roo.bootstrap.NavItem = function(config){
4235 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4240 * The raw click event for the entire grid.
4241 * @param {Roo.EventObject} e
4246 * Fires when the active item active state changes
4247 * @param {Roo.bootstrap.NavItem} this
4248 * @param {boolean} state the new state
4254 * Fires when scroll to element
4255 * @param {Roo.bootstrap.NavItem} this
4256 * @param {Object} options
4257 * @param {Roo.EventObject} e
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4273 preventDefault : false,
4280 getAutoCreate : function(){
4289 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4291 if (this.disabled) {
4292 cfg.cls += ' disabled';
4295 if (this.href || this.html || this.glyphicon || this.icon) {
4299 href : this.href || "#",
4300 html: this.html || ''
4305 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4308 if(this.glyphicon) {
4309 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4314 cfg.cn[0].html += " <span class='caret'></span>";
4318 if (this.badge !== '') {
4320 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4328 initEvents: function()
4330 if (typeof (this.menu) != 'undefined') {
4331 this.menu.parentType = this.xtype;
4332 this.menu.triggerEl = this.el;
4333 this.menu = this.addxtype(Roo.apply({}, this.menu));
4336 this.el.select('a',true).on('click', this.onClick, this);
4338 if(this.tagtype == 'span'){
4339 this.el.select('span',true).on('click', this.onClick, this);
4342 // at this point parent should be available..
4343 this.parent().register(this);
4346 onClick : function(e)
4348 if (e.getTarget('.dropdown-menu-item')) {
4349 // did you click on a menu itemm.... - then don't trigger onclick..
4354 this.preventDefault ||
4357 Roo.log("NavItem - prevent Default?");
4361 if (this.disabled) {
4365 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366 if (tg && tg.transition) {
4367 Roo.log("waiting for the transitionend");
4373 //Roo.log("fire event clicked");
4374 if(this.fireEvent('click', this, e) === false){
4378 if(this.tagtype == 'span'){
4382 //Roo.log(this.href);
4383 var ael = this.el.select('a',true).first();
4386 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389 return; // ignore... - it's a 'hash' to another page.
4391 Roo.log("NavItem - prevent Default?");
4393 this.scrollToElement(e);
4397 var p = this.parent();
4399 if (['tabs','pills'].indexOf(p.type)!==-1) {
4400 if (typeof(p.setActiveItem) !== 'undefined') {
4401 p.setActiveItem(this);
4405 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407 // remove the collapsed menu expand...
4408 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4412 isActive: function () {
4415 setActive : function(state, fire, is_was_active)
4417 if (this.active && !state && this.navId) {
4418 this.was_active = true;
4419 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421 nv.clearWasActive(this);
4425 this.active = state;
4428 this.el.removeClass('active');
4429 } else if (!this.el.hasClass('active')) {
4430 this.el.addClass('active');
4433 this.fireEvent('changed', this, state);
4436 // show a panel if it's registered and related..
4438 if (!this.navId || !this.tabId || !state || is_was_active) {
4442 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4446 var pan = tg.getPanelByName(this.tabId);
4450 // if we can not flip to new panel - go back to old nav highlight..
4451 if (false == tg.showPanel(pan)) {
4452 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454 var onav = nv.getWasActive();
4456 onav.setActive(true, false, true);
4465 // this should not be here...
4466 setDisabled : function(state)
4468 this.disabled = state;
4470 this.el.removeClass('disabled');
4471 } else if (!this.el.hasClass('disabled')) {
4472 this.el.addClass('disabled');
4478 * Fetch the element to display the tooltip on.
4479 * @return {Roo.Element} defaults to this.el
4481 tooltipEl : function()
4483 return this.el.select('' + this.tagtype + '', true).first();
4486 scrollToElement : function(e)
4488 var c = document.body;
4491 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4493 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494 c = document.documentElement;
4497 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4503 var o = target.calcOffsetsTo(c);
4510 this.fireEvent('scrollto', this, options, e);
4512 Roo.get(c).scrollTo('top', options.value, true);
4525 * <span> icon </span>
4526 * <span> text </span>
4527 * <span>badge </span>
4531 * @class Roo.bootstrap.NavSidebarItem
4532 * @extends Roo.bootstrap.NavItem
4533 * Bootstrap Navbar.NavSidebarItem class
4534 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535 * {bool} open is the menu open
4537 * Create a new Navbar Button
4538 * @param {Object} config The config object
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4546 * The raw click event for the entire grid.
4547 * @param {Roo.EventObject} e
4552 * Fires when the active item active state changes
4553 * @param {Roo.bootstrap.NavSidebarItem} this
4554 * @param {boolean} state the new state
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4564 badgeWeight : 'default',
4568 getAutoCreate : function(){
4573 href : this.href || '#',
4585 html : this.html || ''
4590 cfg.cls += ' active';
4593 if (this.disabled) {
4594 cfg.cls += ' disabled';
4597 cfg.cls += ' open x-open';
4600 if (this.glyphicon || this.icon) {
4601 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4602 a.cn.push({ tag : 'i', cls : c }) ;
4607 if (this.badge !== '') {
4609 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4613 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614 a.cls += 'dropdown-toggle treeview' ;
4622 initEvents : function()
4624 if (typeof (this.menu) != 'undefined') {
4625 this.menu.parentType = this.xtype;
4626 this.menu.triggerEl = this.el;
4627 this.menu = this.addxtype(Roo.apply({}, this.menu));
4630 this.el.on('click', this.onClick, this);
4633 if(this.badge !== ''){
4635 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4640 onClick : function(e)
4647 if(this.preventDefault){
4651 this.fireEvent('click', this);
4654 disable : function()
4656 this.setDisabled(true);
4661 this.setDisabled(false);
4664 setDisabled : function(state)
4666 if(this.disabled == state){
4670 this.disabled = state;
4673 this.el.addClass('disabled');
4677 this.el.removeClass('disabled');
4682 setActive : function(state)
4684 if(this.active == state){
4688 this.active = state;
4691 this.el.addClass('active');
4695 this.el.removeClass('active');
4700 isActive: function ()
4705 setBadge : function(str)
4711 this.badgeEl.dom.innerHTML = str;
4728 * @class Roo.bootstrap.Row
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Row class (contains columns...)
4734 * @param {Object} config The config object
4737 Roo.bootstrap.Row = function(config){
4738 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4743 getAutoCreate : function(){
4762 * @class Roo.bootstrap.Element
4763 * @extends Roo.bootstrap.Component
4764 * Bootstrap Element class
4765 * @cfg {String} html contents of the element
4766 * @cfg {String} tag tag of the element
4767 * @cfg {String} cls class of the element
4768 * @cfg {Boolean} preventDefault (true|false) default false
4769 * @cfg {Boolean} clickable (true|false) default false
4772 * Create a new Element
4773 * @param {Object} config The config object
4776 Roo.bootstrap.Element = function(config){
4777 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4783 * When a element is chick
4784 * @param {Roo.bootstrap.Element} this
4785 * @param {Roo.EventObject} e
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4796 preventDefault: false,
4799 getAutoCreate : function(){
4810 initEvents: function()
4812 Roo.bootstrap.Element.superclass.initEvents.call(this);
4815 this.el.on('click', this.onClick, this);
4820 onClick : function(e)
4822 if(this.preventDefault){
4826 this.fireEvent('click', this, e);
4829 getValue : function()
4831 return this.el.dom.innerHTML;
4834 setValue : function(value)
4836 this.el.dom.innerHTML = value;
4851 * @class Roo.bootstrap.Pagination
4852 * @extends Roo.bootstrap.Component
4853 * Bootstrap Pagination class
4854 * @cfg {String} size xs | sm | md | lg
4855 * @cfg {Boolean} inverse false | true
4858 * Create a new Pagination
4859 * @param {Object} config The config object
4862 Roo.bootstrap.Pagination = function(config){
4863 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4872 getAutoCreate : function(){
4878 cfg.cls += ' inverse';
4884 cfg.cls += " " + this.cls;
4902 * @class Roo.bootstrap.PaginationItem
4903 * @extends Roo.bootstrap.Component
4904 * Bootstrap PaginationItem class
4905 * @cfg {String} html text
4906 * @cfg {String} href the link
4907 * @cfg {Boolean} preventDefault (true | false) default true
4908 * @cfg {Boolean} active (true | false) default false
4909 * @cfg {Boolean} disabled default false
4913 * Create a new PaginationItem
4914 * @param {Object} config The config object
4918 Roo.bootstrap.PaginationItem = function(config){
4919 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4924 * The raw click event for the entire grid.
4925 * @param {Roo.EventObject} e
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4935 preventDefault: true,
4940 getAutoCreate : function(){
4946 href : this.href ? this.href : '#',
4947 html : this.html ? this.html : ''
4957 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4961 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4967 initEvents: function() {
4969 this.el.on('click', this.onClick, this);
4972 onClick : function(e)
4974 Roo.log('PaginationItem on click ');
4975 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4999 * @class Roo.bootstrap.Slider
5000 * @extends Roo.bootstrap.Component
5001 * Bootstrap Slider class
5004 * Create a new Slider
5005 * @param {Object} config The config object
5008 Roo.bootstrap.Slider = function(config){
5009 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5014 getAutoCreate : function(){
5018 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5022 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5034 * Ext JS Library 1.1.1
5035 * Copyright(c) 2006-2007, Ext JS, LLC.
5037 * Originally Released Under LGPL - original licence link has changed is not relivant.
5040 * <script type="text/javascript">
5045 * @class Roo.grid.ColumnModel
5046 * @extends Roo.util.Observable
5047 * This is the default implementation of a ColumnModel used by the Grid. It defines
5048 * the columns in the grid.
5051 var colModel = new Roo.grid.ColumnModel([
5052 {header: "Ticker", width: 60, sortable: true, locked: true},
5053 {header: "Company Name", width: 150, sortable: true},
5054 {header: "Market Cap.", width: 100, sortable: true},
5055 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056 {header: "Employees", width: 100, sortable: true, resizable: false}
5061 * The config options listed for this class are options which may appear in each
5062 * individual column definition.
5063 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5065 * @param {Object} config An Array of column config objects. See this class's
5066 * config objects for details.
5068 Roo.grid.ColumnModel = function(config){
5070 * The config passed into the constructor
5072 this.config = config;
5075 // if no id, create one
5076 // if the column does not have a dataIndex mapping,
5077 // map it to the order it is in the config
5078 for(var i = 0, len = config.length; i < len; i++){
5080 if(typeof c.dataIndex == "undefined"){
5083 if(typeof c.renderer == "string"){
5084 c.renderer = Roo.util.Format[c.renderer];
5086 if(typeof c.id == "undefined"){
5089 if(c.editor && c.editor.xtype){
5090 c.editor = Roo.factory(c.editor, Roo.grid);
5092 if(c.editor && c.editor.isFormField){
5093 c.editor = new Roo.grid.GridEditor(c.editor);
5095 this.lookup[c.id] = c;
5099 * The width of columns which have no width specified (defaults to 100)
5102 this.defaultWidth = 100;
5105 * Default sortable of columns which have no sortable specified (defaults to false)
5108 this.defaultSortable = false;
5112 * @event widthchange
5113 * Fires when the width of a column changes.
5114 * @param {ColumnModel} this
5115 * @param {Number} columnIndex The column index
5116 * @param {Number} newWidth The new width
5118 "widthchange": true,
5120 * @event headerchange
5121 * Fires when the text of a header changes.
5122 * @param {ColumnModel} this
5123 * @param {Number} columnIndex The column index
5124 * @param {Number} newText The new header text
5126 "headerchange": true,
5128 * @event hiddenchange
5129 * Fires when a column is hidden or "unhidden".
5130 * @param {ColumnModel} this
5131 * @param {Number} columnIndex The column index
5132 * @param {Boolean} hidden true if hidden, false otherwise
5134 "hiddenchange": true,
5136 * @event columnmoved
5137 * Fires when a column is moved.
5138 * @param {ColumnModel} this
5139 * @param {Number} oldIndex
5140 * @param {Number} newIndex
5142 "columnmoved" : true,
5144 * @event columlockchange
5145 * Fires when a column's locked state is changed
5146 * @param {ColumnModel} this
5147 * @param {Number} colIndex
5148 * @param {Boolean} locked true if locked
5150 "columnlockchange" : true
5152 Roo.grid.ColumnModel.superclass.constructor.call(this);
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5156 * @cfg {String} header The header text to display in the Grid view.
5159 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161 * specified, the column's index is used as an index into the Record's data Array.
5164 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5168 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169 * Defaults to the value of the {@link #defaultSortable} property.
5170 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5173 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5176 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5179 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5182 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5185 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5191 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5194 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5197 * @cfg {String} cursor (Optional)
5200 * @cfg {String} tooltip (Optional)
5203 * @cfg {Number} xs (Optional)
5206 * @cfg {Number} sm (Optional)
5209 * @cfg {Number} md (Optional)
5212 * @cfg {Number} lg (Optional)
5215 * Returns the id of the column at the specified index.
5216 * @param {Number} index The column index
5217 * @return {String} the id
5219 getColumnId : function(index){
5220 return this.config[index].id;
5224 * Returns the column for a specified id.
5225 * @param {String} id The column id
5226 * @return {Object} the column
5228 getColumnById : function(id){
5229 return this.lookup[id];
5234 * Returns the column for a specified dataIndex.
5235 * @param {String} dataIndex The column dataIndex
5236 * @return {Object|Boolean} the column or false if not found
5238 getColumnByDataIndex: function(dataIndex){
5239 var index = this.findColumnIndex(dataIndex);
5240 return index > -1 ? this.config[index] : false;
5244 * Returns the index for a specified column id.
5245 * @param {String} id The column id
5246 * @return {Number} the index, or -1 if not found
5248 getIndexById : function(id){
5249 for(var i = 0, len = this.config.length; i < len; i++){
5250 if(this.config[i].id == id){
5258 * Returns the index for a specified column dataIndex.
5259 * @param {String} dataIndex The column dataIndex
5260 * @return {Number} the index, or -1 if not found
5263 findColumnIndex : function(dataIndex){
5264 for(var i = 0, len = this.config.length; i < len; i++){
5265 if(this.config[i].dataIndex == dataIndex){
5273 moveColumn : function(oldIndex, newIndex){
5274 var c = this.config[oldIndex];
5275 this.config.splice(oldIndex, 1);
5276 this.config.splice(newIndex, 0, c);
5277 this.dataMap = null;
5278 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5281 isLocked : function(colIndex){
5282 return this.config[colIndex].locked === true;
5285 setLocked : function(colIndex, value, suppressEvent){
5286 if(this.isLocked(colIndex) == value){
5289 this.config[colIndex].locked = value;
5291 this.fireEvent("columnlockchange", this, colIndex, value);
5295 getTotalLockedWidth : function(){
5297 for(var i = 0; i < this.config.length; i++){
5298 if(this.isLocked(i) && !this.isHidden(i)){
5299 this.totalWidth += this.getColumnWidth(i);
5305 getLockedCount : function(){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 if(!this.isLocked(i)){
5312 return this.config.length;
5316 * Returns the number of columns.
5319 getColumnCount : function(visibleOnly){
5320 if(visibleOnly === true){
5322 for(var i = 0, len = this.config.length; i < len; i++){
5323 if(!this.isHidden(i)){
5329 return this.config.length;
5333 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334 * @param {Function} fn
5335 * @param {Object} scope (optional)
5336 * @return {Array} result
5338 getColumnsBy : function(fn, scope){
5340 for(var i = 0, len = this.config.length; i < len; i++){
5341 var c = this.config[i];
5342 if(fn.call(scope||this, c, i) === true){
5350 * Returns true if the specified column is sortable.
5351 * @param {Number} col The column index
5354 isSortable : function(col){
5355 if(typeof this.config[col].sortable == "undefined"){
5356 return this.defaultSortable;
5358 return this.config[col].sortable;
5362 * Returns the rendering (formatting) function defined for the column.
5363 * @param {Number} col The column index.
5364 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5366 getRenderer : function(col){
5367 if(!this.config[col].renderer){
5368 return Roo.grid.ColumnModel.defaultRenderer;
5370 return this.config[col].renderer;
5374 * Sets the rendering (formatting) function for a column.
5375 * @param {Number} col The column index
5376 * @param {Function} fn The function to use to process the cell's raw data
5377 * to return HTML markup for the grid view. The render function is called with
5378 * the following parameters:<ul>
5379 * <li>Data value.</li>
5380 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381 * <li>css A CSS style string to apply to the table cell.</li>
5382 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384 * <li>Row index</li>
5385 * <li>Column index</li>
5386 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5388 setRenderer : function(col, fn){
5389 this.config[col].renderer = fn;
5393 * Returns the width for the specified column.
5394 * @param {Number} col The column index
5397 getColumnWidth : function(col){
5398 return this.config[col].width * 1 || this.defaultWidth;
5402 * Sets the width for a column.
5403 * @param {Number} col The column index
5404 * @param {Number} width The new width
5406 setColumnWidth : function(col, width, suppressEvent){
5407 this.config[col].width = width;
5408 this.totalWidth = null;
5410 this.fireEvent("widthchange", this, col, width);
5415 * Returns the total width of all columns.
5416 * @param {Boolean} includeHidden True to include hidden column widths
5419 getTotalWidth : function(includeHidden){
5420 if(!this.totalWidth){
5421 this.totalWidth = 0;
5422 for(var i = 0, len = this.config.length; i < len; i++){
5423 if(includeHidden || !this.isHidden(i)){
5424 this.totalWidth += this.getColumnWidth(i);
5428 return this.totalWidth;
5432 * Returns the header for the specified column.
5433 * @param {Number} col The column index
5436 getColumnHeader : function(col){
5437 return this.config[col].header;
5441 * Sets the header for a column.
5442 * @param {Number} col The column index
5443 * @param {String} header The new header
5445 setColumnHeader : function(col, header){
5446 this.config[col].header = header;
5447 this.fireEvent("headerchange", this, col, header);
5451 * Returns the tooltip for the specified column.
5452 * @param {Number} col The column index
5455 getColumnTooltip : function(col){
5456 return this.config[col].tooltip;
5459 * Sets the tooltip for a column.
5460 * @param {Number} col The column index
5461 * @param {String} tooltip The new tooltip
5463 setColumnTooltip : function(col, tooltip){
5464 this.config[col].tooltip = tooltip;
5468 * Returns the dataIndex for the specified column.
5469 * @param {Number} col The column index
5472 getDataIndex : function(col){
5473 return this.config[col].dataIndex;
5477 * Sets the dataIndex for a column.
5478 * @param {Number} col The column index
5479 * @param {Number} dataIndex The new dataIndex
5481 setDataIndex : function(col, dataIndex){
5482 this.config[col].dataIndex = dataIndex;
5488 * Returns true if the cell is editable.
5489 * @param {Number} colIndex The column index
5490 * @param {Number} rowIndex The row index - this is nto actually used..?
5493 isCellEditable : function(colIndex, rowIndex){
5494 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5498 * Returns the editor defined for the cell/column.
5499 * return false or null to disable editing.
5500 * @param {Number} colIndex The column index
5501 * @param {Number} rowIndex The row index
5504 getCellEditor : function(colIndex, rowIndex){
5505 return this.config[colIndex].editor;
5509 * Sets if a column is editable.
5510 * @param {Number} col The column index
5511 * @param {Boolean} editable True if the column is editable
5513 setEditable : function(col, editable){
5514 this.config[col].editable = editable;
5519 * Returns true if the column is hidden.
5520 * @param {Number} colIndex The column index
5523 isHidden : function(colIndex){
5524 return this.config[colIndex].hidden;
5529 * Returns true if the column width cannot be changed
5531 isFixed : function(colIndex){
5532 return this.config[colIndex].fixed;
5536 * Returns true if the column can be resized
5539 isResizable : function(colIndex){
5540 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5543 * Sets if a column is hidden.
5544 * @param {Number} colIndex The column index
5545 * @param {Boolean} hidden True if the column is hidden
5547 setHidden : function(colIndex, hidden){
5548 this.config[colIndex].hidden = hidden;
5549 this.totalWidth = null;
5550 this.fireEvent("hiddenchange", this, colIndex, hidden);
5554 * Sets the editor for a column.
5555 * @param {Number} col The column index
5556 * @param {Object} editor The editor object
5558 setEditor : function(col, editor){
5559 this.config[col].editor = editor;
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5565 if(typeof value == "object") {
5568 if(typeof value == "string" && value.length < 1){
5572 return String.format("{0}", value);
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5579 * Ext JS Library 1.1.1
5580 * Copyright(c) 2006-2007, Ext JS, LLC.
5582 * Originally Released Under LGPL - original licence link has changed is not relivant.
5585 * <script type="text/javascript">
5589 * @class Roo.LoadMask
5590 * A simple utility class for generically masking elements while loading data. If the element being masked has
5591 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5593 * element's UpdateManager load indicator and will be destroyed after the initial load.
5595 * Create a new LoadMask
5596 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597 * @param {Object} config The config object
5599 Roo.LoadMask = function(el, config){
5600 this.el = Roo.get(el);
5601 Roo.apply(this, config);
5603 this.store.on('beforeload', this.onBeforeLoad, this);
5604 this.store.on('load', this.onLoad, this);
5605 this.store.on('loadexception', this.onLoadException, this);
5606 this.removeMask = false;
5608 var um = this.el.getUpdateManager();
5609 um.showLoadIndicator = false; // disable the default indicator
5610 um.on('beforeupdate', this.onBeforeLoad, this);
5611 um.on('update', this.onLoad, this);
5612 um.on('failure', this.onLoad, this);
5613 this.removeMask = true;
5617 Roo.LoadMask.prototype = {
5619 * @cfg {Boolean} removeMask
5620 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5625 * The text to display in a centered loading message box (defaults to 'Loading...')
5629 * @cfg {String} msgCls
5630 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5632 msgCls : 'x-mask-loading',
5635 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5641 * Disables the mask to prevent it from being displayed
5643 disable : function(){
5644 this.disabled = true;
5648 * Enables the mask so that it can be displayed
5650 enable : function(){
5651 this.disabled = false;
5654 onLoadException : function()
5658 if (typeof(arguments[3]) != 'undefined') {
5659 Roo.MessageBox.alert("Error loading",arguments[3]);
5663 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5673 this.el.unmask(this.removeMask);
5678 this.el.unmask(this.removeMask);
5682 onBeforeLoad : function(){
5684 this.el.mask(this.msg, this.msgCls);
5689 destroy : function(){
5691 this.store.un('beforeload', this.onBeforeLoad, this);
5692 this.store.un('load', this.onLoad, this);
5693 this.store.un('loadexception', this.onLoadException, this);
5695 var um = this.el.getUpdateManager();
5696 um.un('beforeupdate', this.onBeforeLoad, this);
5697 um.un('update', this.onLoad, this);
5698 um.un('failure', this.onLoad, this);
5709 * @class Roo.bootstrap.Table
5710 * @extends Roo.bootstrap.Component
5711 * Bootstrap Table class
5712 * @cfg {String} cls table class
5713 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714 * @cfg {String} bgcolor Specifies the background color for a table
5715 * @cfg {Number} border Specifies whether the table cells should have borders or not
5716 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717 * @cfg {Number} cellspacing Specifies the space between cells
5718 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720 * @cfg {String} sortable Specifies that the table should be sortable
5721 * @cfg {String} summary Specifies a summary of the content of a table
5722 * @cfg {Number} width Specifies the width of a table
5723 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5725 * @cfg {boolean} striped Should the rows be alternative striped
5726 * @cfg {boolean} bordered Add borders to the table
5727 * @cfg {boolean} hover Add hover highlighting
5728 * @cfg {boolean} condensed Format condensed
5729 * @cfg {boolean} responsive Format condensed
5730 * @cfg {Boolean} loadMask (true|false) default false
5731 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733 * @cfg {Boolean} rowSelection (true|false) default false
5734 * @cfg {Boolean} cellSelection (true|false) default false
5735 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5740 * Create a new Table
5741 * @param {Object} config The config object
5744 Roo.bootstrap.Table = function(config){
5745 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5750 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5755 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5757 this.sm.grid = this;
5758 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759 this.sm = this.selModel;
5760 this.sm.xmodule = this.xmodule || false;
5763 if (this.cm && typeof(this.cm.config) == 'undefined') {
5764 this.colModel = new Roo.grid.ColumnModel(this.cm);
5765 this.cm = this.colModel;
5766 this.cm.xmodule = this.xmodule || false;
5769 this.store= Roo.factory(this.store, Roo.data);
5770 this.ds = this.store;
5771 this.ds.xmodule = this.xmodule || false;
5774 if (this.footer && this.store) {
5775 this.footer.dataSource = this.ds;
5776 this.footer = Roo.factory(this.footer);
5783 * Fires when a cell is clicked
5784 * @param {Roo.bootstrap.Table} this
5785 * @param {Roo.Element} el
5786 * @param {Number} rowIndex
5787 * @param {Number} columnIndex
5788 * @param {Roo.EventObject} e
5792 * @event celldblclick
5793 * Fires when a cell is double clicked
5794 * @param {Roo.bootstrap.Table} this
5795 * @param {Roo.Element} el
5796 * @param {Number} rowIndex
5797 * @param {Number} columnIndex
5798 * @param {Roo.EventObject} e
5800 "celldblclick" : true,
5803 * Fires when a row is clicked
5804 * @param {Roo.bootstrap.Table} this
5805 * @param {Roo.Element} el
5806 * @param {Number} rowIndex
5807 * @param {Roo.EventObject} e
5811 * @event rowdblclick
5812 * Fires when a row is double clicked
5813 * @param {Roo.bootstrap.Table} this
5814 * @param {Roo.Element} el
5815 * @param {Number} rowIndex
5816 * @param {Roo.EventObject} e
5818 "rowdblclick" : true,
5821 * Fires when a mouseover occur
5822 * @param {Roo.bootstrap.Table} this
5823 * @param {Roo.Element} el
5824 * @param {Number} rowIndex
5825 * @param {Number} columnIndex
5826 * @param {Roo.EventObject} e
5831 * Fires when a mouseout occur
5832 * @param {Roo.bootstrap.Table} this
5833 * @param {Roo.Element} el
5834 * @param {Number} rowIndex
5835 * @param {Number} columnIndex
5836 * @param {Roo.EventObject} e
5841 * Fires when a row is rendered, so you can change add a style to it.
5842 * @param {Roo.bootstrap.Table} this
5843 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5847 * @event rowsrendered
5848 * Fires when all the rows have been rendered
5849 * @param {Roo.bootstrap.Table} this
5851 'rowsrendered' : true,
5853 * @event contextmenu
5854 * The raw contextmenu event for the entire grid.
5855 * @param {Roo.EventObject} e
5857 "contextmenu" : true,
5859 * @event rowcontextmenu
5860 * Fires when a row is right clicked
5861 * @param {Roo.bootstrap.Table} this
5862 * @param {Number} rowIndex
5863 * @param {Roo.EventObject} e
5865 "rowcontextmenu" : true,
5867 * @event cellcontextmenu
5868 * Fires when a cell is right clicked
5869 * @param {Roo.bootstrap.Table} this
5870 * @param {Number} rowIndex
5871 * @param {Number} cellIndex
5872 * @param {Roo.EventObject} e
5874 "cellcontextmenu" : true,
5876 * @event headercontextmenu
5877 * Fires when a header is right clicked
5878 * @param {Roo.bootstrap.Table} this
5879 * @param {Number} columnIndex
5880 * @param {Roo.EventObject} e
5882 "headercontextmenu" : true
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5912 rowSelection : false,
5913 cellSelection : false,
5916 // Roo.Element - the tbody
5918 // Roo.Element - thead element
5921 container: false, // used by gridpanel...
5923 getAutoCreate : function()
5925 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5932 if (this.scrollBody) {
5933 cfg.cls += ' table-body-fixed';
5936 cfg.cls += ' table-striped';
5940 cfg.cls += ' table-hover';
5942 if (this.bordered) {
5943 cfg.cls += ' table-bordered';
5945 if (this.condensed) {
5946 cfg.cls += ' table-condensed';
5948 if (this.responsive) {
5949 cfg.cls += ' table-responsive';
5953 cfg.cls+= ' ' +this.cls;
5956 // this lot should be simplifed...
5959 cfg.align=this.align;
5962 cfg.bgcolor=this.bgcolor;
5965 cfg.border=this.border;
5967 if (this.cellpadding) {
5968 cfg.cellpadding=this.cellpadding;
5970 if (this.cellspacing) {
5971 cfg.cellspacing=this.cellspacing;
5974 cfg.frame=this.frame;
5977 cfg.rules=this.rules;
5979 if (this.sortable) {
5980 cfg.sortable=this.sortable;
5983 cfg.summary=this.summary;
5986 cfg.width=this.width;
5989 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5992 if(this.store || this.cm){
5993 if(this.headerShow){
5994 cfg.cn.push(this.renderHeader());
5997 cfg.cn.push(this.renderBody());
5999 if(this.footerShow){
6000 cfg.cn.push(this.renderFooter());
6002 // where does this come from?
6003 //cfg.cls+= ' TableGrid';
6006 return { cn : [ cfg ] };
6009 initEvents : function()
6011 if(!this.store || !this.cm){
6014 if (this.selModel) {
6015 this.selModel.initEvents();
6019 //Roo.log('initEvents with ds!!!!');
6021 this.mainBody = this.el.select('tbody', true).first();
6022 this.mainHead = this.el.select('thead', true).first();
6029 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030 e.on('click', _this.sort, _this);
6033 this.mainBody.on("click", this.onClick, this);
6034 this.mainBody.on("dblclick", this.onDblClick, this);
6036 // why is this done????? = it breaks dialogs??
6037 //this.parent().el.setStyle('position', 'relative');
6041 this.footer.parentId = this.id;
6042 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6045 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6047 this.store.on('load', this.onLoad, this);
6048 this.store.on('beforeload', this.onBeforeLoad, this);
6049 this.store.on('update', this.onUpdate, this);
6050 this.store.on('add', this.onAdd, this);
6051 this.store.on("clear", this.clear, this);
6053 this.el.on("contextmenu", this.onContextMenu, this);
6055 this.mainBody.on('scroll', this.onBodyScroll, this);
6060 onContextMenu : function(e, t)
6062 this.processEvent("contextmenu", e);
6065 processEvent : function(name, e)
6067 if (name != 'touchstart' ) {
6068 this.fireEvent(name, e);
6071 var t = e.getTarget();
6073 var cell = Roo.get(t);
6079 if(cell.findParent('tfoot', false, true)){
6083 if(cell.findParent('thead', false, true)){
6085 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086 cell = Roo.get(t).findParent('th', false, true);
6088 Roo.log("failed to find th in thead?");
6089 Roo.log(e.getTarget());
6094 var cellIndex = cell.dom.cellIndex;
6096 var ename = name == 'touchstart' ? 'click' : name;
6097 this.fireEvent("header" + ename, this, cellIndex, e);
6102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103 cell = Roo.get(t).findParent('td', false, true);
6105 Roo.log("failed to find th in tbody?");
6106 Roo.log(e.getTarget());
6111 var row = cell.findParent('tr', false, true);
6112 var cellIndex = cell.dom.cellIndex;
6113 var rowIndex = row.dom.rowIndex - 1;
6117 this.fireEvent("row" + name, this, rowIndex, e);
6121 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6127 onMouseover : function(e, el)
6129 var cell = Roo.get(el);
6135 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136 cell = cell.findParent('td', false, true);
6139 var row = cell.findParent('tr', false, true);
6140 var cellIndex = cell.dom.cellIndex;
6141 var rowIndex = row.dom.rowIndex - 1; // start from 0
6143 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6147 onMouseout : function(e, el)
6149 var cell = Roo.get(el);
6155 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156 cell = cell.findParent('td', false, true);
6159 var row = cell.findParent('tr', false, true);
6160 var cellIndex = cell.dom.cellIndex;
6161 var rowIndex = row.dom.rowIndex - 1; // start from 0
6163 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6167 onClick : function(e, el)
6169 var cell = Roo.get(el);
6171 if(!cell || (!this.cellSelection && !this.rowSelection)){
6175 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176 cell = cell.findParent('td', false, true);
6179 if(!cell || typeof(cell) == 'undefined'){
6183 var row = cell.findParent('tr', false, true);
6185 if(!row || typeof(row) == 'undefined'){
6189 var cellIndex = cell.dom.cellIndex;
6190 var rowIndex = this.getRowIndex(row);
6192 // why??? - should these not be based on SelectionModel?
6193 if(this.cellSelection){
6194 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6197 if(this.rowSelection){
6198 this.fireEvent('rowclick', this, row, rowIndex, e);
6204 onDblClick : function(e,el)
6206 var cell = Roo.get(el);
6208 if(!cell || (!this.cellSelection && !this.rowSelection)){
6212 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213 cell = cell.findParent('td', false, true);
6216 if(!cell || typeof(cell) == 'undefined'){
6220 var row = cell.findParent('tr', false, true);
6222 if(!row || typeof(row) == 'undefined'){
6226 var cellIndex = cell.dom.cellIndex;
6227 var rowIndex = this.getRowIndex(row);
6229 if(this.cellSelection){
6230 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6233 if(this.rowSelection){
6234 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6238 sort : function(e,el)
6240 var col = Roo.get(el);
6242 if(!col.hasClass('sortable')){
6246 var sort = col.attr('sort');
6249 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6253 this.store.sortInfo = {field : sort, direction : dir};
6256 Roo.log("calling footer first");
6257 this.footer.onClick('first');
6260 this.store.load({ params : { start : 0 } });
6264 renderHeader : function()
6272 this.totalWidth = 0;
6274 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6276 var config = cm.config[i];
6281 html: cm.getColumnHeader(i)
6286 if(typeof(config.sortable) != 'undefined' && config.sortable){
6288 c.html = '<i class="glyphicon"></i>' + c.html;
6291 if(typeof(config.lgHeader) != 'undefined'){
6292 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6295 if(typeof(config.mdHeader) != 'undefined'){
6296 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6299 if(typeof(config.smHeader) != 'undefined'){
6300 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6303 if(typeof(config.xsHeader) != 'undefined'){
6304 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6311 if(typeof(config.tooltip) != 'undefined'){
6312 c.tooltip = config.tooltip;
6315 if(typeof(config.colspan) != 'undefined'){
6316 c.colspan = config.colspan;
6319 if(typeof(config.hidden) != 'undefined' && config.hidden){
6320 c.style += ' display:none;';
6323 if(typeof(config.dataIndex) != 'undefined'){
6324 c.sort = config.dataIndex;
6329 if(typeof(config.align) != 'undefined' && config.align.length){
6330 c.style += ' text-align:' + config.align + ';';
6333 if(typeof(config.width) != 'undefined'){
6334 c.style += ' width:' + config.width + 'px;';
6335 this.totalWidth += config.width;
6337 this.totalWidth += 100; // assume minimum of 100 per column?
6340 if(typeof(config.cls) != 'undefined'){
6341 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6344 ['xs','sm','md','lg'].map(function(size){
6346 if(typeof(config[size]) == 'undefined'){
6350 if (!config[size]) { // 0 = hidden
6351 c.cls += ' hidden-' + size;
6355 c.cls += ' col-' + size + '-' + config[size];
6365 renderBody : function()
6375 colspan : this.cm.getColumnCount()
6385 renderFooter : function()
6395 colspan : this.cm.getColumnCount()
6409 // Roo.log('ds onload');
6414 var ds = this.store;
6416 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418 if (_this.store.sortInfo) {
6420 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421 e.select('i', true).addClass(['glyphicon-arrow-up']);
6424 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425 e.select('i', true).addClass(['glyphicon-arrow-down']);
6430 var tbody = this.mainBody;
6432 if(ds.getCount() > 0){
6433 ds.data.each(function(d,rowIndex){
6434 var row = this.renderRow(cm, ds, rowIndex);
6436 tbody.createChild(row);
6440 if(row.cellObjects.length){
6441 Roo.each(row.cellObjects, function(r){
6442 _this.renderCellObject(r);
6449 Roo.each(this.el.select('tbody td', true).elements, function(e){
6450 e.on('mouseover', _this.onMouseover, _this);
6453 Roo.each(this.el.select('tbody td', true).elements, function(e){
6454 e.on('mouseout', _this.onMouseout, _this);
6456 this.fireEvent('rowsrendered', this);
6457 //if(this.loadMask){
6458 // this.maskEl.hide();
6465 onUpdate : function(ds,record)
6467 this.refreshRow(record);
6471 onRemove : function(ds, record, index, isUpdate){
6472 if(isUpdate !== true){
6473 this.fireEvent("beforerowremoved", this, index, record);
6475 var bt = this.mainBody.dom;
6477 var rows = this.el.select('tbody > tr', true).elements;
6479 if(typeof(rows[index]) != 'undefined'){
6480 bt.removeChild(rows[index].dom);
6483 // if(bt.rows[index]){
6484 // bt.removeChild(bt.rows[index]);
6487 if(isUpdate !== true){
6488 //this.stripeRows(index);
6489 //this.syncRowHeights(index, index);
6491 this.fireEvent("rowremoved", this, index, record);
6495 onAdd : function(ds, records, rowIndex)
6497 //Roo.log('on Add called');
6498 // - note this does not handle multiple adding very well..
6499 var bt = this.mainBody.dom;
6500 for (var i =0 ; i < records.length;i++) {
6501 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502 //Roo.log(records[i]);
6503 //Roo.log(this.store.getAt(rowIndex+i));
6504 this.insertRow(this.store, rowIndex + i, false);
6511 refreshRow : function(record){
6512 var ds = this.store, index;
6513 if(typeof record == 'number'){
6515 record = ds.getAt(index);
6517 index = ds.indexOf(record);
6519 this.insertRow(ds, index, true);
6521 this.onRemove(ds, record, index+1, true);
6523 //this.syncRowHeights(index, index);
6525 this.fireEvent("rowupdated", this, index, record);
6528 insertRow : function(dm, rowIndex, isUpdate){
6531 this.fireEvent("beforerowsinserted", this, rowIndex);
6533 //var s = this.getScrollState();
6534 var row = this.renderRow(this.cm, this.store, rowIndex);
6535 // insert before rowIndex..
6536 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6540 if(row.cellObjects.length){
6541 Roo.each(row.cellObjects, function(r){
6542 _this.renderCellObject(r);
6547 this.fireEvent("rowsinserted", this, rowIndex);
6548 //this.syncRowHeights(firstRow, lastRow);
6549 //this.stripeRows(firstRow);
6556 getRowDom : function(rowIndex)
6558 var rows = this.el.select('tbody > tr', true).elements;
6560 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6563 // returns the object tree for a tr..
6566 renderRow : function(cm, ds, rowIndex)
6569 var d = ds.getAt(rowIndex);
6576 var cellObjects = [];
6578 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579 var config = cm.config[i];
6581 var renderer = cm.getRenderer(i);
6585 if(typeof(renderer) !== 'undefined'){
6586 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6588 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589 // and are rendered into the cells after the row is rendered - using the id for the element.
6591 if(typeof(value) === 'object'){
6601 rowIndex : rowIndex,
6606 this.fireEvent('rowclass', this, rowcfg);
6610 cls : rowcfg.rowClass,
6612 html: (typeof(value) === 'object') ? '' : value
6619 if(typeof(config.colspan) != 'undefined'){
6620 td.colspan = config.colspan;
6623 if(typeof(config.hidden) != 'undefined' && config.hidden){
6624 td.style += ' display:none;';
6627 if(typeof(config.align) != 'undefined' && config.align.length){
6628 td.style += ' text-align:' + config.align + ';';
6631 if(typeof(config.width) != 'undefined'){
6632 td.style += ' width:' + config.width + 'px;';
6635 if(typeof(config.cursor) != 'undefined'){
6636 td.style += ' cursor:' + config.cursor + ';';
6639 if(typeof(config.cls) != 'undefined'){
6640 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6643 ['xs','sm','md','lg'].map(function(size){
6645 if(typeof(config[size]) == 'undefined'){
6649 if (!config[size]) { // 0 = hidden
6650 td.cls += ' hidden-' + size;
6654 td.cls += ' col-' + size + '-' + config[size];
6662 row.cellObjects = cellObjects;
6670 onBeforeLoad : function()
6672 //Roo.log('ds onBeforeLoad');
6676 //if(this.loadMask){
6677 // this.maskEl.show();
6685 this.el.select('tbody', true).first().dom.innerHTML = '';
6688 * Show or hide a row.
6689 * @param {Number} rowIndex to show or hide
6690 * @param {Boolean} state hide
6692 setRowVisibility : function(rowIndex, state)
6694 var bt = this.mainBody.dom;
6696 var rows = this.el.select('tbody > tr', true).elements;
6698 if(typeof(rows[rowIndex]) == 'undefined'){
6701 rows[rowIndex].dom.style.display = state ? '' : 'none';
6705 getSelectionModel : function(){
6707 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6709 return this.selModel;
6712 * Render the Roo.bootstrap object from renderder
6714 renderCellObject : function(r)
6718 var t = r.cfg.render(r.container);
6721 Roo.each(r.cfg.cn, function(c){
6723 container: t.getChildContainer(),
6726 _this.renderCellObject(child);
6731 getRowIndex : function(row)
6735 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6746 * Returns the grid's underlying element = used by panel.Grid
6747 * @return {Element} The element
6749 getGridEl : function(){
6753 * Forces a resize - used by panel.Grid
6754 * @return {Element} The element
6756 autoSize : function()
6758 //var ctr = Roo.get(this.container.dom.parentElement);
6759 var ctr = Roo.get(this.el.dom);
6761 var thd = this.getGridEl().select('thead',true).first();
6762 var tbd = this.getGridEl().select('tbody', true).first();
6763 var tfd = this.getGridEl().select('tfoot', true).first();
6765 var cw = ctr.getWidth();
6769 tbd.setSize(ctr.getWidth(),
6770 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6772 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6775 cw = Math.max(cw, this.totalWidth);
6776 this.getGridEl().select('tr',true).setWidth(cw);
6777 // resize 'expandable coloumn?
6779 return; // we doe not have a view in this design..
6782 onBodyScroll: function()
6785 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786 this.mainHead.setStyle({
6787 'position' : 'relative',
6788 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6805 * @class Roo.bootstrap.TableCell
6806 * @extends Roo.bootstrap.Component
6807 * Bootstrap TableCell class
6808 * @cfg {String} html cell contain text
6809 * @cfg {String} cls cell class
6810 * @cfg {String} tag cell tag (td|th) default td
6811 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812 * @cfg {String} align Aligns the content in a cell
6813 * @cfg {String} axis Categorizes cells
6814 * @cfg {String} bgcolor Specifies the background color of a cell
6815 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816 * @cfg {Number} colspan Specifies the number of columns a cell should span
6817 * @cfg {String} headers Specifies one or more header cells a cell is related to
6818 * @cfg {Number} height Sets the height of a cell
6819 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820 * @cfg {Number} rowspan Sets the number of rows a cell should span
6821 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822 * @cfg {String} valign Vertical aligns the content in a cell
6823 * @cfg {Number} width Specifies the width of a cell
6826 * Create a new TableCell
6827 * @param {Object} config The config object
6830 Roo.bootstrap.TableCell = function(config){
6831 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6854 getAutoCreate : function(){
6855 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6875 cfg.align=this.align
6881 cfg.bgcolor=this.bgcolor
6884 cfg.charoff=this.charoff
6887 cfg.colspan=this.colspan
6890 cfg.headers=this.headers
6893 cfg.height=this.height
6896 cfg.nowrap=this.nowrap
6899 cfg.rowspan=this.rowspan
6902 cfg.scope=this.scope
6905 cfg.valign=this.valign
6908 cfg.width=this.width
6927 * @class Roo.bootstrap.TableRow
6928 * @extends Roo.bootstrap.Component
6929 * Bootstrap TableRow class
6930 * @cfg {String} cls row class
6931 * @cfg {String} align Aligns the content in a table row
6932 * @cfg {String} bgcolor Specifies a background color for a table row
6933 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934 * @cfg {String} valign Vertical aligns the content in a table row
6937 * Create a new TableRow
6938 * @param {Object} config The config object
6941 Roo.bootstrap.TableRow = function(config){
6942 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6953 getAutoCreate : function(){
6954 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6964 cfg.align = this.align;
6967 cfg.bgcolor = this.bgcolor;
6970 cfg.charoff = this.charoff;
6973 cfg.valign = this.valign;
6991 * @class Roo.bootstrap.TableBody
6992 * @extends Roo.bootstrap.Component
6993 * Bootstrap TableBody class
6994 * @cfg {String} cls element class
6995 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996 * @cfg {String} align Aligns the content inside the element
6997 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7001 * Create a new TableBody
7002 * @param {Object} config The config object
7005 Roo.bootstrap.TableBody = function(config){
7006 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7017 getAutoCreate : function(){
7018 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7032 cfg.align = this.align;
7035 cfg.charoff = this.charoff;
7038 cfg.valign = this.valign;
7045 // initEvents : function()
7052 // this.store = Roo.factory(this.store, Roo.data);
7053 // this.store.on('load', this.onLoad, this);
7055 // this.store.load();
7059 // onLoad: function ()
7061 // this.fireEvent('load', this);
7071 * Ext JS Library 1.1.1
7072 * Copyright(c) 2006-2007, Ext JS, LLC.
7074 * Originally Released Under LGPL - original licence link has changed is not relivant.
7077 * <script type="text/javascript">
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7083 * @class Roo.form.Action
7084 * Internal Class used to handle form actions
7086 * @param {Roo.form.BasicForm} el The form element or its id
7087 * @param {Object} config Configuration options
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7095 this.options = options || {};
7098 * Client Validation Failed
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7103 * Server Validation Failed
7106 Roo.form.Action.SERVER_INVALID = 'server';
7108 * Connect to Server Failed
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7113 * Reading Data from Server Failed
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7118 Roo.form.Action.prototype = {
7120 failureType : undefined,
7121 response : undefined,
7125 run : function(options){
7130 success : function(response){
7135 handleResponse : function(response){
7139 // default connection failure
7140 failure : function(response){
7142 this.response = response;
7143 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144 this.form.afterAction(this, false);
7147 processResponse : function(response){
7148 this.response = response;
7149 if(!response.responseText){
7152 this.result = this.handleResponse(response);
7156 // utility functions used internally
7157 getUrl : function(appendParams){
7158 var url = this.options.url || this.form.url || this.form.el.dom.action;
7160 var p = this.getParams();
7162 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7168 getMethod : function(){
7169 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7172 getParams : function(){
7173 var bp = this.form.baseParams;
7174 var p = this.options.params;
7176 if(typeof p == "object"){
7177 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178 }else if(typeof p == 'string' && bp){
7179 p += '&' + Roo.urlEncode(bp);
7182 p = Roo.urlEncode(bp);
7187 createCallback : function(){
7189 success: this.success,
7190 failure: this.failure,
7192 timeout: (this.form.timeout*1000),
7193 upload: this.form.fileUpload ? this.success : undefined
7198 Roo.form.Action.Submit = function(form, options){
7199 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7205 haveProgress : false,
7206 uploadComplete : false,
7208 // uploadProgress indicator.
7209 uploadProgress : function()
7211 if (!this.form.progressUrl) {
7215 if (!this.haveProgress) {
7216 Roo.MessageBox.progress("Uploading", "Uploading");
7218 if (this.uploadComplete) {
7219 Roo.MessageBox.hide();
7223 this.haveProgress = true;
7225 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7227 var c = new Roo.data.Connection();
7229 url : this.form.progressUrl,
7234 success : function(req){
7235 //console.log(data);
7239 rdata = Roo.decode(req.responseText)
7241 Roo.log("Invalid data from server..");
7245 if (!rdata || !rdata.success) {
7247 Roo.MessageBox.alert(Roo.encode(rdata));
7250 var data = rdata.data;
7252 if (this.uploadComplete) {
7253 Roo.MessageBox.hide();
7258 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7262 this.uploadProgress.defer(2000,this);
7265 failure: function(data) {
7266 Roo.log('progress url failed ');
7277 // run get Values on the form, so it syncs any secondary forms.
7278 this.form.getValues();
7280 var o = this.options;
7281 var method = this.getMethod();
7282 var isPost = method == 'POST';
7283 if(o.clientValidation === false || this.form.isValid()){
7285 if (this.form.progressUrl) {
7286 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287 (new Date() * 1) + '' + Math.random());
7292 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293 form:this.form.el.dom,
7294 url:this.getUrl(!isPost),
7296 params:isPost ? this.getParams() : null,
7297 isUpload: this.form.fileUpload
7300 this.uploadProgress();
7302 }else if (o.clientValidation !== false){ // client validation failed
7303 this.failureType = Roo.form.Action.CLIENT_INVALID;
7304 this.form.afterAction(this, false);
7308 success : function(response)
7310 this.uploadComplete= true;
7311 if (this.haveProgress) {
7312 Roo.MessageBox.hide();
7316 var result = this.processResponse(response);
7317 if(result === true || result.success){
7318 this.form.afterAction(this, true);
7322 this.form.markInvalid(result.errors);
7323 this.failureType = Roo.form.Action.SERVER_INVALID;
7325 this.form.afterAction(this, false);
7327 failure : function(response)
7329 this.uploadComplete= true;
7330 if (this.haveProgress) {
7331 Roo.MessageBox.hide();
7334 this.response = response;
7335 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336 this.form.afterAction(this, false);
7339 handleResponse : function(response){
7340 if(this.form.errorReader){
7341 var rs = this.form.errorReader.read(response);
7344 for(var i = 0, len = rs.records.length; i < len; i++) {
7345 var r = rs.records[i];
7349 if(errors.length < 1){
7353 success : rs.success,
7359 ret = Roo.decode(response.responseText);
7363 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7373 Roo.form.Action.Load = function(form, options){
7374 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375 this.reader = this.form.reader;
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7383 Roo.Ajax.request(Roo.apply(
7384 this.createCallback(), {
7385 method:this.getMethod(),
7386 url:this.getUrl(false),
7387 params:this.getParams()
7391 success : function(response){
7393 var result = this.processResponse(response);
7394 if(result === true || !result.success || !result.data){
7395 this.failureType = Roo.form.Action.LOAD_FAILURE;
7396 this.form.afterAction(this, false);
7399 this.form.clearInvalid();
7400 this.form.setValues(result.data);
7401 this.form.afterAction(this, true);
7404 handleResponse : function(response){
7405 if(this.form.reader){
7406 var rs = this.form.reader.read(response);
7407 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7409 success : rs.success,
7413 return Roo.decode(response.responseText);
7417 Roo.form.Action.ACTION_TYPES = {
7418 'load' : Roo.form.Action.Load,
7419 'submit' : Roo.form.Action.Submit
7428 * @class Roo.bootstrap.Form
7429 * @extends Roo.bootstrap.Component
7430 * Bootstrap Form class
7431 * @cfg {String} method GET | POST (default POST)
7432 * @cfg {String} labelAlign top | left (default top)
7433 * @cfg {String} align left | right - for navbars
7434 * @cfg {Boolean} loadMask load mask when submit (default true)
7439 * @param {Object} config The config object
7443 Roo.bootstrap.Form = function(config){
7444 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7447 * @event clientvalidation
7448 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449 * @param {Form} this
7450 * @param {Boolean} valid true if the form has passed client-side validation
7452 clientvalidation: true,
7454 * @event beforeaction
7455 * Fires before any action is performed. Return false to cancel the action.
7456 * @param {Form} this
7457 * @param {Action} action The action to be performed
7461 * @event actionfailed
7462 * Fires when an action fails.
7463 * @param {Form} this
7464 * @param {Action} action The action that failed
7466 actionfailed : true,
7468 * @event actioncomplete
7469 * Fires when an action is completed.
7470 * @param {Form} this
7471 * @param {Action} action The action that completed
7473 actioncomplete : true
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7481 * @cfg {String} method
7482 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7487 * The URL to use for form actions if one isn't supplied in the action options.
7490 * @cfg {Boolean} fileUpload
7491 * Set to true if this form is a file upload.
7495 * @cfg {Object} baseParams
7496 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504 * @cfg {Sting} align (left|right) for navbar forms
7509 activeAction : null,
7512 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513 * element by passing it or its id or mask the form itself by passing in true.
7516 waitMsgTarget : false,
7521 * @cfg {Boolean} errPopover (true|false) default false
7525 getAutoCreate : function(){
7529 method : this.method || 'POST',
7530 id : this.id || Roo.id(),
7533 if (this.parent().xtype.match(/^Nav/)) {
7534 cfg.cls = 'navbar-form navbar-' + this.align;
7538 if (this.labelAlign == 'left' ) {
7539 cfg.cls += ' form-horizontal';
7545 initEvents : function()
7547 this.el.on('submit', this.onSubmit, this);
7548 // this was added as random key presses on the form where triggering form submit.
7549 this.el.on('keypress', function(e) {
7550 if (e.getCharCode() != 13) {
7553 // we might need to allow it for textareas.. and some other items.
7554 // check e.getTarget().
7556 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560 Roo.log("keypress blocked");
7566 this.errTooltip = new Roo.bootstrap.Tooltip({
7567 cls : 'roo-form-error-popover'
7570 this.errTooltip.render(this.el);
7572 this.errTooltip.alignment = {
7573 'left' : ['r-l', [-2,0], 'right'],
7574 'right' : ['l-r', [2,0], 'left'],
7575 'bottom' : ['tl-bl', [0,2], 'top'],
7576 'top' : [ 'bl-tl', [0,-2], 'bottom']
7581 onSubmit : function(e){
7586 * Returns true if client-side validation on the form is successful.
7589 isValid : function(){
7590 var items = this.getItems();
7594 items.each(function(f){
7602 if(!target && f.el.isVisible(true)){
7608 if(this.errPopover && !valid){
7609 this.showErrPopover(target);
7615 showErrPopover : function(target)
7617 if(!this.errPopover){
7624 var oIndex = target.el.getStyle('z-index');
7626 target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7628 target.el.addClass('roo-invalid-outline');
7630 target.inputEl().focus();
7635 this.errTooltip.bindEl = target.el;
7637 this.errTooltip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7639 var tip = target.blankText;
7641 if(target.getValue() !== '' && target.regexText.length){
7642 tip = target.regexText;
7645 this.errTooltip.show(tip);
7649 var fadeout = function(){
7651 target.inputEl().un('blur', fadeout);
7652 target.inputEl().un('keyup', fadeout);
7654 target.el.setStyle('z-index', oIndex);
7656 target.el.removeClass('roo-invalid-outline');
7658 _this.errTooltip.hide();
7660 if(!intervalFadeOut){
7664 window.clearInterval(intervalFadeOut);
7665 intervalFadeOut = false;
7669 target.inputEl().on('blur', fadeout, target);
7670 target.inputEl().on('keyup', fadeout, target);
7672 if(intervalFadeOut){
7673 window.clearInterval(intervalFadeOut);
7674 intervalFadeOut = false;
7677 var intervalFadeOut = window.setInterval(function() {
7684 * Returns true if any fields in this form have changed since their original load.
7687 isDirty : function(){
7689 var items = this.getItems();
7690 items.each(function(f){
7700 * Performs a predefined action (submit or load) or custom actions you define on this form.
7701 * @param {String} actionName The name of the action type
7702 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7703 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7704 * accept other config options):
7706 Property Type Description
7707 ---------------- --------------- ----------------------------------------------------------------------------------
7708 url String The url for the action (defaults to the form's url)
7709 method String The form method to use (defaults to the form's method, or POST if not defined)
7710 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7711 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7712 validate the form on the client (defaults to false)
7714 * @return {BasicForm} this
7716 doAction : function(action, options){
7717 if(typeof action == 'string'){
7718 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7720 if(this.fireEvent('beforeaction', this, action) !== false){
7721 this.beforeAction(action);
7722 action.run.defer(100, action);
7728 beforeAction : function(action){
7729 var o = action.options;
7732 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7734 // not really supported yet.. ??
7736 //if(this.waitMsgTarget === true){
7737 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7738 //}else if(this.waitMsgTarget){
7739 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7740 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7742 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7748 afterAction : function(action, success){
7749 this.activeAction = null;
7750 var o = action.options;
7752 //if(this.waitMsgTarget === true){
7754 //}else if(this.waitMsgTarget){
7755 // this.waitMsgTarget.unmask();
7757 // Roo.MessageBox.updateProgress(1);
7758 // Roo.MessageBox.hide();
7765 Roo.callback(o.success, o.scope, [this, action]);
7766 this.fireEvent('actioncomplete', this, action);
7770 // failure condition..
7771 // we have a scenario where updates need confirming.
7772 // eg. if a locking scenario exists..
7773 // we look for { errors : { needs_confirm : true }} in the response.
7775 (typeof(action.result) != 'undefined') &&
7776 (typeof(action.result.errors) != 'undefined') &&
7777 (typeof(action.result.errors.needs_confirm) != 'undefined')
7780 Roo.log("not supported yet");
7783 Roo.MessageBox.confirm(
7784 "Change requires confirmation",
7785 action.result.errorMsg,
7790 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7800 Roo.callback(o.failure, o.scope, [this, action]);
7801 // show an error message if no failed handler is set..
7802 if (!this.hasListener('actionfailed')) {
7803 Roo.log("need to add dialog support");
7805 Roo.MessageBox.alert("Error",
7806 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7807 action.result.errorMsg :
7808 "Saving Failed, please check your entries or try again"
7813 this.fireEvent('actionfailed', this, action);
7818 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7819 * @param {String} id The value to search for
7822 findField : function(id){
7823 var items = this.getItems();
7824 var field = items.get(id);
7826 items.each(function(f){
7827 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7834 return field || null;
7837 * Mark fields in this form invalid in bulk.
7838 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7839 * @return {BasicForm} this
7841 markInvalid : function(errors){
7842 if(errors instanceof Array){
7843 for(var i = 0, len = errors.length; i < len; i++){
7844 var fieldError = errors[i];
7845 var f = this.findField(fieldError.id);
7847 f.markInvalid(fieldError.msg);
7853 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7854 field.markInvalid(errors[id]);
7858 //Roo.each(this.childForms || [], function (f) {
7859 // f.markInvalid(errors);
7866 * Set values for fields in this form in bulk.
7867 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7868 * @return {BasicForm} this
7870 setValues : function(values){
7871 if(values instanceof Array){ // array of objects
7872 for(var i = 0, len = values.length; i < len; i++){
7874 var f = this.findField(v.id);
7876 f.setValue(v.value);
7877 if(this.trackResetOnLoad){
7878 f.originalValue = f.getValue();
7882 }else{ // object hash
7885 if(typeof values[id] != 'function' && (field = this.findField(id))){
7887 if (field.setFromData &&
7889 field.displayField &&
7890 // combos' with local stores can
7891 // be queried via setValue()
7892 // to set their value..
7893 (field.store && !field.store.isLocal)
7897 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7898 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7899 field.setFromData(sd);
7902 field.setValue(values[id]);
7906 if(this.trackResetOnLoad){
7907 field.originalValue = field.getValue();
7913 //Roo.each(this.childForms || [], function (f) {
7914 // f.setValues(values);
7921 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7922 * they are returned as an array.
7923 * @param {Boolean} asString
7926 getValues : function(asString){
7927 //if (this.childForms) {
7928 // copy values from the child forms
7929 // Roo.each(this.childForms, function (f) {
7930 // this.setValues(f.getValues());
7936 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7937 if(asString === true){
7940 return Roo.urlDecode(fs);
7944 * Returns the fields in this form as an object with key/value pairs.
7945 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7948 getFieldValues : function(with_hidden)
7950 var items = this.getItems();
7952 items.each(function(f){
7956 var v = f.getValue();
7957 if (f.inputType =='radio') {
7958 if (typeof(ret[f.getName()]) == 'undefined') {
7959 ret[f.getName()] = ''; // empty..
7962 if (!f.el.dom.checked) {
7970 // not sure if this supported any more..
7971 if ((typeof(v) == 'object') && f.getRawValue) {
7972 v = f.getRawValue() ; // dates..
7974 // combo boxes where name != hiddenName...
7975 if (f.name !== false && f.name != '' && f.name != f.getName()) {
7976 ret[f.name] = f.getRawValue();
7978 ret[f.getName()] = v;
7985 * Clears all invalid messages in this form.
7986 * @return {BasicForm} this
7988 clearInvalid : function(){
7989 var items = this.getItems();
7991 items.each(function(f){
8002 * @return {BasicForm} this
8005 var items = this.getItems();
8006 items.each(function(f){
8010 Roo.each(this.childForms || [], function (f) {
8017 getItems : function()
8019 var r=new Roo.util.MixedCollection(false, function(o){
8020 return o.id || (o.id = Roo.id());
8022 var iter = function(el) {
8029 Roo.each(el.items,function(e) {
8047 * Ext JS Library 1.1.1
8048 * Copyright(c) 2006-2007, Ext JS, LLC.
8050 * Originally Released Under LGPL - original licence link has changed is not relivant.
8053 * <script type="text/javascript">
8056 * @class Roo.form.VTypes
8057 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8060 Roo.form.VTypes = function(){
8061 // closure these in so they are only created once.
8062 var alpha = /^[a-zA-Z_]+$/;
8063 var alphanum = /^[a-zA-Z0-9_]+$/;
8064 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8065 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8067 // All these messages and functions are configurable
8070 * The function used to validate email addresses
8071 * @param {String} value The email address
8073 'email' : function(v){
8074 return email.test(v);
8077 * The error text to display when the email validation function returns false
8080 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8082 * The keystroke filter mask to be applied on email input
8085 'emailMask' : /[a-z0-9_\.\-@]/i,
8088 * The function used to validate URLs
8089 * @param {String} value The URL
8091 'url' : function(v){
8095 * The error text to display when the url validation function returns false
8098 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8101 * The function used to validate alpha values
8102 * @param {String} value The value
8104 'alpha' : function(v){
8105 return alpha.test(v);
8108 * The error text to display when the alpha validation function returns false
8111 'alphaText' : 'This field should only contain letters and _',
8113 * The keystroke filter mask to be applied on alpha input
8116 'alphaMask' : /[a-z_]/i,
8119 * The function used to validate alphanumeric values
8120 * @param {String} value The value
8122 'alphanum' : function(v){
8123 return alphanum.test(v);
8126 * The error text to display when the alphanumeric validation function returns false
8129 'alphanumText' : 'This field should only contain letters, numbers and _',
8131 * The keystroke filter mask to be applied on alphanumeric input
8134 'alphanumMask' : /[a-z0-9_]/i
8144 * @class Roo.bootstrap.Input
8145 * @extends Roo.bootstrap.Component
8146 * Bootstrap Input class
8147 * @cfg {Boolean} disabled is it disabled
8148 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8149 * @cfg {String} name name of the input
8150 * @cfg {string} fieldLabel - the label associated
8151 * @cfg {string} placeholder - placeholder to put in text.
8152 * @cfg {string} before - input group add on before
8153 * @cfg {string} after - input group add on after
8154 * @cfg {string} size - (lg|sm) or leave empty..
8155 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8156 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8157 * @cfg {Number} md colspan out of 12 for computer-sized screens
8158 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8159 * @cfg {string} value default value of the input
8160 * @cfg {Number} labelWidth set the width of label (0-12)
8161 * @cfg {String} labelAlign (top|left)
8162 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8163 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8164 * @cfg {String} indicatorpos (left|right) default left
8166 * @cfg {String} align (left|center|right) Default left
8167 * @cfg {Boolean} forceFeedback (true|false) Default false
8173 * Create a new Input
8174 * @param {Object} config The config object
8177 Roo.bootstrap.Input = function(config){
8178 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8183 * Fires when this field receives input focus.
8184 * @param {Roo.form.Field} this
8189 * Fires when this field loses input focus.
8190 * @param {Roo.form.Field} this
8195 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8196 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8197 * @param {Roo.form.Field} this
8198 * @param {Roo.EventObject} e The event object
8203 * Fires just before the field blurs if the field value has changed.
8204 * @param {Roo.form.Field} this
8205 * @param {Mixed} newValue The new value
8206 * @param {Mixed} oldValue The original value
8211 * Fires after the field has been marked as invalid.
8212 * @param {Roo.form.Field} this
8213 * @param {String} msg The validation message
8218 * Fires after the field has been validated with no errors.
8219 * @param {Roo.form.Field} this
8224 * Fires after the key up
8225 * @param {Roo.form.Field} this
8226 * @param {Roo.EventObject} e The event Object
8232 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8234 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8235 automatic validation (defaults to "keyup").
8237 validationEvent : "keyup",
8239 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8241 validateOnBlur : true,
8243 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8245 validationDelay : 250,
8247 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8249 focusClass : "x-form-focus", // not needed???
8253 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8255 invalidClass : "has-warning",
8258 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8260 validClass : "has-success",
8263 * @cfg {Boolean} hasFeedback (true|false) default true
8268 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8270 invalidFeedbackClass : "glyphicon-warning-sign",
8273 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8275 validFeedbackClass : "glyphicon-ok",
8278 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8280 selectOnFocus : false,
8283 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8287 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8292 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8294 disableKeyFilter : false,
8297 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8301 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8305 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8307 blankText : "This field is required",
8310 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8314 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8316 maxLength : Number.MAX_VALUE,
8318 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8320 minLengthText : "The minimum length for this field is {0}",
8322 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8324 maxLengthText : "The maximum length for this field is {0}",
8328 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8329 * If available, this function will be called only after the basic validators all return true, and will be passed the
8330 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8334 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8335 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8336 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8340 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8344 autocomplete: false,
8363 formatedValue : false,
8364 forceFeedback : false,
8366 indicatorpos : 'left',
8368 parentLabelAlign : function()
8371 while (parent.parent()) {
8372 parent = parent.parent();
8373 if (typeof(parent.labelAlign) !='undefined') {
8374 return parent.labelAlign;
8381 getAutoCreate : function()
8383 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8389 if(this.inputType != 'hidden'){
8390 cfg.cls = 'form-group' //input-group
8396 type : this.inputType,
8398 cls : 'form-control',
8399 placeholder : this.placeholder || '',
8400 autocomplete : this.autocomplete || 'new-password'
8404 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8407 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8408 input.maxLength = this.maxLength;
8411 if (this.disabled) {
8412 input.disabled=true;
8415 if (this.readOnly) {
8416 input.readonly=true;
8420 input.name = this.name;
8424 input.cls += ' input-' + this.size;
8428 ['xs','sm','md','lg'].map(function(size){
8429 if (settings[size]) {
8430 cfg.cls += ' col-' + size + '-' + settings[size];
8434 var inputblock = input;
8438 cls: 'glyphicon form-control-feedback'
8441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8444 cls : 'has-feedback',
8452 if (this.before || this.after) {
8455 cls : 'input-group',
8459 if (this.before && typeof(this.before) == 'string') {
8461 inputblock.cn.push({
8463 cls : 'roo-input-before input-group-addon',
8467 if (this.before && typeof(this.before) == 'object') {
8468 this.before = Roo.factory(this.before);
8470 inputblock.cn.push({
8472 cls : 'roo-input-before input-group-' +
8473 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8477 inputblock.cn.push(input);
8479 if (this.after && typeof(this.after) == 'string') {
8480 inputblock.cn.push({
8482 cls : 'roo-input-after input-group-addon',
8486 if (this.after && typeof(this.after) == 'object') {
8487 this.after = Roo.factory(this.after);
8489 inputblock.cn.push({
8491 cls : 'roo-input-after input-group-' +
8492 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8496 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8497 inputblock.cls += ' has-feedback';
8498 inputblock.cn.push(feedback);
8502 if (align ==='left' && this.fieldLabel.length) {
8507 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8508 tooltip : 'This field is required'
8513 cls : 'control-label col-sm-' + this.labelWidth,
8514 html : this.fieldLabel
8518 cls : "col-sm-" + (12 - this.labelWidth),
8526 if(this.indicatorpos == 'right'){
8531 cls : 'control-label col-sm-' + this.labelWidth,
8532 html : this.fieldLabel
8537 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8538 tooltip : 'This field is required'
8541 cls : "col-sm-" + (12 - this.labelWidth),
8550 } else if ( this.fieldLabel.length) {
8555 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8556 tooltip : 'This field is required'
8560 //cls : 'input-group-addon',
8561 html : this.fieldLabel
8569 if(this.indicatorpos == 'right'){
8574 //cls : 'input-group-addon',
8575 html : this.fieldLabel
8580 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8581 tooltip : 'This field is required'
8601 if (this.parentType === 'Navbar' && this.parent().bar) {
8602 cfg.cls += ' navbar-form';
8605 if (this.parentType === 'NavGroup') {
8606 cfg.cls += ' navbar-form';
8614 * return the real input element.
8616 inputEl: function ()
8618 return this.el.select('input.form-control',true).first();
8621 tooltipEl : function()
8623 return this.inputEl();
8626 indicatorEl : function()
8628 var indicator = this.el.select('i.roo-required-indicator',true).first();
8638 setDisabled : function(v)
8640 var i = this.inputEl().dom;
8642 i.removeAttribute('disabled');
8646 i.setAttribute('disabled','true');
8648 initEvents : function()
8651 this.inputEl().on("keydown" , this.fireKey, this);
8652 this.inputEl().on("focus", this.onFocus, this);
8653 this.inputEl().on("blur", this.onBlur, this);
8655 this.inputEl().relayEvent('keyup', this);
8657 this.indicator = this.indicatorEl();
8660 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8661 this.indicator.hide();
8664 // reference to original value for reset
8665 this.originalValue = this.getValue();
8666 //Roo.form.TextField.superclass.initEvents.call(this);
8667 if(this.validationEvent == 'keyup'){
8668 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8669 this.inputEl().on('keyup', this.filterValidation, this);
8671 else if(this.validationEvent !== false){
8672 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8675 if(this.selectOnFocus){
8676 this.on("focus", this.preFocus, this);
8679 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8680 this.inputEl().on("keypress", this.filterKeys, this);
8682 this.inputEl().relayEvent('keypress', this);
8685 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8686 this.el.on("click", this.autoSize, this);
8689 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8690 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8693 if (typeof(this.before) == 'object') {
8694 this.before.render(this.el.select('.roo-input-before',true).first());
8696 if (typeof(this.after) == 'object') {
8697 this.after.render(this.el.select('.roo-input-after',true).first());
8702 filterValidation : function(e){
8703 if(!e.isNavKeyPress()){
8704 this.validationTask.delay(this.validationDelay);
8708 * Validates the field value
8709 * @return {Boolean} True if the value is valid, else false
8711 validate : function(){
8712 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8713 if(this.disabled || this.validateValue(this.getRawValue())){
8724 * Validates a value according to the field's validation rules and marks the field as invalid
8725 * if the validation fails
8726 * @param {Mixed} value The value to validate
8727 * @return {Boolean} True if the value is valid, else false
8729 validateValue : function(value){
8730 if(value.length < 1) { // if it's blank
8731 if(this.allowBlank){
8737 if(value.length < this.minLength){
8740 if(value.length > this.maxLength){
8744 var vt = Roo.form.VTypes;
8745 if(!vt[this.vtype](value, this)){
8749 if(typeof this.validator == "function"){
8750 var msg = this.validator(value);
8756 if(this.regex && !this.regex.test(value)){
8766 fireKey : function(e){
8767 //Roo.log('field ' + e.getKey());
8768 if(e.isNavKeyPress()){
8769 this.fireEvent("specialkey", this, e);
8772 focus : function (selectText){
8774 this.inputEl().focus();
8775 if(selectText === true){
8776 this.inputEl().dom.select();
8782 onFocus : function(){
8783 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8784 // this.el.addClass(this.focusClass);
8787 this.hasFocus = true;
8788 this.startValue = this.getValue();
8789 this.fireEvent("focus", this);
8793 beforeBlur : Roo.emptyFn,
8797 onBlur : function(){
8799 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8800 //this.el.removeClass(this.focusClass);
8802 this.hasFocus = false;
8803 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8806 var v = this.getValue();
8807 if(String(v) !== String(this.startValue)){
8808 this.fireEvent('change', this, v, this.startValue);
8810 this.fireEvent("blur", this);
8814 * Resets the current field value to the originally loaded value and clears any validation messages
8817 this.setValue(this.originalValue);
8821 * Returns the name of the field
8822 * @return {Mixed} name The name field
8824 getName: function(){
8828 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8829 * @return {Mixed} value The field value
8831 getValue : function(){
8833 var v = this.inputEl().getValue();
8838 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8839 * @return {Mixed} value The field value
8841 getRawValue : function(){
8842 var v = this.inputEl().getValue();
8848 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8849 * @param {Mixed} value The value to set
8851 setRawValue : function(v){
8852 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8855 selectText : function(start, end){
8856 var v = this.getRawValue();
8858 start = start === undefined ? 0 : start;
8859 end = end === undefined ? v.length : end;
8860 var d = this.inputEl().dom;
8861 if(d.setSelectionRange){
8862 d.setSelectionRange(start, end);
8863 }else if(d.createTextRange){
8864 var range = d.createTextRange();
8865 range.moveStart("character", start);
8866 range.moveEnd("character", v.length-end);
8873 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8874 * @param {Mixed} value The value to set
8876 setValue : function(v){
8879 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8885 processValue : function(value){
8886 if(this.stripCharsRe){
8887 var newValue = value.replace(this.stripCharsRe, '');
8888 if(newValue !== value){
8889 this.setRawValue(newValue);
8896 preFocus : function(){
8898 if(this.selectOnFocus){
8899 this.inputEl().dom.select();
8902 filterKeys : function(e){
8904 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8907 var c = e.getCharCode(), cc = String.fromCharCode(c);
8908 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8911 if(!this.maskRe.test(cc)){
8916 * Clear any invalid styles/messages for this field
8918 clearInvalid : function(){
8920 if(!this.el || this.preventMark){ // not rendered
8925 this.indicator.hide();
8928 this.el.removeClass(this.invalidClass);
8930 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8932 var feedback = this.el.select('.form-control-feedback', true).first();
8935 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8940 this.fireEvent('valid', this);
8944 * Mark this field as valid
8946 markValid : function()
8948 if(!this.el || this.preventMark){ // not rendered
8952 this.el.removeClass([this.invalidClass, this.validClass]);
8954 var feedback = this.el.select('.form-control-feedback', true).first();
8957 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8964 if(this.allowBlank && !this.getRawValue().length){
8969 this.indicator.hide();
8972 this.el.addClass(this.validClass);
8974 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8976 var feedback = this.el.select('.form-control-feedback', true).first();
8979 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8980 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8985 this.fireEvent('valid', this);
8989 * Mark this field as invalid
8990 * @param {String} msg The validation message
8992 markInvalid : function(msg)
8994 if(!this.el || this.preventMark){ // not rendered
8998 this.el.removeClass([this.invalidClass, this.validClass]);
9000 var feedback = this.el.select('.form-control-feedback', true).first();
9003 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9010 if(this.allowBlank && !this.getRawValue().length){
9015 this.indicator.show();
9018 this.el.addClass(this.invalidClass);
9020 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9022 var feedback = this.el.select('.form-control-feedback', true).first();
9025 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9027 if(this.getValue().length || this.forceFeedback){
9028 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9035 this.fireEvent('invalid', this, msg);
9038 SafariOnKeyDown : function(event)
9040 // this is a workaround for a password hang bug on chrome/ webkit.
9041 if (this.inputEl().dom.type != 'password') {
9045 var isSelectAll = false;
9047 if(this.inputEl().dom.selectionEnd > 0){
9048 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9050 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9051 event.preventDefault();
9056 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9058 event.preventDefault();
9059 // this is very hacky as keydown always get's upper case.
9061 var cc = String.fromCharCode(event.getCharCode());
9062 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9066 adjustWidth : function(tag, w){
9067 tag = tag.toLowerCase();
9068 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9069 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9073 if(tag == 'textarea'){
9076 }else if(Roo.isOpera){
9080 if(tag == 'textarea'){
9099 * @class Roo.bootstrap.TextArea
9100 * @extends Roo.bootstrap.Input
9101 * Bootstrap TextArea class
9102 * @cfg {Number} cols Specifies the visible width of a text area
9103 * @cfg {Number} rows Specifies the visible number of lines in a text area
9104 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9105 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9106 * @cfg {string} html text
9109 * Create a new TextArea
9110 * @param {Object} config The config object
9113 Roo.bootstrap.TextArea = function(config){
9114 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9118 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9128 getAutoCreate : function(){
9130 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9141 value : this.value || '',
9142 html: this.html || '',
9143 cls : 'form-control',
9144 placeholder : this.placeholder || ''
9148 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9149 input.maxLength = this.maxLength;
9153 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9157 input.cols = this.cols;
9160 if (this.readOnly) {
9161 input.readonly = true;
9165 input.name = this.name;
9169 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9173 ['xs','sm','md','lg'].map(function(size){
9174 if (settings[size]) {
9175 cfg.cls += ' col-' + size + '-' + settings[size];
9179 var inputblock = input;
9181 if(this.hasFeedback && !this.allowBlank){
9185 cls: 'glyphicon form-control-feedback'
9189 cls : 'has-feedback',
9198 if (this.before || this.after) {
9201 cls : 'input-group',
9205 inputblock.cn.push({
9207 cls : 'input-group-addon',
9212 inputblock.cn.push(input);
9214 if(this.hasFeedback && !this.allowBlank){
9215 inputblock.cls += ' has-feedback';
9216 inputblock.cn.push(feedback);
9220 inputblock.cn.push({
9222 cls : 'input-group-addon',
9229 if (align ==='left' && this.fieldLabel.length) {
9230 // Roo.log("left and has label");
9236 cls : 'control-label col-sm-' + this.labelWidth,
9237 html : this.fieldLabel
9241 cls : "col-sm-" + (12 - this.labelWidth),
9248 } else if ( this.fieldLabel.length) {
9249 // Roo.log(" label");
9254 //cls : 'input-group-addon',
9255 html : this.fieldLabel
9265 // Roo.log(" no label && no align");
9275 if (this.disabled) {
9276 input.disabled=true;
9283 * return the real textarea element.
9285 inputEl: function ()
9287 return this.el.select('textarea.form-control',true).first();
9291 * Clear any invalid styles/messages for this field
9293 clearInvalid : function()
9296 if(!this.el || this.preventMark){ // not rendered
9300 var label = this.el.select('label', true).first();
9301 var icon = this.el.select('i.fa-star', true).first();
9307 this.el.removeClass(this.invalidClass);
9309 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9311 var feedback = this.el.select('.form-control-feedback', true).first();
9314 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9319 this.fireEvent('valid', this);
9323 * Mark this field as valid
9325 markValid : function()
9327 if(!this.el || this.preventMark){ // not rendered
9331 this.el.removeClass([this.invalidClass, this.validClass]);
9333 var feedback = this.el.select('.form-control-feedback', true).first();
9336 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9339 if(this.disabled || this.allowBlank){
9343 var label = this.el.select('label', true).first();
9344 var icon = this.el.select('i.fa-star', true).first();
9350 this.el.addClass(this.validClass);
9352 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9354 var feedback = this.el.select('.form-control-feedback', true).first();
9357 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9358 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9363 this.fireEvent('valid', this);
9367 * Mark this field as invalid
9368 * @param {String} msg The validation message
9370 markInvalid : function(msg)
9372 if(!this.el || this.preventMark){ // not rendered
9376 this.el.removeClass([this.invalidClass, this.validClass]);
9378 var feedback = this.el.select('.form-control-feedback', true).first();
9381 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9384 if(this.disabled || this.allowBlank){
9388 var label = this.el.select('label', true).first();
9389 var icon = this.el.select('i.fa-star', true).first();
9391 if(!this.getValue().length && label && !icon){
9392 this.el.createChild({
9394 cls : 'text-danger fa fa-lg fa-star',
9395 tooltip : 'This field is required',
9396 style : 'margin-right:5px;'
9400 this.el.addClass(this.invalidClass);
9402 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9404 var feedback = this.el.select('.form-control-feedback', true).first();
9407 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9409 if(this.getValue().length || this.forceFeedback){
9410 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9417 this.fireEvent('invalid', this, msg);
9425 * trigger field - base class for combo..
9430 * @class Roo.bootstrap.TriggerField
9431 * @extends Roo.bootstrap.Input
9432 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9433 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9434 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9435 * for which you can provide a custom implementation. For example:
9437 var trigger = new Roo.bootstrap.TriggerField();
9438 trigger.onTriggerClick = myTriggerFn;
9439 trigger.applyTo('my-field');
9442 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9443 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9444 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9445 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9446 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9449 * Create a new TriggerField.
9450 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9451 * to the base TextField)
9453 Roo.bootstrap.TriggerField = function(config){
9454 this.mimicing = false;
9455 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9458 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9460 * @cfg {String} triggerClass A CSS class to apply to the trigger
9463 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9468 * @cfg {Boolean} removable (true|false) special filter default false
9472 /** @cfg {Boolean} grow @hide */
9473 /** @cfg {Number} growMin @hide */
9474 /** @cfg {Number} growMax @hide */
9480 autoSize: Roo.emptyFn,
9487 actionMode : 'wrap',
9492 getAutoCreate : function(){
9494 var align = this.labelAlign || this.parentLabelAlign();
9499 cls: 'form-group' //input-group
9506 type : this.inputType,
9507 cls : 'form-control',
9508 autocomplete: 'new-password',
9509 placeholder : this.placeholder || ''
9513 input.name = this.name;
9516 input.cls += ' input-' + this.size;
9519 if (this.disabled) {
9520 input.disabled=true;
9523 var inputblock = input;
9525 if(this.hasFeedback && !this.allowBlank){
9529 cls: 'glyphicon form-control-feedback'
9532 if(this.removable && !this.editable && !this.tickable){
9534 cls : 'has-feedback',
9540 cls : 'roo-combo-removable-btn close'
9547 cls : 'has-feedback',
9556 if(this.removable && !this.editable && !this.tickable){
9558 cls : 'roo-removable',
9564 cls : 'roo-combo-removable-btn close'
9571 if (this.before || this.after) {
9574 cls : 'input-group',
9578 inputblock.cn.push({
9580 cls : 'input-group-addon',
9585 inputblock.cn.push(input);
9587 if(this.hasFeedback && !this.allowBlank){
9588 inputblock.cls += ' has-feedback';
9589 inputblock.cn.push(feedback);
9593 inputblock.cn.push({
9595 cls : 'input-group-addon',
9608 cls: 'form-hidden-field'
9622 cls: 'form-hidden-field'
9626 cls: 'roo-select2-choices',
9630 cls: 'roo-select2-search-field',
9643 cls: 'roo-select2-container input-group',
9648 // cls: 'typeahead typeahead-long dropdown-menu',
9649 // style: 'display:none'
9654 if(!this.multiple && this.showToggleBtn){
9660 if (this.caret != false) {
9663 cls: 'fa fa-' + this.caret
9670 cls : 'input-group-addon btn dropdown-toggle',
9675 cls: 'combobox-clear',
9689 combobox.cls += ' roo-select2-container-multi';
9692 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9694 // Roo.log("left and has label");
9698 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9699 tooltip : 'This field is required'
9704 cls : 'control-label col-sm-' + this.labelWidth,
9705 html : this.fieldLabel
9709 cls : "col-sm-" + (12 - this.labelWidth),
9717 if(this.indicatorpos == 'right'){
9722 cls : 'control-label col-sm-' + this.labelWidth,
9723 html : this.fieldLabel
9728 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9729 tooltip : 'This field is required'
9732 cls : "col-sm-" + (12 - this.labelWidth),
9741 } else if ( this.fieldLabel.length) {
9742 // Roo.log(" label");
9746 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9747 tooltip : 'This field is required'
9751 //cls : 'input-group-addon',
9752 html : this.fieldLabel
9760 if(this.indicatorpos == 'right'){
9765 //cls : 'input-group-addon',
9766 html : this.fieldLabel
9771 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9772 tooltip : 'This field is required'
9783 // Roo.log(" no label && no align");
9790 ['xs','sm','md','lg'].map(function(size){
9791 if (settings[size]) {
9792 cfg.cls += ' col-' + size + '-' + settings[size];
9803 onResize : function(w, h){
9804 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9805 // if(typeof w == 'number'){
9806 // var x = w - this.trigger.getWidth();
9807 // this.inputEl().setWidth(this.adjustWidth('input', x));
9808 // this.trigger.setStyle('left', x+'px');
9813 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9816 getResizeEl : function(){
9817 return this.inputEl();
9821 getPositionEl : function(){
9822 return this.inputEl();
9826 alignErrorIcon : function(){
9827 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9831 initEvents : function(){
9835 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9836 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9837 if(!this.multiple && this.showToggleBtn){
9838 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9839 if(this.hideTrigger){
9840 this.trigger.setDisplayed(false);
9842 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9846 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9849 if(this.removable && !this.editable && !this.tickable){
9850 var close = this.closeTriggerEl();
9853 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9854 close.on('click', this.removeBtnClick, this, close);
9858 //this.trigger.addClassOnOver('x-form-trigger-over');
9859 //this.trigger.addClassOnClick('x-form-trigger-click');
9862 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9866 closeTriggerEl : function()
9868 var close = this.el.select('.roo-combo-removable-btn', true).first();
9869 return close ? close : false;
9872 removeBtnClick : function(e, h, el)
9876 if(this.fireEvent("remove", this) !== false){
9878 this.fireEvent("afterremove", this)
9882 createList : function()
9884 this.list = Roo.get(document.body).createChild({
9886 cls: 'typeahead typeahead-long dropdown-menu',
9887 style: 'display:none'
9890 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9895 initTrigger : function(){
9900 onDestroy : function(){
9902 this.trigger.removeAllListeners();
9903 // this.trigger.remove();
9906 // this.wrap.remove();
9908 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9912 onFocus : function(){
9913 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9916 this.wrap.addClass('x-trigger-wrap-focus');
9917 this.mimicing = true;
9918 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9919 if(this.monitorTab){
9920 this.el.on("keydown", this.checkTab, this);
9927 checkTab : function(e){
9928 if(e.getKey() == e.TAB){
9934 onBlur : function(){
9939 mimicBlur : function(e, t){
9941 if(!this.wrap.contains(t) && this.validateBlur()){
9948 triggerBlur : function(){
9949 this.mimicing = false;
9950 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9951 if(this.monitorTab){
9952 this.el.un("keydown", this.checkTab, this);
9954 //this.wrap.removeClass('x-trigger-wrap-focus');
9955 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9959 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9960 validateBlur : function(e, t){
9965 onDisable : function(){
9966 this.inputEl().dom.disabled = true;
9967 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9969 // this.wrap.addClass('x-item-disabled');
9974 onEnable : function(){
9975 this.inputEl().dom.disabled = false;
9976 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9978 // this.el.removeClass('x-item-disabled');
9983 onShow : function(){
9984 var ae = this.getActionEl();
9987 ae.dom.style.display = '';
9988 ae.dom.style.visibility = 'visible';
9994 onHide : function(){
9995 var ae = this.getActionEl();
9996 ae.dom.style.display = 'none';
10000 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10001 * by an implementing function.
10003 * @param {EventObject} e
10005 onTriggerClick : Roo.emptyFn
10009 * Ext JS Library 1.1.1
10010 * Copyright(c) 2006-2007, Ext JS, LLC.
10012 * Originally Released Under LGPL - original licence link has changed is not relivant.
10015 * <script type="text/javascript">
10020 * @class Roo.data.SortTypes
10022 * Defines the default sorting (casting?) comparison functions used when sorting data.
10024 Roo.data.SortTypes = {
10026 * Default sort that does nothing
10027 * @param {Mixed} s The value being converted
10028 * @return {Mixed} The comparison value
10030 none : function(s){
10035 * The regular expression used to strip tags
10039 stripTagsRE : /<\/?[^>]+>/gi,
10042 * Strips all HTML tags to sort on text only
10043 * @param {Mixed} s The value being converted
10044 * @return {String} The comparison value
10046 asText : function(s){
10047 return String(s).replace(this.stripTagsRE, "");
10051 * Strips all HTML tags to sort on text only - Case insensitive
10052 * @param {Mixed} s The value being converted
10053 * @return {String} The comparison value
10055 asUCText : function(s){
10056 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10060 * Case insensitive string
10061 * @param {Mixed} s The value being converted
10062 * @return {String} The comparison value
10064 asUCString : function(s) {
10065 return String(s).toUpperCase();
10070 * @param {Mixed} s The value being converted
10071 * @return {Number} The comparison value
10073 asDate : function(s) {
10077 if(s instanceof Date){
10078 return s.getTime();
10080 return Date.parse(String(s));
10085 * @param {Mixed} s The value being converted
10086 * @return {Float} The comparison value
10088 asFloat : function(s) {
10089 var val = parseFloat(String(s).replace(/,/g, ""));
10098 * @param {Mixed} s The value being converted
10099 * @return {Number} The comparison value
10101 asInt : function(s) {
10102 var val = parseInt(String(s).replace(/,/g, ""));
10110 * Ext JS Library 1.1.1
10111 * Copyright(c) 2006-2007, Ext JS, LLC.
10113 * Originally Released Under LGPL - original licence link has changed is not relivant.
10116 * <script type="text/javascript">
10120 * @class Roo.data.Record
10121 * Instances of this class encapsulate both record <em>definition</em> information, and record
10122 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10123 * to access Records cached in an {@link Roo.data.Store} object.<br>
10125 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10126 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10129 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10131 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10132 * {@link #create}. The parameters are the same.
10133 * @param {Array} data An associative Array of data values keyed by the field name.
10134 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10135 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10136 * not specified an integer id is generated.
10138 Roo.data.Record = function(data, id){
10139 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10144 * Generate a constructor for a specific record layout.
10145 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10146 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10147 * Each field definition object may contain the following properties: <ul>
10148 * <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,
10149 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10150 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10151 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10152 * is being used, then this is a string containing the javascript expression to reference the data relative to
10153 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10154 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10155 * this may be omitted.</p></li>
10156 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10157 * <ul><li>auto (Default, implies no conversion)</li>
10162 * <li>date</li></ul></p></li>
10163 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10164 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10165 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10166 * by the Reader into an object that will be stored in the Record. It is passed the
10167 * following parameters:<ul>
10168 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10170 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10172 * <br>usage:<br><pre><code>
10173 var TopicRecord = Roo.data.Record.create(
10174 {name: 'title', mapping: 'topic_title'},
10175 {name: 'author', mapping: 'username'},
10176 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10177 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10178 {name: 'lastPoster', mapping: 'user2'},
10179 {name: 'excerpt', mapping: 'post_text'}
10182 var myNewRecord = new TopicRecord({
10183 title: 'Do my job please',
10186 lastPost: new Date(),
10187 lastPoster: 'Animal',
10188 excerpt: 'No way dude!'
10190 myStore.add(myNewRecord);
10195 Roo.data.Record.create = function(o){
10196 var f = function(){
10197 f.superclass.constructor.apply(this, arguments);
10199 Roo.extend(f, Roo.data.Record);
10200 var p = f.prototype;
10201 p.fields = new Roo.util.MixedCollection(false, function(field){
10204 for(var i = 0, len = o.length; i < len; i++){
10205 p.fields.add(new Roo.data.Field(o[i]));
10207 f.getField = function(name){
10208 return p.fields.get(name);
10213 Roo.data.Record.AUTO_ID = 1000;
10214 Roo.data.Record.EDIT = 'edit';
10215 Roo.data.Record.REJECT = 'reject';
10216 Roo.data.Record.COMMIT = 'commit';
10218 Roo.data.Record.prototype = {
10220 * Readonly flag - true if this record has been modified.
10229 join : function(store){
10230 this.store = store;
10234 * Set the named field to the specified value.
10235 * @param {String} name The name of the field to set.
10236 * @param {Object} value The value to set the field to.
10238 set : function(name, value){
10239 if(this.data[name] == value){
10243 if(!this.modified){
10244 this.modified = {};
10246 if(typeof this.modified[name] == 'undefined'){
10247 this.modified[name] = this.data[name];
10249 this.data[name] = value;
10250 if(!this.editing && this.store){
10251 this.store.afterEdit(this);
10256 * Get the value of the named field.
10257 * @param {String} name The name of the field to get the value of.
10258 * @return {Object} The value of the field.
10260 get : function(name){
10261 return this.data[name];
10265 beginEdit : function(){
10266 this.editing = true;
10267 this.modified = {};
10271 cancelEdit : function(){
10272 this.editing = false;
10273 delete this.modified;
10277 endEdit : function(){
10278 this.editing = false;
10279 if(this.dirty && this.store){
10280 this.store.afterEdit(this);
10285 * Usually called by the {@link Roo.data.Store} which owns the Record.
10286 * Rejects all changes made to the Record since either creation, or the last commit operation.
10287 * Modified fields are reverted to their original values.
10289 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10290 * of reject operations.
10292 reject : function(){
10293 var m = this.modified;
10295 if(typeof m[n] != "function"){
10296 this.data[n] = m[n];
10299 this.dirty = false;
10300 delete this.modified;
10301 this.editing = false;
10303 this.store.afterReject(this);
10308 * Usually called by the {@link Roo.data.Store} which owns the Record.
10309 * Commits all changes made to the Record since either creation, or the last commit operation.
10311 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10312 * of commit operations.
10314 commit : function(){
10315 this.dirty = false;
10316 delete this.modified;
10317 this.editing = false;
10319 this.store.afterCommit(this);
10324 hasError : function(){
10325 return this.error != null;
10329 clearError : function(){
10334 * Creates a copy of this record.
10335 * @param {String} id (optional) A new record id if you don't want to use this record's id
10338 copy : function(newId) {
10339 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10343 * Ext JS Library 1.1.1
10344 * Copyright(c) 2006-2007, Ext JS, LLC.
10346 * Originally Released Under LGPL - original licence link has changed is not relivant.
10349 * <script type="text/javascript">
10355 * @class Roo.data.Store
10356 * @extends Roo.util.Observable
10357 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10358 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10360 * 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
10361 * has no knowledge of the format of the data returned by the Proxy.<br>
10363 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10364 * instances from the data object. These records are cached and made available through accessor functions.
10366 * Creates a new Store.
10367 * @param {Object} config A config object containing the objects needed for the Store to access data,
10368 * and read the data into Records.
10370 Roo.data.Store = function(config){
10371 this.data = new Roo.util.MixedCollection(false);
10372 this.data.getKey = function(o){
10375 this.baseParams = {};
10377 this.paramNames = {
10382 "multisort" : "_multisort"
10385 if(config && config.data){
10386 this.inlineData = config.data;
10387 delete config.data;
10390 Roo.apply(this, config);
10392 if(this.reader){ // reader passed
10393 this.reader = Roo.factory(this.reader, Roo.data);
10394 this.reader.xmodule = this.xmodule || false;
10395 if(!this.recordType){
10396 this.recordType = this.reader.recordType;
10398 if(this.reader.onMetaChange){
10399 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10403 if(this.recordType){
10404 this.fields = this.recordType.prototype.fields;
10406 this.modified = [];
10410 * @event datachanged
10411 * Fires when the data cache has changed, and a widget which is using this Store
10412 * as a Record cache should refresh its view.
10413 * @param {Store} this
10415 datachanged : true,
10417 * @event metachange
10418 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10419 * @param {Store} this
10420 * @param {Object} meta The JSON metadata
10425 * Fires when Records have been added to the Store
10426 * @param {Store} this
10427 * @param {Roo.data.Record[]} records The array of Records added
10428 * @param {Number} index The index at which the record(s) were added
10433 * Fires when a Record has been removed from the Store
10434 * @param {Store} this
10435 * @param {Roo.data.Record} record The Record that was removed
10436 * @param {Number} index The index at which the record was removed
10441 * Fires when a Record has been updated
10442 * @param {Store} this
10443 * @param {Roo.data.Record} record The Record that was updated
10444 * @param {String} operation The update operation being performed. Value may be one of:
10446 Roo.data.Record.EDIT
10447 Roo.data.Record.REJECT
10448 Roo.data.Record.COMMIT
10454 * Fires when the data cache has been cleared.
10455 * @param {Store} this
10459 * @event beforeload
10460 * Fires before a request is made for a new data object. If the beforeload handler returns false
10461 * the load action will be canceled.
10462 * @param {Store} this
10463 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10467 * @event beforeloadadd
10468 * Fires after a new set of Records has been loaded.
10469 * @param {Store} this
10470 * @param {Roo.data.Record[]} records The Records that were loaded
10471 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10473 beforeloadadd : true,
10476 * Fires after a new set of Records has been loaded, before they are added to the store.
10477 * @param {Store} this
10478 * @param {Roo.data.Record[]} records The Records that were loaded
10479 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10480 * @params {Object} return from reader
10484 * @event loadexception
10485 * Fires if an exception occurs in the Proxy during loading.
10486 * Called with the signature of the Proxy's "loadexception" event.
10487 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10490 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10491 * @param {Object} load options
10492 * @param {Object} jsonData from your request (normally this contains the Exception)
10494 loadexception : true
10498 this.proxy = Roo.factory(this.proxy, Roo.data);
10499 this.proxy.xmodule = this.xmodule || false;
10500 this.relayEvents(this.proxy, ["loadexception"]);
10502 this.sortToggle = {};
10503 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10505 Roo.data.Store.superclass.constructor.call(this);
10507 if(this.inlineData){
10508 this.loadData(this.inlineData);
10509 delete this.inlineData;
10513 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10515 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10516 * without a remote query - used by combo/forms at present.
10520 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10523 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10526 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10527 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10530 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10531 * on any HTTP request
10534 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10537 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10541 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10542 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10544 remoteSort : false,
10547 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10548 * loaded or when a record is removed. (defaults to false).
10550 pruneModifiedRecords : false,
10553 lastOptions : null,
10556 * Add Records to the Store and fires the add event.
10557 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10559 add : function(records){
10560 records = [].concat(records);
10561 for(var i = 0, len = records.length; i < len; i++){
10562 records[i].join(this);
10564 var index = this.data.length;
10565 this.data.addAll(records);
10566 this.fireEvent("add", this, records, index);
10570 * Remove a Record from the Store and fires the remove event.
10571 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10573 remove : function(record){
10574 var index = this.data.indexOf(record);
10575 this.data.removeAt(index);
10576 if(this.pruneModifiedRecords){
10577 this.modified.remove(record);
10579 this.fireEvent("remove", this, record, index);
10583 * Remove all Records from the Store and fires the clear event.
10585 removeAll : function(){
10587 if(this.pruneModifiedRecords){
10588 this.modified = [];
10590 this.fireEvent("clear", this);
10594 * Inserts Records to the Store at the given index and fires the add event.
10595 * @param {Number} index The start index at which to insert the passed Records.
10596 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10598 insert : function(index, records){
10599 records = [].concat(records);
10600 for(var i = 0, len = records.length; i < len; i++){
10601 this.data.insert(index, records[i]);
10602 records[i].join(this);
10604 this.fireEvent("add", this, records, index);
10608 * Get the index within the cache of the passed Record.
10609 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10610 * @return {Number} The index of the passed Record. Returns -1 if not found.
10612 indexOf : function(record){
10613 return this.data.indexOf(record);
10617 * Get the index within the cache of the Record with the passed id.
10618 * @param {String} id The id of the Record to find.
10619 * @return {Number} The index of the Record. Returns -1 if not found.
10621 indexOfId : function(id){
10622 return this.data.indexOfKey(id);
10626 * Get the Record with the specified id.
10627 * @param {String} id The id of the Record to find.
10628 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10630 getById : function(id){
10631 return this.data.key(id);
10635 * Get the Record at the specified index.
10636 * @param {Number} index The index of the Record to find.
10637 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10639 getAt : function(index){
10640 return this.data.itemAt(index);
10644 * Returns a range of Records between specified indices.
10645 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10646 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10647 * @return {Roo.data.Record[]} An array of Records
10649 getRange : function(start, end){
10650 return this.data.getRange(start, end);
10654 storeOptions : function(o){
10655 o = Roo.apply({}, o);
10658 this.lastOptions = o;
10662 * Loads the Record cache from the configured Proxy using the configured Reader.
10664 * If using remote paging, then the first load call must specify the <em>start</em>
10665 * and <em>limit</em> properties in the options.params property to establish the initial
10666 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10668 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10669 * and this call will return before the new data has been loaded. Perform any post-processing
10670 * in a callback function, or in a "load" event handler.</strong>
10672 * @param {Object} options An object containing properties which control loading options:<ul>
10673 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10674 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10675 * passed the following arguments:<ul>
10676 * <li>r : Roo.data.Record[]</li>
10677 * <li>options: Options object from the load call</li>
10678 * <li>success: Boolean success indicator</li></ul></li>
10679 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10680 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10683 load : function(options){
10684 options = options || {};
10685 if(this.fireEvent("beforeload", this, options) !== false){
10686 this.storeOptions(options);
10687 var p = Roo.apply(options.params || {}, this.baseParams);
10688 // if meta was not loaded from remote source.. try requesting it.
10689 if (!this.reader.metaFromRemote) {
10690 p._requestMeta = 1;
10692 if(this.sortInfo && this.remoteSort){
10693 var pn = this.paramNames;
10694 p[pn["sort"]] = this.sortInfo.field;
10695 p[pn["dir"]] = this.sortInfo.direction;
10697 if (this.multiSort) {
10698 var pn = this.paramNames;
10699 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10702 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10707 * Reloads the Record cache from the configured Proxy using the configured Reader and
10708 * the options from the last load operation performed.
10709 * @param {Object} options (optional) An object containing properties which may override the options
10710 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10711 * the most recently used options are reused).
10713 reload : function(options){
10714 this.load(Roo.applyIf(options||{}, this.lastOptions));
10718 // Called as a callback by the Reader during a load operation.
10719 loadRecords : function(o, options, success){
10720 if(!o || success === false){
10721 if(success !== false){
10722 this.fireEvent("load", this, [], options, o);
10724 if(options.callback){
10725 options.callback.call(options.scope || this, [], options, false);
10729 // if data returned failure - throw an exception.
10730 if (o.success === false) {
10731 // show a message if no listener is registered.
10732 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10733 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10735 // loadmask wil be hooked into this..
10736 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10739 var r = o.records, t = o.totalRecords || r.length;
10741 this.fireEvent("beforeloadadd", this, r, options, o);
10743 if(!options || options.add !== true){
10744 if(this.pruneModifiedRecords){
10745 this.modified = [];
10747 for(var i = 0, len = r.length; i < len; i++){
10751 this.data = this.snapshot;
10752 delete this.snapshot;
10755 this.data.addAll(r);
10756 this.totalLength = t;
10758 this.fireEvent("datachanged", this);
10760 this.totalLength = Math.max(t, this.data.length+r.length);
10763 this.fireEvent("load", this, r, options, o);
10764 if(options.callback){
10765 options.callback.call(options.scope || this, r, options, true);
10771 * Loads data from a passed data block. A Reader which understands the format of the data
10772 * must have been configured in the constructor.
10773 * @param {Object} data The data block from which to read the Records. The format of the data expected
10774 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10775 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10777 loadData : function(o, append){
10778 var r = this.reader.readRecords(o);
10779 this.loadRecords(r, {add: append}, true);
10783 * Gets the number of cached records.
10785 * <em>If using paging, this may not be the total size of the dataset. If the data object
10786 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10787 * the data set size</em>
10789 getCount : function(){
10790 return this.data.length || 0;
10794 * Gets the total number of records in the dataset as returned by the server.
10796 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10797 * the dataset size</em>
10799 getTotalCount : function(){
10800 return this.totalLength || 0;
10804 * Returns the sort state of the Store as an object with two properties:
10806 field {String} The name of the field by which the Records are sorted
10807 direction {String} The sort order, "ASC" or "DESC"
10810 getSortState : function(){
10811 return this.sortInfo;
10815 applySort : function(){
10816 if(this.sortInfo && !this.remoteSort){
10817 var s = this.sortInfo, f = s.field;
10818 var st = this.fields.get(f).sortType;
10819 var fn = function(r1, r2){
10820 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10821 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10823 this.data.sort(s.direction, fn);
10824 if(this.snapshot && this.snapshot != this.data){
10825 this.snapshot.sort(s.direction, fn);
10831 * Sets the default sort column and order to be used by the next load operation.
10832 * @param {String} fieldName The name of the field to sort by.
10833 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10835 setDefaultSort : function(field, dir){
10836 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10840 * Sort the Records.
10841 * If remote sorting is used, the sort is performed on the server, and the cache is
10842 * reloaded. If local sorting is used, the cache is sorted internally.
10843 * @param {String} fieldName The name of the field to sort by.
10844 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10846 sort : function(fieldName, dir){
10847 var f = this.fields.get(fieldName);
10849 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10851 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10852 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10857 this.sortToggle[f.name] = dir;
10858 this.sortInfo = {field: f.name, direction: dir};
10859 if(!this.remoteSort){
10861 this.fireEvent("datachanged", this);
10863 this.load(this.lastOptions);
10868 * Calls the specified function for each of the Records in the cache.
10869 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10870 * Returning <em>false</em> aborts and exits the iteration.
10871 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10873 each : function(fn, scope){
10874 this.data.each(fn, scope);
10878 * Gets all records modified since the last commit. Modified records are persisted across load operations
10879 * (e.g., during paging).
10880 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10882 getModifiedRecords : function(){
10883 return this.modified;
10887 createFilterFn : function(property, value, anyMatch){
10888 if(!value.exec){ // not a regex
10889 value = String(value);
10890 if(value.length == 0){
10893 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10895 return function(r){
10896 return value.test(r.data[property]);
10901 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10902 * @param {String} property A field on your records
10903 * @param {Number} start The record index to start at (defaults to 0)
10904 * @param {Number} end The last record index to include (defaults to length - 1)
10905 * @return {Number} The sum
10907 sum : function(property, start, end){
10908 var rs = this.data.items, v = 0;
10909 start = start || 0;
10910 end = (end || end === 0) ? end : rs.length-1;
10912 for(var i = start; i <= end; i++){
10913 v += (rs[i].data[property] || 0);
10919 * Filter the records by a specified property.
10920 * @param {String} field A field on your records
10921 * @param {String/RegExp} value Either a string that the field
10922 * should start with or a RegExp to test against the field
10923 * @param {Boolean} anyMatch True to match any part not just the beginning
10925 filter : function(property, value, anyMatch){
10926 var fn = this.createFilterFn(property, value, anyMatch);
10927 return fn ? this.filterBy(fn) : this.clearFilter();
10931 * Filter by a function. The specified function will be called with each
10932 * record in this data source. If the function returns true the record is included,
10933 * otherwise it is filtered.
10934 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10935 * @param {Object} scope (optional) The scope of the function (defaults to this)
10937 filterBy : function(fn, scope){
10938 this.snapshot = this.snapshot || this.data;
10939 this.data = this.queryBy(fn, scope||this);
10940 this.fireEvent("datachanged", this);
10944 * Query the records by a specified property.
10945 * @param {String} field A field on your records
10946 * @param {String/RegExp} value Either a string that the field
10947 * should start with or a RegExp to test against the field
10948 * @param {Boolean} anyMatch True to match any part not just the beginning
10949 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10951 query : function(property, value, anyMatch){
10952 var fn = this.createFilterFn(property, value, anyMatch);
10953 return fn ? this.queryBy(fn) : this.data.clone();
10957 * Query by a function. The specified function will be called with each
10958 * record in this data source. If the function returns true the record is included
10960 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10961 * @param {Object} scope (optional) The scope of the function (defaults to this)
10962 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10964 queryBy : function(fn, scope){
10965 var data = this.snapshot || this.data;
10966 return data.filterBy(fn, scope||this);
10970 * Collects unique values for a particular dataIndex from this store.
10971 * @param {String} dataIndex The property to collect
10972 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10973 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10974 * @return {Array} An array of the unique values
10976 collect : function(dataIndex, allowNull, bypassFilter){
10977 var d = (bypassFilter === true && this.snapshot) ?
10978 this.snapshot.items : this.data.items;
10979 var v, sv, r = [], l = {};
10980 for(var i = 0, len = d.length; i < len; i++){
10981 v = d[i].data[dataIndex];
10983 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10992 * Revert to a view of the Record cache with no filtering applied.
10993 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10995 clearFilter : function(suppressEvent){
10996 if(this.snapshot && this.snapshot != this.data){
10997 this.data = this.snapshot;
10998 delete this.snapshot;
10999 if(suppressEvent !== true){
11000 this.fireEvent("datachanged", this);
11006 afterEdit : function(record){
11007 if(this.modified.indexOf(record) == -1){
11008 this.modified.push(record);
11010 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11014 afterReject : function(record){
11015 this.modified.remove(record);
11016 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11020 afterCommit : function(record){
11021 this.modified.remove(record);
11022 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11026 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11027 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11029 commitChanges : function(){
11030 var m = this.modified.slice(0);
11031 this.modified = [];
11032 for(var i = 0, len = m.length; i < len; i++){
11038 * Cancel outstanding changes on all changed records.
11040 rejectChanges : function(){
11041 var m = this.modified.slice(0);
11042 this.modified = [];
11043 for(var i = 0, len = m.length; i < len; i++){
11048 onMetaChange : function(meta, rtype, o){
11049 this.recordType = rtype;
11050 this.fields = rtype.prototype.fields;
11051 delete this.snapshot;
11052 this.sortInfo = meta.sortInfo || this.sortInfo;
11053 this.modified = [];
11054 this.fireEvent('metachange', this, this.reader.meta);
11057 moveIndex : function(data, type)
11059 var index = this.indexOf(data);
11061 var newIndex = index + type;
11065 this.insert(newIndex, data);
11070 * Ext JS Library 1.1.1
11071 * Copyright(c) 2006-2007, Ext JS, LLC.
11073 * Originally Released Under LGPL - original licence link has changed is not relivant.
11076 * <script type="text/javascript">
11080 * @class Roo.data.SimpleStore
11081 * @extends Roo.data.Store
11082 * Small helper class to make creating Stores from Array data easier.
11083 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11084 * @cfg {Array} fields An array of field definition objects, or field name strings.
11085 * @cfg {Array} data The multi-dimensional array of data
11087 * @param {Object} config
11089 Roo.data.SimpleStore = function(config){
11090 Roo.data.SimpleStore.superclass.constructor.call(this, {
11092 reader: new Roo.data.ArrayReader({
11095 Roo.data.Record.create(config.fields)
11097 proxy : new Roo.data.MemoryProxy(config.data)
11101 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11103 * Ext JS Library 1.1.1
11104 * Copyright(c) 2006-2007, Ext JS, LLC.
11106 * Originally Released Under LGPL - original licence link has changed is not relivant.
11109 * <script type="text/javascript">
11114 * @extends Roo.data.Store
11115 * @class Roo.data.JsonStore
11116 * Small helper class to make creating Stores for JSON data easier. <br/>
11118 var store = new Roo.data.JsonStore({
11119 url: 'get-images.php',
11121 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11124 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11125 * JsonReader and HttpProxy (unless inline data is provided).</b>
11126 * @cfg {Array} fields An array of field definition objects, or field name strings.
11128 * @param {Object} config
11130 Roo.data.JsonStore = function(c){
11131 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11132 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11133 reader: new Roo.data.JsonReader(c, c.fields)
11136 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11138 * Ext JS Library 1.1.1
11139 * Copyright(c) 2006-2007, Ext JS, LLC.
11141 * Originally Released Under LGPL - original licence link has changed is not relivant.
11144 * <script type="text/javascript">
11148 Roo.data.Field = function(config){
11149 if(typeof config == "string"){
11150 config = {name: config};
11152 Roo.apply(this, config);
11155 this.type = "auto";
11158 var st = Roo.data.SortTypes;
11159 // named sortTypes are supported, here we look them up
11160 if(typeof this.sortType == "string"){
11161 this.sortType = st[this.sortType];
11164 // set default sortType for strings and dates
11165 if(!this.sortType){
11168 this.sortType = st.asUCString;
11171 this.sortType = st.asDate;
11174 this.sortType = st.none;
11179 var stripRe = /[\$,%]/g;
11181 // prebuilt conversion function for this field, instead of
11182 // switching every time we're reading a value
11184 var cv, dateFormat = this.dateFormat;
11189 cv = function(v){ return v; };
11192 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11196 return v !== undefined && v !== null && v !== '' ?
11197 parseInt(String(v).replace(stripRe, ""), 10) : '';
11202 return v !== undefined && v !== null && v !== '' ?
11203 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11208 cv = function(v){ return v === true || v === "true" || v == 1; };
11215 if(v instanceof Date){
11219 if(dateFormat == "timestamp"){
11220 return new Date(v*1000);
11222 return Date.parseDate(v, dateFormat);
11224 var parsed = Date.parse(v);
11225 return parsed ? new Date(parsed) : null;
11234 Roo.data.Field.prototype = {
11242 * Ext JS Library 1.1.1
11243 * Copyright(c) 2006-2007, Ext JS, LLC.
11245 * Originally Released Under LGPL - original licence link has changed is not relivant.
11248 * <script type="text/javascript">
11251 // Base class for reading structured data from a data source. This class is intended to be
11252 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11255 * @class Roo.data.DataReader
11256 * Base class for reading structured data from a data source. This class is intended to be
11257 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11260 Roo.data.DataReader = function(meta, recordType){
11264 this.recordType = recordType instanceof Array ?
11265 Roo.data.Record.create(recordType) : recordType;
11268 Roo.data.DataReader.prototype = {
11270 * Create an empty record
11271 * @param {Object} data (optional) - overlay some values
11272 * @return {Roo.data.Record} record created.
11274 newRow : function(d) {
11276 this.recordType.prototype.fields.each(function(c) {
11278 case 'int' : da[c.name] = 0; break;
11279 case 'date' : da[c.name] = new Date(); break;
11280 case 'float' : da[c.name] = 0.0; break;
11281 case 'boolean' : da[c.name] = false; break;
11282 default : da[c.name] = ""; break;
11286 return new this.recordType(Roo.apply(da, d));
11291 * Ext JS Library 1.1.1
11292 * Copyright(c) 2006-2007, Ext JS, LLC.
11294 * Originally Released Under LGPL - original licence link has changed is not relivant.
11297 * <script type="text/javascript">
11301 * @class Roo.data.DataProxy
11302 * @extends Roo.data.Observable
11303 * This class is an abstract base class for implementations which provide retrieval of
11304 * unformatted data objects.<br>
11306 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11307 * (of the appropriate type which knows how to parse the data object) to provide a block of
11308 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11310 * Custom implementations must implement the load method as described in
11311 * {@link Roo.data.HttpProxy#load}.
11313 Roo.data.DataProxy = function(){
11316 * @event beforeload
11317 * Fires before a network request is made to retrieve a data object.
11318 * @param {Object} This DataProxy object.
11319 * @param {Object} params The params parameter to the load function.
11324 * Fires before the load method's callback is called.
11325 * @param {Object} This DataProxy object.
11326 * @param {Object} o The data object.
11327 * @param {Object} arg The callback argument object passed to the load function.
11331 * @event loadexception
11332 * Fires if an Exception occurs during data retrieval.
11333 * @param {Object} This DataProxy object.
11334 * @param {Object} o The data object.
11335 * @param {Object} arg The callback argument object passed to the load function.
11336 * @param {Object} e The Exception.
11338 loadexception : true
11340 Roo.data.DataProxy.superclass.constructor.call(this);
11343 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11346 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11350 * Ext JS Library 1.1.1
11351 * Copyright(c) 2006-2007, Ext JS, LLC.
11353 * Originally Released Under LGPL - original licence link has changed is not relivant.
11356 * <script type="text/javascript">
11359 * @class Roo.data.MemoryProxy
11360 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11361 * to the Reader when its load method is called.
11363 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11365 Roo.data.MemoryProxy = function(data){
11369 Roo.data.MemoryProxy.superclass.constructor.call(this);
11373 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11376 * Load data from the requested source (in this case an in-memory
11377 * data object passed to the constructor), read the data object into
11378 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11379 * process that block using the passed callback.
11380 * @param {Object} params This parameter is not used by the MemoryProxy class.
11381 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11382 * object into a block of Roo.data.Records.
11383 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11384 * The function must be passed <ul>
11385 * <li>The Record block object</li>
11386 * <li>The "arg" argument from the load function</li>
11387 * <li>A boolean success indicator</li>
11389 * @param {Object} scope The scope in which to call the callback
11390 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11392 load : function(params, reader, callback, scope, arg){
11393 params = params || {};
11396 result = reader.readRecords(this.data);
11398 this.fireEvent("loadexception", this, arg, null, e);
11399 callback.call(scope, null, arg, false);
11402 callback.call(scope, result, arg, true);
11406 update : function(params, records){
11411 * Ext JS Library 1.1.1
11412 * Copyright(c) 2006-2007, Ext JS, LLC.
11414 * Originally Released Under LGPL - original licence link has changed is not relivant.
11417 * <script type="text/javascript">
11420 * @class Roo.data.HttpProxy
11421 * @extends Roo.data.DataProxy
11422 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11423 * configured to reference a certain URL.<br><br>
11425 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11426 * from which the running page was served.<br><br>
11428 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11430 * Be aware that to enable the browser to parse an XML document, the server must set
11431 * the Content-Type header in the HTTP response to "text/xml".
11433 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11434 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11435 * will be used to make the request.
11437 Roo.data.HttpProxy = function(conn){
11438 Roo.data.HttpProxy.superclass.constructor.call(this);
11439 // is conn a conn config or a real conn?
11441 this.useAjax = !conn || !conn.events;
11445 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11446 // thse are take from connection...
11449 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11452 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11453 * extra parameters to each request made by this object. (defaults to undefined)
11456 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11457 * to each request made by this object. (defaults to undefined)
11460 * @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)
11463 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11466 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11472 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11476 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11477 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11478 * a finer-grained basis than the DataProxy events.
11480 getConnection : function(){
11481 return this.useAjax ? Roo.Ajax : this.conn;
11485 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11486 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11487 * process that block using the passed callback.
11488 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11489 * for the request to the remote server.
11490 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11491 * object into a block of Roo.data.Records.
11492 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11493 * The function must be passed <ul>
11494 * <li>The Record block object</li>
11495 * <li>The "arg" argument from the load function</li>
11496 * <li>A boolean success indicator</li>
11498 * @param {Object} scope The scope in which to call the callback
11499 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11501 load : function(params, reader, callback, scope, arg){
11502 if(this.fireEvent("beforeload", this, params) !== false){
11504 params : params || {},
11506 callback : callback,
11511 callback : this.loadResponse,
11515 Roo.applyIf(o, this.conn);
11516 if(this.activeRequest){
11517 Roo.Ajax.abort(this.activeRequest);
11519 this.activeRequest = Roo.Ajax.request(o);
11521 this.conn.request(o);
11524 callback.call(scope||this, null, arg, false);
11529 loadResponse : function(o, success, response){
11530 delete this.activeRequest;
11532 this.fireEvent("loadexception", this, o, response);
11533 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11538 result = o.reader.read(response);
11540 this.fireEvent("loadexception", this, o, response, e);
11541 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11545 this.fireEvent("load", this, o, o.request.arg);
11546 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11550 update : function(dataSet){
11555 updateResponse : function(dataSet){
11560 * Ext JS Library 1.1.1
11561 * Copyright(c) 2006-2007, Ext JS, LLC.
11563 * Originally Released Under LGPL - original licence link has changed is not relivant.
11566 * <script type="text/javascript">
11570 * @class Roo.data.ScriptTagProxy
11571 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11572 * other than the originating domain of the running page.<br><br>
11574 * <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
11575 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11577 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11578 * source code that is used as the source inside a <script> tag.<br><br>
11580 * In order for the browser to process the returned data, the server must wrap the data object
11581 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11582 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11583 * depending on whether the callback name was passed:
11586 boolean scriptTag = false;
11587 String cb = request.getParameter("callback");
11590 response.setContentType("text/javascript");
11592 response.setContentType("application/x-json");
11594 Writer out = response.getWriter();
11596 out.write(cb + "(");
11598 out.print(dataBlock.toJsonString());
11605 * @param {Object} config A configuration object.
11607 Roo.data.ScriptTagProxy = function(config){
11608 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11609 Roo.apply(this, config);
11610 this.head = document.getElementsByTagName("head")[0];
11613 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11615 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11617 * @cfg {String} url The URL from which to request the data object.
11620 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11624 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11625 * the server the name of the callback function set up by the load call to process the returned data object.
11626 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11627 * javascript output which calls this named function passing the data object as its only parameter.
11629 callbackParam : "callback",
11631 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11632 * name to the request.
11637 * Load data from the configured URL, read the data object into
11638 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11639 * process that block using the passed callback.
11640 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11641 * for the request to the remote server.
11642 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11643 * object into a block of Roo.data.Records.
11644 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11645 * The function must be passed <ul>
11646 * <li>The Record block object</li>
11647 * <li>The "arg" argument from the load function</li>
11648 * <li>A boolean success indicator</li>
11650 * @param {Object} scope The scope in which to call the callback
11651 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11653 load : function(params, reader, callback, scope, arg){
11654 if(this.fireEvent("beforeload", this, params) !== false){
11656 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11658 var url = this.url;
11659 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11661 url += "&_dc=" + (new Date().getTime());
11663 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11666 cb : "stcCallback"+transId,
11667 scriptId : "stcScript"+transId,
11671 callback : callback,
11677 window[trans.cb] = function(o){
11678 conn.handleResponse(o, trans);
11681 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11683 if(this.autoAbort !== false){
11687 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11689 var script = document.createElement("script");
11690 script.setAttribute("src", url);
11691 script.setAttribute("type", "text/javascript");
11692 script.setAttribute("id", trans.scriptId);
11693 this.head.appendChild(script);
11695 this.trans = trans;
11697 callback.call(scope||this, null, arg, false);
11702 isLoading : function(){
11703 return this.trans ? true : false;
11707 * Abort the current server request.
11709 abort : function(){
11710 if(this.isLoading()){
11711 this.destroyTrans(this.trans);
11716 destroyTrans : function(trans, isLoaded){
11717 this.head.removeChild(document.getElementById(trans.scriptId));
11718 clearTimeout(trans.timeoutId);
11720 window[trans.cb] = undefined;
11722 delete window[trans.cb];
11725 // if hasn't been loaded, wait for load to remove it to prevent script error
11726 window[trans.cb] = function(){
11727 window[trans.cb] = undefined;
11729 delete window[trans.cb];
11736 handleResponse : function(o, trans){
11737 this.trans = false;
11738 this.destroyTrans(trans, true);
11741 result = trans.reader.readRecords(o);
11743 this.fireEvent("loadexception", this, o, trans.arg, e);
11744 trans.callback.call(trans.scope||window, null, trans.arg, false);
11747 this.fireEvent("load", this, o, trans.arg);
11748 trans.callback.call(trans.scope||window, result, trans.arg, true);
11752 handleFailure : function(trans){
11753 this.trans = false;
11754 this.destroyTrans(trans, false);
11755 this.fireEvent("loadexception", this, null, trans.arg);
11756 trans.callback.call(trans.scope||window, null, trans.arg, false);
11760 * Ext JS Library 1.1.1
11761 * Copyright(c) 2006-2007, Ext JS, LLC.
11763 * Originally Released Under LGPL - original licence link has changed is not relivant.
11766 * <script type="text/javascript">
11770 * @class Roo.data.JsonReader
11771 * @extends Roo.data.DataReader
11772 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11773 * based on mappings in a provided Roo.data.Record constructor.
11775 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11776 * in the reply previously.
11781 var RecordDef = Roo.data.Record.create([
11782 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11783 {name: 'occupation'} // This field will use "occupation" as the mapping.
11785 var myReader = new Roo.data.JsonReader({
11786 totalProperty: "results", // The property which contains the total dataset size (optional)
11787 root: "rows", // The property which contains an Array of row objects
11788 id: "id" // The property within each row object that provides an ID for the record (optional)
11792 * This would consume a JSON file like this:
11794 { 'results': 2, 'rows': [
11795 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11796 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11799 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11800 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11801 * paged from the remote server.
11802 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11803 * @cfg {String} root name of the property which contains the Array of row objects.
11804 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11805 * @cfg {Array} fields Array of field definition objects
11807 * Create a new JsonReader
11808 * @param {Object} meta Metadata configuration options
11809 * @param {Object} recordType Either an Array of field definition objects,
11810 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11812 Roo.data.JsonReader = function(meta, recordType){
11815 // set some defaults:
11816 Roo.applyIf(meta, {
11817 totalProperty: 'total',
11818 successProperty : 'success',
11823 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11825 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11828 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11829 * Used by Store query builder to append _requestMeta to params.
11832 metaFromRemote : false,
11834 * This method is only used by a DataProxy which has retrieved data from a remote server.
11835 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11836 * @return {Object} data A data block which is used by an Roo.data.Store object as
11837 * a cache of Roo.data.Records.
11839 read : function(response){
11840 var json = response.responseText;
11842 var o = /* eval:var:o */ eval("("+json+")");
11844 throw {message: "JsonReader.read: Json object not found"};
11850 this.metaFromRemote = true;
11851 this.meta = o.metaData;
11852 this.recordType = Roo.data.Record.create(o.metaData.fields);
11853 this.onMetaChange(this.meta, this.recordType, o);
11855 return this.readRecords(o);
11858 // private function a store will implement
11859 onMetaChange : function(meta, recordType, o){
11866 simpleAccess: function(obj, subsc) {
11873 getJsonAccessor: function(){
11875 return function(expr) {
11877 return(re.test(expr))
11878 ? new Function("obj", "return obj." + expr)
11883 return Roo.emptyFn;
11888 * Create a data block containing Roo.data.Records from an XML document.
11889 * @param {Object} o An object which contains an Array of row objects in the property specified
11890 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11891 * which contains the total size of the dataset.
11892 * @return {Object} data A data block which is used by an Roo.data.Store object as
11893 * a cache of Roo.data.Records.
11895 readRecords : function(o){
11897 * After any data loads, the raw JSON data is available for further custom processing.
11901 var s = this.meta, Record = this.recordType,
11902 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11904 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11906 if(s.totalProperty) {
11907 this.getTotal = this.getJsonAccessor(s.totalProperty);
11909 if(s.successProperty) {
11910 this.getSuccess = this.getJsonAccessor(s.successProperty);
11912 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11914 var g = this.getJsonAccessor(s.id);
11915 this.getId = function(rec) {
11917 return (r === undefined || r === "") ? null : r;
11920 this.getId = function(){return null;};
11923 for(var jj = 0; jj < fl; jj++){
11925 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11926 this.ef[jj] = this.getJsonAccessor(map);
11930 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11931 if(s.totalProperty){
11932 var vt = parseInt(this.getTotal(o), 10);
11937 if(s.successProperty){
11938 var vs = this.getSuccess(o);
11939 if(vs === false || vs === 'false'){
11944 for(var i = 0; i < c; i++){
11947 var id = this.getId(n);
11948 for(var j = 0; j < fl; j++){
11950 var v = this.ef[j](n);
11952 Roo.log('missing convert for ' + f.name);
11956 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11958 var record = new Record(values, id);
11960 records[i] = record;
11966 totalRecords : totalRecords
11971 * Ext JS Library 1.1.1
11972 * Copyright(c) 2006-2007, Ext JS, LLC.
11974 * Originally Released Under LGPL - original licence link has changed is not relivant.
11977 * <script type="text/javascript">
11981 * @class Roo.data.ArrayReader
11982 * @extends Roo.data.DataReader
11983 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11984 * Each element of that Array represents a row of data fields. The
11985 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11986 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11990 var RecordDef = Roo.data.Record.create([
11991 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11992 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11994 var myReader = new Roo.data.ArrayReader({
11995 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11999 * This would consume an Array like this:
12001 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12003 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12005 * Create a new JsonReader
12006 * @param {Object} meta Metadata configuration options.
12007 * @param {Object} recordType Either an Array of field definition objects
12008 * as specified to {@link Roo.data.Record#create},
12009 * or an {@link Roo.data.Record} object
12010 * created using {@link Roo.data.Record#create}.
12012 Roo.data.ArrayReader = function(meta, recordType){
12013 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12016 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12018 * Create a data block containing Roo.data.Records from an XML document.
12019 * @param {Object} o An Array of row objects which represents the dataset.
12020 * @return {Object} data A data block which is used by an Roo.data.Store object as
12021 * a cache of Roo.data.Records.
12023 readRecords : function(o){
12024 var sid = this.meta ? this.meta.id : null;
12025 var recordType = this.recordType, fields = recordType.prototype.fields;
12028 for(var i = 0; i < root.length; i++){
12031 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12032 for(var j = 0, jlen = fields.length; j < jlen; j++){
12033 var f = fields.items[j];
12034 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12035 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12037 values[f.name] = v;
12039 var record = new recordType(values, id);
12041 records[records.length] = record;
12045 totalRecords : records.length
12054 * @class Roo.bootstrap.ComboBox
12055 * @extends Roo.bootstrap.TriggerField
12056 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12057 * @cfg {Boolean} append (true|false) default false
12058 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12059 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12060 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12061 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12062 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12063 * @cfg {Boolean} animate default true
12064 * @cfg {Boolean} emptyResultText only for touch device
12065 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12067 * Create a new ComboBox.
12068 * @param {Object} config Configuration options
12070 Roo.bootstrap.ComboBox = function(config){
12071 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12075 * Fires when the dropdown list is expanded
12076 * @param {Roo.bootstrap.ComboBox} combo This combo box
12081 * Fires when the dropdown list is collapsed
12082 * @param {Roo.bootstrap.ComboBox} combo This combo box
12086 * @event beforeselect
12087 * Fires before a list item is selected. Return false to cancel the selection.
12088 * @param {Roo.bootstrap.ComboBox} combo This combo box
12089 * @param {Roo.data.Record} record The data record returned from the underlying store
12090 * @param {Number} index The index of the selected item in the dropdown list
12092 'beforeselect' : true,
12095 * Fires when a list item is selected
12096 * @param {Roo.bootstrap.ComboBox} combo This combo box
12097 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12098 * @param {Number} index The index of the selected item in the dropdown list
12102 * @event beforequery
12103 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12104 * The event object passed has these properties:
12105 * @param {Roo.bootstrap.ComboBox} combo This combo box
12106 * @param {String} query The query
12107 * @param {Boolean} forceAll true to force "all" query
12108 * @param {Boolean} cancel true to cancel the query
12109 * @param {Object} e The query event object
12111 'beforequery': true,
12114 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12115 * @param {Roo.bootstrap.ComboBox} combo This combo box
12120 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12121 * @param {Roo.bootstrap.ComboBox} combo This combo box
12122 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12127 * Fires when the remove value from the combobox array
12128 * @param {Roo.bootstrap.ComboBox} combo This combo box
12132 * @event afterremove
12133 * Fires when the remove value from the combobox array
12134 * @param {Roo.bootstrap.ComboBox} combo This combo box
12136 'afterremove' : true,
12138 * @event specialfilter
12139 * Fires when specialfilter
12140 * @param {Roo.bootstrap.ComboBox} combo This combo box
12142 'specialfilter' : true,
12145 * Fires when tick the element
12146 * @param {Roo.bootstrap.ComboBox} combo This combo box
12150 * @event touchviewdisplay
12151 * Fires when touch view require special display (default is using displayField)
12152 * @param {Roo.bootstrap.ComboBox} combo This combo box
12153 * @param {Object} cfg set html .
12155 'touchviewdisplay' : true
12160 this.tickItems = [];
12162 this.selectedIndex = -1;
12163 if(this.mode == 'local'){
12164 if(config.queryDelay === undefined){
12165 this.queryDelay = 10;
12167 if(config.minChars === undefined){
12173 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12176 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12177 * rendering into an Roo.Editor, defaults to false)
12180 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12181 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12184 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12187 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12188 * the dropdown list (defaults to undefined, with no header element)
12192 * @cfg {String/Roo.Template} tpl The template to use to render the output
12196 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12198 listWidth: undefined,
12200 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12201 * mode = 'remote' or 'text' if mode = 'local')
12203 displayField: undefined,
12206 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12207 * mode = 'remote' or 'value' if mode = 'local').
12208 * Note: use of a valueField requires the user make a selection
12209 * in order for a value to be mapped.
12211 valueField: undefined,
12213 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12218 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12219 * field's data value (defaults to the underlying DOM element's name)
12221 hiddenName: undefined,
12223 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12227 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12229 selectedClass: 'active',
12232 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12236 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12237 * anchor positions (defaults to 'tl-bl')
12239 listAlign: 'tl-bl?',
12241 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12245 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12246 * query specified by the allQuery config option (defaults to 'query')
12248 triggerAction: 'query',
12250 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12251 * (defaults to 4, does not apply if editable = false)
12255 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12256 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12260 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12261 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12265 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12266 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12270 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12271 * when editable = true (defaults to false)
12273 selectOnFocus:false,
12275 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12277 queryParam: 'query',
12279 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12280 * when mode = 'remote' (defaults to 'Loading...')
12282 loadingText: 'Loading...',
12284 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12288 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12292 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12293 * traditional select (defaults to true)
12297 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12301 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12305 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12306 * listWidth has a higher value)
12310 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12311 * allow the user to set arbitrary text into the field (defaults to false)
12313 forceSelection:false,
12315 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12316 * if typeAhead = true (defaults to 250)
12318 typeAheadDelay : 250,
12320 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12321 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12323 valueNotFoundText : undefined,
12325 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12327 blockFocus : false,
12330 * @cfg {Boolean} disableClear Disable showing of clear button.
12332 disableClear : false,
12334 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12336 alwaysQuery : false,
12339 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12344 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12346 invalidClass : "has-warning",
12349 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12351 validClass : "has-success",
12354 * @cfg {Boolean} specialFilter (true|false) special filter default false
12356 specialFilter : false,
12359 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12361 mobileTouchView : true,
12364 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12366 useNativeIOS : false,
12368 ios_options : false,
12380 btnPosition : 'right',
12381 triggerList : true,
12382 showToggleBtn : true,
12384 emptyResultText: 'Empty',
12385 triggerText : 'Select',
12387 // element that contains real text value.. (when hidden is used..)
12389 getAutoCreate : function()
12394 * Render classic select for iso
12397 if(Roo.isIOS && this.useNativeIOS){
12398 cfg = this.getAutoCreateNativeIOS();
12406 if(Roo.isTouch && this.mobileTouchView){
12407 cfg = this.getAutoCreateTouchView();
12414 if(!this.tickable){
12415 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12420 * ComboBox with tickable selections
12423 var align = this.labelAlign || this.parentLabelAlign();
12426 cls : 'form-group roo-combobox-tickable' //input-group
12429 var btn_text_select = '';
12430 var btn_text_done = '';
12431 var btn_text_cancel = '';
12433 if (this.btn_text_show) {
12434 btn_text_select = 'Select';
12435 btn_text_done = 'Done';
12436 btn_text_cancel = 'Cancel';
12441 cls : 'tickable-buttons',
12446 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12447 //html : this.triggerText
12448 html: btn_text_select
12454 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12456 html: btn_text_done
12462 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12464 html: btn_text_cancel
12470 buttons.cn.unshift({
12472 cls: 'roo-select2-search-field-input'
12478 Roo.each(buttons.cn, function(c){
12480 c.cls += ' btn-' + _this.size;
12483 if (_this.disabled) {
12494 cls: 'form-hidden-field'
12498 cls: 'roo-select2-choices',
12502 cls: 'roo-select2-search-field',
12514 cls: 'roo-select2-container input-group roo-select2-container-multi',
12519 // cls: 'typeahead typeahead-long dropdown-menu',
12520 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12525 if(this.hasFeedback && !this.allowBlank){
12529 cls: 'glyphicon form-control-feedback'
12532 combobox.cn.push(feedback);
12535 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12537 // Roo.log("left and has label");
12541 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12542 tooltip : 'This field is required'
12547 cls : 'control-label col-sm-' + this.labelWidth,
12548 html : this.fieldLabel
12552 cls : "col-sm-" + (12 - this.labelWidth),
12560 if(this.indicatorpos == 'right'){
12566 cls : 'control-label col-sm-' + this.labelWidth,
12567 html : this.fieldLabel
12572 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12573 tooltip : 'This field is required'
12576 cls : "col-sm-" + (12 - this.labelWidth),
12587 } else if ( this.fieldLabel.length) {
12588 // Roo.log(" label");
12592 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12593 tooltip : 'This field is required'
12597 //cls : 'input-group-addon',
12598 html : this.fieldLabel
12606 if(this.indicatorpos == 'right'){
12611 //cls : 'input-group-addon',
12612 html : this.fieldLabel
12618 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12619 tooltip : 'This field is required'
12630 // Roo.log(" no label && no align");
12637 ['xs','sm','md','lg'].map(function(size){
12638 if (settings[size]) {
12639 cfg.cls += ' col-' + size + '-' + settings[size];
12647 _initEventsCalled : false,
12650 initEvents: function()
12652 if (this._initEventsCalled) { // as we call render... prevent looping...
12655 this._initEventsCalled = true;
12658 throw "can not find store for combo";
12661 this.store = Roo.factory(this.store, Roo.data);
12663 // if we are building from html. then this element is so complex, that we can not really
12664 // use the rendered HTML.
12665 // so we have to trash and replace the previous code.
12666 if (Roo.XComponent.build_from_html) {
12668 // remove this element....
12669 var e = this.el.dom, k=0;
12670 while (e ) { e = e.previousSibling; ++k;}
12675 this.rendered = false;
12677 this.render(this.parent().getChildContainer(true), k);
12683 if(Roo.isIOS && this.useNativeIOS){
12684 this.initIOSView();
12692 if(Roo.isTouch && this.mobileTouchView){
12693 this.initTouchView();
12698 this.initTickableEvents();
12702 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12704 if(this.hiddenName){
12706 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12708 this.hiddenField.dom.value =
12709 this.hiddenValue !== undefined ? this.hiddenValue :
12710 this.value !== undefined ? this.value : '';
12712 // prevent input submission
12713 this.el.dom.removeAttribute('name');
12714 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12719 // this.el.dom.setAttribute('autocomplete', 'off');
12722 var cls = 'x-combo-list';
12724 //this.list = new Roo.Layer({
12725 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12731 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12732 _this.list.setWidth(lw);
12735 this.list.on('mouseover', this.onViewOver, this);
12736 this.list.on('mousemove', this.onViewMove, this);
12738 this.list.on('scroll', this.onViewScroll, this);
12741 this.list.swallowEvent('mousewheel');
12742 this.assetHeight = 0;
12745 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12746 this.assetHeight += this.header.getHeight();
12749 this.innerList = this.list.createChild({cls:cls+'-inner'});
12750 this.innerList.on('mouseover', this.onViewOver, this);
12751 this.innerList.on('mousemove', this.onViewMove, this);
12752 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12754 if(this.allowBlank && !this.pageSize && !this.disableClear){
12755 this.footer = this.list.createChild({cls:cls+'-ft'});
12756 this.pageTb = new Roo.Toolbar(this.footer);
12760 this.footer = this.list.createChild({cls:cls+'-ft'});
12761 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12762 {pageSize: this.pageSize});
12766 if (this.pageTb && this.allowBlank && !this.disableClear) {
12768 this.pageTb.add(new Roo.Toolbar.Fill(), {
12769 cls: 'x-btn-icon x-btn-clear',
12771 handler: function()
12774 _this.clearValue();
12775 _this.onSelect(false, -1);
12780 this.assetHeight += this.footer.getHeight();
12785 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12788 this.view = new Roo.View(this.list, this.tpl, {
12789 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12791 //this.view.wrapEl.setDisplayed(false);
12792 this.view.on('click', this.onViewClick, this);
12796 this.store.on('beforeload', this.onBeforeLoad, this);
12797 this.store.on('load', this.onLoad, this);
12798 this.store.on('loadexception', this.onLoadException, this);
12800 if(this.resizable){
12801 this.resizer = new Roo.Resizable(this.list, {
12802 pinned:true, handles:'se'
12804 this.resizer.on('resize', function(r, w, h){
12805 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12806 this.listWidth = w;
12807 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12808 this.restrictHeight();
12810 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12813 if(!this.editable){
12814 this.editable = true;
12815 this.setEditable(false);
12820 if (typeof(this.events.add.listeners) != 'undefined') {
12822 this.addicon = this.wrap.createChild(
12823 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12825 this.addicon.on('click', function(e) {
12826 this.fireEvent('add', this);
12829 if (typeof(this.events.edit.listeners) != 'undefined') {
12831 this.editicon = this.wrap.createChild(
12832 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12833 if (this.addicon) {
12834 this.editicon.setStyle('margin-left', '40px');
12836 this.editicon.on('click', function(e) {
12838 // we fire even if inothing is selected..
12839 this.fireEvent('edit', this, this.lastData );
12845 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12846 "up" : function(e){
12847 this.inKeyMode = true;
12851 "down" : function(e){
12852 if(!this.isExpanded()){
12853 this.onTriggerClick();
12855 this.inKeyMode = true;
12860 "enter" : function(e){
12861 // this.onViewClick();
12865 if(this.fireEvent("specialkey", this, e)){
12866 this.onViewClick(false);
12872 "esc" : function(e){
12876 "tab" : function(e){
12879 if(this.fireEvent("specialkey", this, e)){
12880 this.onViewClick(false);
12888 doRelay : function(foo, bar, hname){
12889 if(hname == 'down' || this.scope.isExpanded()){
12890 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12899 this.queryDelay = Math.max(this.queryDelay || 10,
12900 this.mode == 'local' ? 10 : 250);
12903 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12905 if(this.typeAhead){
12906 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12908 if(this.editable !== false){
12909 this.inputEl().on("keyup", this.onKeyUp, this);
12911 if(this.forceSelection){
12912 this.inputEl().on('blur', this.doForce, this);
12916 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12917 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12921 initTickableEvents: function()
12925 if(this.hiddenName){
12927 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12929 this.hiddenField.dom.value =
12930 this.hiddenValue !== undefined ? this.hiddenValue :
12931 this.value !== undefined ? this.value : '';
12933 // prevent input submission
12934 this.el.dom.removeAttribute('name');
12935 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12940 // this.list = this.el.select('ul.dropdown-menu',true).first();
12942 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12943 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12944 if(this.triggerList){
12945 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12948 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12949 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12951 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12952 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12954 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12955 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12957 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12958 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12959 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12962 this.cancelBtn.hide();
12967 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12968 _this.list.setWidth(lw);
12971 this.list.on('mouseover', this.onViewOver, this);
12972 this.list.on('mousemove', this.onViewMove, this);
12974 this.list.on('scroll', this.onViewScroll, this);
12977 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
12980 this.view = new Roo.View(this.list, this.tpl, {
12981 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12984 //this.view.wrapEl.setDisplayed(false);
12985 this.view.on('click', this.onViewClick, this);
12989 this.store.on('beforeload', this.onBeforeLoad, this);
12990 this.store.on('load', this.onLoad, this);
12991 this.store.on('loadexception', this.onLoadException, this);
12994 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12995 "up" : function(e){
12996 this.inKeyMode = true;
13000 "down" : function(e){
13001 this.inKeyMode = true;
13005 "enter" : function(e){
13006 if(this.fireEvent("specialkey", this, e)){
13007 this.onViewClick(false);
13013 "esc" : function(e){
13014 this.onTickableFooterButtonClick(e, false, false);
13017 "tab" : function(e){
13018 this.fireEvent("specialkey", this, e);
13020 this.onTickableFooterButtonClick(e, false, false);
13027 doRelay : function(e, fn, key){
13028 if(this.scope.isExpanded()){
13029 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13038 this.queryDelay = Math.max(this.queryDelay || 10,
13039 this.mode == 'local' ? 10 : 250);
13042 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13044 if(this.typeAhead){
13045 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13048 if(this.editable !== false){
13049 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13054 onDestroy : function(){
13056 this.view.setStore(null);
13057 this.view.el.removeAllListeners();
13058 this.view.el.remove();
13059 this.view.purgeListeners();
13062 this.list.dom.innerHTML = '';
13066 this.store.un('beforeload', this.onBeforeLoad, this);
13067 this.store.un('load', this.onLoad, this);
13068 this.store.un('loadexception', this.onLoadException, this);
13070 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13074 fireKey : function(e){
13075 if(e.isNavKeyPress() && !this.list.isVisible()){
13076 this.fireEvent("specialkey", this, e);
13081 onResize: function(w, h){
13082 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13084 // if(typeof w != 'number'){
13085 // // we do not handle it!?!?
13088 // var tw = this.trigger.getWidth();
13089 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13090 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13092 // this.inputEl().setWidth( this.adjustWidth('input', x));
13094 // //this.trigger.setStyle('left', x+'px');
13096 // if(this.list && this.listWidth === undefined){
13097 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13098 // this.list.setWidth(lw);
13099 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13107 * Allow or prevent the user from directly editing the field text. If false is passed,
13108 * the user will only be able to select from the items defined in the dropdown list. This method
13109 * is the runtime equivalent of setting the 'editable' config option at config time.
13110 * @param {Boolean} value True to allow the user to directly edit the field text
13112 setEditable : function(value){
13113 if(value == this.editable){
13116 this.editable = value;
13118 this.inputEl().dom.setAttribute('readOnly', true);
13119 this.inputEl().on('mousedown', this.onTriggerClick, this);
13120 this.inputEl().addClass('x-combo-noedit');
13122 this.inputEl().dom.setAttribute('readOnly', false);
13123 this.inputEl().un('mousedown', this.onTriggerClick, this);
13124 this.inputEl().removeClass('x-combo-noedit');
13130 onBeforeLoad : function(combo,opts){
13131 if(!this.hasFocus){
13135 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13137 this.restrictHeight();
13138 this.selectedIndex = -1;
13142 onLoad : function(){
13144 this.hasQuery = false;
13146 if(!this.hasFocus){
13150 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13151 this.loading.hide();
13154 if(this.store.getCount() > 0){
13156 this.restrictHeight();
13157 if(this.lastQuery == this.allQuery){
13158 if(this.editable && !this.tickable){
13159 this.inputEl().dom.select();
13163 !this.selectByValue(this.value, true) &&
13166 !this.store.lastOptions ||
13167 typeof(this.store.lastOptions.add) == 'undefined' ||
13168 this.store.lastOptions.add != true
13171 this.select(0, true);
13174 if(this.autoFocus){
13177 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13178 this.taTask.delay(this.typeAheadDelay);
13182 this.onEmptyResults();
13188 onLoadException : function()
13190 this.hasQuery = false;
13192 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13193 this.loading.hide();
13196 if(this.tickable && this.editable){
13201 // only causes errors at present
13202 //Roo.log(this.store.reader.jsonData);
13203 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13205 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13211 onTypeAhead : function(){
13212 if(this.store.getCount() > 0){
13213 var r = this.store.getAt(0);
13214 var newValue = r.data[this.displayField];
13215 var len = newValue.length;
13216 var selStart = this.getRawValue().length;
13218 if(selStart != len){
13219 this.setRawValue(newValue);
13220 this.selectText(selStart, newValue.length);
13226 onSelect : function(record, index){
13228 if(this.fireEvent('beforeselect', this, record, index) !== false){
13230 this.setFromData(index > -1 ? record.data : false);
13233 this.fireEvent('select', this, record, index);
13238 * Returns the currently selected field value or empty string if no value is set.
13239 * @return {String} value The selected value
13241 getValue : function()
13243 if(Roo.isIOS && this.useNativeIOS){
13244 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13248 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13251 if(this.valueField){
13252 return typeof this.value != 'undefined' ? this.value : '';
13254 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13258 getRawValue : function()
13260 if(Roo.isIOS && this.useNativeIOS){
13261 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13264 var v = this.inputEl().getValue();
13270 * Clears any text/value currently set in the field
13272 clearValue : function(){
13274 if(this.hiddenField){
13275 this.hiddenField.dom.value = '';
13278 this.setRawValue('');
13279 this.lastSelectionText = '';
13280 this.lastData = false;
13282 var close = this.closeTriggerEl();
13293 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13294 * will be displayed in the field. If the value does not match the data value of an existing item,
13295 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13296 * Otherwise the field will be blank (although the value will still be set).
13297 * @param {String} value The value to match
13299 setValue : function(v)
13301 if(Roo.isIOS && this.useNativeIOS){
13302 this.setIOSValue(v);
13312 if(this.valueField){
13313 var r = this.findRecord(this.valueField, v);
13315 text = r.data[this.displayField];
13316 }else if(this.valueNotFoundText !== undefined){
13317 text = this.valueNotFoundText;
13320 this.lastSelectionText = text;
13321 if(this.hiddenField){
13322 this.hiddenField.dom.value = v;
13324 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13327 var close = this.closeTriggerEl();
13330 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13336 * @property {Object} the last set data for the element
13341 * Sets the value of the field based on a object which is related to the record format for the store.
13342 * @param {Object} value the value to set as. or false on reset?
13344 setFromData : function(o){
13351 var dv = ''; // display value
13352 var vv = ''; // value value..
13354 if (this.displayField) {
13355 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13357 // this is an error condition!!!
13358 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13361 if(this.valueField){
13362 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13365 var close = this.closeTriggerEl();
13368 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13371 if(this.hiddenField){
13372 this.hiddenField.dom.value = vv;
13374 this.lastSelectionText = dv;
13375 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13379 // no hidden field.. - we store the value in 'value', but still display
13380 // display field!!!!
13381 this.lastSelectionText = dv;
13382 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13389 reset : function(){
13390 // overridden so that last data is reset..
13397 this.setValue(this.originalValue);
13398 //this.clearInvalid();
13399 this.lastData = false;
13401 this.view.clearSelections();
13407 findRecord : function(prop, value){
13409 if(this.store.getCount() > 0){
13410 this.store.each(function(r){
13411 if(r.data[prop] == value){
13421 getName: function()
13423 // returns hidden if it's set..
13424 if (!this.rendered) {return ''};
13425 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13429 onViewMove : function(e, t){
13430 this.inKeyMode = false;
13434 onViewOver : function(e, t){
13435 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13438 var item = this.view.findItemFromChild(t);
13441 var index = this.view.indexOf(item);
13442 this.select(index, false);
13447 onViewClick : function(view, doFocus, el, e)
13449 var index = this.view.getSelectedIndexes()[0];
13451 var r = this.store.getAt(index);
13455 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13462 Roo.each(this.tickItems, function(v,k){
13464 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13466 _this.tickItems.splice(k, 1);
13468 if(typeof(e) == 'undefined' && view == false){
13469 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13481 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13482 this.tickItems.push(r.data);
13485 if(typeof(e) == 'undefined' && view == false){
13486 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13493 this.onSelect(r, index);
13495 if(doFocus !== false && !this.blockFocus){
13496 this.inputEl().focus();
13501 restrictHeight : function(){
13502 //this.innerList.dom.style.height = '';
13503 //var inner = this.innerList.dom;
13504 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13505 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13506 //this.list.beginUpdate();
13507 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13508 this.list.alignTo(this.inputEl(), this.listAlign);
13509 this.list.alignTo(this.inputEl(), this.listAlign);
13510 //this.list.endUpdate();
13514 onEmptyResults : function(){
13516 if(this.tickable && this.editable){
13517 this.restrictHeight();
13525 * Returns true if the dropdown list is expanded, else false.
13527 isExpanded : function(){
13528 return this.list.isVisible();
13532 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13533 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13534 * @param {String} value The data value of the item to select
13535 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13536 * selected item if it is not currently in view (defaults to true)
13537 * @return {Boolean} True if the value matched an item in the list, else false
13539 selectByValue : function(v, scrollIntoView){
13540 if(v !== undefined && v !== null){
13541 var r = this.findRecord(this.valueField || this.displayField, v);
13543 this.select(this.store.indexOf(r), scrollIntoView);
13551 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13552 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13553 * @param {Number} index The zero-based index of the list item to select
13554 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13555 * selected item if it is not currently in view (defaults to true)
13557 select : function(index, scrollIntoView){
13558 this.selectedIndex = index;
13559 this.view.select(index);
13560 if(scrollIntoView !== false){
13561 var el = this.view.getNode(index);
13563 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13566 this.list.scrollChildIntoView(el, false);
13572 selectNext : function(){
13573 var ct = this.store.getCount();
13575 if(this.selectedIndex == -1){
13577 }else if(this.selectedIndex < ct-1){
13578 this.select(this.selectedIndex+1);
13584 selectPrev : function(){
13585 var ct = this.store.getCount();
13587 if(this.selectedIndex == -1){
13589 }else if(this.selectedIndex != 0){
13590 this.select(this.selectedIndex-1);
13596 onKeyUp : function(e){
13597 if(this.editable !== false && !e.isSpecialKey()){
13598 this.lastKey = e.getKey();
13599 this.dqTask.delay(this.queryDelay);
13604 validateBlur : function(){
13605 return !this.list || !this.list.isVisible();
13609 initQuery : function(){
13611 var v = this.getRawValue();
13613 if(this.tickable && this.editable){
13614 v = this.tickableInputEl().getValue();
13621 doForce : function(){
13622 if(this.inputEl().dom.value.length > 0){
13623 this.inputEl().dom.value =
13624 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13630 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13631 * query allowing the query action to be canceled if needed.
13632 * @param {String} query The SQL query to execute
13633 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13634 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13635 * saved in the current store (defaults to false)
13637 doQuery : function(q, forceAll){
13639 if(q === undefined || q === null){
13644 forceAll: forceAll,
13648 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13653 forceAll = qe.forceAll;
13654 if(forceAll === true || (q.length >= this.minChars)){
13656 this.hasQuery = true;
13658 if(this.lastQuery != q || this.alwaysQuery){
13659 this.lastQuery = q;
13660 if(this.mode == 'local'){
13661 this.selectedIndex = -1;
13663 this.store.clearFilter();
13666 if(this.specialFilter){
13667 this.fireEvent('specialfilter', this);
13672 this.store.filter(this.displayField, q);
13675 this.store.fireEvent("datachanged", this.store);
13682 this.store.baseParams[this.queryParam] = q;
13684 var options = {params : this.getParams(q)};
13687 options.add = true;
13688 options.params.start = this.page * this.pageSize;
13691 this.store.load(options);
13694 * this code will make the page width larger, at the beginning, the list not align correctly,
13695 * we should expand the list on onLoad
13696 * so command out it
13701 this.selectedIndex = -1;
13706 this.loadNext = false;
13710 getParams : function(q){
13712 //p[this.queryParam] = q;
13716 p.limit = this.pageSize;
13722 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13724 collapse : function(){
13725 if(!this.isExpanded()){
13731 this.hasFocus = false;
13735 this.cancelBtn.hide();
13736 this.trigger.show();
13739 this.tickableInputEl().dom.value = '';
13740 this.tickableInputEl().blur();
13745 Roo.get(document).un('mousedown', this.collapseIf, this);
13746 Roo.get(document).un('mousewheel', this.collapseIf, this);
13747 if (!this.editable) {
13748 Roo.get(document).un('keydown', this.listKeyPress, this);
13750 this.fireEvent('collapse', this);
13756 collapseIf : function(e){
13757 var in_combo = e.within(this.el);
13758 var in_list = e.within(this.list);
13759 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13761 if (in_combo || in_list || is_list) {
13762 //e.stopPropagation();
13767 this.onTickableFooterButtonClick(e, false, false);
13775 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13777 expand : function(){
13779 if(this.isExpanded() || !this.hasFocus){
13783 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13784 this.list.setWidth(lw);
13790 this.restrictHeight();
13794 this.tickItems = Roo.apply([], this.item);
13797 this.cancelBtn.show();
13798 this.trigger.hide();
13801 this.tickableInputEl().focus();
13806 Roo.get(document).on('mousedown', this.collapseIf, this);
13807 Roo.get(document).on('mousewheel', this.collapseIf, this);
13808 if (!this.editable) {
13809 Roo.get(document).on('keydown', this.listKeyPress, this);
13812 this.fireEvent('expand', this);
13816 // Implements the default empty TriggerField.onTriggerClick function
13817 onTriggerClick : function(e)
13819 Roo.log('trigger click');
13821 if(this.disabled || !this.triggerList){
13826 this.loadNext = false;
13828 if(this.isExpanded()){
13830 if (!this.blockFocus) {
13831 this.inputEl().focus();
13835 this.hasFocus = true;
13836 if(this.triggerAction == 'all') {
13837 this.doQuery(this.allQuery, true);
13839 this.doQuery(this.getRawValue());
13841 if (!this.blockFocus) {
13842 this.inputEl().focus();
13847 onTickableTriggerClick : function(e)
13854 this.loadNext = false;
13855 this.hasFocus = true;
13857 if(this.triggerAction == 'all') {
13858 this.doQuery(this.allQuery, true);
13860 this.doQuery(this.getRawValue());
13864 onSearchFieldClick : function(e)
13866 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13867 this.onTickableFooterButtonClick(e, false, false);
13871 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13876 this.loadNext = false;
13877 this.hasFocus = true;
13879 if(this.triggerAction == 'all') {
13880 this.doQuery(this.allQuery, true);
13882 this.doQuery(this.getRawValue());
13886 listKeyPress : function(e)
13888 //Roo.log('listkeypress');
13889 // scroll to first matching element based on key pres..
13890 if (e.isSpecialKey()) {
13893 var k = String.fromCharCode(e.getKey()).toUpperCase();
13896 var csel = this.view.getSelectedNodes();
13897 var cselitem = false;
13899 var ix = this.view.indexOf(csel[0]);
13900 cselitem = this.store.getAt(ix);
13901 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13907 this.store.each(function(v) {
13909 // start at existing selection.
13910 if (cselitem.id == v.id) {
13916 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13917 match = this.store.indexOf(v);
13923 if (match === false) {
13924 return true; // no more action?
13927 this.view.select(match);
13928 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13929 sn.scrollIntoView(sn.dom.parentNode, false);
13932 onViewScroll : function(e, t){
13934 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){
13938 this.hasQuery = true;
13940 this.loading = this.list.select('.loading', true).first();
13942 if(this.loading === null){
13943 this.list.createChild({
13945 cls: 'loading roo-select2-more-results roo-select2-active',
13946 html: 'Loading more results...'
13949 this.loading = this.list.select('.loading', true).first();
13951 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13953 this.loading.hide();
13956 this.loading.show();
13961 this.loadNext = true;
13963 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13968 addItem : function(o)
13970 var dv = ''; // display value
13972 if (this.displayField) {
13973 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13975 // this is an error condition!!!
13976 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13983 var choice = this.choices.createChild({
13985 cls: 'roo-select2-search-choice',
13994 cls: 'roo-select2-search-choice-close',
13999 }, this.searchField);
14001 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14003 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14011 this.inputEl().dom.value = '';
14016 onRemoveItem : function(e, _self, o)
14018 e.preventDefault();
14020 this.lastItem = Roo.apply([], this.item);
14022 var index = this.item.indexOf(o.data) * 1;
14025 Roo.log('not this item?!');
14029 this.item.splice(index, 1);
14034 this.fireEvent('remove', this, e);
14040 syncValue : function()
14042 if(!this.item.length){
14049 Roo.each(this.item, function(i){
14050 if(_this.valueField){
14051 value.push(i[_this.valueField]);
14058 this.value = value.join(',');
14060 if(this.hiddenField){
14061 this.hiddenField.dom.value = this.value;
14064 this.store.fireEvent("datachanged", this.store);
14069 clearItem : function()
14071 if(!this.multiple){
14077 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14085 if(this.tickable && !Roo.isTouch){
14086 this.view.refresh();
14090 inputEl: function ()
14092 if(Roo.isIOS && this.useNativeIOS){
14093 return this.el.select('select.roo-ios-select', true).first();
14096 if(Roo.isTouch && this.mobileTouchView){
14097 return this.el.select('input.form-control',true).first();
14101 return this.searchField;
14104 return this.el.select('input.form-control',true).first();
14107 onTickableFooterButtonClick : function(e, btn, el)
14109 e.preventDefault();
14111 this.lastItem = Roo.apply([], this.item);
14113 if(btn && btn.name == 'cancel'){
14114 this.tickItems = Roo.apply([], this.item);
14123 Roo.each(this.tickItems, function(o){
14131 validate : function()
14133 var v = this.getRawValue();
14136 v = this.getValue();
14139 if(this.disabled || this.allowBlank || v.length){
14144 this.markInvalid();
14148 tickableInputEl : function()
14150 if(!this.tickable || !this.editable){
14151 return this.inputEl();
14154 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14158 getAutoCreateTouchView : function()
14163 cls: 'form-group' //input-group
14169 type : this.inputType,
14170 cls : 'form-control x-combo-noedit',
14171 autocomplete: 'new-password',
14172 placeholder : this.placeholder || '',
14177 input.name = this.name;
14181 input.cls += ' input-' + this.size;
14184 if (this.disabled) {
14185 input.disabled = true;
14196 inputblock.cls += ' input-group';
14198 inputblock.cn.unshift({
14200 cls : 'input-group-addon',
14205 if(this.removable && !this.multiple){
14206 inputblock.cls += ' roo-removable';
14208 inputblock.cn.push({
14211 cls : 'roo-combo-removable-btn close'
14215 if(this.hasFeedback && !this.allowBlank){
14217 inputblock.cls += ' has-feedback';
14219 inputblock.cn.push({
14221 cls: 'glyphicon form-control-feedback'
14228 inputblock.cls += (this.before) ? '' : ' input-group';
14230 inputblock.cn.push({
14232 cls : 'input-group-addon',
14243 cls: 'form-hidden-field'
14257 cls: 'form-hidden-field'
14261 cls: 'roo-select2-choices',
14265 cls: 'roo-select2-search-field',
14278 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14284 if(!this.multiple && this.showToggleBtn){
14291 if (this.caret != false) {
14294 cls: 'fa fa-' + this.caret
14301 cls : 'input-group-addon btn dropdown-toggle',
14306 cls: 'combobox-clear',
14320 combobox.cls += ' roo-select2-container-multi';
14323 var align = this.labelAlign || this.parentLabelAlign();
14327 if(this.fieldLabel.length && this.labelWidth){
14329 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14330 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14335 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14336 tooltip : 'This field is required'
14340 cls : 'control-label ' + lw,
14341 html : this.fieldLabel
14352 if(this.indicatorpos == 'right'){
14356 cls : 'control-label ' + lw,
14357 html : this.fieldLabel
14362 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14363 tooltip : 'This field is required'
14375 var settings = this;
14377 ['xs','sm','md','lg'].map(function(size){
14378 if (settings[size]) {
14379 cfg.cls += ' col-' + size + '-' + settings[size];
14386 initTouchView : function()
14388 this.renderTouchView();
14390 this.touchViewEl.on('scroll', function(){
14391 this.el.dom.scrollTop = 0;
14394 this.originalValue = this.getValue();
14396 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14398 this.inputEl().on("click", this.showTouchView, this);
14399 if (this.triggerEl) {
14400 this.triggerEl.on("click", this.showTouchView, this);
14404 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14405 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14407 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14409 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14410 this.store.on('load', this.onTouchViewLoad, this);
14411 this.store.on('loadexception', this.onTouchViewLoadException, this);
14413 if(this.hiddenName){
14415 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14417 this.hiddenField.dom.value =
14418 this.hiddenValue !== undefined ? this.hiddenValue :
14419 this.value !== undefined ? this.value : '';
14421 this.el.dom.removeAttribute('name');
14422 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14426 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14427 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14430 if(this.removable && !this.multiple){
14431 var close = this.closeTriggerEl();
14433 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14434 close.on('click', this.removeBtnClick, this, close);
14438 * fix the bug in Safari iOS8
14440 this.inputEl().on("focus", function(e){
14441 document.activeElement.blur();
14449 renderTouchView : function()
14451 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14452 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14454 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14455 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14457 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14458 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14459 this.touchViewBodyEl.setStyle('overflow', 'auto');
14461 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14462 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14464 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14465 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14469 showTouchView : function()
14475 this.touchViewHeaderEl.hide();
14477 if(this.modalTitle.length){
14478 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14479 this.touchViewHeaderEl.show();
14482 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14483 this.touchViewEl.show();
14485 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14486 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14487 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14489 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14491 if(this.modalTitle.length){
14492 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14495 this.touchViewBodyEl.setHeight(bodyHeight);
14499 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14501 this.touchViewEl.addClass('in');
14504 this.doTouchViewQuery();
14508 hideTouchView : function()
14510 this.touchViewEl.removeClass('in');
14514 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14516 this.touchViewEl.setStyle('display', 'none');
14521 setTouchViewValue : function()
14528 Roo.each(this.tickItems, function(o){
14533 this.hideTouchView();
14536 doTouchViewQuery : function()
14545 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14549 if(!this.alwaysQuery || this.mode == 'local'){
14550 this.onTouchViewLoad();
14557 onTouchViewBeforeLoad : function(combo,opts)
14563 onTouchViewLoad : function()
14565 if(this.store.getCount() < 1){
14566 this.onTouchViewEmptyResults();
14570 this.clearTouchView();
14572 var rawValue = this.getRawValue();
14574 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14576 this.tickItems = [];
14578 this.store.data.each(function(d, rowIndex){
14579 var row = this.touchViewListGroup.createChild(template);
14581 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14582 row.addClass(d.data.cls);
14585 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14588 html : d.data[this.displayField]
14591 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14592 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14595 row.removeClass('selected');
14596 if(!this.multiple && this.valueField &&
14597 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14600 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14601 row.addClass('selected');
14604 if(this.multiple && this.valueField &&
14605 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14609 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14610 this.tickItems.push(d.data);
14613 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14617 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14619 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14621 if(this.modalTitle.length){
14622 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14625 var listHeight = this.touchViewListGroup.getHeight();
14629 if(firstChecked && listHeight > bodyHeight){
14630 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14635 onTouchViewLoadException : function()
14637 this.hideTouchView();
14640 onTouchViewEmptyResults : function()
14642 this.clearTouchView();
14644 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14646 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14650 clearTouchView : function()
14652 this.touchViewListGroup.dom.innerHTML = '';
14655 onTouchViewClick : function(e, el, o)
14657 e.preventDefault();
14660 var rowIndex = o.rowIndex;
14662 var r = this.store.getAt(rowIndex);
14664 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14666 if(!this.multiple){
14667 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14668 c.dom.removeAttribute('checked');
14671 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14673 this.setFromData(r.data);
14675 var close = this.closeTriggerEl();
14681 this.hideTouchView();
14683 this.fireEvent('select', this, r, rowIndex);
14688 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14689 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14690 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14694 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14695 this.addItem(r.data);
14696 this.tickItems.push(r.data);
14700 getAutoCreateNativeIOS : function()
14703 cls: 'form-group' //input-group,
14708 cls : 'roo-ios-select'
14712 combobox.name = this.name;
14715 if (this.disabled) {
14716 combobox.disabled = true;
14719 var settings = this;
14721 ['xs','sm','md','lg'].map(function(size){
14722 if (settings[size]) {
14723 cfg.cls += ' col-' + size + '-' + settings[size];
14733 initIOSView : function()
14735 this.store.on('load', this.onIOSViewLoad, this);
14740 onIOSViewLoad : function()
14742 if(this.store.getCount() < 1){
14746 this.clearIOSView();
14748 if(this.allowBlank) {
14750 var default_text = '-- SELECT --';
14752 var opt = this.inputEl().createChild({
14755 html : default_text
14759 o[this.valueField] = 0;
14760 o[this.displayField] = default_text;
14762 this.ios_options.push({
14769 this.store.data.each(function(d, rowIndex){
14773 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14774 html = d.data[this.displayField];
14779 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14780 value = d.data[this.valueField];
14789 if(this.value == d.data[this.valueField]){
14790 option['selected'] = true;
14793 var opt = this.inputEl().createChild(option);
14795 this.ios_options.push({
14802 this.inputEl().on('change', function(){
14803 this.fireEvent('select', this);
14808 clearIOSView: function()
14810 this.inputEl().dom.innerHTML = '';
14812 this.ios_options = [];
14815 setIOSValue: function(v)
14819 if(!this.ios_options){
14823 Roo.each(this.ios_options, function(opts){
14825 opts.el.dom.removeAttribute('selected');
14827 if(opts.data[this.valueField] != v){
14831 opts.el.dom.setAttribute('selected', true);
14837 * @cfg {Boolean} grow
14841 * @cfg {Number} growMin
14845 * @cfg {Number} growMax
14854 Roo.apply(Roo.bootstrap.ComboBox, {
14858 cls: 'modal-header',
14880 cls: 'list-group-item',
14884 cls: 'roo-combobox-list-group-item-value'
14888 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14902 listItemCheckbox : {
14904 cls: 'list-group-item',
14908 cls: 'roo-combobox-list-group-item-value'
14912 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14928 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14933 cls: 'modal-footer',
14941 cls: 'col-xs-6 text-left',
14944 cls: 'btn btn-danger roo-touch-view-cancel',
14950 cls: 'col-xs-6 text-right',
14953 cls: 'btn btn-success roo-touch-view-ok',
14964 Roo.apply(Roo.bootstrap.ComboBox, {
14966 touchViewTemplate : {
14968 cls: 'modal fade roo-combobox-touch-view',
14972 cls: 'modal-dialog',
14973 style : 'position:fixed', // we have to fix position....
14977 cls: 'modal-content',
14979 Roo.bootstrap.ComboBox.header,
14980 Roo.bootstrap.ComboBox.body,
14981 Roo.bootstrap.ComboBox.footer
14990 * Ext JS Library 1.1.1
14991 * Copyright(c) 2006-2007, Ext JS, LLC.
14993 * Originally Released Under LGPL - original licence link has changed is not relivant.
14996 * <script type="text/javascript">
15001 * @extends Roo.util.Observable
15002 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15003 * This class also supports single and multi selection modes. <br>
15004 * Create a data model bound view:
15006 var store = new Roo.data.Store(...);
15008 var view = new Roo.View({
15010 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15012 singleSelect: true,
15013 selectedClass: "ydataview-selected",
15017 // listen for node click?
15018 view.on("click", function(vw, index, node, e){
15019 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15023 dataModel.load("foobar.xml");
15025 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15027 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15028 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15030 * Note: old style constructor is still suported (container, template, config)
15033 * Create a new View
15034 * @param {Object} config The config object
15037 Roo.View = function(config, depreciated_tpl, depreciated_config){
15039 this.parent = false;
15041 if (typeof(depreciated_tpl) == 'undefined') {
15042 // new way.. - universal constructor.
15043 Roo.apply(this, config);
15044 this.el = Roo.get(this.el);
15047 this.el = Roo.get(config);
15048 this.tpl = depreciated_tpl;
15049 Roo.apply(this, depreciated_config);
15051 this.wrapEl = this.el.wrap().wrap();
15052 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15055 if(typeof(this.tpl) == "string"){
15056 this.tpl = new Roo.Template(this.tpl);
15058 // support xtype ctors..
15059 this.tpl = new Roo.factory(this.tpl, Roo);
15063 this.tpl.compile();
15068 * @event beforeclick
15069 * Fires before a click is processed. Returns false to cancel the default action.
15070 * @param {Roo.View} this
15071 * @param {Number} index The index of the target node
15072 * @param {HTMLElement} node The target node
15073 * @param {Roo.EventObject} e The raw event object
15075 "beforeclick" : true,
15078 * Fires when a template node is clicked.
15079 * @param {Roo.View} this
15080 * @param {Number} index The index of the target node
15081 * @param {HTMLElement} node The target node
15082 * @param {Roo.EventObject} e The raw event object
15087 * Fires when a template node is double clicked.
15088 * @param {Roo.View} this
15089 * @param {Number} index The index of the target node
15090 * @param {HTMLElement} node The target node
15091 * @param {Roo.EventObject} e The raw event object
15095 * @event contextmenu
15096 * Fires when a template node is right clicked.
15097 * @param {Roo.View} this
15098 * @param {Number} index The index of the target node
15099 * @param {HTMLElement} node The target node
15100 * @param {Roo.EventObject} e The raw event object
15102 "contextmenu" : true,
15104 * @event selectionchange
15105 * Fires when the selected nodes change.
15106 * @param {Roo.View} this
15107 * @param {Array} selections Array of the selected nodes
15109 "selectionchange" : true,
15112 * @event beforeselect
15113 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15114 * @param {Roo.View} this
15115 * @param {HTMLElement} node The node to be selected
15116 * @param {Array} selections Array of currently selected nodes
15118 "beforeselect" : true,
15120 * @event preparedata
15121 * Fires on every row to render, to allow you to change the data.
15122 * @param {Roo.View} this
15123 * @param {Object} data to be rendered (change this)
15125 "preparedata" : true
15133 "click": this.onClick,
15134 "dblclick": this.onDblClick,
15135 "contextmenu": this.onContextMenu,
15139 this.selections = [];
15141 this.cmp = new Roo.CompositeElementLite([]);
15143 this.store = Roo.factory(this.store, Roo.data);
15144 this.setStore(this.store, true);
15147 if ( this.footer && this.footer.xtype) {
15149 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15151 this.footer.dataSource = this.store;
15152 this.footer.container = fctr;
15153 this.footer = Roo.factory(this.footer, Roo);
15154 fctr.insertFirst(this.el);
15156 // this is a bit insane - as the paging toolbar seems to detach the el..
15157 // dom.parentNode.parentNode.parentNode
15158 // they get detached?
15162 Roo.View.superclass.constructor.call(this);
15167 Roo.extend(Roo.View, Roo.util.Observable, {
15170 * @cfg {Roo.data.Store} store Data store to load data from.
15175 * @cfg {String|Roo.Element} el The container element.
15180 * @cfg {String|Roo.Template} tpl The template used by this View
15184 * @cfg {String} dataName the named area of the template to use as the data area
15185 * Works with domtemplates roo-name="name"
15189 * @cfg {String} selectedClass The css class to add to selected nodes
15191 selectedClass : "x-view-selected",
15193 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15198 * @cfg {String} text to display on mask (default Loading)
15202 * @cfg {Boolean} multiSelect Allow multiple selection
15204 multiSelect : false,
15206 * @cfg {Boolean} singleSelect Allow single selection
15208 singleSelect: false,
15211 * @cfg {Boolean} toggleSelect - selecting
15213 toggleSelect : false,
15216 * @cfg {Boolean} tickable - selecting
15221 * Returns the element this view is bound to.
15222 * @return {Roo.Element}
15224 getEl : function(){
15225 return this.wrapEl;
15231 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15233 refresh : function(){
15234 //Roo.log('refresh');
15237 // if we are using something like 'domtemplate', then
15238 // the what gets used is:
15239 // t.applySubtemplate(NAME, data, wrapping data..)
15240 // the outer template then get' applied with
15241 // the store 'extra data'
15242 // and the body get's added to the
15243 // roo-name="data" node?
15244 // <span class='roo-tpl-{name}'></span> ?????
15248 this.clearSelections();
15249 this.el.update("");
15251 var records = this.store.getRange();
15252 if(records.length < 1) {
15254 // is this valid?? = should it render a template??
15256 this.el.update(this.emptyText);
15260 if (this.dataName) {
15261 this.el.update(t.apply(this.store.meta)); //????
15262 el = this.el.child('.roo-tpl-' + this.dataName);
15265 for(var i = 0, len = records.length; i < len; i++){
15266 var data = this.prepareData(records[i].data, i, records[i]);
15267 this.fireEvent("preparedata", this, data, i, records[i]);
15269 var d = Roo.apply({}, data);
15272 Roo.apply(d, {'roo-id' : Roo.id()});
15276 Roo.each(this.parent.item, function(item){
15277 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15280 Roo.apply(d, {'roo-data-checked' : 'checked'});
15284 html[html.length] = Roo.util.Format.trim(
15286 t.applySubtemplate(this.dataName, d, this.store.meta) :
15293 el.update(html.join(""));
15294 this.nodes = el.dom.childNodes;
15295 this.updateIndexes(0);
15300 * Function to override to reformat the data that is sent to
15301 * the template for each node.
15302 * DEPRICATED - use the preparedata event handler.
15303 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15304 * a JSON object for an UpdateManager bound view).
15306 prepareData : function(data, index, record)
15308 this.fireEvent("preparedata", this, data, index, record);
15312 onUpdate : function(ds, record){
15313 // Roo.log('on update');
15314 this.clearSelections();
15315 var index = this.store.indexOf(record);
15316 var n = this.nodes[index];
15317 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15318 n.parentNode.removeChild(n);
15319 this.updateIndexes(index, index);
15325 onAdd : function(ds, records, index)
15327 //Roo.log(['on Add', ds, records, index] );
15328 this.clearSelections();
15329 if(this.nodes.length == 0){
15333 var n = this.nodes[index];
15334 for(var i = 0, len = records.length; i < len; i++){
15335 var d = this.prepareData(records[i].data, i, records[i]);
15337 this.tpl.insertBefore(n, d);
15340 this.tpl.append(this.el, d);
15343 this.updateIndexes(index);
15346 onRemove : function(ds, record, index){
15347 // Roo.log('onRemove');
15348 this.clearSelections();
15349 var el = this.dataName ?
15350 this.el.child('.roo-tpl-' + this.dataName) :
15353 el.dom.removeChild(this.nodes[index]);
15354 this.updateIndexes(index);
15358 * Refresh an individual node.
15359 * @param {Number} index
15361 refreshNode : function(index){
15362 this.onUpdate(this.store, this.store.getAt(index));
15365 updateIndexes : function(startIndex, endIndex){
15366 var ns = this.nodes;
15367 startIndex = startIndex || 0;
15368 endIndex = endIndex || ns.length - 1;
15369 for(var i = startIndex; i <= endIndex; i++){
15370 ns[i].nodeIndex = i;
15375 * Changes the data store this view uses and refresh the view.
15376 * @param {Store} store
15378 setStore : function(store, initial){
15379 if(!initial && this.store){
15380 this.store.un("datachanged", this.refresh);
15381 this.store.un("add", this.onAdd);
15382 this.store.un("remove", this.onRemove);
15383 this.store.un("update", this.onUpdate);
15384 this.store.un("clear", this.refresh);
15385 this.store.un("beforeload", this.onBeforeLoad);
15386 this.store.un("load", this.onLoad);
15387 this.store.un("loadexception", this.onLoad);
15391 store.on("datachanged", this.refresh, this);
15392 store.on("add", this.onAdd, this);
15393 store.on("remove", this.onRemove, this);
15394 store.on("update", this.onUpdate, this);
15395 store.on("clear", this.refresh, this);
15396 store.on("beforeload", this.onBeforeLoad, this);
15397 store.on("load", this.onLoad, this);
15398 store.on("loadexception", this.onLoad, this);
15406 * onbeforeLoad - masks the loading area.
15409 onBeforeLoad : function(store,opts)
15411 //Roo.log('onBeforeLoad');
15413 this.el.update("");
15415 this.el.mask(this.mask ? this.mask : "Loading" );
15417 onLoad : function ()
15424 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15425 * @param {HTMLElement} node
15426 * @return {HTMLElement} The template node
15428 findItemFromChild : function(node){
15429 var el = this.dataName ?
15430 this.el.child('.roo-tpl-' + this.dataName,true) :
15433 if(!node || node.parentNode == el){
15436 var p = node.parentNode;
15437 while(p && p != el){
15438 if(p.parentNode == el){
15447 onClick : function(e){
15448 var item = this.findItemFromChild(e.getTarget());
15450 var index = this.indexOf(item);
15451 if(this.onItemClick(item, index, e) !== false){
15452 this.fireEvent("click", this, index, item, e);
15455 this.clearSelections();
15460 onContextMenu : function(e){
15461 var item = this.findItemFromChild(e.getTarget());
15463 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15468 onDblClick : function(e){
15469 var item = this.findItemFromChild(e.getTarget());
15471 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15475 onItemClick : function(item, index, e)
15477 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15480 if (this.toggleSelect) {
15481 var m = this.isSelected(item) ? 'unselect' : 'select';
15484 _t[m](item, true, false);
15487 if(this.multiSelect || this.singleSelect){
15488 if(this.multiSelect && e.shiftKey && this.lastSelection){
15489 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15491 this.select(item, this.multiSelect && e.ctrlKey);
15492 this.lastSelection = item;
15495 if(!this.tickable){
15496 e.preventDefault();
15504 * Get the number of selected nodes.
15507 getSelectionCount : function(){
15508 return this.selections.length;
15512 * Get the currently selected nodes.
15513 * @return {Array} An array of HTMLElements
15515 getSelectedNodes : function(){
15516 return this.selections;
15520 * Get the indexes of the selected nodes.
15523 getSelectedIndexes : function(){
15524 var indexes = [], s = this.selections;
15525 for(var i = 0, len = s.length; i < len; i++){
15526 indexes.push(s[i].nodeIndex);
15532 * Clear all selections
15533 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15535 clearSelections : function(suppressEvent){
15536 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15537 this.cmp.elements = this.selections;
15538 this.cmp.removeClass(this.selectedClass);
15539 this.selections = [];
15540 if(!suppressEvent){
15541 this.fireEvent("selectionchange", this, this.selections);
15547 * Returns true if the passed node is selected
15548 * @param {HTMLElement/Number} node The node or node index
15549 * @return {Boolean}
15551 isSelected : function(node){
15552 var s = this.selections;
15556 node = this.getNode(node);
15557 return s.indexOf(node) !== -1;
15562 * @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
15563 * @param {Boolean} keepExisting (optional) true to keep existing selections
15564 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15566 select : function(nodeInfo, keepExisting, suppressEvent){
15567 if(nodeInfo instanceof Array){
15569 this.clearSelections(true);
15571 for(var i = 0, len = nodeInfo.length; i < len; i++){
15572 this.select(nodeInfo[i], true, true);
15576 var node = this.getNode(nodeInfo);
15577 if(!node || this.isSelected(node)){
15578 return; // already selected.
15581 this.clearSelections(true);
15584 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15585 Roo.fly(node).addClass(this.selectedClass);
15586 this.selections.push(node);
15587 if(!suppressEvent){
15588 this.fireEvent("selectionchange", this, this.selections);
15596 * @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
15597 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15598 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15600 unselect : function(nodeInfo, keepExisting, suppressEvent)
15602 if(nodeInfo instanceof Array){
15603 Roo.each(this.selections, function(s) {
15604 this.unselect(s, nodeInfo);
15608 var node = this.getNode(nodeInfo);
15609 if(!node || !this.isSelected(node)){
15610 //Roo.log("not selected");
15611 return; // not selected.
15615 Roo.each(this.selections, function(s) {
15617 Roo.fly(node).removeClass(this.selectedClass);
15624 this.selections= ns;
15625 this.fireEvent("selectionchange", this, this.selections);
15629 * Gets a template node.
15630 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15631 * @return {HTMLElement} The node or null if it wasn't found
15633 getNode : function(nodeInfo){
15634 if(typeof nodeInfo == "string"){
15635 return document.getElementById(nodeInfo);
15636 }else if(typeof nodeInfo == "number"){
15637 return this.nodes[nodeInfo];
15643 * Gets a range template nodes.
15644 * @param {Number} startIndex
15645 * @param {Number} endIndex
15646 * @return {Array} An array of nodes
15648 getNodes : function(start, end){
15649 var ns = this.nodes;
15650 start = start || 0;
15651 end = typeof end == "undefined" ? ns.length - 1 : end;
15654 for(var i = start; i <= end; i++){
15658 for(var i = start; i >= end; i--){
15666 * Finds the index of the passed node
15667 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15668 * @return {Number} The index of the node or -1
15670 indexOf : function(node){
15671 node = this.getNode(node);
15672 if(typeof node.nodeIndex == "number"){
15673 return node.nodeIndex;
15675 var ns = this.nodes;
15676 for(var i = 0, len = ns.length; i < len; i++){
15687 * based on jquery fullcalendar
15691 Roo.bootstrap = Roo.bootstrap || {};
15693 * @class Roo.bootstrap.Calendar
15694 * @extends Roo.bootstrap.Component
15695 * Bootstrap Calendar class
15696 * @cfg {Boolean} loadMask (true|false) default false
15697 * @cfg {Object} header generate the user specific header of the calendar, default false
15700 * Create a new Container
15701 * @param {Object} config The config object
15706 Roo.bootstrap.Calendar = function(config){
15707 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15711 * Fires when a date is selected
15712 * @param {DatePicker} this
15713 * @param {Date} date The selected date
15717 * @event monthchange
15718 * Fires when the displayed month changes
15719 * @param {DatePicker} this
15720 * @param {Date} date The selected month
15722 'monthchange': true,
15724 * @event evententer
15725 * Fires when mouse over an event
15726 * @param {Calendar} this
15727 * @param {event} Event
15729 'evententer': true,
15731 * @event eventleave
15732 * Fires when the mouse leaves an
15733 * @param {Calendar} this
15736 'eventleave': true,
15738 * @event eventclick
15739 * Fires when the mouse click an
15740 * @param {Calendar} this
15749 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15752 * @cfg {Number} startDay
15753 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15761 getAutoCreate : function(){
15764 var fc_button = function(name, corner, style, content ) {
15765 return Roo.apply({},{
15767 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15769 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15772 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15783 style : 'width:100%',
15790 cls : 'fc-header-left',
15792 fc_button('prev', 'left', 'arrow', '‹' ),
15793 fc_button('next', 'right', 'arrow', '›' ),
15794 { tag: 'span', cls: 'fc-header-space' },
15795 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15803 cls : 'fc-header-center',
15807 cls: 'fc-header-title',
15810 html : 'month / year'
15818 cls : 'fc-header-right',
15820 /* fc_button('month', 'left', '', 'month' ),
15821 fc_button('week', '', '', 'week' ),
15822 fc_button('day', 'right', '', 'day' )
15834 header = this.header;
15837 var cal_heads = function() {
15839 // fixme - handle this.
15841 for (var i =0; i < Date.dayNames.length; i++) {
15842 var d = Date.dayNames[i];
15845 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15846 html : d.substring(0,3)
15850 ret[0].cls += ' fc-first';
15851 ret[6].cls += ' fc-last';
15854 var cal_cell = function(n) {
15857 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15862 cls: 'fc-day-number',
15866 cls: 'fc-day-content',
15870 style: 'position: relative;' // height: 17px;
15882 var cal_rows = function() {
15885 for (var r = 0; r < 6; r++) {
15892 for (var i =0; i < Date.dayNames.length; i++) {
15893 var d = Date.dayNames[i];
15894 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15897 row.cn[0].cls+=' fc-first';
15898 row.cn[0].cn[0].style = 'min-height:90px';
15899 row.cn[6].cls+=' fc-last';
15903 ret[0].cls += ' fc-first';
15904 ret[4].cls += ' fc-prev-last';
15905 ret[5].cls += ' fc-last';
15912 cls: 'fc-border-separate',
15913 style : 'width:100%',
15921 cls : 'fc-first fc-last',
15939 cls : 'fc-content',
15940 style : "position: relative;",
15943 cls : 'fc-view fc-view-month fc-grid',
15944 style : 'position: relative',
15945 unselectable : 'on',
15948 cls : 'fc-event-container',
15949 style : 'position:absolute;z-index:8;top:0;left:0;'
15967 initEvents : function()
15970 throw "can not find store for calendar";
15976 style: "text-align:center",
15980 style: "background-color:white;width:50%;margin:250 auto",
15984 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15995 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15997 var size = this.el.select('.fc-content', true).first().getSize();
15998 this.maskEl.setSize(size.width, size.height);
15999 this.maskEl.enableDisplayMode("block");
16000 if(!this.loadMask){
16001 this.maskEl.hide();
16004 this.store = Roo.factory(this.store, Roo.data);
16005 this.store.on('load', this.onLoad, this);
16006 this.store.on('beforeload', this.onBeforeLoad, this);
16010 this.cells = this.el.select('.fc-day',true);
16011 //Roo.log(this.cells);
16012 this.textNodes = this.el.query('.fc-day-number');
16013 this.cells.addClassOnOver('fc-state-hover');
16015 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16016 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16017 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16018 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16020 this.on('monthchange', this.onMonthChange, this);
16022 this.update(new Date().clearTime());
16025 resize : function() {
16026 var sz = this.el.getSize();
16028 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16029 this.el.select('.fc-day-content div',true).setHeight(34);
16034 showPrevMonth : function(e){
16035 this.update(this.activeDate.add("mo", -1));
16037 showToday : function(e){
16038 this.update(new Date().clearTime());
16041 showNextMonth : function(e){
16042 this.update(this.activeDate.add("mo", 1));
16046 showPrevYear : function(){
16047 this.update(this.activeDate.add("y", -1));
16051 showNextYear : function(){
16052 this.update(this.activeDate.add("y", 1));
16057 update : function(date)
16059 var vd = this.activeDate;
16060 this.activeDate = date;
16061 // if(vd && this.el){
16062 // var t = date.getTime();
16063 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16064 // Roo.log('using add remove');
16066 // this.fireEvent('monthchange', this, date);
16068 // this.cells.removeClass("fc-state-highlight");
16069 // this.cells.each(function(c){
16070 // if(c.dateValue == t){
16071 // c.addClass("fc-state-highlight");
16072 // setTimeout(function(){
16073 // try{c.dom.firstChild.focus();}catch(e){}
16083 var days = date.getDaysInMonth();
16085 var firstOfMonth = date.getFirstDateOfMonth();
16086 var startingPos = firstOfMonth.getDay()-this.startDay;
16088 if(startingPos < this.startDay){
16092 var pm = date.add(Date.MONTH, -1);
16093 var prevStart = pm.getDaysInMonth()-startingPos;
16095 this.cells = this.el.select('.fc-day',true);
16096 this.textNodes = this.el.query('.fc-day-number');
16097 this.cells.addClassOnOver('fc-state-hover');
16099 var cells = this.cells.elements;
16100 var textEls = this.textNodes;
16102 Roo.each(cells, function(cell){
16103 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16106 days += startingPos;
16108 // convert everything to numbers so it's fast
16109 var day = 86400000;
16110 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16113 //Roo.log(prevStart);
16115 var today = new Date().clearTime().getTime();
16116 var sel = date.clearTime().getTime();
16117 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16118 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16119 var ddMatch = this.disabledDatesRE;
16120 var ddText = this.disabledDatesText;
16121 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16122 var ddaysText = this.disabledDaysText;
16123 var format = this.format;
16125 var setCellClass = function(cal, cell){
16129 //Roo.log('set Cell Class');
16131 var t = d.getTime();
16135 cell.dateValue = t;
16137 cell.className += " fc-today";
16138 cell.className += " fc-state-highlight";
16139 cell.title = cal.todayText;
16142 // disable highlight in other month..
16143 //cell.className += " fc-state-highlight";
16148 cell.className = " fc-state-disabled";
16149 cell.title = cal.minText;
16153 cell.className = " fc-state-disabled";
16154 cell.title = cal.maxText;
16158 if(ddays.indexOf(d.getDay()) != -1){
16159 cell.title = ddaysText;
16160 cell.className = " fc-state-disabled";
16163 if(ddMatch && format){
16164 var fvalue = d.dateFormat(format);
16165 if(ddMatch.test(fvalue)){
16166 cell.title = ddText.replace("%0", fvalue);
16167 cell.className = " fc-state-disabled";
16171 if (!cell.initialClassName) {
16172 cell.initialClassName = cell.dom.className;
16175 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16180 for(; i < startingPos; i++) {
16181 textEls[i].innerHTML = (++prevStart);
16182 d.setDate(d.getDate()+1);
16184 cells[i].className = "fc-past fc-other-month";
16185 setCellClass(this, cells[i]);
16190 for(; i < days; i++){
16191 intDay = i - startingPos + 1;
16192 textEls[i].innerHTML = (intDay);
16193 d.setDate(d.getDate()+1);
16195 cells[i].className = ''; // "x-date-active";
16196 setCellClass(this, cells[i]);
16200 for(; i < 42; i++) {
16201 textEls[i].innerHTML = (++extraDays);
16202 d.setDate(d.getDate()+1);
16204 cells[i].className = "fc-future fc-other-month";
16205 setCellClass(this, cells[i]);
16208 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16210 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16212 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16213 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16215 if(totalRows != 6){
16216 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16217 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16220 this.fireEvent('monthchange', this, date);
16224 if(!this.internalRender){
16225 var main = this.el.dom.firstChild;
16226 var w = main.offsetWidth;
16227 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16228 Roo.fly(main).setWidth(w);
16229 this.internalRender = true;
16230 // opera does not respect the auto grow header center column
16231 // then, after it gets a width opera refuses to recalculate
16232 // without a second pass
16233 if(Roo.isOpera && !this.secondPass){
16234 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16235 this.secondPass = true;
16236 this.update.defer(10, this, [date]);
16243 findCell : function(dt) {
16244 dt = dt.clearTime().getTime();
16246 this.cells.each(function(c){
16247 //Roo.log("check " +c.dateValue + '?=' + dt);
16248 if(c.dateValue == dt){
16258 findCells : function(ev) {
16259 var s = ev.start.clone().clearTime().getTime();
16261 var e= ev.end.clone().clearTime().getTime();
16264 this.cells.each(function(c){
16265 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16267 if(c.dateValue > e){
16270 if(c.dateValue < s){
16279 // findBestRow: function(cells)
16283 // for (var i =0 ; i < cells.length;i++) {
16284 // ret = Math.max(cells[i].rows || 0,ret);
16291 addItem : function(ev)
16293 // look for vertical location slot in
16294 var cells = this.findCells(ev);
16296 // ev.row = this.findBestRow(cells);
16298 // work out the location.
16302 for(var i =0; i < cells.length; i++) {
16304 cells[i].row = cells[0].row;
16307 cells[i].row = cells[i].row + 1;
16317 if (crow.start.getY() == cells[i].getY()) {
16319 crow.end = cells[i];
16336 cells[0].events.push(ev);
16338 this.calevents.push(ev);
16341 clearEvents: function() {
16343 if(!this.calevents){
16347 Roo.each(this.cells.elements, function(c){
16353 Roo.each(this.calevents, function(e) {
16354 Roo.each(e.els, function(el) {
16355 el.un('mouseenter' ,this.onEventEnter, this);
16356 el.un('mouseleave' ,this.onEventLeave, this);
16361 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16367 renderEvents: function()
16371 this.cells.each(function(c) {
16380 if(c.row != c.events.length){
16381 r = 4 - (4 - (c.row - c.events.length));
16384 c.events = ev.slice(0, r);
16385 c.more = ev.slice(r);
16387 if(c.more.length && c.more.length == 1){
16388 c.events.push(c.more.pop());
16391 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16395 this.cells.each(function(c) {
16397 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16400 for (var e = 0; e < c.events.length; e++){
16401 var ev = c.events[e];
16402 var rows = ev.rows;
16404 for(var i = 0; i < rows.length; i++) {
16406 // how many rows should it span..
16409 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16410 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16412 unselectable : "on",
16415 cls: 'fc-event-inner',
16419 // cls: 'fc-event-time',
16420 // html : cells.length > 1 ? '' : ev.time
16424 cls: 'fc-event-title',
16425 html : String.format('{0}', ev.title)
16432 cls: 'ui-resizable-handle ui-resizable-e',
16433 html : '  '
16440 cfg.cls += ' fc-event-start';
16442 if ((i+1) == rows.length) {
16443 cfg.cls += ' fc-event-end';
16446 var ctr = _this.el.select('.fc-event-container',true).first();
16447 var cg = ctr.createChild(cfg);
16449 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16450 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16452 var r = (c.more.length) ? 1 : 0;
16453 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16454 cg.setWidth(ebox.right - sbox.x -2);
16456 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16457 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16458 cg.on('click', _this.onEventClick, _this, ev);
16469 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16470 style : 'position: absolute',
16471 unselectable : "on",
16474 cls: 'fc-event-inner',
16478 cls: 'fc-event-title',
16486 cls: 'ui-resizable-handle ui-resizable-e',
16487 html : '  '
16493 var ctr = _this.el.select('.fc-event-container',true).first();
16494 var cg = ctr.createChild(cfg);
16496 var sbox = c.select('.fc-day-content',true).first().getBox();
16497 var ebox = c.select('.fc-day-content',true).first().getBox();
16499 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16500 cg.setWidth(ebox.right - sbox.x -2);
16502 cg.on('click', _this.onMoreEventClick, _this, c.more);
16512 onEventEnter: function (e, el,event,d) {
16513 this.fireEvent('evententer', this, el, event);
16516 onEventLeave: function (e, el,event,d) {
16517 this.fireEvent('eventleave', this, el, event);
16520 onEventClick: function (e, el,event,d) {
16521 this.fireEvent('eventclick', this, el, event);
16524 onMonthChange: function () {
16528 onMoreEventClick: function(e, el, more)
16532 this.calpopover.placement = 'right';
16533 this.calpopover.setTitle('More');
16535 this.calpopover.setContent('');
16537 var ctr = this.calpopover.el.select('.popover-content', true).first();
16539 Roo.each(more, function(m){
16541 cls : 'fc-event-hori fc-event-draggable',
16544 var cg = ctr.createChild(cfg);
16546 cg.on('click', _this.onEventClick, _this, m);
16549 this.calpopover.show(el);
16554 onLoad: function ()
16556 this.calevents = [];
16559 if(this.store.getCount() > 0){
16560 this.store.data.each(function(d){
16563 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16564 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16565 time : d.data.start_time,
16566 title : d.data.title,
16567 description : d.data.description,
16568 venue : d.data.venue
16573 this.renderEvents();
16575 if(this.calevents.length && this.loadMask){
16576 this.maskEl.hide();
16580 onBeforeLoad: function()
16582 this.clearEvents();
16584 this.maskEl.show();
16598 * @class Roo.bootstrap.Popover
16599 * @extends Roo.bootstrap.Component
16600 * Bootstrap Popover class
16601 * @cfg {String} html contents of the popover (or false to use children..)
16602 * @cfg {String} title of popover (or false to hide)
16603 * @cfg {String} placement how it is placed
16604 * @cfg {String} trigger click || hover (or false to trigger manually)
16605 * @cfg {String} over what (parent or false to trigger manually.)
16606 * @cfg {Number} delay - delay before showing
16609 * Create a new Popover
16610 * @param {Object} config The config object
16613 Roo.bootstrap.Popover = function(config){
16614 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16620 * After the popover show
16622 * @param {Roo.bootstrap.Popover} this
16627 * After the popover hide
16629 * @param {Roo.bootstrap.Popover} this
16635 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16637 title: 'Fill in a title',
16640 placement : 'right',
16641 trigger : 'hover', // hover
16647 can_build_overlaid : false,
16649 getChildContainer : function()
16651 return this.el.select('.popover-content',true).first();
16654 getAutoCreate : function(){
16657 cls : 'popover roo-dynamic',
16658 style: 'display:block',
16664 cls : 'popover-inner',
16668 cls: 'popover-title',
16672 cls : 'popover-content',
16683 setTitle: function(str)
16686 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16688 setContent: function(str)
16691 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16693 // as it get's added to the bottom of the page.
16694 onRender : function(ct, position)
16696 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16698 var cfg = Roo.apply({}, this.getAutoCreate());
16702 cfg.cls += ' ' + this.cls;
16705 cfg.style = this.style;
16707 //Roo.log("adding to ");
16708 this.el = Roo.get(document.body).createChild(cfg, position);
16709 // Roo.log(this.el);
16714 initEvents : function()
16716 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16717 this.el.enableDisplayMode('block');
16719 if (this.over === false) {
16722 if (this.triggers === false) {
16725 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16726 var triggers = this.trigger ? this.trigger.split(' ') : [];
16727 Roo.each(triggers, function(trigger) {
16729 if (trigger == 'click') {
16730 on_el.on('click', this.toggle, this);
16731 } else if (trigger != 'manual') {
16732 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16733 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16735 on_el.on(eventIn ,this.enter, this);
16736 on_el.on(eventOut, this.leave, this);
16747 toggle : function () {
16748 this.hoverState == 'in' ? this.leave() : this.enter();
16751 enter : function () {
16753 clearTimeout(this.timeout);
16755 this.hoverState = 'in';
16757 if (!this.delay || !this.delay.show) {
16762 this.timeout = setTimeout(function () {
16763 if (_t.hoverState == 'in') {
16766 }, this.delay.show)
16769 leave : function() {
16770 clearTimeout(this.timeout);
16772 this.hoverState = 'out';
16774 if (!this.delay || !this.delay.hide) {
16779 this.timeout = setTimeout(function () {
16780 if (_t.hoverState == 'out') {
16783 }, this.delay.hide)
16786 show : function (on_el)
16789 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16793 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16794 if (this.html !== false) {
16795 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16797 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16798 if (!this.title.length) {
16799 this.el.select('.popover-title',true).hide();
16802 var placement = typeof this.placement == 'function' ?
16803 this.placement.call(this, this.el, on_el) :
16806 var autoToken = /\s?auto?\s?/i;
16807 var autoPlace = autoToken.test(placement);
16809 placement = placement.replace(autoToken, '') || 'top';
16813 //this.el.setXY([0,0]);
16815 this.el.dom.style.display='block';
16816 this.el.addClass(placement);
16818 //this.el.appendTo(on_el);
16820 var p = this.getPosition();
16821 var box = this.el.getBox();
16826 var align = Roo.bootstrap.Popover.alignment[placement];
16827 this.el.alignTo(on_el, align[0],align[1]);
16828 //var arrow = this.el.select('.arrow',true).first();
16829 //arrow.set(align[2],
16831 this.el.addClass('in');
16834 if (this.el.hasClass('fade')) {
16838 this.hoverState = 'in';
16840 this.fireEvent('show', this);
16845 this.el.setXY([0,0]);
16846 this.el.removeClass('in');
16848 this.hoverState = null;
16850 this.fireEvent('hide', this);
16855 Roo.bootstrap.Popover.alignment = {
16856 'left' : ['r-l', [-10,0], 'right'],
16857 'right' : ['l-r', [10,0], 'left'],
16858 'bottom' : ['t-b', [0,10], 'top'],
16859 'top' : [ 'b-t', [0,-10], 'bottom']
16870 * @class Roo.bootstrap.Progress
16871 * @extends Roo.bootstrap.Component
16872 * Bootstrap Progress class
16873 * @cfg {Boolean} striped striped of the progress bar
16874 * @cfg {Boolean} active animated of the progress bar
16878 * Create a new Progress
16879 * @param {Object} config The config object
16882 Roo.bootstrap.Progress = function(config){
16883 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16886 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16891 getAutoCreate : function(){
16899 cfg.cls += ' progress-striped';
16903 cfg.cls += ' active';
16922 * @class Roo.bootstrap.ProgressBar
16923 * @extends Roo.bootstrap.Component
16924 * Bootstrap ProgressBar class
16925 * @cfg {Number} aria_valuenow aria-value now
16926 * @cfg {Number} aria_valuemin aria-value min
16927 * @cfg {Number} aria_valuemax aria-value max
16928 * @cfg {String} label label for the progress bar
16929 * @cfg {String} panel (success | info | warning | danger )
16930 * @cfg {String} role role of the progress bar
16931 * @cfg {String} sr_only text
16935 * Create a new ProgressBar
16936 * @param {Object} config The config object
16939 Roo.bootstrap.ProgressBar = function(config){
16940 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16943 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16947 aria_valuemax : 100,
16953 getAutoCreate : function()
16958 cls: 'progress-bar',
16959 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16971 cfg.role = this.role;
16974 if(this.aria_valuenow){
16975 cfg['aria-valuenow'] = this.aria_valuenow;
16978 if(this.aria_valuemin){
16979 cfg['aria-valuemin'] = this.aria_valuemin;
16982 if(this.aria_valuemax){
16983 cfg['aria-valuemax'] = this.aria_valuemax;
16986 if(this.label && !this.sr_only){
16987 cfg.html = this.label;
16991 cfg.cls += ' progress-bar-' + this.panel;
16997 update : function(aria_valuenow)
16999 this.aria_valuenow = aria_valuenow;
17001 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17016 * @class Roo.bootstrap.TabGroup
17017 * @extends Roo.bootstrap.Column
17018 * Bootstrap Column class
17019 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17020 * @cfg {Boolean} carousel true to make the group behave like a carousel
17021 * @cfg {Boolean} bullets show bullets for the panels
17022 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17023 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17024 * @cfg {Boolean} showarrow (true|false) show arrow default true
17027 * Create a new TabGroup
17028 * @param {Object} config The config object
17031 Roo.bootstrap.TabGroup = function(config){
17032 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17034 this.navId = Roo.id();
17037 Roo.bootstrap.TabGroup.register(this);
17041 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17044 transition : false,
17049 slideOnTouch : false,
17052 getAutoCreate : function()
17054 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17056 cfg.cls += ' tab-content';
17058 if (this.carousel) {
17059 cfg.cls += ' carousel slide';
17062 cls : 'carousel-inner',
17066 if(this.bullets && !Roo.isTouch){
17069 cls : 'carousel-bullets',
17073 if(this.bullets_cls){
17074 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17081 cfg.cn[0].cn.push(bullets);
17084 if(this.showarrow){
17085 cfg.cn[0].cn.push({
17087 class : 'carousel-arrow',
17091 class : 'carousel-prev',
17095 class : 'fa fa-chevron-left'
17101 class : 'carousel-next',
17105 class : 'fa fa-chevron-right'
17118 initEvents: function()
17120 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17121 // this.el.on("touchstart", this.onTouchStart, this);
17124 if(this.autoslide){
17127 this.slideFn = window.setInterval(function() {
17128 _this.showPanelNext();
17132 if(this.showarrow){
17133 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17134 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17140 // onTouchStart : function(e, el, o)
17142 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17146 // this.showPanelNext();
17150 getChildContainer : function()
17152 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17156 * register a Navigation item
17157 * @param {Roo.bootstrap.NavItem} the navitem to add
17159 register : function(item)
17161 this.tabs.push( item);
17162 item.navId = this.navId; // not really needed..
17167 getActivePanel : function()
17170 Roo.each(this.tabs, function(t) {
17180 getPanelByName : function(n)
17183 Roo.each(this.tabs, function(t) {
17184 if (t.tabId == n) {
17192 indexOfPanel : function(p)
17195 Roo.each(this.tabs, function(t,i) {
17196 if (t.tabId == p.tabId) {
17205 * show a specific panel
17206 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17207 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17209 showPanel : function (pan)
17211 if(this.transition || typeof(pan) == 'undefined'){
17212 Roo.log("waiting for the transitionend");
17216 if (typeof(pan) == 'number') {
17217 pan = this.tabs[pan];
17220 if (typeof(pan) == 'string') {
17221 pan = this.getPanelByName(pan);
17224 var cur = this.getActivePanel();
17227 Roo.log('pan or acitve pan is undefined');
17231 if (pan.tabId == this.getActivePanel().tabId) {
17235 if (false === cur.fireEvent('beforedeactivate')) {
17239 if(this.bullets > 0 && !Roo.isTouch){
17240 this.setActiveBullet(this.indexOfPanel(pan));
17243 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17245 this.transition = true;
17246 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17247 var lr = dir == 'next' ? 'left' : 'right';
17248 pan.el.addClass(dir); // or prev
17249 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17250 cur.el.addClass(lr); // or right
17251 pan.el.addClass(lr);
17254 cur.el.on('transitionend', function() {
17255 Roo.log("trans end?");
17257 pan.el.removeClass([lr,dir]);
17258 pan.setActive(true);
17260 cur.el.removeClass([lr]);
17261 cur.setActive(false);
17263 _this.transition = false;
17265 }, this, { single: true } );
17270 cur.setActive(false);
17271 pan.setActive(true);
17276 showPanelNext : function()
17278 var i = this.indexOfPanel(this.getActivePanel());
17280 if (i >= this.tabs.length - 1 && !this.autoslide) {
17284 if (i >= this.tabs.length - 1 && this.autoslide) {
17288 this.showPanel(this.tabs[i+1]);
17291 showPanelPrev : function()
17293 var i = this.indexOfPanel(this.getActivePanel());
17295 if (i < 1 && !this.autoslide) {
17299 if (i < 1 && this.autoslide) {
17300 i = this.tabs.length;
17303 this.showPanel(this.tabs[i-1]);
17307 addBullet: function()
17309 if(!this.bullets || Roo.isTouch){
17312 var ctr = this.el.select('.carousel-bullets',true).first();
17313 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17314 var bullet = ctr.createChild({
17315 cls : 'bullet bullet-' + i
17316 },ctr.dom.lastChild);
17321 bullet.on('click', (function(e, el, o, ii, t){
17323 e.preventDefault();
17325 this.showPanel(ii);
17327 if(this.autoslide && this.slideFn){
17328 clearInterval(this.slideFn);
17329 this.slideFn = window.setInterval(function() {
17330 _this.showPanelNext();
17334 }).createDelegate(this, [i, bullet], true));
17339 setActiveBullet : function(i)
17345 Roo.each(this.el.select('.bullet', true).elements, function(el){
17346 el.removeClass('selected');
17349 var bullet = this.el.select('.bullet-' + i, true).first();
17355 bullet.addClass('selected');
17366 Roo.apply(Roo.bootstrap.TabGroup, {
17370 * register a Navigation Group
17371 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17373 register : function(navgrp)
17375 this.groups[navgrp.navId] = navgrp;
17379 * fetch a Navigation Group based on the navigation ID
17380 * if one does not exist , it will get created.
17381 * @param {string} the navgroup to add
17382 * @returns {Roo.bootstrap.NavGroup} the navgroup
17384 get: function(navId) {
17385 if (typeof(this.groups[navId]) == 'undefined') {
17386 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17388 return this.groups[navId] ;
17403 * @class Roo.bootstrap.TabPanel
17404 * @extends Roo.bootstrap.Component
17405 * Bootstrap TabPanel class
17406 * @cfg {Boolean} active panel active
17407 * @cfg {String} html panel content
17408 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17409 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17410 * @cfg {String} href click to link..
17414 * Create a new TabPanel
17415 * @param {Object} config The config object
17418 Roo.bootstrap.TabPanel = function(config){
17419 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17423 * Fires when the active status changes
17424 * @param {Roo.bootstrap.TabPanel} this
17425 * @param {Boolean} state the new state
17430 * @event beforedeactivate
17431 * Fires before a tab is de-activated - can be used to do validation on a form.
17432 * @param {Roo.bootstrap.TabPanel} this
17433 * @return {Boolean} false if there is an error
17436 'beforedeactivate': true
17439 this.tabId = this.tabId || Roo.id();
17443 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17451 getAutoCreate : function(){
17454 // item is needed for carousel - not sure if it has any effect otherwise
17455 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17456 html: this.html || ''
17460 cfg.cls += ' active';
17464 cfg.tabId = this.tabId;
17471 initEvents: function()
17473 var p = this.parent();
17475 this.navId = this.navId || p.navId;
17477 if (typeof(this.navId) != 'undefined') {
17478 // not really needed.. but just in case.. parent should be a NavGroup.
17479 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17483 var i = tg.tabs.length - 1;
17485 if(this.active && tg.bullets > 0 && i < tg.bullets){
17486 tg.setActiveBullet(i);
17490 this.el.on('click', this.onClick, this);
17493 this.el.on("touchstart", this.onTouchStart, this);
17494 this.el.on("touchmove", this.onTouchMove, this);
17495 this.el.on("touchend", this.onTouchEnd, this);
17500 onRender : function(ct, position)
17502 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17505 setActive : function(state)
17507 Roo.log("panel - set active " + this.tabId + "=" + state);
17509 this.active = state;
17511 this.el.removeClass('active');
17513 } else if (!this.el.hasClass('active')) {
17514 this.el.addClass('active');
17517 this.fireEvent('changed', this, state);
17520 onClick : function(e)
17522 e.preventDefault();
17524 if(!this.href.length){
17528 window.location.href = this.href;
17537 onTouchStart : function(e)
17539 this.swiping = false;
17541 this.startX = e.browserEvent.touches[0].clientX;
17542 this.startY = e.browserEvent.touches[0].clientY;
17545 onTouchMove : function(e)
17547 this.swiping = true;
17549 this.endX = e.browserEvent.touches[0].clientX;
17550 this.endY = e.browserEvent.touches[0].clientY;
17553 onTouchEnd : function(e)
17560 var tabGroup = this.parent();
17562 if(this.endX > this.startX){ // swiping right
17563 tabGroup.showPanelPrev();
17567 if(this.startX > this.endX){ // swiping left
17568 tabGroup.showPanelNext();
17587 * @class Roo.bootstrap.DateField
17588 * @extends Roo.bootstrap.Input
17589 * Bootstrap DateField class
17590 * @cfg {Number} weekStart default 0
17591 * @cfg {String} viewMode default empty, (months|years)
17592 * @cfg {String} minViewMode default empty, (months|years)
17593 * @cfg {Number} startDate default -Infinity
17594 * @cfg {Number} endDate default Infinity
17595 * @cfg {Boolean} todayHighlight default false
17596 * @cfg {Boolean} todayBtn default false
17597 * @cfg {Boolean} calendarWeeks default false
17598 * @cfg {Object} daysOfWeekDisabled default empty
17599 * @cfg {Boolean} singleMode default false (true | false)
17601 * @cfg {Boolean} keyboardNavigation default true
17602 * @cfg {String} language default en
17605 * Create a new DateField
17606 * @param {Object} config The config object
17609 Roo.bootstrap.DateField = function(config){
17610 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17614 * Fires when this field show.
17615 * @param {Roo.bootstrap.DateField} this
17616 * @param {Mixed} date The date value
17621 * Fires when this field hide.
17622 * @param {Roo.bootstrap.DateField} this
17623 * @param {Mixed} date The date value
17628 * Fires when select a date.
17629 * @param {Roo.bootstrap.DateField} this
17630 * @param {Mixed} date The date value
17634 * @event beforeselect
17635 * Fires when before select a date.
17636 * @param {Roo.bootstrap.DateField} this
17637 * @param {Mixed} date The date value
17639 beforeselect : true
17643 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17646 * @cfg {String} format
17647 * The default date format string which can be overriden for localization support. The format must be
17648 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17652 * @cfg {String} altFormats
17653 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17654 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17656 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17664 todayHighlight : false,
17670 keyboardNavigation: true,
17672 calendarWeeks: false,
17674 startDate: -Infinity,
17678 daysOfWeekDisabled: [],
17682 singleMode : false,
17684 UTCDate: function()
17686 return new Date(Date.UTC.apply(Date, arguments));
17689 UTCToday: function()
17691 var today = new Date();
17692 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17695 getDate: function() {
17696 var d = this.getUTCDate();
17697 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17700 getUTCDate: function() {
17704 setDate: function(d) {
17705 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17708 setUTCDate: function(d) {
17710 this.setValue(this.formatDate(this.date));
17713 onRender: function(ct, position)
17716 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17718 this.language = this.language || 'en';
17719 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17720 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17722 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17723 this.format = this.format || 'm/d/y';
17724 this.isInline = false;
17725 this.isInput = true;
17726 this.component = this.el.select('.add-on', true).first() || false;
17727 this.component = (this.component && this.component.length === 0) ? false : this.component;
17728 this.hasInput = this.component && this.inputEl().length;
17730 if (typeof(this.minViewMode === 'string')) {
17731 switch (this.minViewMode) {
17733 this.minViewMode = 1;
17736 this.minViewMode = 2;
17739 this.minViewMode = 0;
17744 if (typeof(this.viewMode === 'string')) {
17745 switch (this.viewMode) {
17758 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17760 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17762 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17764 this.picker().on('mousedown', this.onMousedown, this);
17765 this.picker().on('click', this.onClick, this);
17767 this.picker().addClass('datepicker-dropdown');
17769 this.startViewMode = this.viewMode;
17771 if(this.singleMode){
17772 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17773 v.setVisibilityMode(Roo.Element.DISPLAY);
17777 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17778 v.setStyle('width', '189px');
17782 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17783 if(!this.calendarWeeks){
17788 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17789 v.attr('colspan', function(i, val){
17790 return parseInt(val) + 1;
17795 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17797 this.setStartDate(this.startDate);
17798 this.setEndDate(this.endDate);
17800 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17807 if(this.isInline) {
17812 picker : function()
17814 return this.pickerEl;
17815 // return this.el.select('.datepicker', true).first();
17818 fillDow: function()
17820 var dowCnt = this.weekStart;
17829 if(this.calendarWeeks){
17837 while (dowCnt < this.weekStart + 7) {
17841 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17845 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17848 fillMonths: function()
17851 var months = this.picker().select('>.datepicker-months td', true).first();
17853 months.dom.innerHTML = '';
17859 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17862 months.createChild(month);
17869 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;
17871 if (this.date < this.startDate) {
17872 this.viewDate = new Date(this.startDate);
17873 } else if (this.date > this.endDate) {
17874 this.viewDate = new Date(this.endDate);
17876 this.viewDate = new Date(this.date);
17884 var d = new Date(this.viewDate),
17885 year = d.getUTCFullYear(),
17886 month = d.getUTCMonth(),
17887 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17888 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17889 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17890 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17891 currentDate = this.date && this.date.valueOf(),
17892 today = this.UTCToday();
17894 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17896 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17898 // this.picker.select('>tfoot th.today').
17899 // .text(dates[this.language].today)
17900 // .toggle(this.todayBtn !== false);
17902 this.updateNavArrows();
17905 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17907 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17909 prevMonth.setUTCDate(day);
17911 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17913 var nextMonth = new Date(prevMonth);
17915 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17917 nextMonth = nextMonth.valueOf();
17919 var fillMonths = false;
17921 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17923 while(prevMonth.valueOf() < nextMonth) {
17926 if (prevMonth.getUTCDay() === this.weekStart) {
17928 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17936 if(this.calendarWeeks){
17937 // ISO 8601: First week contains first thursday.
17938 // ISO also states week starts on Monday, but we can be more abstract here.
17940 // Start of current week: based on weekstart/current date
17941 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17942 // Thursday of this week
17943 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17944 // First Thursday of year, year from thursday
17945 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17946 // Calendar week: ms between thursdays, div ms per day, div 7 days
17947 calWeek = (th - yth) / 864e5 / 7 + 1;
17949 fillMonths.cn.push({
17957 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17959 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17962 if (this.todayHighlight &&
17963 prevMonth.getUTCFullYear() == today.getFullYear() &&
17964 prevMonth.getUTCMonth() == today.getMonth() &&
17965 prevMonth.getUTCDate() == today.getDate()) {
17966 clsName += ' today';
17969 if (currentDate && prevMonth.valueOf() === currentDate) {
17970 clsName += ' active';
17973 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17974 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17975 clsName += ' disabled';
17978 fillMonths.cn.push({
17980 cls: 'day ' + clsName,
17981 html: prevMonth.getDate()
17984 prevMonth.setDate(prevMonth.getDate()+1);
17987 var currentYear = this.date && this.date.getUTCFullYear();
17988 var currentMonth = this.date && this.date.getUTCMonth();
17990 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17992 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17993 v.removeClass('active');
17995 if(currentYear === year && k === currentMonth){
17996 v.addClass('active');
17999 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18000 v.addClass('disabled');
18006 year = parseInt(year/10, 10) * 10;
18008 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18010 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18013 for (var i = -1; i < 11; i++) {
18014 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18016 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18024 showMode: function(dir)
18027 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18030 Roo.each(this.picker().select('>div',true).elements, function(v){
18031 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18034 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18039 if(this.isInline) {
18043 this.picker().removeClass(['bottom', 'top']);
18045 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18047 * place to the top of element!
18051 this.picker().addClass('top');
18052 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18057 this.picker().addClass('bottom');
18059 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18062 parseDate : function(value)
18064 if(!value || value instanceof Date){
18067 var v = Date.parseDate(value, this.format);
18068 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18069 v = Date.parseDate(value, 'Y-m-d');
18071 if(!v && this.altFormats){
18072 if(!this.altFormatsArray){
18073 this.altFormatsArray = this.altFormats.split("|");
18075 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18076 v = Date.parseDate(value, this.altFormatsArray[i]);
18082 formatDate : function(date, fmt)
18084 return (!date || !(date instanceof Date)) ?
18085 date : date.dateFormat(fmt || this.format);
18088 onFocus : function()
18090 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18094 onBlur : function()
18096 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18098 var d = this.inputEl().getValue();
18107 this.picker().show();
18111 this.fireEvent('show', this, this.date);
18116 if(this.isInline) {
18119 this.picker().hide();
18120 this.viewMode = this.startViewMode;
18123 this.fireEvent('hide', this, this.date);
18127 onMousedown: function(e)
18129 e.stopPropagation();
18130 e.preventDefault();
18135 Roo.bootstrap.DateField.superclass.keyup.call(this);
18139 setValue: function(v)
18141 if(this.fireEvent('beforeselect', this, v) !== false){
18142 var d = new Date(this.parseDate(v) ).clearTime();
18144 if(isNaN(d.getTime())){
18145 this.date = this.viewDate = '';
18146 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18150 v = this.formatDate(d);
18152 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18154 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18158 this.fireEvent('select', this, this.date);
18162 getValue: function()
18164 return this.formatDate(this.date);
18167 fireKey: function(e)
18169 if (!this.picker().isVisible()){
18170 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18176 var dateChanged = false,
18178 newDate, newViewDate;
18183 e.preventDefault();
18187 if (!this.keyboardNavigation) {
18190 dir = e.keyCode == 37 ? -1 : 1;
18193 newDate = this.moveYear(this.date, dir);
18194 newViewDate = this.moveYear(this.viewDate, dir);
18195 } else if (e.shiftKey){
18196 newDate = this.moveMonth(this.date, dir);
18197 newViewDate = this.moveMonth(this.viewDate, dir);
18199 newDate = new Date(this.date);
18200 newDate.setUTCDate(this.date.getUTCDate() + dir);
18201 newViewDate = new Date(this.viewDate);
18202 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18204 if (this.dateWithinRange(newDate)){
18205 this.date = newDate;
18206 this.viewDate = newViewDate;
18207 this.setValue(this.formatDate(this.date));
18209 e.preventDefault();
18210 dateChanged = true;
18215 if (!this.keyboardNavigation) {
18218 dir = e.keyCode == 38 ? -1 : 1;
18220 newDate = this.moveYear(this.date, dir);
18221 newViewDate = this.moveYear(this.viewDate, dir);
18222 } else if (e.shiftKey){
18223 newDate = this.moveMonth(this.date, dir);
18224 newViewDate = this.moveMonth(this.viewDate, dir);
18226 newDate = new Date(this.date);
18227 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18228 newViewDate = new Date(this.viewDate);
18229 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18231 if (this.dateWithinRange(newDate)){
18232 this.date = newDate;
18233 this.viewDate = newViewDate;
18234 this.setValue(this.formatDate(this.date));
18236 e.preventDefault();
18237 dateChanged = true;
18241 this.setValue(this.formatDate(this.date));
18243 e.preventDefault();
18246 this.setValue(this.formatDate(this.date));
18260 onClick: function(e)
18262 e.stopPropagation();
18263 e.preventDefault();
18265 var target = e.getTarget();
18267 if(target.nodeName.toLowerCase() === 'i'){
18268 target = Roo.get(target).dom.parentNode;
18271 var nodeName = target.nodeName;
18272 var className = target.className;
18273 var html = target.innerHTML;
18274 //Roo.log(nodeName);
18276 switch(nodeName.toLowerCase()) {
18278 switch(className) {
18284 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18285 switch(this.viewMode){
18287 this.viewDate = this.moveMonth(this.viewDate, dir);
18291 this.viewDate = this.moveYear(this.viewDate, dir);
18297 var date = new Date();
18298 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18300 this.setValue(this.formatDate(this.date));
18307 if (className.indexOf('disabled') < 0) {
18308 this.viewDate.setUTCDate(1);
18309 if (className.indexOf('month') > -1) {
18310 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18312 var year = parseInt(html, 10) || 0;
18313 this.viewDate.setUTCFullYear(year);
18317 if(this.singleMode){
18318 this.setValue(this.formatDate(this.viewDate));
18329 //Roo.log(className);
18330 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18331 var day = parseInt(html, 10) || 1;
18332 var year = this.viewDate.getUTCFullYear(),
18333 month = this.viewDate.getUTCMonth();
18335 if (className.indexOf('old') > -1) {
18342 } else if (className.indexOf('new') > -1) {
18350 //Roo.log([year,month,day]);
18351 this.date = this.UTCDate(year, month, day,0,0,0,0);
18352 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18354 //Roo.log(this.formatDate(this.date));
18355 this.setValue(this.formatDate(this.date));
18362 setStartDate: function(startDate)
18364 this.startDate = startDate || -Infinity;
18365 if (this.startDate !== -Infinity) {
18366 this.startDate = this.parseDate(this.startDate);
18369 this.updateNavArrows();
18372 setEndDate: function(endDate)
18374 this.endDate = endDate || Infinity;
18375 if (this.endDate !== Infinity) {
18376 this.endDate = this.parseDate(this.endDate);
18379 this.updateNavArrows();
18382 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18384 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18385 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18386 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18388 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18389 return parseInt(d, 10);
18392 this.updateNavArrows();
18395 updateNavArrows: function()
18397 if(this.singleMode){
18401 var d = new Date(this.viewDate),
18402 year = d.getUTCFullYear(),
18403 month = d.getUTCMonth();
18405 Roo.each(this.picker().select('.prev', true).elements, function(v){
18407 switch (this.viewMode) {
18410 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18416 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18423 Roo.each(this.picker().select('.next', true).elements, function(v){
18425 switch (this.viewMode) {
18428 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18434 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18442 moveMonth: function(date, dir)
18447 var new_date = new Date(date.valueOf()),
18448 day = new_date.getUTCDate(),
18449 month = new_date.getUTCMonth(),
18450 mag = Math.abs(dir),
18452 dir = dir > 0 ? 1 : -1;
18455 // If going back one month, make sure month is not current month
18456 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18458 return new_date.getUTCMonth() == month;
18460 // If going forward one month, make sure month is as expected
18461 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18463 return new_date.getUTCMonth() != new_month;
18465 new_month = month + dir;
18466 new_date.setUTCMonth(new_month);
18467 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18468 if (new_month < 0 || new_month > 11) {
18469 new_month = (new_month + 12) % 12;
18472 // For magnitudes >1, move one month at a time...
18473 for (var i=0; i<mag; i++) {
18474 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18475 new_date = this.moveMonth(new_date, dir);
18477 // ...then reset the day, keeping it in the new month
18478 new_month = new_date.getUTCMonth();
18479 new_date.setUTCDate(day);
18481 return new_month != new_date.getUTCMonth();
18484 // Common date-resetting loop -- if date is beyond end of month, make it
18487 new_date.setUTCDate(--day);
18488 new_date.setUTCMonth(new_month);
18493 moveYear: function(date, dir)
18495 return this.moveMonth(date, dir*12);
18498 dateWithinRange: function(date)
18500 return date >= this.startDate && date <= this.endDate;
18506 this.picker().remove();
18509 validateValue : function(value)
18511 if(value.length < 1) {
18512 if(this.allowBlank){
18518 if(value.length < this.minLength){
18521 if(value.length > this.maxLength){
18525 var vt = Roo.form.VTypes;
18526 if(!vt[this.vtype](value, this)){
18530 if(typeof this.validator == "function"){
18531 var msg = this.validator(value);
18537 if(this.regex && !this.regex.test(value)){
18541 if(typeof(this.parseDate(value)) == 'undefined'){
18545 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18549 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18559 Roo.apply(Roo.bootstrap.DateField, {
18570 html: '<i class="fa fa-arrow-left"/>'
18580 html: '<i class="fa fa-arrow-right"/>'
18622 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18623 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18624 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18625 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18626 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18639 navFnc: 'FullYear',
18644 navFnc: 'FullYear',
18649 Roo.apply(Roo.bootstrap.DateField, {
18653 cls: 'datepicker dropdown-menu roo-dynamic',
18657 cls: 'datepicker-days',
18661 cls: 'table-condensed',
18663 Roo.bootstrap.DateField.head,
18667 Roo.bootstrap.DateField.footer
18674 cls: 'datepicker-months',
18678 cls: 'table-condensed',
18680 Roo.bootstrap.DateField.head,
18681 Roo.bootstrap.DateField.content,
18682 Roo.bootstrap.DateField.footer
18689 cls: 'datepicker-years',
18693 cls: 'table-condensed',
18695 Roo.bootstrap.DateField.head,
18696 Roo.bootstrap.DateField.content,
18697 Roo.bootstrap.DateField.footer
18716 * @class Roo.bootstrap.TimeField
18717 * @extends Roo.bootstrap.Input
18718 * Bootstrap DateField class
18722 * Create a new TimeField
18723 * @param {Object} config The config object
18726 Roo.bootstrap.TimeField = function(config){
18727 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18731 * Fires when this field show.
18732 * @param {Roo.bootstrap.DateField} thisthis
18733 * @param {Mixed} date The date value
18738 * Fires when this field hide.
18739 * @param {Roo.bootstrap.DateField} this
18740 * @param {Mixed} date The date value
18745 * Fires when select a date.
18746 * @param {Roo.bootstrap.DateField} this
18747 * @param {Mixed} date The date value
18753 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18756 * @cfg {String} format
18757 * The default time format string which can be overriden for localization support. The format must be
18758 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18762 onRender: function(ct, position)
18765 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18767 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18769 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18771 this.pop = this.picker().select('>.datepicker-time',true).first();
18772 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18774 this.picker().on('mousedown', this.onMousedown, this);
18775 this.picker().on('click', this.onClick, this);
18777 this.picker().addClass('datepicker-dropdown');
18782 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18783 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18784 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18785 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18786 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18787 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18791 fireKey: function(e){
18792 if (!this.picker().isVisible()){
18793 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18799 e.preventDefault();
18807 this.onTogglePeriod();
18810 this.onIncrementMinutes();
18813 this.onDecrementMinutes();
18822 onClick: function(e) {
18823 e.stopPropagation();
18824 e.preventDefault();
18827 picker : function()
18829 return this.el.select('.datepicker', true).first();
18832 fillTime: function()
18834 var time = this.pop.select('tbody', true).first();
18836 time.dom.innerHTML = '';
18851 cls: 'hours-up glyphicon glyphicon-chevron-up'
18871 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18892 cls: 'timepicker-hour',
18907 cls: 'timepicker-minute',
18922 cls: 'btn btn-primary period',
18944 cls: 'hours-down glyphicon glyphicon-chevron-down'
18964 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18982 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18989 var hours = this.time.getHours();
18990 var minutes = this.time.getMinutes();
19003 hours = hours - 12;
19007 hours = '0' + hours;
19011 minutes = '0' + minutes;
19014 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19015 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19016 this.pop.select('button', true).first().dom.innerHTML = period;
19022 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19024 var cls = ['bottom'];
19026 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19033 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19038 this.picker().addClass(cls.join('-'));
19042 Roo.each(cls, function(c){
19044 _this.picker().setTop(_this.inputEl().getHeight());
19048 _this.picker().setTop(0 - _this.picker().getHeight());
19053 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19057 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19064 onFocus : function()
19066 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19070 onBlur : function()
19072 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19078 this.picker().show();
19083 this.fireEvent('show', this, this.date);
19088 this.picker().hide();
19091 this.fireEvent('hide', this, this.date);
19094 setTime : function()
19097 this.setValue(this.time.format(this.format));
19099 this.fireEvent('select', this, this.date);
19104 onMousedown: function(e){
19105 e.stopPropagation();
19106 e.preventDefault();
19109 onIncrementHours: function()
19111 Roo.log('onIncrementHours');
19112 this.time = this.time.add(Date.HOUR, 1);
19117 onDecrementHours: function()
19119 Roo.log('onDecrementHours');
19120 this.time = this.time.add(Date.HOUR, -1);
19124 onIncrementMinutes: function()
19126 Roo.log('onIncrementMinutes');
19127 this.time = this.time.add(Date.MINUTE, 1);
19131 onDecrementMinutes: function()
19133 Roo.log('onDecrementMinutes');
19134 this.time = this.time.add(Date.MINUTE, -1);
19138 onTogglePeriod: function()
19140 Roo.log('onTogglePeriod');
19141 this.time = this.time.add(Date.HOUR, 12);
19148 Roo.apply(Roo.bootstrap.TimeField, {
19178 cls: 'btn btn-info ok',
19190 Roo.apply(Roo.bootstrap.TimeField, {
19194 cls: 'datepicker dropdown-menu',
19198 cls: 'datepicker-time',
19202 cls: 'table-condensed',
19204 Roo.bootstrap.TimeField.content,
19205 Roo.bootstrap.TimeField.footer
19224 * @class Roo.bootstrap.MonthField
19225 * @extends Roo.bootstrap.Input
19226 * Bootstrap MonthField class
19228 * @cfg {String} language default en
19231 * Create a new MonthField
19232 * @param {Object} config The config object
19235 Roo.bootstrap.MonthField = function(config){
19236 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19241 * Fires when this field show.
19242 * @param {Roo.bootstrap.MonthField} this
19243 * @param {Mixed} date The date value
19248 * Fires when this field hide.
19249 * @param {Roo.bootstrap.MonthField} this
19250 * @param {Mixed} date The date value
19255 * Fires when select a date.
19256 * @param {Roo.bootstrap.MonthField} this
19257 * @param {String} oldvalue The old value
19258 * @param {String} newvalue The new value
19264 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19266 onRender: function(ct, position)
19269 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19271 this.language = this.language || 'en';
19272 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19273 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19275 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19276 this.isInline = false;
19277 this.isInput = true;
19278 this.component = this.el.select('.add-on', true).first() || false;
19279 this.component = (this.component && this.component.length === 0) ? false : this.component;
19280 this.hasInput = this.component && this.inputEL().length;
19282 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19284 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19286 this.picker().on('mousedown', this.onMousedown, this);
19287 this.picker().on('click', this.onClick, this);
19289 this.picker().addClass('datepicker-dropdown');
19291 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19292 v.setStyle('width', '189px');
19299 if(this.isInline) {
19305 setValue: function(v, suppressEvent)
19307 var o = this.getValue();
19309 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19313 if(suppressEvent !== true){
19314 this.fireEvent('select', this, o, v);
19319 getValue: function()
19324 onClick: function(e)
19326 e.stopPropagation();
19327 e.preventDefault();
19329 var target = e.getTarget();
19331 if(target.nodeName.toLowerCase() === 'i'){
19332 target = Roo.get(target).dom.parentNode;
19335 var nodeName = target.nodeName;
19336 var className = target.className;
19337 var html = target.innerHTML;
19339 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19343 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19345 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19351 picker : function()
19353 return this.pickerEl;
19356 fillMonths: function()
19359 var months = this.picker().select('>.datepicker-months td', true).first();
19361 months.dom.innerHTML = '';
19367 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19370 months.createChild(month);
19379 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19380 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19383 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19384 e.removeClass('active');
19386 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19387 e.addClass('active');
19394 if(this.isInline) {
19398 this.picker().removeClass(['bottom', 'top']);
19400 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19402 * place to the top of element!
19406 this.picker().addClass('top');
19407 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19412 this.picker().addClass('bottom');
19414 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19417 onFocus : function()
19419 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19423 onBlur : function()
19425 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19427 var d = this.inputEl().getValue();
19436 this.picker().show();
19437 this.picker().select('>.datepicker-months', true).first().show();
19441 this.fireEvent('show', this, this.date);
19446 if(this.isInline) {
19449 this.picker().hide();
19450 this.fireEvent('hide', this, this.date);
19454 onMousedown: function(e)
19456 e.stopPropagation();
19457 e.preventDefault();
19462 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19466 fireKey: function(e)
19468 if (!this.picker().isVisible()){
19469 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19480 e.preventDefault();
19484 dir = e.keyCode == 37 ? -1 : 1;
19486 this.vIndex = this.vIndex + dir;
19488 if(this.vIndex < 0){
19492 if(this.vIndex > 11){
19496 if(isNaN(this.vIndex)){
19500 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19506 dir = e.keyCode == 38 ? -1 : 1;
19508 this.vIndex = this.vIndex + dir * 4;
19510 if(this.vIndex < 0){
19514 if(this.vIndex > 11){
19518 if(isNaN(this.vIndex)){
19522 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19527 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19528 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19532 e.preventDefault();
19535 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19536 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19552 this.picker().remove();
19557 Roo.apply(Roo.bootstrap.MonthField, {
19576 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19577 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19582 Roo.apply(Roo.bootstrap.MonthField, {
19586 cls: 'datepicker dropdown-menu roo-dynamic',
19590 cls: 'datepicker-months',
19594 cls: 'table-condensed',
19596 Roo.bootstrap.DateField.content
19616 * @class Roo.bootstrap.CheckBox
19617 * @extends Roo.bootstrap.Input
19618 * Bootstrap CheckBox class
19620 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19621 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19622 * @cfg {String} boxLabel The text that appears beside the checkbox
19623 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19624 * @cfg {Boolean} checked initnal the element
19625 * @cfg {Boolean} inline inline the element (default false)
19626 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19629 * Create a new CheckBox
19630 * @param {Object} config The config object
19633 Roo.bootstrap.CheckBox = function(config){
19634 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19639 * Fires when the element is checked or unchecked.
19640 * @param {Roo.bootstrap.CheckBox} this This input
19641 * @param {Boolean} checked The new checked value
19648 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19650 inputType: 'checkbox',
19658 getAutoCreate : function()
19660 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19666 cfg.cls = 'form-group ' + this.inputType; //input-group
19669 cfg.cls += ' ' + this.inputType + '-inline';
19675 type : this.inputType,
19676 value : this.inputValue,
19677 cls : 'roo-' + this.inputType, //'form-box',
19678 placeholder : this.placeholder || ''
19682 if(this.inputType != 'radio'){
19686 cls : 'roo-hidden-value',
19687 value : this.checked ? this.valueOff : this.inputValue
19692 if (this.weight) { // Validity check?
19693 cfg.cls += " " + this.inputType + "-" + this.weight;
19696 if (this.disabled) {
19697 input.disabled=true;
19701 input.checked = this.checked;
19708 input.name = this.name;
19710 if(this.inputType != 'radio'){
19711 hidden.name = this.name;
19712 input.name = '_hidden_' + this.name;
19717 input.cls += ' input-' + this.size;
19722 ['xs','sm','md','lg'].map(function(size){
19723 if (settings[size]) {
19724 cfg.cls += ' col-' + size + '-' + settings[size];
19728 var inputblock = input;
19730 if (this.before || this.after) {
19733 cls : 'input-group',
19738 inputblock.cn.push({
19740 cls : 'input-group-addon',
19745 inputblock.cn.push(input);
19747 if(this.inputType != 'radio'){
19748 inputblock.cn.push(hidden);
19752 inputblock.cn.push({
19754 cls : 'input-group-addon',
19761 if (align ==='left' && this.fieldLabel.length) {
19762 // Roo.log("left and has label");
19768 cls : 'control-label col-md-' + this.labelWidth,
19769 html : this.fieldLabel
19773 cls : "col-md-" + (12 - this.labelWidth),
19780 } else if ( this.fieldLabel.length) {
19781 // Roo.log(" label");
19785 tag: this.boxLabel ? 'span' : 'label',
19787 cls: 'control-label box-input-label',
19788 //cls : 'input-group-addon',
19789 html : this.fieldLabel
19799 // Roo.log(" no label && no align");
19800 cfg.cn = [ inputblock ] ;
19806 var boxLabelCfg = {
19808 //'for': id, // box label is handled by onclick - so no for...
19810 html: this.boxLabel
19814 boxLabelCfg.tooltip = this.tooltip;
19817 cfg.cn.push(boxLabelCfg);
19820 if(this.inputType != 'radio'){
19821 cfg.cn.push(hidden);
19829 * return the real input element.
19831 inputEl: function ()
19833 return this.el.select('input.roo-' + this.inputType,true).first();
19835 hiddenEl: function ()
19837 return this.el.select('input.roo-hidden-value',true).first();
19840 labelEl: function()
19842 return this.el.select('label.control-label',true).first();
19844 /* depricated... */
19848 return this.labelEl();
19851 boxLabelEl: function()
19853 return this.el.select('label.box-label',true).first();
19856 initEvents : function()
19858 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19860 this.inputEl().on('click', this.onClick, this);
19862 if (this.boxLabel) {
19863 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19866 this.startValue = this.getValue();
19869 Roo.bootstrap.CheckBox.register(this);
19873 onClick : function()
19875 this.setChecked(!this.checked);
19878 setChecked : function(state,suppressEvent)
19880 this.startValue = this.getValue();
19882 if(this.inputType == 'radio'){
19884 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19885 e.dom.checked = false;
19888 this.inputEl().dom.checked = true;
19890 this.inputEl().dom.value = this.inputValue;
19892 if(suppressEvent !== true){
19893 this.fireEvent('check', this, true);
19901 this.checked = state;
19903 this.inputEl().dom.checked = state;
19906 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19908 if(suppressEvent !== true){
19909 this.fireEvent('check', this, state);
19915 getValue : function()
19917 if(this.inputType == 'radio'){
19918 return this.getGroupValue();
19921 return this.hiddenEl().dom.value;
19925 getGroupValue : function()
19927 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19931 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19934 setValue : function(v,suppressEvent)
19936 if(this.inputType == 'radio'){
19937 this.setGroupValue(v, suppressEvent);
19941 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19946 setGroupValue : function(v, suppressEvent)
19948 this.startValue = this.getValue();
19950 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19951 e.dom.checked = false;
19953 if(e.dom.value == v){
19954 e.dom.checked = true;
19958 if(suppressEvent !== true){
19959 this.fireEvent('check', this, true);
19967 validate : function()
19971 (this.inputType == 'radio' && this.validateRadio()) ||
19972 (this.inputType == 'checkbox' && this.validateCheckbox())
19978 this.markInvalid();
19982 validateRadio : function()
19984 if(this.allowBlank){
19990 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19991 if(!e.dom.checked){
20003 validateCheckbox : function()
20006 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20009 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20017 for(var i in group){
20022 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20029 * Mark this field as valid
20031 markValid : function()
20035 this.fireEvent('valid', this);
20037 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20040 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20047 if(this.inputType == 'radio'){
20048 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20049 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20050 e.findParent('.form-group', false, true).addClass(_this.validClass);
20057 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20058 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20062 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20068 for(var i in group){
20069 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20070 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20075 * Mark this field as invalid
20076 * @param {String} msg The validation message
20078 markInvalid : function(msg)
20080 if(this.allowBlank){
20086 this.fireEvent('invalid', this, msg);
20088 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20091 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20095 label.markInvalid();
20098 if(this.inputType == 'radio'){
20099 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20100 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20101 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20108 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20109 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20113 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20119 for(var i in group){
20120 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20121 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20126 clearInvalid : function()
20128 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20130 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20133 label.iconEl.removeClass(label.validClass);
20134 label.iconEl.removeClass(label.invalidClass);
20138 disable : function()
20140 if(this.inputType != 'radio'){
20141 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20148 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20149 _this.getActionEl().addClass(this.disabledClass);
20150 e.dom.disabled = true;
20154 this.disabled = true;
20155 this.fireEvent("disable", this);
20159 enable : function()
20161 if(this.inputType != 'radio'){
20162 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20169 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20170 _this.getActionEl().removeClass(this.disabledClass);
20171 e.dom.disabled = false;
20175 this.disabled = false;
20176 this.fireEvent("enable", this);
20182 Roo.apply(Roo.bootstrap.CheckBox, {
20187 * register a CheckBox Group
20188 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20190 register : function(checkbox)
20192 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20193 this.groups[checkbox.groupId] = {};
20196 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20200 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20204 * fetch a CheckBox Group based on the group ID
20205 * @param {string} the group ID
20206 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20208 get: function(groupId) {
20209 if (typeof(this.groups[groupId]) == 'undefined') {
20213 return this.groups[groupId] ;
20226 * @class Roo.bootstrap.Radio
20227 * @extends Roo.bootstrap.Component
20228 * Bootstrap Radio class
20229 * @cfg {String} boxLabel - the label associated
20230 * @cfg {String} value - the value of radio
20233 * Create a new Radio
20234 * @param {Object} config The config object
20236 Roo.bootstrap.Radio = function(config){
20237 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20241 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20247 getAutoCreate : function()
20251 cls : 'form-group radio',
20256 html : this.boxLabel
20264 initEvents : function()
20266 this.parent().register(this);
20268 this.el.on('click', this.onClick, this);
20272 onClick : function()
20274 this.setChecked(true);
20277 setChecked : function(state, suppressEvent)
20279 this.parent().setValue(this.value, suppressEvent);
20286 //<script type="text/javascript">
20289 * Based Ext JS Library 1.1.1
20290 * Copyright(c) 2006-2007, Ext JS, LLC.
20296 * @class Roo.HtmlEditorCore
20297 * @extends Roo.Component
20298 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20300 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20303 Roo.HtmlEditorCore = function(config){
20306 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20311 * @event initialize
20312 * Fires when the editor is fully initialized (including the iframe)
20313 * @param {Roo.HtmlEditorCore} this
20318 * Fires when the editor is first receives the focus. Any insertion must wait
20319 * until after this event.
20320 * @param {Roo.HtmlEditorCore} this
20324 * @event beforesync
20325 * Fires before the textarea is updated with content from the editor iframe. Return false
20326 * to cancel the sync.
20327 * @param {Roo.HtmlEditorCore} this
20328 * @param {String} html
20332 * @event beforepush
20333 * Fires before the iframe editor is updated with content from the textarea. Return false
20334 * to cancel the push.
20335 * @param {Roo.HtmlEditorCore} this
20336 * @param {String} html
20341 * Fires when the textarea is updated with content from the editor iframe.
20342 * @param {Roo.HtmlEditorCore} this
20343 * @param {String} html
20348 * Fires when the iframe editor is updated with content from the textarea.
20349 * @param {Roo.HtmlEditorCore} this
20350 * @param {String} html
20355 * @event editorevent
20356 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20357 * @param {Roo.HtmlEditorCore} this
20363 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20365 // defaults : white / black...
20366 this.applyBlacklists();
20373 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20377 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20383 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20388 * @cfg {Number} height (in pixels)
20392 * @cfg {Number} width (in pixels)
20397 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20400 stylesheets: false,
20405 // private properties
20406 validationEvent : false,
20408 initialized : false,
20410 sourceEditMode : false,
20411 onFocus : Roo.emptyFn,
20413 hideMode:'offsets',
20417 // blacklist + whitelisted elements..
20424 * Protected method that will not generally be called directly. It
20425 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20426 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20428 getDocMarkup : function(){
20432 // inherit styels from page...??
20433 if (this.stylesheets === false) {
20435 Roo.get(document.head).select('style').each(function(node) {
20436 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20439 Roo.get(document.head).select('link').each(function(node) {
20440 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20443 } else if (!this.stylesheets.length) {
20445 st = '<style type="text/css">' +
20446 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20452 st += '<style type="text/css">' +
20453 'IMG { cursor: pointer } ' +
20457 return '<html><head>' + st +
20458 //<style type="text/css">' +
20459 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20461 ' </head><body class="roo-htmleditor-body"></body></html>';
20465 onRender : function(ct, position)
20468 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20469 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20472 this.el.dom.style.border = '0 none';
20473 this.el.dom.setAttribute('tabIndex', -1);
20474 this.el.addClass('x-hidden hide');
20478 if(Roo.isIE){ // fix IE 1px bogus margin
20479 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20483 this.frameId = Roo.id();
20487 var iframe = this.owner.wrap.createChild({
20489 cls: 'form-control', // bootstrap..
20491 name: this.frameId,
20492 frameBorder : 'no',
20493 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20498 this.iframe = iframe.dom;
20500 this.assignDocWin();
20502 this.doc.designMode = 'on';
20505 this.doc.write(this.getDocMarkup());
20509 var task = { // must defer to wait for browser to be ready
20511 //console.log("run task?" + this.doc.readyState);
20512 this.assignDocWin();
20513 if(this.doc.body || this.doc.readyState == 'complete'){
20515 this.doc.designMode="on";
20519 Roo.TaskMgr.stop(task);
20520 this.initEditor.defer(10, this);
20527 Roo.TaskMgr.start(task);
20532 onResize : function(w, h)
20534 Roo.log('resize: ' +w + ',' + h );
20535 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20539 if(typeof w == 'number'){
20541 this.iframe.style.width = w + 'px';
20543 if(typeof h == 'number'){
20545 this.iframe.style.height = h + 'px';
20547 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20554 * Toggles the editor between standard and source edit mode.
20555 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20557 toggleSourceEdit : function(sourceEditMode){
20559 this.sourceEditMode = sourceEditMode === true;
20561 if(this.sourceEditMode){
20563 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20566 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20567 //this.iframe.className = '';
20570 //this.setSize(this.owner.wrap.getSize());
20571 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20578 * Protected method that will not generally be called directly. If you need/want
20579 * custom HTML cleanup, this is the method you should override.
20580 * @param {String} html The HTML to be cleaned
20581 * return {String} The cleaned HTML
20583 cleanHtml : function(html){
20584 html = String(html);
20585 if(html.length > 5){
20586 if(Roo.isSafari){ // strip safari nonsense
20587 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20590 if(html == ' '){
20597 * HTML Editor -> Textarea
20598 * Protected method that will not generally be called directly. Syncs the contents
20599 * of the editor iframe with the textarea.
20601 syncValue : function(){
20602 if(this.initialized){
20603 var bd = (this.doc.body || this.doc.documentElement);
20604 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20605 var html = bd.innerHTML;
20607 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20608 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20610 html = '<div style="'+m[0]+'">' + html + '</div>';
20613 html = this.cleanHtml(html);
20614 // fix up the special chars.. normaly like back quotes in word...
20615 // however we do not want to do this with chinese..
20616 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20617 var cc = b.charCodeAt();
20619 (cc >= 0x4E00 && cc < 0xA000 ) ||
20620 (cc >= 0x3400 && cc < 0x4E00 ) ||
20621 (cc >= 0xf900 && cc < 0xfb00 )
20627 if(this.owner.fireEvent('beforesync', this, html) !== false){
20628 this.el.dom.value = html;
20629 this.owner.fireEvent('sync', this, html);
20635 * Protected method that will not generally be called directly. Pushes the value of the textarea
20636 * into the iframe editor.
20638 pushValue : function(){
20639 if(this.initialized){
20640 var v = this.el.dom.value.trim();
20642 // if(v.length < 1){
20646 if(this.owner.fireEvent('beforepush', this, v) !== false){
20647 var d = (this.doc.body || this.doc.documentElement);
20649 this.cleanUpPaste();
20650 this.el.dom.value = d.innerHTML;
20651 this.owner.fireEvent('push', this, v);
20657 deferFocus : function(){
20658 this.focus.defer(10, this);
20662 focus : function(){
20663 if(this.win && !this.sourceEditMode){
20670 assignDocWin: function()
20672 var iframe = this.iframe;
20675 this.doc = iframe.contentWindow.document;
20676 this.win = iframe.contentWindow;
20678 // if (!Roo.get(this.frameId)) {
20681 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20682 // this.win = Roo.get(this.frameId).dom.contentWindow;
20684 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20688 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20689 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20694 initEditor : function(){
20695 //console.log("INIT EDITOR");
20696 this.assignDocWin();
20700 this.doc.designMode="on";
20702 this.doc.write(this.getDocMarkup());
20705 var dbody = (this.doc.body || this.doc.documentElement);
20706 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20707 // this copies styles from the containing element into thsi one..
20708 // not sure why we need all of this..
20709 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20711 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20712 //ss['background-attachment'] = 'fixed'; // w3c
20713 dbody.bgProperties = 'fixed'; // ie
20714 //Roo.DomHelper.applyStyles(dbody, ss);
20715 Roo.EventManager.on(this.doc, {
20716 //'mousedown': this.onEditorEvent,
20717 'mouseup': this.onEditorEvent,
20718 'dblclick': this.onEditorEvent,
20719 'click': this.onEditorEvent,
20720 'keyup': this.onEditorEvent,
20725 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20727 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20728 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20730 this.initialized = true;
20732 this.owner.fireEvent('initialize', this);
20737 onDestroy : function(){
20743 //for (var i =0; i < this.toolbars.length;i++) {
20744 // // fixme - ask toolbars for heights?
20745 // this.toolbars[i].onDestroy();
20748 //this.wrap.dom.innerHTML = '';
20749 //this.wrap.remove();
20754 onFirstFocus : function(){
20756 this.assignDocWin();
20759 this.activated = true;
20762 if(Roo.isGecko){ // prevent silly gecko errors
20764 var s = this.win.getSelection();
20765 if(!s.focusNode || s.focusNode.nodeType != 3){
20766 var r = s.getRangeAt(0);
20767 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20772 this.execCmd('useCSS', true);
20773 this.execCmd('styleWithCSS', false);
20776 this.owner.fireEvent('activate', this);
20780 adjustFont: function(btn){
20781 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20782 //if(Roo.isSafari){ // safari
20785 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20786 if(Roo.isSafari){ // safari
20787 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20788 v = (v < 10) ? 10 : v;
20789 v = (v > 48) ? 48 : v;
20790 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20795 v = Math.max(1, v+adjust);
20797 this.execCmd('FontSize', v );
20800 onEditorEvent : function(e)
20802 this.owner.fireEvent('editorevent', this, e);
20803 // this.updateToolbar();
20804 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20807 insertTag : function(tg)
20809 // could be a bit smarter... -> wrap the current selected tRoo..
20810 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20812 range = this.createRange(this.getSelection());
20813 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20814 wrappingNode.appendChild(range.extractContents());
20815 range.insertNode(wrappingNode);
20822 this.execCmd("formatblock", tg);
20826 insertText : function(txt)
20830 var range = this.createRange();
20831 range.deleteContents();
20832 //alert(Sender.getAttribute('label'));
20834 range.insertNode(this.doc.createTextNode(txt));
20840 * Executes a Midas editor command on the editor document and performs necessary focus and
20841 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20842 * @param {String} cmd The Midas command
20843 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20845 relayCmd : function(cmd, value){
20847 this.execCmd(cmd, value);
20848 this.owner.fireEvent('editorevent', this);
20849 //this.updateToolbar();
20850 this.owner.deferFocus();
20854 * Executes a Midas editor command directly on the editor document.
20855 * For visual commands, you should use {@link #relayCmd} instead.
20856 * <b>This should only be called after the editor is initialized.</b>
20857 * @param {String} cmd The Midas command
20858 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20860 execCmd : function(cmd, value){
20861 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20868 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20870 * @param {String} text | dom node..
20872 insertAtCursor : function(text)
20877 if(!this.activated){
20883 var r = this.doc.selection.createRange();
20894 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20898 // from jquery ui (MIT licenced)
20900 var win = this.win;
20902 if (win.getSelection && win.getSelection().getRangeAt) {
20903 range = win.getSelection().getRangeAt(0);
20904 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20905 range.insertNode(node);
20906 } else if (win.document.selection && win.document.selection.createRange) {
20907 // no firefox support
20908 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20909 win.document.selection.createRange().pasteHTML(txt);
20911 // no firefox support
20912 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20913 this.execCmd('InsertHTML', txt);
20922 mozKeyPress : function(e){
20924 var c = e.getCharCode(), cmd;
20927 c = String.fromCharCode(c).toLowerCase();
20941 this.cleanUpPaste.defer(100, this);
20949 e.preventDefault();
20957 fixKeys : function(){ // load time branching for fastest keydown performance
20959 return function(e){
20960 var k = e.getKey(), r;
20963 r = this.doc.selection.createRange();
20966 r.pasteHTML('    ');
20973 r = this.doc.selection.createRange();
20975 var target = r.parentElement();
20976 if(!target || target.tagName.toLowerCase() != 'li'){
20978 r.pasteHTML('<br />');
20984 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20985 this.cleanUpPaste.defer(100, this);
20991 }else if(Roo.isOpera){
20992 return function(e){
20993 var k = e.getKey();
20997 this.execCmd('InsertHTML','    ');
21000 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21001 this.cleanUpPaste.defer(100, this);
21006 }else if(Roo.isSafari){
21007 return function(e){
21008 var k = e.getKey();
21012 this.execCmd('InsertText','\t');
21016 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21017 this.cleanUpPaste.defer(100, this);
21025 getAllAncestors: function()
21027 var p = this.getSelectedNode();
21030 a.push(p); // push blank onto stack..
21031 p = this.getParentElement();
21035 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21039 a.push(this.doc.body);
21043 lastSelNode : false,
21046 getSelection : function()
21048 this.assignDocWin();
21049 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21052 getSelectedNode: function()
21054 // this may only work on Gecko!!!
21056 // should we cache this!!!!
21061 var range = this.createRange(this.getSelection()).cloneRange();
21064 var parent = range.parentElement();
21066 var testRange = range.duplicate();
21067 testRange.moveToElementText(parent);
21068 if (testRange.inRange(range)) {
21071 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21074 parent = parent.parentElement;
21079 // is ancestor a text element.
21080 var ac = range.commonAncestorContainer;
21081 if (ac.nodeType == 3) {
21082 ac = ac.parentNode;
21085 var ar = ac.childNodes;
21088 var other_nodes = [];
21089 var has_other_nodes = false;
21090 for (var i=0;i<ar.length;i++) {
21091 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21094 // fullly contained node.
21096 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21101 // probably selected..
21102 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21103 other_nodes.push(ar[i]);
21107 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21112 has_other_nodes = true;
21114 if (!nodes.length && other_nodes.length) {
21115 nodes= other_nodes;
21117 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21123 createRange: function(sel)
21125 // this has strange effects when using with
21126 // top toolbar - not sure if it's a great idea.
21127 //this.editor.contentWindow.focus();
21128 if (typeof sel != "undefined") {
21130 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21132 return this.doc.createRange();
21135 return this.doc.createRange();
21138 getParentElement: function()
21141 this.assignDocWin();
21142 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21144 var range = this.createRange(sel);
21147 var p = range.commonAncestorContainer;
21148 while (p.nodeType == 3) { // text node
21159 * Range intersection.. the hard stuff...
21163 * [ -- selected range --- ]
21167 * if end is before start or hits it. fail.
21168 * if start is after end or hits it fail.
21170 * if either hits (but other is outside. - then it's not
21176 // @see http://www.thismuchiknow.co.uk/?p=64.
21177 rangeIntersectsNode : function(range, node)
21179 var nodeRange = node.ownerDocument.createRange();
21181 nodeRange.selectNode(node);
21183 nodeRange.selectNodeContents(node);
21186 var rangeStartRange = range.cloneRange();
21187 rangeStartRange.collapse(true);
21189 var rangeEndRange = range.cloneRange();
21190 rangeEndRange.collapse(false);
21192 var nodeStartRange = nodeRange.cloneRange();
21193 nodeStartRange.collapse(true);
21195 var nodeEndRange = nodeRange.cloneRange();
21196 nodeEndRange.collapse(false);
21198 return rangeStartRange.compareBoundaryPoints(
21199 Range.START_TO_START, nodeEndRange) == -1 &&
21200 rangeEndRange.compareBoundaryPoints(
21201 Range.START_TO_START, nodeStartRange) == 1;
21205 rangeCompareNode : function(range, node)
21207 var nodeRange = node.ownerDocument.createRange();
21209 nodeRange.selectNode(node);
21211 nodeRange.selectNodeContents(node);
21215 range.collapse(true);
21217 nodeRange.collapse(true);
21219 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21220 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21222 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21224 var nodeIsBefore = ss == 1;
21225 var nodeIsAfter = ee == -1;
21227 if (nodeIsBefore && nodeIsAfter) {
21230 if (!nodeIsBefore && nodeIsAfter) {
21231 return 1; //right trailed.
21234 if (nodeIsBefore && !nodeIsAfter) {
21235 return 2; // left trailed.
21241 // private? - in a new class?
21242 cleanUpPaste : function()
21244 // cleans up the whole document..
21245 Roo.log('cleanuppaste');
21247 this.cleanUpChildren(this.doc.body);
21248 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21249 if (clean != this.doc.body.innerHTML) {
21250 this.doc.body.innerHTML = clean;
21255 cleanWordChars : function(input) {// change the chars to hex code
21256 var he = Roo.HtmlEditorCore;
21258 var output = input;
21259 Roo.each(he.swapCodes, function(sw) {
21260 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21262 output = output.replace(swapper, sw[1]);
21269 cleanUpChildren : function (n)
21271 if (!n.childNodes.length) {
21274 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21275 this.cleanUpChild(n.childNodes[i]);
21282 cleanUpChild : function (node)
21285 //console.log(node);
21286 if (node.nodeName == "#text") {
21287 // clean up silly Windows -- stuff?
21290 if (node.nodeName == "#comment") {
21291 node.parentNode.removeChild(node);
21292 // clean up silly Windows -- stuff?
21295 var lcname = node.tagName.toLowerCase();
21296 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21297 // whitelist of tags..
21299 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21301 node.parentNode.removeChild(node);
21306 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21308 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21309 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21311 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21312 // remove_keep_children = true;
21315 if (remove_keep_children) {
21316 this.cleanUpChildren(node);
21317 // inserts everything just before this node...
21318 while (node.childNodes.length) {
21319 var cn = node.childNodes[0];
21320 node.removeChild(cn);
21321 node.parentNode.insertBefore(cn, node);
21323 node.parentNode.removeChild(node);
21327 if (!node.attributes || !node.attributes.length) {
21328 this.cleanUpChildren(node);
21332 function cleanAttr(n,v)
21335 if (v.match(/^\./) || v.match(/^\//)) {
21338 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21341 if (v.match(/^#/)) {
21344 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21345 node.removeAttribute(n);
21349 var cwhite = this.cwhite;
21350 var cblack = this.cblack;
21352 function cleanStyle(n,v)
21354 if (v.match(/expression/)) { //XSS?? should we even bother..
21355 node.removeAttribute(n);
21359 var parts = v.split(/;/);
21362 Roo.each(parts, function(p) {
21363 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21367 var l = p.split(':').shift().replace(/\s+/g,'');
21368 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21370 if ( cwhite.length && cblack.indexOf(l) > -1) {
21371 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21372 //node.removeAttribute(n);
21376 // only allow 'c whitelisted system attributes'
21377 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21378 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21379 //node.removeAttribute(n);
21389 if (clean.length) {
21390 node.setAttribute(n, clean.join(';'));
21392 node.removeAttribute(n);
21398 for (var i = node.attributes.length-1; i > -1 ; i--) {
21399 var a = node.attributes[i];
21402 if (a.name.toLowerCase().substr(0,2)=='on') {
21403 node.removeAttribute(a.name);
21406 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21407 node.removeAttribute(a.name);
21410 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21411 cleanAttr(a.name,a.value); // fixme..
21414 if (a.name == 'style') {
21415 cleanStyle(a.name,a.value);
21418 /// clean up MS crap..
21419 // tecnically this should be a list of valid class'es..
21422 if (a.name == 'class') {
21423 if (a.value.match(/^Mso/)) {
21424 node.className = '';
21427 if (a.value.match(/body/)) {
21428 node.className = '';
21439 this.cleanUpChildren(node);
21445 * Clean up MS wordisms...
21447 cleanWord : function(node)
21452 this.cleanWord(this.doc.body);
21455 if (node.nodeName == "#text") {
21456 // clean up silly Windows -- stuff?
21459 if (node.nodeName == "#comment") {
21460 node.parentNode.removeChild(node);
21461 // clean up silly Windows -- stuff?
21465 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21466 node.parentNode.removeChild(node);
21470 // remove - but keep children..
21471 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21472 while (node.childNodes.length) {
21473 var cn = node.childNodes[0];
21474 node.removeChild(cn);
21475 node.parentNode.insertBefore(cn, node);
21477 node.parentNode.removeChild(node);
21478 this.iterateChildren(node, this.cleanWord);
21482 if (node.className.length) {
21484 var cn = node.className.split(/\W+/);
21486 Roo.each(cn, function(cls) {
21487 if (cls.match(/Mso[a-zA-Z]+/)) {
21492 node.className = cna.length ? cna.join(' ') : '';
21494 node.removeAttribute("class");
21498 if (node.hasAttribute("lang")) {
21499 node.removeAttribute("lang");
21502 if (node.hasAttribute("style")) {
21504 var styles = node.getAttribute("style").split(";");
21506 Roo.each(styles, function(s) {
21507 if (!s.match(/:/)) {
21510 var kv = s.split(":");
21511 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21514 // what ever is left... we allow.
21517 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21518 if (!nstyle.length) {
21519 node.removeAttribute('style');
21522 this.iterateChildren(node, this.cleanWord);
21528 * iterateChildren of a Node, calling fn each time, using this as the scole..
21529 * @param {DomNode} node node to iterate children of.
21530 * @param {Function} fn method of this class to call on each item.
21532 iterateChildren : function(node, fn)
21534 if (!node.childNodes.length) {
21537 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21538 fn.call(this, node.childNodes[i])
21544 * cleanTableWidths.
21546 * Quite often pasting from word etc.. results in tables with column and widths.
21547 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21550 cleanTableWidths : function(node)
21555 this.cleanTableWidths(this.doc.body);
21560 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21563 Roo.log(node.tagName);
21564 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21565 this.iterateChildren(node, this.cleanTableWidths);
21568 if (node.hasAttribute('width')) {
21569 node.removeAttribute('width');
21573 if (node.hasAttribute("style")) {
21576 var styles = node.getAttribute("style").split(";");
21578 Roo.each(styles, function(s) {
21579 if (!s.match(/:/)) {
21582 var kv = s.split(":");
21583 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21586 // what ever is left... we allow.
21589 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21590 if (!nstyle.length) {
21591 node.removeAttribute('style');
21595 this.iterateChildren(node, this.cleanTableWidths);
21603 domToHTML : function(currentElement, depth, nopadtext) {
21605 depth = depth || 0;
21606 nopadtext = nopadtext || false;
21608 if (!currentElement) {
21609 return this.domToHTML(this.doc.body);
21612 //Roo.log(currentElement);
21614 var allText = false;
21615 var nodeName = currentElement.nodeName;
21616 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21618 if (nodeName == '#text') {
21620 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21625 if (nodeName != 'BODY') {
21628 // Prints the node tagName, such as <A>, <IMG>, etc
21631 for(i = 0; i < currentElement.attributes.length;i++) {
21633 var aname = currentElement.attributes.item(i).name;
21634 if (!currentElement.attributes.item(i).value.length) {
21637 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21640 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21649 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21652 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21657 // Traverse the tree
21659 var currentElementChild = currentElement.childNodes.item(i);
21660 var allText = true;
21661 var innerHTML = '';
21663 while (currentElementChild) {
21664 // Formatting code (indent the tree so it looks nice on the screen)
21665 var nopad = nopadtext;
21666 if (lastnode == 'SPAN') {
21670 if (currentElementChild.nodeName == '#text') {
21671 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21672 toadd = nopadtext ? toadd : toadd.trim();
21673 if (!nopad && toadd.length > 80) {
21674 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21676 innerHTML += toadd;
21679 currentElementChild = currentElement.childNodes.item(i);
21685 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21687 // Recursively traverse the tree structure of the child node
21688 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21689 lastnode = currentElementChild.nodeName;
21691 currentElementChild=currentElement.childNodes.item(i);
21697 // The remaining code is mostly for formatting the tree
21698 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21703 ret+= "</"+tagName+">";
21709 applyBlacklists : function()
21711 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21712 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21716 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21717 if (b.indexOf(tag) > -1) {
21720 this.white.push(tag);
21724 Roo.each(w, function(tag) {
21725 if (b.indexOf(tag) > -1) {
21728 if (this.white.indexOf(tag) > -1) {
21731 this.white.push(tag);
21736 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21737 if (w.indexOf(tag) > -1) {
21740 this.black.push(tag);
21744 Roo.each(b, function(tag) {
21745 if (w.indexOf(tag) > -1) {
21748 if (this.black.indexOf(tag) > -1) {
21751 this.black.push(tag);
21756 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21757 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21761 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21762 if (b.indexOf(tag) > -1) {
21765 this.cwhite.push(tag);
21769 Roo.each(w, function(tag) {
21770 if (b.indexOf(tag) > -1) {
21773 if (this.cwhite.indexOf(tag) > -1) {
21776 this.cwhite.push(tag);
21781 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21782 if (w.indexOf(tag) > -1) {
21785 this.cblack.push(tag);
21789 Roo.each(b, function(tag) {
21790 if (w.indexOf(tag) > -1) {
21793 if (this.cblack.indexOf(tag) > -1) {
21796 this.cblack.push(tag);
21801 setStylesheets : function(stylesheets)
21803 if(typeof(stylesheets) == 'string'){
21804 Roo.get(this.iframe.contentDocument.head).createChild({
21806 rel : 'stylesheet',
21815 Roo.each(stylesheets, function(s) {
21820 Roo.get(_this.iframe.contentDocument.head).createChild({
21822 rel : 'stylesheet',
21831 removeStylesheets : function()
21835 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21840 // hide stuff that is not compatible
21854 * @event specialkey
21858 * @cfg {String} fieldClass @hide
21861 * @cfg {String} focusClass @hide
21864 * @cfg {String} autoCreate @hide
21867 * @cfg {String} inputType @hide
21870 * @cfg {String} invalidClass @hide
21873 * @cfg {String} invalidText @hide
21876 * @cfg {String} msgFx @hide
21879 * @cfg {String} validateOnBlur @hide
21883 Roo.HtmlEditorCore.white = [
21884 'area', 'br', 'img', 'input', 'hr', 'wbr',
21886 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21887 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21888 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21889 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21890 'table', 'ul', 'xmp',
21892 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21895 'dir', 'menu', 'ol', 'ul', 'dl',
21901 Roo.HtmlEditorCore.black = [
21902 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21904 'base', 'basefont', 'bgsound', 'blink', 'body',
21905 'frame', 'frameset', 'head', 'html', 'ilayer',
21906 'iframe', 'layer', 'link', 'meta', 'object',
21907 'script', 'style' ,'title', 'xml' // clean later..
21909 Roo.HtmlEditorCore.clean = [
21910 'script', 'style', 'title', 'xml'
21912 Roo.HtmlEditorCore.remove = [
21917 Roo.HtmlEditorCore.ablack = [
21921 Roo.HtmlEditorCore.aclean = [
21922 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21926 Roo.HtmlEditorCore.pwhite= [
21927 'http', 'https', 'mailto'
21930 // white listed style attributes.
21931 Roo.HtmlEditorCore.cwhite= [
21932 // 'text-align', /// default is to allow most things..
21938 // black listed style attributes.
21939 Roo.HtmlEditorCore.cblack= [
21940 // 'font-size' -- this can be set by the project
21944 Roo.HtmlEditorCore.swapCodes =[
21963 * @class Roo.bootstrap.HtmlEditor
21964 * @extends Roo.bootstrap.TextArea
21965 * Bootstrap HtmlEditor class
21968 * Create a new HtmlEditor
21969 * @param {Object} config The config object
21972 Roo.bootstrap.HtmlEditor = function(config){
21973 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21974 if (!this.toolbars) {
21975 this.toolbars = [];
21977 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21980 * @event initialize
21981 * Fires when the editor is fully initialized (including the iframe)
21982 * @param {HtmlEditor} this
21987 * Fires when the editor is first receives the focus. Any insertion must wait
21988 * until after this event.
21989 * @param {HtmlEditor} this
21993 * @event beforesync
21994 * Fires before the textarea is updated with content from the editor iframe. Return false
21995 * to cancel the sync.
21996 * @param {HtmlEditor} this
21997 * @param {String} html
22001 * @event beforepush
22002 * Fires before the iframe editor is updated with content from the textarea. Return false
22003 * to cancel the push.
22004 * @param {HtmlEditor} this
22005 * @param {String} html
22010 * Fires when the textarea is updated with content from the editor iframe.
22011 * @param {HtmlEditor} this
22012 * @param {String} html
22017 * Fires when the iframe editor is updated with content from the textarea.
22018 * @param {HtmlEditor} this
22019 * @param {String} html
22023 * @event editmodechange
22024 * Fires when the editor switches edit modes
22025 * @param {HtmlEditor} this
22026 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22028 editmodechange: true,
22030 * @event editorevent
22031 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22032 * @param {HtmlEditor} this
22036 * @event firstfocus
22037 * Fires when on first focus - needed by toolbars..
22038 * @param {HtmlEditor} this
22043 * Auto save the htmlEditor value as a file into Events
22044 * @param {HtmlEditor} this
22048 * @event savedpreview
22049 * preview the saved version of htmlEditor
22050 * @param {HtmlEditor} this
22057 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22061 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22066 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22071 * @cfg {Number} height (in pixels)
22075 * @cfg {Number} width (in pixels)
22080 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22083 stylesheets: false,
22088 // private properties
22089 validationEvent : false,
22091 initialized : false,
22094 onFocus : Roo.emptyFn,
22096 hideMode:'offsets',
22099 tbContainer : false,
22101 toolbarContainer :function() {
22102 return this.wrap.select('.x-html-editor-tb',true).first();
22106 * Protected method that will not generally be called directly. It
22107 * is called when the editor creates its toolbar. Override this method if you need to
22108 * add custom toolbar buttons.
22109 * @param {HtmlEditor} editor
22111 createToolbar : function(){
22113 Roo.log("create toolbars");
22115 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22116 this.toolbars[0].render(this.toolbarContainer());
22120 // if (!editor.toolbars || !editor.toolbars.length) {
22121 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22124 // for (var i =0 ; i < editor.toolbars.length;i++) {
22125 // editor.toolbars[i] = Roo.factory(
22126 // typeof(editor.toolbars[i]) == 'string' ?
22127 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22128 // Roo.bootstrap.HtmlEditor);
22129 // editor.toolbars[i].init(editor);
22135 onRender : function(ct, position)
22137 // Roo.log("Call onRender: " + this.xtype);
22139 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22141 this.wrap = this.inputEl().wrap({
22142 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22145 this.editorcore.onRender(ct, position);
22147 if (this.resizable) {
22148 this.resizeEl = new Roo.Resizable(this.wrap, {
22152 minHeight : this.height,
22153 height: this.height,
22154 handles : this.resizable,
22157 resize : function(r, w, h) {
22158 _t.onResize(w,h); // -something
22164 this.createToolbar(this);
22167 if(!this.width && this.resizable){
22168 this.setSize(this.wrap.getSize());
22170 if (this.resizeEl) {
22171 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22172 // should trigger onReize..
22178 onResize : function(w, h)
22180 Roo.log('resize: ' +w + ',' + h );
22181 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22185 if(this.inputEl() ){
22186 if(typeof w == 'number'){
22187 var aw = w - this.wrap.getFrameWidth('lr');
22188 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22191 if(typeof h == 'number'){
22192 var tbh = -11; // fixme it needs to tool bar size!
22193 for (var i =0; i < this.toolbars.length;i++) {
22194 // fixme - ask toolbars for heights?
22195 tbh += this.toolbars[i].el.getHeight();
22196 //if (this.toolbars[i].footer) {
22197 // tbh += this.toolbars[i].footer.el.getHeight();
22205 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22206 ah -= 5; // knock a few pixes off for look..
22207 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22211 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22212 this.editorcore.onResize(ew,eh);
22217 * Toggles the editor between standard and source edit mode.
22218 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22220 toggleSourceEdit : function(sourceEditMode)
22222 this.editorcore.toggleSourceEdit(sourceEditMode);
22224 if(this.editorcore.sourceEditMode){
22225 Roo.log('editor - showing textarea');
22228 // Roo.log(this.syncValue());
22230 this.inputEl().removeClass(['hide', 'x-hidden']);
22231 this.inputEl().dom.removeAttribute('tabIndex');
22232 this.inputEl().focus();
22234 Roo.log('editor - hiding textarea');
22236 // Roo.log(this.pushValue());
22239 this.inputEl().addClass(['hide', 'x-hidden']);
22240 this.inputEl().dom.setAttribute('tabIndex', -1);
22241 //this.deferFocus();
22244 if(this.resizable){
22245 this.setSize(this.wrap.getSize());
22248 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22251 // private (for BoxComponent)
22252 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22254 // private (for BoxComponent)
22255 getResizeEl : function(){
22259 // private (for BoxComponent)
22260 getPositionEl : function(){
22265 initEvents : function(){
22266 this.originalValue = this.getValue();
22270 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22273 // markInvalid : Roo.emptyFn,
22275 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22278 // clearInvalid : Roo.emptyFn,
22280 setValue : function(v){
22281 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22282 this.editorcore.pushValue();
22287 deferFocus : function(){
22288 this.focus.defer(10, this);
22292 focus : function(){
22293 this.editorcore.focus();
22299 onDestroy : function(){
22305 for (var i =0; i < this.toolbars.length;i++) {
22306 // fixme - ask toolbars for heights?
22307 this.toolbars[i].onDestroy();
22310 this.wrap.dom.innerHTML = '';
22311 this.wrap.remove();
22316 onFirstFocus : function(){
22317 //Roo.log("onFirstFocus");
22318 this.editorcore.onFirstFocus();
22319 for (var i =0; i < this.toolbars.length;i++) {
22320 this.toolbars[i].onFirstFocus();
22326 syncValue : function()
22328 this.editorcore.syncValue();
22331 pushValue : function()
22333 this.editorcore.pushValue();
22337 // hide stuff that is not compatible
22351 * @event specialkey
22355 * @cfg {String} fieldClass @hide
22358 * @cfg {String} focusClass @hide
22361 * @cfg {String} autoCreate @hide
22364 * @cfg {String} inputType @hide
22367 * @cfg {String} invalidClass @hide
22370 * @cfg {String} invalidText @hide
22373 * @cfg {String} msgFx @hide
22376 * @cfg {String} validateOnBlur @hide
22385 Roo.namespace('Roo.bootstrap.htmleditor');
22387 * @class Roo.bootstrap.HtmlEditorToolbar1
22392 new Roo.bootstrap.HtmlEditor({
22395 new Roo.bootstrap.HtmlEditorToolbar1({
22396 disable : { fonts: 1 , format: 1, ..., ... , ...],
22402 * @cfg {Object} disable List of elements to disable..
22403 * @cfg {Array} btns List of additional buttons.
22407 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22410 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22413 Roo.apply(this, config);
22415 // default disabled, based on 'good practice'..
22416 this.disable = this.disable || {};
22417 Roo.applyIf(this.disable, {
22420 specialElements : true
22422 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22424 this.editor = config.editor;
22425 this.editorcore = config.editor.editorcore;
22427 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22429 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22430 // dont call parent... till later.
22432 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22437 editorcore : false,
22442 "h1","h2","h3","h4","h5","h6",
22444 "abbr", "acronym", "address", "cite", "samp", "var",
22448 onRender : function(ct, position)
22450 // Roo.log("Call onRender: " + this.xtype);
22452 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22454 this.el.dom.style.marginBottom = '0';
22456 var editorcore = this.editorcore;
22457 var editor= this.editor;
22460 var btn = function(id,cmd , toggle, handler){
22462 var event = toggle ? 'toggle' : 'click';
22467 xns: Roo.bootstrap,
22470 enableToggle:toggle !== false,
22472 pressed : toggle ? false : null,
22475 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22476 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22485 xns: Roo.bootstrap,
22486 glyphicon : 'font',
22490 xns: Roo.bootstrap,
22494 Roo.each(this.formats, function(f) {
22495 style.menu.items.push({
22497 xns: Roo.bootstrap,
22498 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22503 editorcore.insertTag(this.tagname);
22510 children.push(style);
22513 btn('bold',false,true);
22514 btn('italic',false,true);
22515 btn('align-left', 'justifyleft',true);
22516 btn('align-center', 'justifycenter',true);
22517 btn('align-right' , 'justifyright',true);
22518 btn('link', false, false, function(btn) {
22519 //Roo.log("create link?");
22520 var url = prompt(this.createLinkText, this.defaultLinkValue);
22521 if(url && url != 'http:/'+'/'){
22522 this.editorcore.relayCmd('createlink', url);
22525 btn('list','insertunorderedlist',true);
22526 btn('pencil', false,true, function(btn){
22529 this.toggleSourceEdit(btn.pressed);
22535 xns: Roo.bootstrap,
22540 xns: Roo.bootstrap,
22545 cog.menu.items.push({
22547 xns: Roo.bootstrap,
22548 html : Clean styles,
22553 editorcore.insertTag(this.tagname);
22562 this.xtype = 'NavSimplebar';
22564 for(var i=0;i< children.length;i++) {
22566 this.buttons.add(this.addxtypeChild(children[i]));
22570 editor.on('editorevent', this.updateToolbar, this);
22572 onBtnClick : function(id)
22574 this.editorcore.relayCmd(id);
22575 this.editorcore.focus();
22579 * Protected method that will not generally be called directly. It triggers
22580 * a toolbar update by reading the markup state of the current selection in the editor.
22582 updateToolbar: function(){
22584 if(!this.editorcore.activated){
22585 this.editor.onFirstFocus(); // is this neeed?
22589 var btns = this.buttons;
22590 var doc = this.editorcore.doc;
22591 btns.get('bold').setActive(doc.queryCommandState('bold'));
22592 btns.get('italic').setActive(doc.queryCommandState('italic'));
22593 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22595 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22596 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22597 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22599 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22600 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22603 var ans = this.editorcore.getAllAncestors();
22604 if (this.formatCombo) {
22607 var store = this.formatCombo.store;
22608 this.formatCombo.setValue("");
22609 for (var i =0; i < ans.length;i++) {
22610 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22612 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22620 // hides menus... - so this cant be on a menu...
22621 Roo.bootstrap.MenuMgr.hideAll();
22623 Roo.bootstrap.MenuMgr.hideAll();
22624 //this.editorsyncValue();
22626 onFirstFocus: function() {
22627 this.buttons.each(function(item){
22631 toggleSourceEdit : function(sourceEditMode){
22634 if(sourceEditMode){
22635 Roo.log("disabling buttons");
22636 this.buttons.each( function(item){
22637 if(item.cmd != 'pencil'){
22643 Roo.log("enabling buttons");
22644 if(this.editorcore.initialized){
22645 this.buttons.each( function(item){
22651 Roo.log("calling toggole on editor");
22652 // tell the editor that it's been pressed..
22653 this.editor.toggleSourceEdit(sourceEditMode);
22663 * @class Roo.bootstrap.Table.AbstractSelectionModel
22664 * @extends Roo.util.Observable
22665 * Abstract base class for grid SelectionModels. It provides the interface that should be
22666 * implemented by descendant classes. This class should not be directly instantiated.
22669 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22670 this.locked = false;
22671 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22675 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22676 /** @ignore Called by the grid automatically. Do not call directly. */
22677 init : function(grid){
22683 * Locks the selections.
22686 this.locked = true;
22690 * Unlocks the selections.
22692 unlock : function(){
22693 this.locked = false;
22697 * Returns true if the selections are locked.
22698 * @return {Boolean}
22700 isLocked : function(){
22701 return this.locked;
22705 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22706 * @class Roo.bootstrap.Table.RowSelectionModel
22707 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22708 * It supports multiple selections and keyboard selection/navigation.
22710 * @param {Object} config
22713 Roo.bootstrap.Table.RowSelectionModel = function(config){
22714 Roo.apply(this, config);
22715 this.selections = new Roo.util.MixedCollection(false, function(o){
22720 this.lastActive = false;
22724 * @event selectionchange
22725 * Fires when the selection changes
22726 * @param {SelectionModel} this
22728 "selectionchange" : true,
22730 * @event afterselectionchange
22731 * Fires after the selection changes (eg. by key press or clicking)
22732 * @param {SelectionModel} this
22734 "afterselectionchange" : true,
22736 * @event beforerowselect
22737 * Fires when a row is selected being selected, return false to cancel.
22738 * @param {SelectionModel} this
22739 * @param {Number} rowIndex The selected index
22740 * @param {Boolean} keepExisting False if other selections will be cleared
22742 "beforerowselect" : true,
22745 * Fires when a row is selected.
22746 * @param {SelectionModel} this
22747 * @param {Number} rowIndex The selected index
22748 * @param {Roo.data.Record} r The record
22750 "rowselect" : true,
22752 * @event rowdeselect
22753 * Fires when a row is deselected.
22754 * @param {SelectionModel} this
22755 * @param {Number} rowIndex The selected index
22757 "rowdeselect" : true
22759 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22760 this.locked = false;
22763 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22765 * @cfg {Boolean} singleSelect
22766 * True to allow selection of only one row at a time (defaults to false)
22768 singleSelect : false,
22771 initEvents : function()
22774 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22775 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22776 //}else{ // allow click to work like normal
22777 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22779 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22780 this.grid.on("rowclick", this.handleMouseDown, this);
22782 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22783 "up" : function(e){
22785 this.selectPrevious(e.shiftKey);
22786 }else if(this.last !== false && this.lastActive !== false){
22787 var last = this.last;
22788 this.selectRange(this.last, this.lastActive-1);
22789 this.grid.getView().focusRow(this.lastActive);
22790 if(last !== false){
22794 this.selectFirstRow();
22796 this.fireEvent("afterselectionchange", this);
22798 "down" : function(e){
22800 this.selectNext(e.shiftKey);
22801 }else if(this.last !== false && this.lastActive !== false){
22802 var last = this.last;
22803 this.selectRange(this.last, this.lastActive+1);
22804 this.grid.getView().focusRow(this.lastActive);
22805 if(last !== false){
22809 this.selectFirstRow();
22811 this.fireEvent("afterselectionchange", this);
22815 this.grid.store.on('load', function(){
22816 this.selections.clear();
22819 var view = this.grid.view;
22820 view.on("refresh", this.onRefresh, this);
22821 view.on("rowupdated", this.onRowUpdated, this);
22822 view.on("rowremoved", this.onRemove, this);
22827 onRefresh : function()
22829 var ds = this.grid.store, i, v = this.grid.view;
22830 var s = this.selections;
22831 s.each(function(r){
22832 if((i = ds.indexOfId(r.id)) != -1){
22841 onRemove : function(v, index, r){
22842 this.selections.remove(r);
22846 onRowUpdated : function(v, index, r){
22847 if(this.isSelected(r)){
22848 v.onRowSelect(index);
22854 * @param {Array} records The records to select
22855 * @param {Boolean} keepExisting (optional) True to keep existing selections
22857 selectRecords : function(records, keepExisting)
22860 this.clearSelections();
22862 var ds = this.grid.store;
22863 for(var i = 0, len = records.length; i < len; i++){
22864 this.selectRow(ds.indexOf(records[i]), true);
22869 * Gets the number of selected rows.
22872 getCount : function(){
22873 return this.selections.length;
22877 * Selects the first row in the grid.
22879 selectFirstRow : function(){
22884 * Select the last row.
22885 * @param {Boolean} keepExisting (optional) True to keep existing selections
22887 selectLastRow : function(keepExisting){
22888 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22889 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22893 * Selects the row immediately following the last selected row.
22894 * @param {Boolean} keepExisting (optional) True to keep existing selections
22896 selectNext : function(keepExisting)
22898 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22899 this.selectRow(this.last+1, keepExisting);
22900 this.grid.getView().focusRow(this.last);
22905 * Selects the row that precedes the last selected row.
22906 * @param {Boolean} keepExisting (optional) True to keep existing selections
22908 selectPrevious : function(keepExisting){
22910 this.selectRow(this.last-1, keepExisting);
22911 this.grid.getView().focusRow(this.last);
22916 * Returns the selected records
22917 * @return {Array} Array of selected records
22919 getSelections : function(){
22920 return [].concat(this.selections.items);
22924 * Returns the first selected record.
22927 getSelected : function(){
22928 return this.selections.itemAt(0);
22933 * Clears all selections.
22935 clearSelections : function(fast)
22941 var ds = this.grid.store;
22942 var s = this.selections;
22943 s.each(function(r){
22944 this.deselectRow(ds.indexOfId(r.id));
22948 this.selections.clear();
22955 * Selects all rows.
22957 selectAll : function(){
22961 this.selections.clear();
22962 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22963 this.selectRow(i, true);
22968 * Returns True if there is a selection.
22969 * @return {Boolean}
22971 hasSelection : function(){
22972 return this.selections.length > 0;
22976 * Returns True if the specified row is selected.
22977 * @param {Number/Record} record The record or index of the record to check
22978 * @return {Boolean}
22980 isSelected : function(index){
22981 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22982 return (r && this.selections.key(r.id) ? true : false);
22986 * Returns True if the specified record id is selected.
22987 * @param {String} id The id of record to check
22988 * @return {Boolean}
22990 isIdSelected : function(id){
22991 return (this.selections.key(id) ? true : false);
22996 handleMouseDBClick : function(e, t){
23000 handleMouseDown : function(e, t)
23002 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23003 if(this.isLocked() || rowIndex < 0 ){
23006 if(e.shiftKey && this.last !== false){
23007 var last = this.last;
23008 this.selectRange(last, rowIndex, e.ctrlKey);
23009 this.last = last; // reset the last
23013 var isSelected = this.isSelected(rowIndex);
23014 //Roo.log("select row:" + rowIndex);
23016 this.deselectRow(rowIndex);
23018 this.selectRow(rowIndex, true);
23022 if(e.button !== 0 && isSelected){
23023 alert('rowIndex 2: ' + rowIndex);
23024 view.focusRow(rowIndex);
23025 }else if(e.ctrlKey && isSelected){
23026 this.deselectRow(rowIndex);
23027 }else if(!isSelected){
23028 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23029 view.focusRow(rowIndex);
23033 this.fireEvent("afterselectionchange", this);
23036 handleDragableRowClick : function(grid, rowIndex, e)
23038 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23039 this.selectRow(rowIndex, false);
23040 grid.view.focusRow(rowIndex);
23041 this.fireEvent("afterselectionchange", this);
23046 * Selects multiple rows.
23047 * @param {Array} rows Array of the indexes of the row to select
23048 * @param {Boolean} keepExisting (optional) True to keep existing selections
23050 selectRows : function(rows, keepExisting){
23052 this.clearSelections();
23054 for(var i = 0, len = rows.length; i < len; i++){
23055 this.selectRow(rows[i], true);
23060 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23061 * @param {Number} startRow The index of the first row in the range
23062 * @param {Number} endRow The index of the last row in the range
23063 * @param {Boolean} keepExisting (optional) True to retain existing selections
23065 selectRange : function(startRow, endRow, keepExisting){
23070 this.clearSelections();
23072 if(startRow <= endRow){
23073 for(var i = startRow; i <= endRow; i++){
23074 this.selectRow(i, true);
23077 for(var i = startRow; i >= endRow; i--){
23078 this.selectRow(i, true);
23084 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23085 * @param {Number} startRow The index of the first row in the range
23086 * @param {Number} endRow The index of the last row in the range
23088 deselectRange : function(startRow, endRow, preventViewNotify){
23092 for(var i = startRow; i <= endRow; i++){
23093 this.deselectRow(i, preventViewNotify);
23099 * @param {Number} row The index of the row to select
23100 * @param {Boolean} keepExisting (optional) True to keep existing selections
23102 selectRow : function(index, keepExisting, preventViewNotify)
23104 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23107 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23108 if(!keepExisting || this.singleSelect){
23109 this.clearSelections();
23112 var r = this.grid.store.getAt(index);
23113 //console.log('selectRow - record id :' + r.id);
23115 this.selections.add(r);
23116 this.last = this.lastActive = index;
23117 if(!preventViewNotify){
23118 var proxy = new Roo.Element(
23119 this.grid.getRowDom(index)
23121 proxy.addClass('bg-info info');
23123 this.fireEvent("rowselect", this, index, r);
23124 this.fireEvent("selectionchange", this);
23130 * @param {Number} row The index of the row to deselect
23132 deselectRow : function(index, preventViewNotify)
23137 if(this.last == index){
23140 if(this.lastActive == index){
23141 this.lastActive = false;
23144 var r = this.grid.store.getAt(index);
23149 this.selections.remove(r);
23150 //.console.log('deselectRow - record id :' + r.id);
23151 if(!preventViewNotify){
23153 var proxy = new Roo.Element(
23154 this.grid.getRowDom(index)
23156 proxy.removeClass('bg-info info');
23158 this.fireEvent("rowdeselect", this, index);
23159 this.fireEvent("selectionchange", this);
23163 restoreLast : function(){
23165 this.last = this._last;
23170 acceptsNav : function(row, col, cm){
23171 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23175 onEditorKey : function(field, e){
23176 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23181 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23183 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23185 }else if(k == e.ENTER && !e.ctrlKey){
23189 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23191 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23193 }else if(k == e.ESC){
23197 g.startEditing(newCell[0], newCell[1]);
23203 * Ext JS Library 1.1.1
23204 * Copyright(c) 2006-2007, Ext JS, LLC.
23206 * Originally Released Under LGPL - original licence link has changed is not relivant.
23209 * <script type="text/javascript">
23213 * @class Roo.bootstrap.PagingToolbar
23214 * @extends Roo.bootstrap.NavSimplebar
23215 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23217 * Create a new PagingToolbar
23218 * @param {Object} config The config object
23219 * @param {Roo.data.Store} store
23221 Roo.bootstrap.PagingToolbar = function(config)
23223 // old args format still supported... - xtype is prefered..
23224 // created from xtype...
23226 this.ds = config.dataSource;
23228 if (config.store && !this.ds) {
23229 this.store= Roo.factory(config.store, Roo.data);
23230 this.ds = this.store;
23231 this.ds.xmodule = this.xmodule || false;
23234 this.toolbarItems = [];
23235 if (config.items) {
23236 this.toolbarItems = config.items;
23239 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23244 this.bind(this.ds);
23247 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23251 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23253 * @cfg {Roo.data.Store} dataSource
23254 * The underlying data store providing the paged data
23257 * @cfg {String/HTMLElement/Element} container
23258 * container The id or element that will contain the toolbar
23261 * @cfg {Boolean} displayInfo
23262 * True to display the displayMsg (defaults to false)
23265 * @cfg {Number} pageSize
23266 * The number of records to display per page (defaults to 20)
23270 * @cfg {String} displayMsg
23271 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23273 displayMsg : 'Displaying {0} - {1} of {2}',
23275 * @cfg {String} emptyMsg
23276 * The message to display when no records are found (defaults to "No data to display")
23278 emptyMsg : 'No data to display',
23280 * Customizable piece of the default paging text (defaults to "Page")
23283 beforePageText : "Page",
23285 * Customizable piece of the default paging text (defaults to "of %0")
23288 afterPageText : "of {0}",
23290 * Customizable piece of the default paging text (defaults to "First Page")
23293 firstText : "First Page",
23295 * Customizable piece of the default paging text (defaults to "Previous Page")
23298 prevText : "Previous Page",
23300 * Customizable piece of the default paging text (defaults to "Next Page")
23303 nextText : "Next Page",
23305 * Customizable piece of the default paging text (defaults to "Last Page")
23308 lastText : "Last Page",
23310 * Customizable piece of the default paging text (defaults to "Refresh")
23313 refreshText : "Refresh",
23317 onRender : function(ct, position)
23319 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23320 this.navgroup.parentId = this.id;
23321 this.navgroup.onRender(this.el, null);
23322 // add the buttons to the navgroup
23324 if(this.displayInfo){
23325 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23326 this.displayEl = this.el.select('.x-paging-info', true).first();
23327 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23328 // this.displayEl = navel.el.select('span',true).first();
23334 Roo.each(_this.buttons, function(e){ // this might need to use render????
23335 Roo.factory(e).onRender(_this.el, null);
23339 Roo.each(_this.toolbarItems, function(e) {
23340 _this.navgroup.addItem(e);
23344 this.first = this.navgroup.addItem({
23345 tooltip: this.firstText,
23347 icon : 'fa fa-backward',
23349 preventDefault: true,
23350 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23353 this.prev = this.navgroup.addItem({
23354 tooltip: this.prevText,
23356 icon : 'fa fa-step-backward',
23358 preventDefault: true,
23359 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23361 //this.addSeparator();
23364 var field = this.navgroup.addItem( {
23366 cls : 'x-paging-position',
23368 html : this.beforePageText +
23369 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23370 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23373 this.field = field.el.select('input', true).first();
23374 this.field.on("keydown", this.onPagingKeydown, this);
23375 this.field.on("focus", function(){this.dom.select();});
23378 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23379 //this.field.setHeight(18);
23380 //this.addSeparator();
23381 this.next = this.navgroup.addItem({
23382 tooltip: this.nextText,
23384 html : ' <i class="fa fa-step-forward">',
23386 preventDefault: true,
23387 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23389 this.last = this.navgroup.addItem({
23390 tooltip: this.lastText,
23391 icon : 'fa fa-forward',
23394 preventDefault: true,
23395 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23397 //this.addSeparator();
23398 this.loading = this.navgroup.addItem({
23399 tooltip: this.refreshText,
23400 icon: 'fa fa-refresh',
23401 preventDefault: true,
23402 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23408 updateInfo : function(){
23409 if(this.displayEl){
23410 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23411 var msg = count == 0 ?
23415 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23417 this.displayEl.update(msg);
23422 onLoad : function(ds, r, o){
23423 this.cursor = o.params ? o.params.start : 0;
23424 var d = this.getPageData(),
23428 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23429 this.field.dom.value = ap;
23430 this.first.setDisabled(ap == 1);
23431 this.prev.setDisabled(ap == 1);
23432 this.next.setDisabled(ap == ps);
23433 this.last.setDisabled(ap == ps);
23434 this.loading.enable();
23439 getPageData : function(){
23440 var total = this.ds.getTotalCount();
23443 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23444 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23449 onLoadError : function(){
23450 this.loading.enable();
23454 onPagingKeydown : function(e){
23455 var k = e.getKey();
23456 var d = this.getPageData();
23458 var v = this.field.dom.value, pageNum;
23459 if(!v || isNaN(pageNum = parseInt(v, 10))){
23460 this.field.dom.value = d.activePage;
23463 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23464 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23467 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))
23469 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23470 this.field.dom.value = pageNum;
23471 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23474 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23476 var v = this.field.dom.value, pageNum;
23477 var increment = (e.shiftKey) ? 10 : 1;
23478 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23481 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23482 this.field.dom.value = d.activePage;
23485 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23487 this.field.dom.value = parseInt(v, 10) + increment;
23488 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23489 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23496 beforeLoad : function(){
23498 this.loading.disable();
23503 onClick : function(which){
23512 ds.load({params:{start: 0, limit: this.pageSize}});
23515 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23518 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23521 var total = ds.getTotalCount();
23522 var extra = total % this.pageSize;
23523 var lastStart = extra ? (total - extra) : total-this.pageSize;
23524 ds.load({params:{start: lastStart, limit: this.pageSize}});
23527 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23533 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23534 * @param {Roo.data.Store} store The data store to unbind
23536 unbind : function(ds){
23537 ds.un("beforeload", this.beforeLoad, this);
23538 ds.un("load", this.onLoad, this);
23539 ds.un("loadexception", this.onLoadError, this);
23540 ds.un("remove", this.updateInfo, this);
23541 ds.un("add", this.updateInfo, this);
23542 this.ds = undefined;
23546 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23547 * @param {Roo.data.Store} store The data store to bind
23549 bind : function(ds){
23550 ds.on("beforeload", this.beforeLoad, this);
23551 ds.on("load", this.onLoad, this);
23552 ds.on("loadexception", this.onLoadError, this);
23553 ds.on("remove", this.updateInfo, this);
23554 ds.on("add", this.updateInfo, this);
23565 * @class Roo.bootstrap.MessageBar
23566 * @extends Roo.bootstrap.Component
23567 * Bootstrap MessageBar class
23568 * @cfg {String} html contents of the MessageBar
23569 * @cfg {String} weight (info | success | warning | danger) default info
23570 * @cfg {String} beforeClass insert the bar before the given class
23571 * @cfg {Boolean} closable (true | false) default false
23572 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23575 * Create a new Element
23576 * @param {Object} config The config object
23579 Roo.bootstrap.MessageBar = function(config){
23580 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23583 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23589 beforeClass: 'bootstrap-sticky-wrap',
23591 getAutoCreate : function(){
23595 cls: 'alert alert-dismissable alert-' + this.weight,
23600 html: this.html || ''
23606 cfg.cls += ' alert-messages-fixed';
23620 onRender : function(ct, position)
23622 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23625 var cfg = Roo.apply({}, this.getAutoCreate());
23629 cfg.cls += ' ' + this.cls;
23632 cfg.style = this.style;
23634 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23636 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23639 this.el.select('>button.close').on('click', this.hide, this);
23645 if (!this.rendered) {
23651 this.fireEvent('show', this);
23657 if (!this.rendered) {
23663 this.fireEvent('hide', this);
23666 update : function()
23668 // var e = this.el.dom.firstChild;
23670 // if(this.closable){
23671 // e = e.nextSibling;
23674 // e.data = this.html || '';
23676 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23692 * @class Roo.bootstrap.Graph
23693 * @extends Roo.bootstrap.Component
23694 * Bootstrap Graph class
23698 @cfg {String} graphtype bar | vbar | pie
23699 @cfg {number} g_x coodinator | centre x (pie)
23700 @cfg {number} g_y coodinator | centre y (pie)
23701 @cfg {number} g_r radius (pie)
23702 @cfg {number} g_height height of the chart (respected by all elements in the set)
23703 @cfg {number} g_width width of the chart (respected by all elements in the set)
23704 @cfg {Object} title The title of the chart
23707 -opts (object) options for the chart
23709 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23710 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23712 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.
23713 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23715 o stretch (boolean)
23717 -opts (object) options for the pie
23720 o startAngle (number)
23721 o endAngle (number)
23725 * Create a new Input
23726 * @param {Object} config The config object
23729 Roo.bootstrap.Graph = function(config){
23730 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23736 * The img click event for the img.
23737 * @param {Roo.EventObject} e
23743 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23754 //g_colors: this.colors,
23761 getAutoCreate : function(){
23772 onRender : function(ct,position){
23775 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23777 if (typeof(Raphael) == 'undefined') {
23778 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23782 this.raphael = Raphael(this.el.dom);
23784 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23785 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23786 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23787 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23789 r.text(160, 10, "Single Series Chart").attr(txtattr);
23790 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23791 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23792 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23794 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23795 r.barchart(330, 10, 300, 220, data1);
23796 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23797 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23800 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23801 // r.barchart(30, 30, 560, 250, xdata, {
23802 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23803 // axis : "0 0 1 1",
23804 // axisxlabels : xdata
23805 // //yvalues : cols,
23808 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23810 // this.load(null,xdata,{
23811 // axis : "0 0 1 1",
23812 // axisxlabels : xdata
23817 load : function(graphtype,xdata,opts)
23819 this.raphael.clear();
23821 graphtype = this.graphtype;
23826 var r = this.raphael,
23827 fin = function () {
23828 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23830 fout = function () {
23831 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23833 pfin = function() {
23834 this.sector.stop();
23835 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23838 this.label[0].stop();
23839 this.label[0].attr({ r: 7.5 });
23840 this.label[1].attr({ "font-weight": 800 });
23843 pfout = function() {
23844 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23847 this.label[0].animate({ r: 5 }, 500, "bounce");
23848 this.label[1].attr({ "font-weight": 400 });
23854 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23857 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23860 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23861 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23863 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23870 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23875 setTitle: function(o)
23880 initEvents: function() {
23883 this.el.on('click', this.onClick, this);
23887 onClick : function(e)
23889 Roo.log('img onclick');
23890 this.fireEvent('click', this, e);
23902 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23905 * @class Roo.bootstrap.dash.NumberBox
23906 * @extends Roo.bootstrap.Component
23907 * Bootstrap NumberBox class
23908 * @cfg {String} headline Box headline
23909 * @cfg {String} content Box content
23910 * @cfg {String} icon Box icon
23911 * @cfg {String} footer Footer text
23912 * @cfg {String} fhref Footer href
23915 * Create a new NumberBox
23916 * @param {Object} config The config object
23920 Roo.bootstrap.dash.NumberBox = function(config){
23921 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23925 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23934 getAutoCreate : function(){
23938 cls : 'small-box ',
23946 cls : 'roo-headline',
23947 html : this.headline
23951 cls : 'roo-content',
23952 html : this.content
23966 cls : 'ion ' + this.icon
23975 cls : 'small-box-footer',
23976 href : this.fhref || '#',
23980 cfg.cn.push(footer);
23987 onRender : function(ct,position){
23988 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23995 setHeadline: function (value)
23997 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24000 setFooter: function (value, href)
24002 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24005 this.el.select('a.small-box-footer',true).first().attr('href', href);
24010 setContent: function (value)
24012 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24015 initEvents: function()
24029 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24032 * @class Roo.bootstrap.dash.TabBox
24033 * @extends Roo.bootstrap.Component
24034 * Bootstrap TabBox class
24035 * @cfg {String} title Title of the TabBox
24036 * @cfg {String} icon Icon of the TabBox
24037 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24038 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24041 * Create a new TabBox
24042 * @param {Object} config The config object
24046 Roo.bootstrap.dash.TabBox = function(config){
24047 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24052 * When a pane is added
24053 * @param {Roo.bootstrap.dash.TabPane} pane
24057 * @event activatepane
24058 * When a pane is activated
24059 * @param {Roo.bootstrap.dash.TabPane} pane
24061 "activatepane" : true
24069 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24074 tabScrollable : false,
24076 getChildContainer : function()
24078 return this.el.select('.tab-content', true).first();
24081 getAutoCreate : function(){
24085 cls: 'pull-left header',
24093 cls: 'fa ' + this.icon
24099 cls: 'nav nav-tabs pull-right',
24105 if(this.tabScrollable){
24112 cls: 'nav nav-tabs pull-right',
24123 cls: 'nav-tabs-custom',
24128 cls: 'tab-content no-padding',
24136 initEvents : function()
24138 //Roo.log('add add pane handler');
24139 this.on('addpane', this.onAddPane, this);
24142 * Updates the box title
24143 * @param {String} html to set the title to.
24145 setTitle : function(value)
24147 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24149 onAddPane : function(pane)
24151 this.panes.push(pane);
24152 //Roo.log('addpane');
24154 // tabs are rendere left to right..
24155 if(!this.showtabs){
24159 var ctr = this.el.select('.nav-tabs', true).first();
24162 var existing = ctr.select('.nav-tab',true);
24163 var qty = existing.getCount();;
24166 var tab = ctr.createChild({
24168 cls : 'nav-tab' + (qty ? '' : ' active'),
24176 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24179 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24181 pane.el.addClass('active');
24186 onTabClick : function(ev,un,ob,pane)
24188 //Roo.log('tab - prev default');
24189 ev.preventDefault();
24192 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24193 pane.tab.addClass('active');
24194 //Roo.log(pane.title);
24195 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24196 // technically we should have a deactivate event.. but maybe add later.
24197 // and it should not de-activate the selected tab...
24198 this.fireEvent('activatepane', pane);
24199 pane.el.addClass('active');
24200 pane.fireEvent('activate');
24205 getActivePane : function()
24208 Roo.each(this.panes, function(p) {
24209 if(p.el.hasClass('active')){
24230 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24232 * @class Roo.bootstrap.TabPane
24233 * @extends Roo.bootstrap.Component
24234 * Bootstrap TabPane class
24235 * @cfg {Boolean} active (false | true) Default false
24236 * @cfg {String} title title of panel
24240 * Create a new TabPane
24241 * @param {Object} config The config object
24244 Roo.bootstrap.dash.TabPane = function(config){
24245 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24251 * When a pane is activated
24252 * @param {Roo.bootstrap.dash.TabPane} pane
24259 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24264 // the tabBox that this is attached to.
24267 getAutoCreate : function()
24275 cfg.cls += ' active';
24280 initEvents : function()
24282 //Roo.log('trigger add pane handler');
24283 this.parent().fireEvent('addpane', this)
24287 * Updates the tab title
24288 * @param {String} html to set the title to.
24290 setTitle: function(str)
24296 this.tab.select('a', true).first().dom.innerHTML = str;
24313 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24316 * @class Roo.bootstrap.menu.Menu
24317 * @extends Roo.bootstrap.Component
24318 * Bootstrap Menu class - container for Menu
24319 * @cfg {String} html Text of the menu
24320 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24321 * @cfg {String} icon Font awesome icon
24322 * @cfg {String} pos Menu align to (top | bottom) default bottom
24326 * Create a new Menu
24327 * @param {Object} config The config object
24331 Roo.bootstrap.menu.Menu = function(config){
24332 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24336 * @event beforeshow
24337 * Fires before this menu is displayed
24338 * @param {Roo.bootstrap.menu.Menu} this
24342 * @event beforehide
24343 * Fires before this menu is hidden
24344 * @param {Roo.bootstrap.menu.Menu} this
24349 * Fires after this menu is displayed
24350 * @param {Roo.bootstrap.menu.Menu} this
24355 * Fires after this menu is hidden
24356 * @param {Roo.bootstrap.menu.Menu} this
24361 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24362 * @param {Roo.bootstrap.menu.Menu} this
24363 * @param {Roo.EventObject} e
24370 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24374 weight : 'default',
24379 getChildContainer : function() {
24380 if(this.isSubMenu){
24384 return this.el.select('ul.dropdown-menu', true).first();
24387 getAutoCreate : function()
24392 cls : 'roo-menu-text',
24400 cls : 'fa ' + this.icon
24411 cls : 'dropdown-button btn btn-' + this.weight,
24416 cls : 'dropdown-toggle btn btn-' + this.weight,
24426 cls : 'dropdown-menu'
24432 if(this.pos == 'top'){
24433 cfg.cls += ' dropup';
24436 if(this.isSubMenu){
24439 cls : 'dropdown-menu'
24446 onRender : function(ct, position)
24448 this.isSubMenu = ct.hasClass('dropdown-submenu');
24450 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24453 initEvents : function()
24455 if(this.isSubMenu){
24459 this.hidden = true;
24461 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24462 this.triggerEl.on('click', this.onTriggerPress, this);
24464 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24465 this.buttonEl.on('click', this.onClick, this);
24471 if(this.isSubMenu){
24475 return this.el.select('ul.dropdown-menu', true).first();
24478 onClick : function(e)
24480 this.fireEvent("click", this, e);
24483 onTriggerPress : function(e)
24485 if (this.isVisible()) {
24492 isVisible : function(){
24493 return !this.hidden;
24498 this.fireEvent("beforeshow", this);
24500 this.hidden = false;
24501 this.el.addClass('open');
24503 Roo.get(document).on("mouseup", this.onMouseUp, this);
24505 this.fireEvent("show", this);
24512 this.fireEvent("beforehide", this);
24514 this.hidden = true;
24515 this.el.removeClass('open');
24517 Roo.get(document).un("mouseup", this.onMouseUp);
24519 this.fireEvent("hide", this);
24522 onMouseUp : function()
24536 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24539 * @class Roo.bootstrap.menu.Item
24540 * @extends Roo.bootstrap.Component
24541 * Bootstrap MenuItem class
24542 * @cfg {Boolean} submenu (true | false) default false
24543 * @cfg {String} html text of the item
24544 * @cfg {String} href the link
24545 * @cfg {Boolean} disable (true | false) default false
24546 * @cfg {Boolean} preventDefault (true | false) default true
24547 * @cfg {String} icon Font awesome icon
24548 * @cfg {String} pos Submenu align to (left | right) default right
24552 * Create a new Item
24553 * @param {Object} config The config object
24557 Roo.bootstrap.menu.Item = function(config){
24558 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24562 * Fires when the mouse is hovering over this menu
24563 * @param {Roo.bootstrap.menu.Item} this
24564 * @param {Roo.EventObject} e
24569 * Fires when the mouse exits this menu
24570 * @param {Roo.bootstrap.menu.Item} this
24571 * @param {Roo.EventObject} e
24577 * The raw click event for the entire grid.
24578 * @param {Roo.EventObject} e
24584 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24589 preventDefault: true,
24594 getAutoCreate : function()
24599 cls : 'roo-menu-item-text',
24607 cls : 'fa ' + this.icon
24616 href : this.href || '#',
24623 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24627 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24629 if(this.pos == 'left'){
24630 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24637 initEvents : function()
24639 this.el.on('mouseover', this.onMouseOver, this);
24640 this.el.on('mouseout', this.onMouseOut, this);
24642 this.el.select('a', true).first().on('click', this.onClick, this);
24646 onClick : function(e)
24648 if(this.preventDefault){
24649 e.preventDefault();
24652 this.fireEvent("click", this, e);
24655 onMouseOver : function(e)
24657 if(this.submenu && this.pos == 'left'){
24658 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24661 this.fireEvent("mouseover", this, e);
24664 onMouseOut : function(e)
24666 this.fireEvent("mouseout", this, e);
24678 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24681 * @class Roo.bootstrap.menu.Separator
24682 * @extends Roo.bootstrap.Component
24683 * Bootstrap Separator class
24686 * Create a new Separator
24687 * @param {Object} config The config object
24691 Roo.bootstrap.menu.Separator = function(config){
24692 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24695 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24697 getAutoCreate : function(){
24718 * @class Roo.bootstrap.Tooltip
24719 * Bootstrap Tooltip class
24720 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24721 * to determine which dom element triggers the tooltip.
24723 * It needs to add support for additional attributes like tooltip-position
24726 * Create a new Toolti
24727 * @param {Object} config The config object
24730 Roo.bootstrap.Tooltip = function(config){
24731 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24734 Roo.apply(Roo.bootstrap.Tooltip, {
24736 * @function init initialize tooltip monitoring.
24740 currentTip : false,
24741 currentRegion : false,
24747 Roo.get(document).on('mouseover', this.enter ,this);
24748 Roo.get(document).on('mouseout', this.leave, this);
24751 this.currentTip = new Roo.bootstrap.Tooltip();
24754 enter : function(ev)
24756 var dom = ev.getTarget();
24758 //Roo.log(['enter',dom]);
24759 var el = Roo.fly(dom);
24760 if (this.currentEl) {
24762 //Roo.log(this.currentEl);
24763 //Roo.log(this.currentEl.contains(dom));
24764 if (this.currentEl == el) {
24767 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24773 if (this.currentTip.el) {
24774 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24778 if(!el || el.dom == document){
24784 // you can not look for children, as if el is the body.. then everythign is the child..
24785 if (!el.attr('tooltip')) { //
24786 if (!el.select("[tooltip]").elements.length) {
24789 // is the mouse over this child...?
24790 bindEl = el.select("[tooltip]").first();
24791 var xy = ev.getXY();
24792 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24793 //Roo.log("not in region.");
24796 //Roo.log("child element over..");
24799 this.currentEl = bindEl;
24800 this.currentTip.bind(bindEl);
24801 this.currentRegion = Roo.lib.Region.getRegion(dom);
24802 this.currentTip.enter();
24805 leave : function(ev)
24807 var dom = ev.getTarget();
24808 //Roo.log(['leave',dom]);
24809 if (!this.currentEl) {
24814 if (dom != this.currentEl.dom) {
24817 var xy = ev.getXY();
24818 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24821 // only activate leave if mouse cursor is outside... bounding box..
24826 if (this.currentTip) {
24827 this.currentTip.leave();
24829 //Roo.log('clear currentEl');
24830 this.currentEl = false;
24835 'left' : ['r-l', [-2,0], 'right'],
24836 'right' : ['l-r', [2,0], 'left'],
24837 'bottom' : ['t-b', [0,2], 'top'],
24838 'top' : [ 'b-t', [0,-2], 'bottom']
24844 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24849 delay : null, // can be { show : 300 , hide: 500}
24853 hoverState : null, //???
24855 placement : 'bottom',
24857 getAutoCreate : function(){
24864 cls : 'tooltip-arrow'
24867 cls : 'tooltip-inner'
24874 bind : function(el)
24880 enter : function () {
24882 if (this.timeout != null) {
24883 clearTimeout(this.timeout);
24886 this.hoverState = 'in';
24887 //Roo.log("enter - show");
24888 if (!this.delay || !this.delay.show) {
24893 this.timeout = setTimeout(function () {
24894 if (_t.hoverState == 'in') {
24897 }, this.delay.show);
24901 clearTimeout(this.timeout);
24903 this.hoverState = 'out';
24904 if (!this.delay || !this.delay.hide) {
24910 this.timeout = setTimeout(function () {
24911 //Roo.log("leave - timeout");
24913 if (_t.hoverState == 'out') {
24915 Roo.bootstrap.Tooltip.currentEl = false;
24920 show : function (msg)
24923 this.render(document.body);
24926 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24928 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24930 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24932 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24934 var placement = typeof this.placement == 'function' ?
24935 this.placement.call(this, this.el, on_el) :
24938 var autoToken = /\s?auto?\s?/i;
24939 var autoPlace = autoToken.test(placement);
24941 placement = placement.replace(autoToken, '') || 'top';
24945 //this.el.setXY([0,0]);
24947 //this.el.dom.style.display='block';
24949 //this.el.appendTo(on_el);
24951 var p = this.getPosition();
24952 var box = this.el.getBox();
24958 var align = this.alignment ? this.alignment[placement] : Roo.bootstrap.Tooltip.alignment[placement];
24960 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24962 if(placement == 'top' || placement == 'bottom'){
24964 placement = 'right';
24967 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24968 placement = 'left';
24971 var scroll = Roo.select('body', true).first().getScroll();
24973 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24979 this.el.alignTo(this.bindEl, align[0],align[1]);
24980 //var arrow = this.el.select('.arrow',true).first();
24981 //arrow.set(align[2],
24983 this.el.addClass(placement);
24985 this.el.addClass('in fade');
24987 this.hoverState = null;
24989 if (this.el.hasClass('fade')) {
25000 //this.el.setXY([0,0]);
25001 this.el.removeClass('in');
25017 * @class Roo.bootstrap.LocationPicker
25018 * @extends Roo.bootstrap.Component
25019 * Bootstrap LocationPicker class
25020 * @cfg {Number} latitude Position when init default 0
25021 * @cfg {Number} longitude Position when init default 0
25022 * @cfg {Number} zoom default 15
25023 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25024 * @cfg {Boolean} mapTypeControl default false
25025 * @cfg {Boolean} disableDoubleClickZoom default false
25026 * @cfg {Boolean} scrollwheel default true
25027 * @cfg {Boolean} streetViewControl default false
25028 * @cfg {Number} radius default 0
25029 * @cfg {String} locationName
25030 * @cfg {Boolean} draggable default true
25031 * @cfg {Boolean} enableAutocomplete default false
25032 * @cfg {Boolean} enableReverseGeocode default true
25033 * @cfg {String} markerTitle
25036 * Create a new LocationPicker
25037 * @param {Object} config The config object
25041 Roo.bootstrap.LocationPicker = function(config){
25043 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25048 * Fires when the picker initialized.
25049 * @param {Roo.bootstrap.LocationPicker} this
25050 * @param {Google Location} location
25054 * @event positionchanged
25055 * Fires when the picker position changed.
25056 * @param {Roo.bootstrap.LocationPicker} this
25057 * @param {Google Location} location
25059 positionchanged : true,
25062 * Fires when the map resize.
25063 * @param {Roo.bootstrap.LocationPicker} this
25068 * Fires when the map show.
25069 * @param {Roo.bootstrap.LocationPicker} this
25074 * Fires when the map hide.
25075 * @param {Roo.bootstrap.LocationPicker} this
25080 * Fires when click the map.
25081 * @param {Roo.bootstrap.LocationPicker} this
25082 * @param {Map event} e
25086 * @event mapRightClick
25087 * Fires when right click the map.
25088 * @param {Roo.bootstrap.LocationPicker} this
25089 * @param {Map event} e
25091 mapRightClick : true,
25093 * @event markerClick
25094 * Fires when click the marker.
25095 * @param {Roo.bootstrap.LocationPicker} this
25096 * @param {Map event} e
25098 markerClick : true,
25100 * @event markerRightClick
25101 * Fires when right click the marker.
25102 * @param {Roo.bootstrap.LocationPicker} this
25103 * @param {Map event} e
25105 markerRightClick : true,
25107 * @event OverlayViewDraw
25108 * Fires when OverlayView Draw
25109 * @param {Roo.bootstrap.LocationPicker} this
25111 OverlayViewDraw : true,
25113 * @event OverlayViewOnAdd
25114 * Fires when OverlayView Draw
25115 * @param {Roo.bootstrap.LocationPicker} this
25117 OverlayViewOnAdd : true,
25119 * @event OverlayViewOnRemove
25120 * Fires when OverlayView Draw
25121 * @param {Roo.bootstrap.LocationPicker} this
25123 OverlayViewOnRemove : true,
25125 * @event OverlayViewShow
25126 * Fires when OverlayView Draw
25127 * @param {Roo.bootstrap.LocationPicker} this
25128 * @param {Pixel} cpx
25130 OverlayViewShow : true,
25132 * @event OverlayViewHide
25133 * Fires when OverlayView Draw
25134 * @param {Roo.bootstrap.LocationPicker} this
25136 OverlayViewHide : true,
25138 * @event loadexception
25139 * Fires when load google lib failed.
25140 * @param {Roo.bootstrap.LocationPicker} this
25142 loadexception : true
25147 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25149 gMapContext: false,
25155 mapTypeControl: false,
25156 disableDoubleClickZoom: false,
25158 streetViewControl: false,
25162 enableAutocomplete: false,
25163 enableReverseGeocode: true,
25166 getAutoCreate: function()
25171 cls: 'roo-location-picker'
25177 initEvents: function(ct, position)
25179 if(!this.el.getWidth() || this.isApplied()){
25183 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25188 initial: function()
25190 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25191 this.fireEvent('loadexception', this);
25195 if(!this.mapTypeId){
25196 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25199 this.gMapContext = this.GMapContext();
25201 this.initOverlayView();
25203 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25207 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25208 _this.setPosition(_this.gMapContext.marker.position);
25211 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25212 _this.fireEvent('mapClick', this, event);
25216 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25217 _this.fireEvent('mapRightClick', this, event);
25221 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25222 _this.fireEvent('markerClick', this, event);
25226 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25227 _this.fireEvent('markerRightClick', this, event);
25231 this.setPosition(this.gMapContext.location);
25233 this.fireEvent('initial', this, this.gMapContext.location);
25236 initOverlayView: function()
25240 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25244 _this.fireEvent('OverlayViewDraw', _this);
25249 _this.fireEvent('OverlayViewOnAdd', _this);
25252 onRemove: function()
25254 _this.fireEvent('OverlayViewOnRemove', _this);
25257 show: function(cpx)
25259 _this.fireEvent('OverlayViewShow', _this, cpx);
25264 _this.fireEvent('OverlayViewHide', _this);
25270 fromLatLngToContainerPixel: function(event)
25272 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25275 isApplied: function()
25277 return this.getGmapContext() == false ? false : true;
25280 getGmapContext: function()
25282 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25285 GMapContext: function()
25287 var position = new google.maps.LatLng(this.latitude, this.longitude);
25289 var _map = new google.maps.Map(this.el.dom, {
25292 mapTypeId: this.mapTypeId,
25293 mapTypeControl: this.mapTypeControl,
25294 disableDoubleClickZoom: this.disableDoubleClickZoom,
25295 scrollwheel: this.scrollwheel,
25296 streetViewControl: this.streetViewControl,
25297 locationName: this.locationName,
25298 draggable: this.draggable,
25299 enableAutocomplete: this.enableAutocomplete,
25300 enableReverseGeocode: this.enableReverseGeocode
25303 var _marker = new google.maps.Marker({
25304 position: position,
25306 title: this.markerTitle,
25307 draggable: this.draggable
25314 location: position,
25315 radius: this.radius,
25316 locationName: this.locationName,
25317 addressComponents: {
25318 formatted_address: null,
25319 addressLine1: null,
25320 addressLine2: null,
25322 streetNumber: null,
25326 stateOrProvince: null
25329 domContainer: this.el.dom,
25330 geodecoder: new google.maps.Geocoder()
25334 drawCircle: function(center, radius, options)
25336 if (this.gMapContext.circle != null) {
25337 this.gMapContext.circle.setMap(null);
25341 options = Roo.apply({}, options, {
25342 strokeColor: "#0000FF",
25343 strokeOpacity: .35,
25345 fillColor: "#0000FF",
25349 options.map = this.gMapContext.map;
25350 options.radius = radius;
25351 options.center = center;
25352 this.gMapContext.circle = new google.maps.Circle(options);
25353 return this.gMapContext.circle;
25359 setPosition: function(location)
25361 this.gMapContext.location = location;
25362 this.gMapContext.marker.setPosition(location);
25363 this.gMapContext.map.panTo(location);
25364 this.drawCircle(location, this.gMapContext.radius, {});
25368 if (this.gMapContext.settings.enableReverseGeocode) {
25369 this.gMapContext.geodecoder.geocode({
25370 latLng: this.gMapContext.location
25371 }, function(results, status) {
25373 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25374 _this.gMapContext.locationName = results[0].formatted_address;
25375 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25377 _this.fireEvent('positionchanged', this, location);
25384 this.fireEvent('positionchanged', this, location);
25389 google.maps.event.trigger(this.gMapContext.map, "resize");
25391 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25393 this.fireEvent('resize', this);
25396 setPositionByLatLng: function(latitude, longitude)
25398 this.setPosition(new google.maps.LatLng(latitude, longitude));
25401 getCurrentPosition: function()
25404 latitude: this.gMapContext.location.lat(),
25405 longitude: this.gMapContext.location.lng()
25409 getAddressName: function()
25411 return this.gMapContext.locationName;
25414 getAddressComponents: function()
25416 return this.gMapContext.addressComponents;
25419 address_component_from_google_geocode: function(address_components)
25423 for (var i = 0; i < address_components.length; i++) {
25424 var component = address_components[i];
25425 if (component.types.indexOf("postal_code") >= 0) {
25426 result.postalCode = component.short_name;
25427 } else if (component.types.indexOf("street_number") >= 0) {
25428 result.streetNumber = component.short_name;
25429 } else if (component.types.indexOf("route") >= 0) {
25430 result.streetName = component.short_name;
25431 } else if (component.types.indexOf("neighborhood") >= 0) {
25432 result.city = component.short_name;
25433 } else if (component.types.indexOf("locality") >= 0) {
25434 result.city = component.short_name;
25435 } else if (component.types.indexOf("sublocality") >= 0) {
25436 result.district = component.short_name;
25437 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25438 result.stateOrProvince = component.short_name;
25439 } else if (component.types.indexOf("country") >= 0) {
25440 result.country = component.short_name;
25444 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25445 result.addressLine2 = "";
25449 setZoomLevel: function(zoom)
25451 this.gMapContext.map.setZoom(zoom);
25464 this.fireEvent('show', this);
25475 this.fireEvent('hide', this);
25480 Roo.apply(Roo.bootstrap.LocationPicker, {
25482 OverlayView : function(map, options)
25484 options = options || {};
25498 * @class Roo.bootstrap.Alert
25499 * @extends Roo.bootstrap.Component
25500 * Bootstrap Alert class
25501 * @cfg {String} title The title of alert
25502 * @cfg {String} html The content of alert
25503 * @cfg {String} weight ( success | info | warning | danger )
25504 * @cfg {String} faicon font-awesomeicon
25507 * Create a new alert
25508 * @param {Object} config The config object
25512 Roo.bootstrap.Alert = function(config){
25513 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25517 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25524 getAutoCreate : function()
25533 cls : 'roo-alert-icon'
25538 cls : 'roo-alert-title',
25543 cls : 'roo-alert-text',
25550 cfg.cn[0].cls += ' fa ' + this.faicon;
25554 cfg.cls += ' alert-' + this.weight;
25560 initEvents: function()
25562 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25565 setTitle : function(str)
25567 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25570 setText : function(str)
25572 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25575 setWeight : function(weight)
25578 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25581 this.weight = weight;
25583 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25586 setIcon : function(icon)
25589 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25592 this.faicon = icon;
25594 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25615 * @class Roo.bootstrap.UploadCropbox
25616 * @extends Roo.bootstrap.Component
25617 * Bootstrap UploadCropbox class
25618 * @cfg {String} emptyText show when image has been loaded
25619 * @cfg {String} rotateNotify show when image too small to rotate
25620 * @cfg {Number} errorTimeout default 3000
25621 * @cfg {Number} minWidth default 300
25622 * @cfg {Number} minHeight default 300
25623 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25624 * @cfg {Boolean} isDocument (true|false) default false
25625 * @cfg {String} url action url
25626 * @cfg {String} paramName default 'imageUpload'
25627 * @cfg {String} method default POST
25628 * @cfg {Boolean} loadMask (true|false) default true
25629 * @cfg {Boolean} loadingText default 'Loading...'
25632 * Create a new UploadCropbox
25633 * @param {Object} config The config object
25636 Roo.bootstrap.UploadCropbox = function(config){
25637 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25641 * @event beforeselectfile
25642 * Fire before select file
25643 * @param {Roo.bootstrap.UploadCropbox} this
25645 "beforeselectfile" : true,
25648 * Fire after initEvent
25649 * @param {Roo.bootstrap.UploadCropbox} this
25654 * Fire after initEvent
25655 * @param {Roo.bootstrap.UploadCropbox} this
25656 * @param {String} data
25661 * Fire when preparing the file data
25662 * @param {Roo.bootstrap.UploadCropbox} this
25663 * @param {Object} file
25668 * Fire when get exception
25669 * @param {Roo.bootstrap.UploadCropbox} this
25670 * @param {XMLHttpRequest} xhr
25672 "exception" : true,
25674 * @event beforeloadcanvas
25675 * Fire before load the canvas
25676 * @param {Roo.bootstrap.UploadCropbox} this
25677 * @param {String} src
25679 "beforeloadcanvas" : true,
25682 * Fire when trash image
25683 * @param {Roo.bootstrap.UploadCropbox} this
25688 * Fire when download the image
25689 * @param {Roo.bootstrap.UploadCropbox} this
25693 * @event footerbuttonclick
25694 * Fire when footerbuttonclick
25695 * @param {Roo.bootstrap.UploadCropbox} this
25696 * @param {String} type
25698 "footerbuttonclick" : true,
25702 * @param {Roo.bootstrap.UploadCropbox} this
25707 * Fire when rotate the image
25708 * @param {Roo.bootstrap.UploadCropbox} this
25709 * @param {String} pos
25714 * Fire when inspect the file
25715 * @param {Roo.bootstrap.UploadCropbox} this
25716 * @param {Object} file
25721 * Fire when xhr upload the file
25722 * @param {Roo.bootstrap.UploadCropbox} this
25723 * @param {Object} data
25728 * Fire when arrange the file data
25729 * @param {Roo.bootstrap.UploadCropbox} this
25730 * @param {Object} formData
25735 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25738 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25740 emptyText : 'Click to upload image',
25741 rotateNotify : 'Image is too small to rotate',
25742 errorTimeout : 3000,
25756 cropType : 'image/jpeg',
25758 canvasLoaded : false,
25759 isDocument : false,
25761 paramName : 'imageUpload',
25763 loadingText : 'Loading...',
25766 getAutoCreate : function()
25770 cls : 'roo-upload-cropbox',
25774 cls : 'roo-upload-cropbox-selector',
25779 cls : 'roo-upload-cropbox-body',
25780 style : 'cursor:pointer',
25784 cls : 'roo-upload-cropbox-preview'
25788 cls : 'roo-upload-cropbox-thumb'
25792 cls : 'roo-upload-cropbox-empty-notify',
25793 html : this.emptyText
25797 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25798 html : this.rotateNotify
25804 cls : 'roo-upload-cropbox-footer',
25807 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25817 onRender : function(ct, position)
25819 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25821 if (this.buttons.length) {
25823 Roo.each(this.buttons, function(bb) {
25825 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25827 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25833 this.maskEl = this.el;
25837 initEvents : function()
25839 this.urlAPI = (window.createObjectURL && window) ||
25840 (window.URL && URL.revokeObjectURL && URL) ||
25841 (window.webkitURL && webkitURL);
25843 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25844 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25846 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25847 this.selectorEl.hide();
25849 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25850 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25852 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25853 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25854 this.thumbEl.hide();
25856 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25857 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25859 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25860 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25861 this.errorEl.hide();
25863 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25864 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25865 this.footerEl.hide();
25867 this.setThumbBoxSize();
25873 this.fireEvent('initial', this);
25880 window.addEventListener("resize", function() { _this.resize(); } );
25882 this.bodyEl.on('click', this.beforeSelectFile, this);
25885 this.bodyEl.on('touchstart', this.onTouchStart, this);
25886 this.bodyEl.on('touchmove', this.onTouchMove, this);
25887 this.bodyEl.on('touchend', this.onTouchEnd, this);
25891 this.bodyEl.on('mousedown', this.onMouseDown, this);
25892 this.bodyEl.on('mousemove', this.onMouseMove, this);
25893 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25894 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25895 Roo.get(document).on('mouseup', this.onMouseUp, this);
25898 this.selectorEl.on('change', this.onFileSelected, this);
25904 this.baseScale = 1;
25906 this.baseRotate = 1;
25907 this.dragable = false;
25908 this.pinching = false;
25911 this.cropData = false;
25912 this.notifyEl.dom.innerHTML = this.emptyText;
25914 this.selectorEl.dom.value = '';
25918 resize : function()
25920 if(this.fireEvent('resize', this) != false){
25921 this.setThumbBoxPosition();
25922 this.setCanvasPosition();
25926 onFooterButtonClick : function(e, el, o, type)
25929 case 'rotate-left' :
25930 this.onRotateLeft(e);
25932 case 'rotate-right' :
25933 this.onRotateRight(e);
25936 this.beforeSelectFile(e);
25951 this.fireEvent('footerbuttonclick', this, type);
25954 beforeSelectFile : function(e)
25956 e.preventDefault();
25958 if(this.fireEvent('beforeselectfile', this) != false){
25959 this.selectorEl.dom.click();
25963 onFileSelected : function(e)
25965 e.preventDefault();
25967 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25971 var file = this.selectorEl.dom.files[0];
25973 if(this.fireEvent('inspect', this, file) != false){
25974 this.prepare(file);
25979 trash : function(e)
25981 this.fireEvent('trash', this);
25984 download : function(e)
25986 this.fireEvent('download', this);
25989 loadCanvas : function(src)
25991 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25995 this.imageEl = document.createElement('img');
25999 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26001 this.imageEl.src = src;
26005 onLoadCanvas : function()
26007 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26008 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26010 this.bodyEl.un('click', this.beforeSelectFile, this);
26012 this.notifyEl.hide();
26013 this.thumbEl.show();
26014 this.footerEl.show();
26016 this.baseRotateLevel();
26018 if(this.isDocument){
26019 this.setThumbBoxSize();
26022 this.setThumbBoxPosition();
26024 this.baseScaleLevel();
26030 this.canvasLoaded = true;
26033 this.maskEl.unmask();
26038 setCanvasPosition : function()
26040 if(!this.canvasEl){
26044 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26045 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26047 this.previewEl.setLeft(pw);
26048 this.previewEl.setTop(ph);
26052 onMouseDown : function(e)
26056 this.dragable = true;
26057 this.pinching = false;
26059 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26060 this.dragable = false;
26064 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26065 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26069 onMouseMove : function(e)
26073 if(!this.canvasLoaded){
26077 if (!this.dragable){
26081 var minX = Math.ceil(this.thumbEl.getLeft(true));
26082 var minY = Math.ceil(this.thumbEl.getTop(true));
26084 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26085 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26087 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26088 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26090 x = x - this.mouseX;
26091 y = y - this.mouseY;
26093 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26094 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26096 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26097 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26099 this.previewEl.setLeft(bgX);
26100 this.previewEl.setTop(bgY);
26102 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26103 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26106 onMouseUp : function(e)
26110 this.dragable = false;
26113 onMouseWheel : function(e)
26117 this.startScale = this.scale;
26119 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26121 if(!this.zoomable()){
26122 this.scale = this.startScale;
26131 zoomable : function()
26133 var minScale = this.thumbEl.getWidth() / this.minWidth;
26135 if(this.minWidth < this.minHeight){
26136 minScale = this.thumbEl.getHeight() / this.minHeight;
26139 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26140 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26144 (this.rotate == 0 || this.rotate == 180) &&
26146 width > this.imageEl.OriginWidth ||
26147 height > this.imageEl.OriginHeight ||
26148 (width < this.minWidth && height < this.minHeight)
26156 (this.rotate == 90 || this.rotate == 270) &&
26158 width > this.imageEl.OriginWidth ||
26159 height > this.imageEl.OriginHeight ||
26160 (width < this.minHeight && height < this.minWidth)
26167 !this.isDocument &&
26168 (this.rotate == 0 || this.rotate == 180) &&
26170 width < this.minWidth ||
26171 width > this.imageEl.OriginWidth ||
26172 height < this.minHeight ||
26173 height > this.imageEl.OriginHeight
26180 !this.isDocument &&
26181 (this.rotate == 90 || this.rotate == 270) &&
26183 width < this.minHeight ||
26184 width > this.imageEl.OriginWidth ||
26185 height < this.minWidth ||
26186 height > this.imageEl.OriginHeight
26196 onRotateLeft : function(e)
26198 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26200 var minScale = this.thumbEl.getWidth() / this.minWidth;
26202 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26203 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26205 this.startScale = this.scale;
26207 while (this.getScaleLevel() < minScale){
26209 this.scale = this.scale + 1;
26211 if(!this.zoomable()){
26216 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26217 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26222 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26229 this.scale = this.startScale;
26231 this.onRotateFail();
26236 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26238 if(this.isDocument){
26239 this.setThumbBoxSize();
26240 this.setThumbBoxPosition();
26241 this.setCanvasPosition();
26246 this.fireEvent('rotate', this, 'left');
26250 onRotateRight : function(e)
26252 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26254 var minScale = this.thumbEl.getWidth() / this.minWidth;
26256 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26257 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26259 this.startScale = this.scale;
26261 while (this.getScaleLevel() < minScale){
26263 this.scale = this.scale + 1;
26265 if(!this.zoomable()){
26270 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26271 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26276 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26283 this.scale = this.startScale;
26285 this.onRotateFail();
26290 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26292 if(this.isDocument){
26293 this.setThumbBoxSize();
26294 this.setThumbBoxPosition();
26295 this.setCanvasPosition();
26300 this.fireEvent('rotate', this, 'right');
26303 onRotateFail : function()
26305 this.errorEl.show(true);
26309 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26314 this.previewEl.dom.innerHTML = '';
26316 var canvasEl = document.createElement("canvas");
26318 var contextEl = canvasEl.getContext("2d");
26320 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26321 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26322 var center = this.imageEl.OriginWidth / 2;
26324 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26325 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26326 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26327 center = this.imageEl.OriginHeight / 2;
26330 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26332 contextEl.translate(center, center);
26333 contextEl.rotate(this.rotate * Math.PI / 180);
26335 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26337 this.canvasEl = document.createElement("canvas");
26339 this.contextEl = this.canvasEl.getContext("2d");
26341 switch (this.rotate) {
26344 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26345 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26347 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26352 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26353 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26355 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26356 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);
26360 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26365 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26366 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26368 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26369 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);
26373 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);
26378 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26379 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26381 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26382 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26386 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);
26393 this.previewEl.appendChild(this.canvasEl);
26395 this.setCanvasPosition();
26400 if(!this.canvasLoaded){
26404 var imageCanvas = document.createElement("canvas");
26406 var imageContext = imageCanvas.getContext("2d");
26408 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26409 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26411 var center = imageCanvas.width / 2;
26413 imageContext.translate(center, center);
26415 imageContext.rotate(this.rotate * Math.PI / 180);
26417 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26419 var canvas = document.createElement("canvas");
26421 var context = canvas.getContext("2d");
26423 canvas.width = this.minWidth;
26424 canvas.height = this.minHeight;
26426 switch (this.rotate) {
26429 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26430 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26432 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26433 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26435 var targetWidth = this.minWidth - 2 * x;
26436 var targetHeight = this.minHeight - 2 * y;
26440 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26441 scale = targetWidth / width;
26444 if(x > 0 && y == 0){
26445 scale = targetHeight / height;
26448 if(x > 0 && y > 0){
26449 scale = targetWidth / width;
26451 if(width < height){
26452 scale = targetHeight / height;
26456 context.scale(scale, scale);
26458 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26459 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26461 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26462 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26464 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26469 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26470 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26472 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26473 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26475 var targetWidth = this.minWidth - 2 * x;
26476 var targetHeight = this.minHeight - 2 * y;
26480 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26481 scale = targetWidth / width;
26484 if(x > 0 && y == 0){
26485 scale = targetHeight / height;
26488 if(x > 0 && y > 0){
26489 scale = targetWidth / width;
26491 if(width < height){
26492 scale = targetHeight / height;
26496 context.scale(scale, scale);
26498 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26499 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26501 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26502 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26504 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26506 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26511 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26512 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26514 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26515 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26517 var targetWidth = this.minWidth - 2 * x;
26518 var targetHeight = this.minHeight - 2 * y;
26522 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26523 scale = targetWidth / width;
26526 if(x > 0 && y == 0){
26527 scale = targetHeight / height;
26530 if(x > 0 && y > 0){
26531 scale = targetWidth / width;
26533 if(width < height){
26534 scale = targetHeight / height;
26538 context.scale(scale, scale);
26540 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26541 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26543 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26544 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26546 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26547 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26549 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26554 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26555 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26557 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26558 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26560 var targetWidth = this.minWidth - 2 * x;
26561 var targetHeight = this.minHeight - 2 * y;
26565 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26566 scale = targetWidth / width;
26569 if(x > 0 && y == 0){
26570 scale = targetHeight / height;
26573 if(x > 0 && y > 0){
26574 scale = targetWidth / width;
26576 if(width < height){
26577 scale = targetHeight / height;
26581 context.scale(scale, scale);
26583 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26584 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26586 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26587 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26589 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26591 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26598 this.cropData = canvas.toDataURL(this.cropType);
26600 if(this.fireEvent('crop', this, this.cropData) !== false){
26601 this.process(this.file, this.cropData);
26608 setThumbBoxSize : function()
26612 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26613 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26614 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26616 this.minWidth = width;
26617 this.minHeight = height;
26619 if(this.rotate == 90 || this.rotate == 270){
26620 this.minWidth = height;
26621 this.minHeight = width;
26626 width = Math.ceil(this.minWidth * height / this.minHeight);
26628 if(this.minWidth > this.minHeight){
26630 height = Math.ceil(this.minHeight * width / this.minWidth);
26633 this.thumbEl.setStyle({
26634 width : width + 'px',
26635 height : height + 'px'
26642 setThumbBoxPosition : function()
26644 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26645 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26647 this.thumbEl.setLeft(x);
26648 this.thumbEl.setTop(y);
26652 baseRotateLevel : function()
26654 this.baseRotate = 1;
26657 typeof(this.exif) != 'undefined' &&
26658 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26659 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26661 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26664 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26668 baseScaleLevel : function()
26672 if(this.isDocument){
26674 if(this.baseRotate == 6 || this.baseRotate == 8){
26676 height = this.thumbEl.getHeight();
26677 this.baseScale = height / this.imageEl.OriginWidth;
26679 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26680 width = this.thumbEl.getWidth();
26681 this.baseScale = width / this.imageEl.OriginHeight;
26687 height = this.thumbEl.getHeight();
26688 this.baseScale = height / this.imageEl.OriginHeight;
26690 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26691 width = this.thumbEl.getWidth();
26692 this.baseScale = width / this.imageEl.OriginWidth;
26698 if(this.baseRotate == 6 || this.baseRotate == 8){
26700 width = this.thumbEl.getHeight();
26701 this.baseScale = width / this.imageEl.OriginHeight;
26703 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26704 height = this.thumbEl.getWidth();
26705 this.baseScale = height / this.imageEl.OriginHeight;
26708 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26709 height = this.thumbEl.getWidth();
26710 this.baseScale = height / this.imageEl.OriginHeight;
26712 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26713 width = this.thumbEl.getHeight();
26714 this.baseScale = width / this.imageEl.OriginWidth;
26721 width = this.thumbEl.getWidth();
26722 this.baseScale = width / this.imageEl.OriginWidth;
26724 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26725 height = this.thumbEl.getHeight();
26726 this.baseScale = height / this.imageEl.OriginHeight;
26729 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26731 height = this.thumbEl.getHeight();
26732 this.baseScale = height / this.imageEl.OriginHeight;
26734 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26735 width = this.thumbEl.getWidth();
26736 this.baseScale = width / this.imageEl.OriginWidth;
26744 getScaleLevel : function()
26746 return this.baseScale * Math.pow(1.1, this.scale);
26749 onTouchStart : function(e)
26751 if(!this.canvasLoaded){
26752 this.beforeSelectFile(e);
26756 var touches = e.browserEvent.touches;
26762 if(touches.length == 1){
26763 this.onMouseDown(e);
26767 if(touches.length != 2){
26773 for(var i = 0, finger; finger = touches[i]; i++){
26774 coords.push(finger.pageX, finger.pageY);
26777 var x = Math.pow(coords[0] - coords[2], 2);
26778 var y = Math.pow(coords[1] - coords[3], 2);
26780 this.startDistance = Math.sqrt(x + y);
26782 this.startScale = this.scale;
26784 this.pinching = true;
26785 this.dragable = false;
26789 onTouchMove : function(e)
26791 if(!this.pinching && !this.dragable){
26795 var touches = e.browserEvent.touches;
26802 this.onMouseMove(e);
26808 for(var i = 0, finger; finger = touches[i]; i++){
26809 coords.push(finger.pageX, finger.pageY);
26812 var x = Math.pow(coords[0] - coords[2], 2);
26813 var y = Math.pow(coords[1] - coords[3], 2);
26815 this.endDistance = Math.sqrt(x + y);
26817 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26819 if(!this.zoomable()){
26820 this.scale = this.startScale;
26828 onTouchEnd : function(e)
26830 this.pinching = false;
26831 this.dragable = false;
26835 process : function(file, crop)
26838 this.maskEl.mask(this.loadingText);
26841 this.xhr = new XMLHttpRequest();
26843 file.xhr = this.xhr;
26845 this.xhr.open(this.method, this.url, true);
26848 "Accept": "application/json",
26849 "Cache-Control": "no-cache",
26850 "X-Requested-With": "XMLHttpRequest"
26853 for (var headerName in headers) {
26854 var headerValue = headers[headerName];
26856 this.xhr.setRequestHeader(headerName, headerValue);
26862 this.xhr.onload = function()
26864 _this.xhrOnLoad(_this.xhr);
26867 this.xhr.onerror = function()
26869 _this.xhrOnError(_this.xhr);
26872 var formData = new FormData();
26874 formData.append('returnHTML', 'NO');
26877 formData.append('crop', crop);
26880 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26881 formData.append(this.paramName, file, file.name);
26884 if(typeof(file.filename) != 'undefined'){
26885 formData.append('filename', file.filename);
26888 if(typeof(file.mimetype) != 'undefined'){
26889 formData.append('mimetype', file.mimetype);
26892 if(this.fireEvent('arrange', this, formData) != false){
26893 this.xhr.send(formData);
26897 xhrOnLoad : function(xhr)
26900 this.maskEl.unmask();
26903 if (xhr.readyState !== 4) {
26904 this.fireEvent('exception', this, xhr);
26908 var response = Roo.decode(xhr.responseText);
26910 if(!response.success){
26911 this.fireEvent('exception', this, xhr);
26915 var response = Roo.decode(xhr.responseText);
26917 this.fireEvent('upload', this, response);
26921 xhrOnError : function()
26924 this.maskEl.unmask();
26927 Roo.log('xhr on error');
26929 var response = Roo.decode(xhr.responseText);
26935 prepare : function(file)
26938 this.maskEl.mask(this.loadingText);
26944 if(typeof(file) === 'string'){
26945 this.loadCanvas(file);
26949 if(!file || !this.urlAPI){
26954 this.cropType = file.type;
26958 if(this.fireEvent('prepare', this, this.file) != false){
26960 var reader = new FileReader();
26962 reader.onload = function (e) {
26963 if (e.target.error) {
26964 Roo.log(e.target.error);
26968 var buffer = e.target.result,
26969 dataView = new DataView(buffer),
26971 maxOffset = dataView.byteLength - 4,
26975 if (dataView.getUint16(0) === 0xffd8) {
26976 while (offset < maxOffset) {
26977 markerBytes = dataView.getUint16(offset);
26979 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26980 markerLength = dataView.getUint16(offset + 2) + 2;
26981 if (offset + markerLength > dataView.byteLength) {
26982 Roo.log('Invalid meta data: Invalid segment size.');
26986 if(markerBytes == 0xffe1){
26987 _this.parseExifData(
26994 offset += markerLength;
27004 var url = _this.urlAPI.createObjectURL(_this.file);
27006 _this.loadCanvas(url);
27011 reader.readAsArrayBuffer(this.file);
27017 parseExifData : function(dataView, offset, length)
27019 var tiffOffset = offset + 10,
27023 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27024 // No Exif data, might be XMP data instead
27028 // Check for the ASCII code for "Exif" (0x45786966):
27029 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27030 // No Exif data, might be XMP data instead
27033 if (tiffOffset + 8 > dataView.byteLength) {
27034 Roo.log('Invalid Exif data: Invalid segment size.');
27037 // Check for the two null bytes:
27038 if (dataView.getUint16(offset + 8) !== 0x0000) {
27039 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27042 // Check the byte alignment:
27043 switch (dataView.getUint16(tiffOffset)) {
27045 littleEndian = true;
27048 littleEndian = false;
27051 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27054 // Check for the TIFF tag marker (0x002A):
27055 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27056 Roo.log('Invalid Exif data: Missing TIFF marker.');
27059 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27060 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27062 this.parseExifTags(
27065 tiffOffset + dirOffset,
27070 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27075 if (dirOffset + 6 > dataView.byteLength) {
27076 Roo.log('Invalid Exif data: Invalid directory offset.');
27079 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27080 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27081 if (dirEndOffset + 4 > dataView.byteLength) {
27082 Roo.log('Invalid Exif data: Invalid directory size.');
27085 for (i = 0; i < tagsNumber; i += 1) {
27089 dirOffset + 2 + 12 * i, // tag offset
27093 // Return the offset to the next directory:
27094 return dataView.getUint32(dirEndOffset, littleEndian);
27097 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27099 var tag = dataView.getUint16(offset, littleEndian);
27101 this.exif[tag] = this.getExifValue(
27105 dataView.getUint16(offset + 2, littleEndian), // tag type
27106 dataView.getUint32(offset + 4, littleEndian), // tag length
27111 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27113 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27122 Roo.log('Invalid Exif data: Invalid tag type.');
27126 tagSize = tagType.size * length;
27127 // Determine if the value is contained in the dataOffset bytes,
27128 // or if the value at the dataOffset is a pointer to the actual data:
27129 dataOffset = tagSize > 4 ?
27130 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27131 if (dataOffset + tagSize > dataView.byteLength) {
27132 Roo.log('Invalid Exif data: Invalid data offset.');
27135 if (length === 1) {
27136 return tagType.getValue(dataView, dataOffset, littleEndian);
27139 for (i = 0; i < length; i += 1) {
27140 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27143 if (tagType.ascii) {
27145 // Concatenate the chars:
27146 for (i = 0; i < values.length; i += 1) {
27148 // Ignore the terminating NULL byte(s):
27149 if (c === '\u0000') {
27161 Roo.apply(Roo.bootstrap.UploadCropbox, {
27163 'Orientation': 0x0112
27167 1: 0, //'top-left',
27169 3: 180, //'bottom-right',
27170 // 4: 'bottom-left',
27172 6: 90, //'right-top',
27173 // 7: 'right-bottom',
27174 8: 270 //'left-bottom'
27178 // byte, 8-bit unsigned int:
27180 getValue: function (dataView, dataOffset) {
27181 return dataView.getUint8(dataOffset);
27185 // ascii, 8-bit byte:
27187 getValue: function (dataView, dataOffset) {
27188 return String.fromCharCode(dataView.getUint8(dataOffset));
27193 // short, 16 bit int:
27195 getValue: function (dataView, dataOffset, littleEndian) {
27196 return dataView.getUint16(dataOffset, littleEndian);
27200 // long, 32 bit int:
27202 getValue: function (dataView, dataOffset, littleEndian) {
27203 return dataView.getUint32(dataOffset, littleEndian);
27207 // rational = two long values, first is numerator, second is denominator:
27209 getValue: function (dataView, dataOffset, littleEndian) {
27210 return dataView.getUint32(dataOffset, littleEndian) /
27211 dataView.getUint32(dataOffset + 4, littleEndian);
27215 // slong, 32 bit signed int:
27217 getValue: function (dataView, dataOffset, littleEndian) {
27218 return dataView.getInt32(dataOffset, littleEndian);
27222 // srational, two slongs, first is numerator, second is denominator:
27224 getValue: function (dataView, dataOffset, littleEndian) {
27225 return dataView.getInt32(dataOffset, littleEndian) /
27226 dataView.getInt32(dataOffset + 4, littleEndian);
27236 cls : 'btn-group roo-upload-cropbox-rotate-left',
27237 action : 'rotate-left',
27241 cls : 'btn btn-default',
27242 html : '<i class="fa fa-undo"></i>'
27248 cls : 'btn-group roo-upload-cropbox-picture',
27249 action : 'picture',
27253 cls : 'btn btn-default',
27254 html : '<i class="fa fa-picture-o"></i>'
27260 cls : 'btn-group roo-upload-cropbox-rotate-right',
27261 action : 'rotate-right',
27265 cls : 'btn btn-default',
27266 html : '<i class="fa fa-repeat"></i>'
27274 cls : 'btn-group roo-upload-cropbox-rotate-left',
27275 action : 'rotate-left',
27279 cls : 'btn btn-default',
27280 html : '<i class="fa fa-undo"></i>'
27286 cls : 'btn-group roo-upload-cropbox-download',
27287 action : 'download',
27291 cls : 'btn btn-default',
27292 html : '<i class="fa fa-download"></i>'
27298 cls : 'btn-group roo-upload-cropbox-crop',
27303 cls : 'btn btn-default',
27304 html : '<i class="fa fa-crop"></i>'
27310 cls : 'btn-group roo-upload-cropbox-trash',
27315 cls : 'btn btn-default',
27316 html : '<i class="fa fa-trash"></i>'
27322 cls : 'btn-group roo-upload-cropbox-rotate-right',
27323 action : 'rotate-right',
27327 cls : 'btn btn-default',
27328 html : '<i class="fa fa-repeat"></i>'
27336 cls : 'btn-group roo-upload-cropbox-rotate-left',
27337 action : 'rotate-left',
27341 cls : 'btn btn-default',
27342 html : '<i class="fa fa-undo"></i>'
27348 cls : 'btn-group roo-upload-cropbox-rotate-right',
27349 action : 'rotate-right',
27353 cls : 'btn btn-default',
27354 html : '<i class="fa fa-repeat"></i>'
27367 * @class Roo.bootstrap.DocumentManager
27368 * @extends Roo.bootstrap.Component
27369 * Bootstrap DocumentManager class
27370 * @cfg {String} paramName default 'imageUpload'
27371 * @cfg {String} toolTipName default 'filename'
27372 * @cfg {String} method default POST
27373 * @cfg {String} url action url
27374 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27375 * @cfg {Boolean} multiple multiple upload default true
27376 * @cfg {Number} thumbSize default 300
27377 * @cfg {String} fieldLabel
27378 * @cfg {Number} labelWidth default 4
27379 * @cfg {String} labelAlign (left|top) default left
27380 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27383 * Create a new DocumentManager
27384 * @param {Object} config The config object
27387 Roo.bootstrap.DocumentManager = function(config){
27388 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27391 this.delegates = [];
27396 * Fire when initial the DocumentManager
27397 * @param {Roo.bootstrap.DocumentManager} this
27402 * inspect selected file
27403 * @param {Roo.bootstrap.DocumentManager} this
27404 * @param {File} file
27409 * Fire when xhr load exception
27410 * @param {Roo.bootstrap.DocumentManager} this
27411 * @param {XMLHttpRequest} xhr
27413 "exception" : true,
27415 * @event afterupload
27416 * Fire when xhr load exception
27417 * @param {Roo.bootstrap.DocumentManager} this
27418 * @param {XMLHttpRequest} xhr
27420 "afterupload" : true,
27423 * prepare the form data
27424 * @param {Roo.bootstrap.DocumentManager} this
27425 * @param {Object} formData
27430 * Fire when remove the file
27431 * @param {Roo.bootstrap.DocumentManager} this
27432 * @param {Object} file
27437 * Fire after refresh the file
27438 * @param {Roo.bootstrap.DocumentManager} this
27443 * Fire after click the image
27444 * @param {Roo.bootstrap.DocumentManager} this
27445 * @param {Object} file
27450 * Fire when upload a image and editable set to true
27451 * @param {Roo.bootstrap.DocumentManager} this
27452 * @param {Object} file
27456 * @event beforeselectfile
27457 * Fire before select file
27458 * @param {Roo.bootstrap.DocumentManager} this
27460 "beforeselectfile" : true,
27463 * Fire before process file
27464 * @param {Roo.bootstrap.DocumentManager} this
27465 * @param {Object} file
27472 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27481 paramName : 'imageUpload',
27482 toolTipName : 'filename',
27485 labelAlign : 'left',
27490 getAutoCreate : function()
27492 var managerWidget = {
27494 cls : 'roo-document-manager',
27498 cls : 'roo-document-manager-selector',
27503 cls : 'roo-document-manager-uploader',
27507 cls : 'roo-document-manager-upload-btn',
27508 html : '<i class="fa fa-plus"></i>'
27519 cls : 'column col-md-12',
27524 if(this.fieldLabel.length){
27529 cls : 'column col-md-12',
27530 html : this.fieldLabel
27534 cls : 'column col-md-12',
27539 if(this.labelAlign == 'left'){
27543 cls : 'column col-md-' + this.labelWidth,
27544 html : this.fieldLabel
27548 cls : 'column col-md-' + (12 - this.labelWidth),
27558 cls : 'row clearfix',
27566 initEvents : function()
27568 this.managerEl = this.el.select('.roo-document-manager', true).first();
27569 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27571 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27572 this.selectorEl.hide();
27575 this.selectorEl.attr('multiple', 'multiple');
27578 this.selectorEl.on('change', this.onFileSelected, this);
27580 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27581 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27583 this.uploader.on('click', this.onUploaderClick, this);
27585 this.renderProgressDialog();
27589 window.addEventListener("resize", function() { _this.refresh(); } );
27591 this.fireEvent('initial', this);
27594 renderProgressDialog : function()
27598 this.progressDialog = new Roo.bootstrap.Modal({
27599 cls : 'roo-document-manager-progress-dialog',
27600 allow_close : false,
27610 btnclick : function() {
27611 _this.uploadCancel();
27617 this.progressDialog.render(Roo.get(document.body));
27619 this.progress = new Roo.bootstrap.Progress({
27620 cls : 'roo-document-manager-progress',
27625 this.progress.render(this.progressDialog.getChildContainer());
27627 this.progressBar = new Roo.bootstrap.ProgressBar({
27628 cls : 'roo-document-manager-progress-bar',
27631 aria_valuemax : 12,
27635 this.progressBar.render(this.progress.getChildContainer());
27638 onUploaderClick : function(e)
27640 e.preventDefault();
27642 if(this.fireEvent('beforeselectfile', this) != false){
27643 this.selectorEl.dom.click();
27648 onFileSelected : function(e)
27650 e.preventDefault();
27652 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27656 Roo.each(this.selectorEl.dom.files, function(file){
27657 if(this.fireEvent('inspect', this, file) != false){
27658 this.files.push(file);
27668 this.selectorEl.dom.value = '';
27670 if(!this.files.length){
27674 if(this.boxes > 0 && this.files.length > this.boxes){
27675 this.files = this.files.slice(0, this.boxes);
27678 this.uploader.show();
27680 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27681 this.uploader.hide();
27690 Roo.each(this.files, function(file){
27692 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27693 var f = this.renderPreview(file);
27698 if(file.type.indexOf('image') != -1){
27699 this.delegates.push(
27701 _this.process(file);
27702 }).createDelegate(this)
27710 _this.process(file);
27711 }).createDelegate(this)
27716 this.files = files;
27718 this.delegates = this.delegates.concat(docs);
27720 if(!this.delegates.length){
27725 this.progressBar.aria_valuemax = this.delegates.length;
27732 arrange : function()
27734 if(!this.delegates.length){
27735 this.progressDialog.hide();
27740 var delegate = this.delegates.shift();
27742 this.progressDialog.show();
27744 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27746 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27751 refresh : function()
27753 this.uploader.show();
27755 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27756 this.uploader.hide();
27759 Roo.isTouch ? this.closable(false) : this.closable(true);
27761 this.fireEvent('refresh', this);
27764 onRemove : function(e, el, o)
27766 e.preventDefault();
27768 this.fireEvent('remove', this, o);
27772 remove : function(o)
27776 Roo.each(this.files, function(file){
27777 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27786 this.files = files;
27793 Roo.each(this.files, function(file){
27798 file.target.remove();
27807 onClick : function(e, el, o)
27809 e.preventDefault();
27811 this.fireEvent('click', this, o);
27815 closable : function(closable)
27817 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27819 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27831 xhrOnLoad : function(xhr)
27833 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27837 if (xhr.readyState !== 4) {
27839 this.fireEvent('exception', this, xhr);
27843 var response = Roo.decode(xhr.responseText);
27845 if(!response.success){
27847 this.fireEvent('exception', this, xhr);
27851 var file = this.renderPreview(response.data);
27853 this.files.push(file);
27857 this.fireEvent('afterupload', this, xhr);
27861 xhrOnError : function(xhr)
27863 Roo.log('xhr on error');
27865 var response = Roo.decode(xhr.responseText);
27872 process : function(file)
27874 if(this.fireEvent('process', this, file) !== false){
27875 if(this.editable && file.type.indexOf('image') != -1){
27876 this.fireEvent('edit', this, file);
27880 this.uploadStart(file, false);
27887 uploadStart : function(file, crop)
27889 this.xhr = new XMLHttpRequest();
27891 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27896 file.xhr = this.xhr;
27898 this.managerEl.createChild({
27900 cls : 'roo-document-manager-loading',
27904 tooltip : file.name,
27905 cls : 'roo-document-manager-thumb',
27906 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27912 this.xhr.open(this.method, this.url, true);
27915 "Accept": "application/json",
27916 "Cache-Control": "no-cache",
27917 "X-Requested-With": "XMLHttpRequest"
27920 for (var headerName in headers) {
27921 var headerValue = headers[headerName];
27923 this.xhr.setRequestHeader(headerName, headerValue);
27929 this.xhr.onload = function()
27931 _this.xhrOnLoad(_this.xhr);
27934 this.xhr.onerror = function()
27936 _this.xhrOnError(_this.xhr);
27939 var formData = new FormData();
27941 formData.append('returnHTML', 'NO');
27944 formData.append('crop', crop);
27947 formData.append(this.paramName, file, file.name);
27954 if(this.fireEvent('prepare', this, formData, options) != false){
27956 if(options.manually){
27960 this.xhr.send(formData);
27964 this.uploadCancel();
27967 uploadCancel : function()
27973 this.delegates = [];
27975 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27982 renderPreview : function(file)
27984 if(typeof(file.target) != 'undefined' && file.target){
27988 var previewEl = this.managerEl.createChild({
27990 cls : 'roo-document-manager-preview',
27994 tooltip : file[this.toolTipName],
27995 cls : 'roo-document-manager-thumb',
27996 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28001 html : '<i class="fa fa-times-circle"></i>'
28006 var close = previewEl.select('button.close', true).first();
28008 close.on('click', this.onRemove, this, file);
28010 file.target = previewEl;
28012 var image = previewEl.select('img', true).first();
28016 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28018 image.on('click', this.onClick, this, file);
28024 onPreviewLoad : function(file, image)
28026 if(typeof(file.target) == 'undefined' || !file.target){
28030 var width = image.dom.naturalWidth || image.dom.width;
28031 var height = image.dom.naturalHeight || image.dom.height;
28033 if(width > height){
28034 file.target.addClass('wide');
28038 file.target.addClass('tall');
28043 uploadFromSource : function(file, crop)
28045 this.xhr = new XMLHttpRequest();
28047 this.managerEl.createChild({
28049 cls : 'roo-document-manager-loading',
28053 tooltip : file.name,
28054 cls : 'roo-document-manager-thumb',
28055 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28061 this.xhr.open(this.method, this.url, true);
28064 "Accept": "application/json",
28065 "Cache-Control": "no-cache",
28066 "X-Requested-With": "XMLHttpRequest"
28069 for (var headerName in headers) {
28070 var headerValue = headers[headerName];
28072 this.xhr.setRequestHeader(headerName, headerValue);
28078 this.xhr.onload = function()
28080 _this.xhrOnLoad(_this.xhr);
28083 this.xhr.onerror = function()
28085 _this.xhrOnError(_this.xhr);
28088 var formData = new FormData();
28090 formData.append('returnHTML', 'NO');
28092 formData.append('crop', crop);
28094 if(typeof(file.filename) != 'undefined'){
28095 formData.append('filename', file.filename);
28098 if(typeof(file.mimetype) != 'undefined'){
28099 formData.append('mimetype', file.mimetype);
28102 if(this.fireEvent('prepare', this, formData) != false){
28103 this.xhr.send(formData);
28113 * @class Roo.bootstrap.DocumentViewer
28114 * @extends Roo.bootstrap.Component
28115 * Bootstrap DocumentViewer class
28116 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28117 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28120 * Create a new DocumentViewer
28121 * @param {Object} config The config object
28124 Roo.bootstrap.DocumentViewer = function(config){
28125 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28130 * Fire after initEvent
28131 * @param {Roo.bootstrap.DocumentViewer} this
28137 * @param {Roo.bootstrap.DocumentViewer} this
28142 * Fire after download button
28143 * @param {Roo.bootstrap.DocumentViewer} this
28148 * Fire after trash button
28149 * @param {Roo.bootstrap.DocumentViewer} this
28156 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28158 showDownload : true,
28162 getAutoCreate : function()
28166 cls : 'roo-document-viewer',
28170 cls : 'roo-document-viewer-body',
28174 cls : 'roo-document-viewer-thumb',
28178 cls : 'roo-document-viewer-image'
28186 cls : 'roo-document-viewer-footer',
28189 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28193 cls : 'btn-group roo-document-viewer-download',
28197 cls : 'btn btn-default',
28198 html : '<i class="fa fa-download"></i>'
28204 cls : 'btn-group roo-document-viewer-trash',
28208 cls : 'btn btn-default',
28209 html : '<i class="fa fa-trash"></i>'
28222 initEvents : function()
28224 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28225 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28227 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28228 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28230 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28231 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28233 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28234 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28236 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28237 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28239 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28240 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28242 this.bodyEl.on('click', this.onClick, this);
28243 this.downloadBtn.on('click', this.onDownload, this);
28244 this.trashBtn.on('click', this.onTrash, this);
28246 this.downloadBtn.hide();
28247 this.trashBtn.hide();
28249 if(this.showDownload){
28250 this.downloadBtn.show();
28253 if(this.showTrash){
28254 this.trashBtn.show();
28257 if(!this.showDownload && !this.showTrash) {
28258 this.footerEl.hide();
28263 initial : function()
28265 this.fireEvent('initial', this);
28269 onClick : function(e)
28271 e.preventDefault();
28273 this.fireEvent('click', this);
28276 onDownload : function(e)
28278 e.preventDefault();
28280 this.fireEvent('download', this);
28283 onTrash : function(e)
28285 e.preventDefault();
28287 this.fireEvent('trash', this);
28299 * @class Roo.bootstrap.NavProgressBar
28300 * @extends Roo.bootstrap.Component
28301 * Bootstrap NavProgressBar class
28304 * Create a new nav progress bar
28305 * @param {Object} config The config object
28308 Roo.bootstrap.NavProgressBar = function(config){
28309 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28311 this.bullets = this.bullets || [];
28313 // Roo.bootstrap.NavProgressBar.register(this);
28317 * Fires when the active item changes
28318 * @param {Roo.bootstrap.NavProgressBar} this
28319 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28320 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28327 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28332 getAutoCreate : function()
28334 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28338 cls : 'roo-navigation-bar-group',
28342 cls : 'roo-navigation-top-bar'
28346 cls : 'roo-navigation-bullets-bar',
28350 cls : 'roo-navigation-bar'
28357 cls : 'roo-navigation-bottom-bar'
28367 initEvents: function()
28372 onRender : function(ct, position)
28374 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28376 if(this.bullets.length){
28377 Roo.each(this.bullets, function(b){
28386 addItem : function(cfg)
28388 var item = new Roo.bootstrap.NavProgressItem(cfg);
28390 item.parentId = this.id;
28391 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28394 var top = new Roo.bootstrap.Element({
28396 cls : 'roo-navigation-bar-text'
28399 var bottom = new Roo.bootstrap.Element({
28401 cls : 'roo-navigation-bar-text'
28404 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28405 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28407 var topText = new Roo.bootstrap.Element({
28409 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28412 var bottomText = new Roo.bootstrap.Element({
28414 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28417 topText.onRender(top.el, null);
28418 bottomText.onRender(bottom.el, null);
28421 item.bottomEl = bottom;
28424 this.barItems.push(item);
28429 getActive : function()
28431 var active = false;
28433 Roo.each(this.barItems, function(v){
28435 if (!v.isActive()) {
28447 setActiveItem : function(item)
28451 Roo.each(this.barItems, function(v){
28452 if (v.rid == item.rid) {
28456 if (v.isActive()) {
28457 v.setActive(false);
28462 item.setActive(true);
28464 this.fireEvent('changed', this, item, prev);
28467 getBarItem: function(rid)
28471 Roo.each(this.barItems, function(e) {
28472 if (e.rid != rid) {
28483 indexOfItem : function(item)
28487 Roo.each(this.barItems, function(v, i){
28489 if (v.rid != item.rid) {
28500 setActiveNext : function()
28502 var i = this.indexOfItem(this.getActive());
28504 if (i > this.barItems.length) {
28508 this.setActiveItem(this.barItems[i+1]);
28511 setActivePrev : function()
28513 var i = this.indexOfItem(this.getActive());
28519 this.setActiveItem(this.barItems[i-1]);
28522 format : function()
28524 if(!this.barItems.length){
28528 var width = 100 / this.barItems.length;
28530 Roo.each(this.barItems, function(i){
28531 i.el.setStyle('width', width + '%');
28532 i.topEl.el.setStyle('width', width + '%');
28533 i.bottomEl.el.setStyle('width', width + '%');
28542 * Nav Progress Item
28547 * @class Roo.bootstrap.NavProgressItem
28548 * @extends Roo.bootstrap.Component
28549 * Bootstrap NavProgressItem class
28550 * @cfg {String} rid the reference id
28551 * @cfg {Boolean} active (true|false) Is item active default false
28552 * @cfg {Boolean} disabled (true|false) Is item active default false
28553 * @cfg {String} html
28554 * @cfg {String} position (top|bottom) text position default bottom
28555 * @cfg {String} icon show icon instead of number
28558 * Create a new NavProgressItem
28559 * @param {Object} config The config object
28561 Roo.bootstrap.NavProgressItem = function(config){
28562 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28567 * The raw click event for the entire grid.
28568 * @param {Roo.bootstrap.NavProgressItem} this
28569 * @param {Roo.EventObject} e
28576 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28582 position : 'bottom',
28585 getAutoCreate : function()
28587 var iconCls = 'roo-navigation-bar-item-icon';
28589 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28593 cls: 'roo-navigation-bar-item',
28603 cfg.cls += ' active';
28606 cfg.cls += ' disabled';
28612 disable : function()
28614 this.setDisabled(true);
28617 enable : function()
28619 this.setDisabled(false);
28622 initEvents: function()
28624 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28626 this.iconEl.on('click', this.onClick, this);
28629 onClick : function(e)
28631 e.preventDefault();
28637 if(this.fireEvent('click', this, e) === false){
28641 this.parent().setActiveItem(this);
28644 isActive: function ()
28646 return this.active;
28649 setActive : function(state)
28651 if(this.active == state){
28655 this.active = state;
28658 this.el.addClass('active');
28662 this.el.removeClass('active');
28667 setDisabled : function(state)
28669 if(this.disabled == state){
28673 this.disabled = state;
28676 this.el.addClass('disabled');
28680 this.el.removeClass('disabled');
28683 tooltipEl : function()
28685 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28698 * @class Roo.bootstrap.FieldLabel
28699 * @extends Roo.bootstrap.Component
28700 * Bootstrap FieldLabel class
28701 * @cfg {String} html contents of the element
28702 * @cfg {String} tag tag of the element default label
28703 * @cfg {String} cls class of the element
28704 * @cfg {String} target label target
28705 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28706 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28707 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28708 * @cfg {String} iconTooltip default "This field is required"
28711 * Create a new FieldLabel
28712 * @param {Object} config The config object
28715 Roo.bootstrap.FieldLabel = function(config){
28716 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28721 * Fires after the field has been marked as invalid.
28722 * @param {Roo.form.FieldLabel} this
28723 * @param {String} msg The validation message
28728 * Fires after the field has been validated with no errors.
28729 * @param {Roo.form.FieldLabel} this
28735 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28742 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28743 validClass : 'text-success fa fa-lg fa-check',
28744 iconTooltip : 'This field is required',
28746 getAutoCreate : function(){
28750 cls : 'roo-bootstrap-field-label ' + this.cls,
28756 tooltip : this.iconTooltip
28768 initEvents: function()
28770 Roo.bootstrap.Element.superclass.initEvents.call(this);
28772 this.iconEl = this.el.select('i', true).first();
28774 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28776 Roo.bootstrap.FieldLabel.register(this);
28780 * Mark this field as valid
28782 markValid : function()
28784 this.iconEl.show();
28786 this.iconEl.removeClass(this.invalidClass);
28788 this.iconEl.addClass(this.validClass);
28790 this.fireEvent('valid', this);
28794 * Mark this field as invalid
28795 * @param {String} msg The validation message
28797 markInvalid : function(msg)
28799 this.iconEl.show();
28801 this.iconEl.removeClass(this.validClass);
28803 this.iconEl.addClass(this.invalidClass);
28805 this.fireEvent('invalid', this, msg);
28811 Roo.apply(Roo.bootstrap.FieldLabel, {
28816 * register a FieldLabel Group
28817 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28819 register : function(label)
28821 if(this.groups.hasOwnProperty(label.target)){
28825 this.groups[label.target] = label;
28829 * fetch a FieldLabel Group based on the target
28830 * @param {string} target
28831 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28833 get: function(target) {
28834 if (typeof(this.groups[target]) == 'undefined') {
28838 return this.groups[target] ;
28847 * page DateSplitField.
28853 * @class Roo.bootstrap.DateSplitField
28854 * @extends Roo.bootstrap.Component
28855 * Bootstrap DateSplitField class
28856 * @cfg {string} fieldLabel - the label associated
28857 * @cfg {Number} labelWidth set the width of label (0-12)
28858 * @cfg {String} labelAlign (top|left)
28859 * @cfg {Boolean} dayAllowBlank (true|false) default false
28860 * @cfg {Boolean} monthAllowBlank (true|false) default false
28861 * @cfg {Boolean} yearAllowBlank (true|false) default false
28862 * @cfg {string} dayPlaceholder
28863 * @cfg {string} monthPlaceholder
28864 * @cfg {string} yearPlaceholder
28865 * @cfg {string} dayFormat default 'd'
28866 * @cfg {string} monthFormat default 'm'
28867 * @cfg {string} yearFormat default 'Y'
28871 * Create a new DateSplitField
28872 * @param {Object} config The config object
28875 Roo.bootstrap.DateSplitField = function(config){
28876 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28882 * getting the data of years
28883 * @param {Roo.bootstrap.DateSplitField} this
28884 * @param {Object} years
28889 * getting the data of days
28890 * @param {Roo.bootstrap.DateSplitField} this
28891 * @param {Object} days
28896 * Fires after the field has been marked as invalid.
28897 * @param {Roo.form.Field} this
28898 * @param {String} msg The validation message
28903 * Fires after the field has been validated with no errors.
28904 * @param {Roo.form.Field} this
28910 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28913 labelAlign : 'top',
28915 dayAllowBlank : false,
28916 monthAllowBlank : false,
28917 yearAllowBlank : false,
28918 dayPlaceholder : '',
28919 monthPlaceholder : '',
28920 yearPlaceholder : '',
28924 isFormField : true,
28926 getAutoCreate : function()
28930 cls : 'row roo-date-split-field-group',
28935 cls : 'form-hidden-field roo-date-split-field-group-value',
28941 if(this.fieldLabel){
28944 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28948 html : this.fieldLabel
28954 Roo.each(['day', 'month', 'year'], function(t){
28957 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28964 inputEl: function ()
28966 return this.el.select('.roo-date-split-field-group-value', true).first();
28969 onRender : function(ct, position)
28973 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28975 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28977 this.dayField = new Roo.bootstrap.ComboBox({
28978 allowBlank : this.dayAllowBlank,
28979 alwaysQuery : true,
28980 displayField : 'value',
28983 forceSelection : true,
28985 placeholder : this.dayPlaceholder,
28986 selectOnFocus : true,
28987 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28988 triggerAction : 'all',
28990 valueField : 'value',
28991 store : new Roo.data.SimpleStore({
28992 data : (function() {
28994 _this.fireEvent('days', _this, days);
28997 fields : [ 'value' ]
29000 select : function (_self, record, index)
29002 _this.setValue(_this.getValue());
29007 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29009 this.monthField = new Roo.bootstrap.MonthField({
29010 after : '<i class=\"fa fa-calendar\"></i>',
29011 allowBlank : this.monthAllowBlank,
29012 placeholder : this.monthPlaceholder,
29015 render : function (_self)
29017 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29018 e.preventDefault();
29022 select : function (_self, oldvalue, newvalue)
29024 _this.setValue(_this.getValue());
29029 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29031 this.yearField = new Roo.bootstrap.ComboBox({
29032 allowBlank : this.yearAllowBlank,
29033 alwaysQuery : true,
29034 displayField : 'value',
29037 forceSelection : true,
29039 placeholder : this.yearPlaceholder,
29040 selectOnFocus : true,
29041 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29042 triggerAction : 'all',
29044 valueField : 'value',
29045 store : new Roo.data.SimpleStore({
29046 data : (function() {
29048 _this.fireEvent('years', _this, years);
29051 fields : [ 'value' ]
29054 select : function (_self, record, index)
29056 _this.setValue(_this.getValue());
29061 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29064 setValue : function(v, format)
29066 this.inputEl.dom.value = v;
29068 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29070 var d = Date.parseDate(v, f);
29077 this.setDay(d.format(this.dayFormat));
29078 this.setMonth(d.format(this.monthFormat));
29079 this.setYear(d.format(this.yearFormat));
29086 setDay : function(v)
29088 this.dayField.setValue(v);
29089 this.inputEl.dom.value = this.getValue();
29094 setMonth : function(v)
29096 this.monthField.setValue(v, true);
29097 this.inputEl.dom.value = this.getValue();
29102 setYear : function(v)
29104 this.yearField.setValue(v);
29105 this.inputEl.dom.value = this.getValue();
29110 getDay : function()
29112 return this.dayField.getValue();
29115 getMonth : function()
29117 return this.monthField.getValue();
29120 getYear : function()
29122 return this.yearField.getValue();
29125 getValue : function()
29127 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29129 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29139 this.inputEl.dom.value = '';
29144 validate : function()
29146 var d = this.dayField.validate();
29147 var m = this.monthField.validate();
29148 var y = this.yearField.validate();
29153 (!this.dayAllowBlank && !d) ||
29154 (!this.monthAllowBlank && !m) ||
29155 (!this.yearAllowBlank && !y)
29160 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29169 this.markInvalid();
29174 markValid : function()
29177 var label = this.el.select('label', true).first();
29178 var icon = this.el.select('i.fa-star', true).first();
29184 this.fireEvent('valid', this);
29188 * Mark this field as invalid
29189 * @param {String} msg The validation message
29191 markInvalid : function(msg)
29194 var label = this.el.select('label', true).first();
29195 var icon = this.el.select('i.fa-star', true).first();
29197 if(label && !icon){
29198 this.el.select('.roo-date-split-field-label', true).createChild({
29200 cls : 'text-danger fa fa-lg fa-star',
29201 tooltip : 'This field is required',
29202 style : 'margin-right:5px;'
29206 this.fireEvent('invalid', this, msg);
29209 clearInvalid : function()
29211 var label = this.el.select('label', true).first();
29212 var icon = this.el.select('i.fa-star', true).first();
29218 this.fireEvent('valid', this);
29221 getName: function()
29231 * http://masonry.desandro.com
29233 * The idea is to render all the bricks based on vertical width...
29235 * The original code extends 'outlayer' - we might need to use that....
29241 * @class Roo.bootstrap.LayoutMasonry
29242 * @extends Roo.bootstrap.Component
29243 * Bootstrap Layout Masonry class
29246 * Create a new Element
29247 * @param {Object} config The config object
29250 Roo.bootstrap.LayoutMasonry = function(config){
29251 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29257 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29260 * @cfg {Boolean} isLayoutInstant = no animation?
29262 isLayoutInstant : false, // needed?
29265 * @cfg {Number} boxWidth width of the columns
29270 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29275 * @cfg {Number} padWidth padding below box..
29280 * @cfg {Number} gutter gutter width..
29285 * @cfg {Number} maxCols maximum number of columns
29291 * @cfg {Boolean} isAutoInitial defalut true
29293 isAutoInitial : true,
29298 * @cfg {Boolean} isHorizontal defalut false
29300 isHorizontal : false,
29302 currentSize : null,
29308 bricks: null, //CompositeElement
29312 _isLayoutInited : false,
29314 // isAlternative : false, // only use for vertical layout...
29317 * @cfg {Number} alternativePadWidth padding below box..
29319 alternativePadWidth : 50,
29321 getAutoCreate : function(){
29325 cls: 'blog-masonary-wrapper ' + this.cls,
29327 cls : 'mas-boxes masonary'
29334 getChildContainer: function( )
29336 if (this.boxesEl) {
29337 return this.boxesEl;
29340 this.boxesEl = this.el.select('.mas-boxes').first();
29342 return this.boxesEl;
29346 initEvents : function()
29350 if(this.isAutoInitial){
29351 Roo.log('hook children rendered');
29352 this.on('childrenrendered', function() {
29353 Roo.log('children rendered');
29359 initial : function()
29361 this.currentSize = this.el.getBox(true);
29363 Roo.EventManager.onWindowResize(this.resize, this);
29365 if(!this.isAutoInitial){
29373 //this.layout.defer(500,this);
29377 resize : function()
29381 var cs = this.el.getBox(true);
29383 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29384 Roo.log("no change in with or X");
29388 this.currentSize = cs;
29394 layout : function()
29396 this._resetLayout();
29398 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29400 this.layoutItems( isInstant );
29402 this._isLayoutInited = true;
29406 _resetLayout : function()
29408 if(this.isHorizontal){
29409 this.horizontalMeasureColumns();
29413 this.verticalMeasureColumns();
29417 verticalMeasureColumns : function()
29419 this.getContainerWidth();
29421 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29422 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29426 var boxWidth = this.boxWidth + this.padWidth;
29428 if(this.containerWidth < this.boxWidth){
29429 boxWidth = this.containerWidth
29432 var containerWidth = this.containerWidth;
29434 var cols = Math.floor(containerWidth / boxWidth);
29436 this.cols = Math.max( cols, 1 );
29438 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29440 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29442 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29444 this.colWidth = boxWidth + avail - this.padWidth;
29446 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29447 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29450 horizontalMeasureColumns : function()
29452 this.getContainerWidth();
29454 var boxWidth = this.boxWidth;
29456 if(this.containerWidth < boxWidth){
29457 boxWidth = this.containerWidth;
29460 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29462 this.el.setHeight(boxWidth);
29466 getContainerWidth : function()
29468 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29471 layoutItems : function( isInstant )
29473 var items = Roo.apply([], this.bricks);
29475 if(this.isHorizontal){
29476 this._horizontalLayoutItems( items , isInstant );
29480 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29481 // this._verticalAlternativeLayoutItems( items , isInstant );
29485 this._verticalLayoutItems( items , isInstant );
29489 _verticalLayoutItems : function ( items , isInstant)
29491 if ( !items || !items.length ) {
29496 ['xs', 'xs', 'xs', 'tall'],
29497 ['xs', 'xs', 'tall'],
29498 ['xs', 'xs', 'sm'],
29499 ['xs', 'xs', 'xs'],
29505 ['sm', 'xs', 'xs'],
29509 ['tall', 'xs', 'xs', 'xs'],
29510 ['tall', 'xs', 'xs'],
29522 Roo.each(items, function(item, k){
29524 switch (item.size) {
29525 // these layouts take up a full box,
29536 boxes.push([item]);
29559 var filterPattern = function(box, length)
29567 var pattern = box.slice(0, length);
29571 Roo.each(pattern, function(i){
29572 format.push(i.size);
29575 Roo.each(standard, function(s){
29577 if(String(s) != String(format)){
29586 if(!match && length == 1){
29591 filterPattern(box, length - 1);
29595 queue.push(pattern);
29597 box = box.slice(length, box.length);
29599 filterPattern(box, 4);
29605 Roo.each(boxes, function(box, k){
29611 if(box.length == 1){
29616 filterPattern(box, 4);
29620 this._processVerticalLayoutQueue( queue, isInstant );
29624 // _verticalAlternativeLayoutItems : function( items , isInstant )
29626 // if ( !items || !items.length ) {
29630 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29634 _horizontalLayoutItems : function ( items , isInstant)
29636 if ( !items || !items.length || items.length < 3) {
29642 var eItems = items.slice(0, 3);
29644 items = items.slice(3, items.length);
29647 ['xs', 'xs', 'xs', 'wide'],
29648 ['xs', 'xs', 'wide'],
29649 ['xs', 'xs', 'sm'],
29650 ['xs', 'xs', 'xs'],
29656 ['sm', 'xs', 'xs'],
29660 ['wide', 'xs', 'xs', 'xs'],
29661 ['wide', 'xs', 'xs'],
29674 Roo.each(items, function(item, k){
29676 switch (item.size) {
29687 boxes.push([item]);
29711 var filterPattern = function(box, length)
29719 var pattern = box.slice(0, length);
29723 Roo.each(pattern, function(i){
29724 format.push(i.size);
29727 Roo.each(standard, function(s){
29729 if(String(s) != String(format)){
29738 if(!match && length == 1){
29743 filterPattern(box, length - 1);
29747 queue.push(pattern);
29749 box = box.slice(length, box.length);
29751 filterPattern(box, 4);
29757 Roo.each(boxes, function(box, k){
29763 if(box.length == 1){
29768 filterPattern(box, 4);
29775 var pos = this.el.getBox(true);
29779 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29781 var hit_end = false;
29783 Roo.each(queue, function(box){
29787 Roo.each(box, function(b){
29789 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29799 Roo.each(box, function(b){
29801 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29804 mx = Math.max(mx, b.x);
29808 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29812 Roo.each(box, function(b){
29814 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29828 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29831 /** Sets position of item in DOM
29832 * @param {Element} item
29833 * @param {Number} x - horizontal position
29834 * @param {Number} y - vertical position
29835 * @param {Boolean} isInstant - disables transitions
29837 _processVerticalLayoutQueue : function( queue, isInstant )
29839 var pos = this.el.getBox(true);
29844 for (var i = 0; i < this.cols; i++){
29848 Roo.each(queue, function(box, k){
29850 var col = k % this.cols;
29852 Roo.each(box, function(b,kk){
29854 b.el.position('absolute');
29856 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29857 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29859 if(b.size == 'md-left' || b.size == 'md-right'){
29860 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29861 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29864 b.el.setWidth(width);
29865 b.el.setHeight(height);
29867 b.el.select('iframe',true).setSize(width,height);
29871 for (var i = 0; i < this.cols; i++){
29873 if(maxY[i] < maxY[col]){
29878 col = Math.min(col, i);
29882 x = pos.x + col * (this.colWidth + this.padWidth);
29886 var positions = [];
29888 switch (box.length){
29890 positions = this.getVerticalOneBoxColPositions(x, y, box);
29893 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29896 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29899 positions = this.getVerticalFourBoxColPositions(x, y, box);
29905 Roo.each(box, function(b,kk){
29907 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29909 var sz = b.el.getSize();
29911 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29919 for (var i = 0; i < this.cols; i++){
29920 mY = Math.max(mY, maxY[i]);
29923 this.el.setHeight(mY - pos.y);
29927 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29929 // var pos = this.el.getBox(true);
29932 // var maxX = pos.right;
29934 // var maxHeight = 0;
29936 // Roo.each(items, function(item, k){
29940 // item.el.position('absolute');
29942 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29944 // item.el.setWidth(width);
29946 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29948 // item.el.setHeight(height);
29951 // item.el.setXY([x, y], isInstant ? false : true);
29953 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29956 // y = y + height + this.alternativePadWidth;
29958 // maxHeight = maxHeight + height + this.alternativePadWidth;
29962 // this.el.setHeight(maxHeight);
29966 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29968 var pos = this.el.getBox(true);
29973 var maxX = pos.right;
29975 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29977 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29979 Roo.each(queue, function(box, k){
29981 Roo.each(box, function(b, kk){
29983 b.el.position('absolute');
29985 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29986 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29988 if(b.size == 'md-left' || b.size == 'md-right'){
29989 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29990 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29993 b.el.setWidth(width);
29994 b.el.setHeight(height);
30002 var positions = [];
30004 switch (box.length){
30006 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30009 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30012 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30015 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30021 Roo.each(box, function(b,kk){
30023 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30025 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30033 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30035 Roo.each(eItems, function(b,k){
30037 b.size = (k == 0) ? 'sm' : 'xs';
30038 b.x = (k == 0) ? 2 : 1;
30039 b.y = (k == 0) ? 2 : 1;
30041 b.el.position('absolute');
30043 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30045 b.el.setWidth(width);
30047 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30049 b.el.setHeight(height);
30053 var positions = [];
30056 x : maxX - this.unitWidth * 2 - this.gutter,
30061 x : maxX - this.unitWidth,
30062 y : minY + (this.unitWidth + this.gutter) * 2
30066 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30070 Roo.each(eItems, function(b,k){
30072 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30078 getVerticalOneBoxColPositions : function(x, y, box)
30082 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30084 if(box[0].size == 'md-left'){
30088 if(box[0].size == 'md-right'){
30093 x : x + (this.unitWidth + this.gutter) * rand,
30100 getVerticalTwoBoxColPositions : function(x, y, box)
30104 if(box[0].size == 'xs'){
30108 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30112 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30126 x : x + (this.unitWidth + this.gutter) * 2,
30127 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30134 getVerticalThreeBoxColPositions : function(x, y, box)
30138 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30146 x : x + (this.unitWidth + this.gutter) * 1,
30151 x : x + (this.unitWidth + this.gutter) * 2,
30159 if(box[0].size == 'xs' && box[1].size == 'xs'){
30168 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30172 x : x + (this.unitWidth + this.gutter) * 1,
30186 x : x + (this.unitWidth + this.gutter) * 2,
30191 x : x + (this.unitWidth + this.gutter) * 2,
30192 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30199 getVerticalFourBoxColPositions : function(x, y, box)
30203 if(box[0].size == 'xs'){
30212 y : y + (this.unitHeight + this.gutter) * 1
30217 y : y + (this.unitHeight + this.gutter) * 2
30221 x : x + (this.unitWidth + this.gutter) * 1,
30235 x : x + (this.unitWidth + this.gutter) * 2,
30240 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30241 y : y + (this.unitHeight + this.gutter) * 1
30245 x : x + (this.unitWidth + this.gutter) * 2,
30246 y : y + (this.unitWidth + this.gutter) * 2
30253 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30257 if(box[0].size == 'md-left'){
30259 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30266 if(box[0].size == 'md-right'){
30268 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30269 y : minY + (this.unitWidth + this.gutter) * 1
30275 var rand = Math.floor(Math.random() * (4 - box[0].y));
30278 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30279 y : minY + (this.unitWidth + this.gutter) * rand
30286 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30290 if(box[0].size == 'xs'){
30293 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30298 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30299 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30312 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30313 y : minY + (this.unitWidth + this.gutter) * 2
30320 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30324 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30327 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30332 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30333 y : minY + (this.unitWidth + this.gutter) * 1
30337 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30338 y : minY + (this.unitWidth + this.gutter) * 2
30345 if(box[0].size == 'xs' && box[1].size == 'xs'){
30348 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30353 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30358 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30359 y : minY + (this.unitWidth + this.gutter) * 1
30367 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30372 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30373 y : minY + (this.unitWidth + this.gutter) * 2
30377 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30378 y : minY + (this.unitWidth + this.gutter) * 2
30385 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30389 if(box[0].size == 'xs'){
30392 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30397 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30402 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30407 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30408 y : minY + (this.unitWidth + this.gutter) * 1
30416 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30421 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30422 y : minY + (this.unitWidth + this.gutter) * 2
30426 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30427 y : minY + (this.unitWidth + this.gutter) * 2
30431 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30432 y : minY + (this.unitWidth + this.gutter) * 2
30446 * http://masonry.desandro.com
30448 * The idea is to render all the bricks based on vertical width...
30450 * The original code extends 'outlayer' - we might need to use that....
30456 * @class Roo.bootstrap.LayoutMasonryAuto
30457 * @extends Roo.bootstrap.Component
30458 * Bootstrap Layout Masonry class
30461 * Create a new Element
30462 * @param {Object} config The config object
30465 Roo.bootstrap.LayoutMasonryAuto = function(config){
30466 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30469 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30472 * @cfg {Boolean} isFitWidth - resize the width..
30474 isFitWidth : false, // options..
30476 * @cfg {Boolean} isOriginLeft = left align?
30478 isOriginLeft : true,
30480 * @cfg {Boolean} isOriginTop = top align?
30482 isOriginTop : false,
30484 * @cfg {Boolean} isLayoutInstant = no animation?
30486 isLayoutInstant : false, // needed?
30488 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30490 isResizingContainer : true,
30492 * @cfg {Number} columnWidth width of the columns
30498 * @cfg {Number} maxCols maximum number of columns
30503 * @cfg {Number} padHeight padding below box..
30509 * @cfg {Boolean} isAutoInitial defalut true
30512 isAutoInitial : true,
30518 initialColumnWidth : 0,
30519 currentSize : null,
30521 colYs : null, // array.
30528 bricks: null, //CompositeElement
30529 cols : 0, // array?
30530 // element : null, // wrapped now this.el
30531 _isLayoutInited : null,
30534 getAutoCreate : function(){
30538 cls: 'blog-masonary-wrapper ' + this.cls,
30540 cls : 'mas-boxes masonary'
30547 getChildContainer: function( )
30549 if (this.boxesEl) {
30550 return this.boxesEl;
30553 this.boxesEl = this.el.select('.mas-boxes').first();
30555 return this.boxesEl;
30559 initEvents : function()
30563 if(this.isAutoInitial){
30564 Roo.log('hook children rendered');
30565 this.on('childrenrendered', function() {
30566 Roo.log('children rendered');
30573 initial : function()
30575 this.reloadItems();
30577 this.currentSize = this.el.getBox(true);
30579 /// was window resize... - let's see if this works..
30580 Roo.EventManager.onWindowResize(this.resize, this);
30582 if(!this.isAutoInitial){
30587 this.layout.defer(500,this);
30590 reloadItems: function()
30592 this.bricks = this.el.select('.masonry-brick', true);
30594 this.bricks.each(function(b) {
30595 //Roo.log(b.getSize());
30596 if (!b.attr('originalwidth')) {
30597 b.attr('originalwidth', b.getSize().width);
30602 Roo.log(this.bricks.elements.length);
30605 resize : function()
30608 var cs = this.el.getBox(true);
30610 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30611 Roo.log("no change in with or X");
30614 this.currentSize = cs;
30618 layout : function()
30621 this._resetLayout();
30622 //this._manageStamps();
30624 // don't animate first layout
30625 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30626 this.layoutItems( isInstant );
30628 // flag for initalized
30629 this._isLayoutInited = true;
30632 layoutItems : function( isInstant )
30634 //var items = this._getItemsForLayout( this.items );
30635 // original code supports filtering layout items.. we just ignore it..
30637 this._layoutItems( this.bricks , isInstant );
30639 this._postLayout();
30641 _layoutItems : function ( items , isInstant)
30643 //this.fireEvent( 'layout', this, items );
30646 if ( !items || !items.elements.length ) {
30647 // no items, emit event with empty array
30652 items.each(function(item) {
30653 Roo.log("layout item");
30655 // get x/y object from method
30656 var position = this._getItemLayoutPosition( item );
30658 position.item = item;
30659 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30660 queue.push( position );
30663 this._processLayoutQueue( queue );
30665 /** Sets position of item in DOM
30666 * @param {Element} item
30667 * @param {Number} x - horizontal position
30668 * @param {Number} y - vertical position
30669 * @param {Boolean} isInstant - disables transitions
30671 _processLayoutQueue : function( queue )
30673 for ( var i=0, len = queue.length; i < len; i++ ) {
30674 var obj = queue[i];
30675 obj.item.position('absolute');
30676 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30682 * Any logic you want to do after each layout,
30683 * i.e. size the container
30685 _postLayout : function()
30687 this.resizeContainer();
30690 resizeContainer : function()
30692 if ( !this.isResizingContainer ) {
30695 var size = this._getContainerSize();
30697 this.el.setSize(size.width,size.height);
30698 this.boxesEl.setSize(size.width,size.height);
30704 _resetLayout : function()
30706 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30707 this.colWidth = this.el.getWidth();
30708 //this.gutter = this.el.getWidth();
30710 this.measureColumns();
30716 this.colYs.push( 0 );
30722 measureColumns : function()
30724 this.getContainerWidth();
30725 // if columnWidth is 0, default to outerWidth of first item
30726 if ( !this.columnWidth ) {
30727 var firstItem = this.bricks.first();
30728 Roo.log(firstItem);
30729 this.columnWidth = this.containerWidth;
30730 if (firstItem && firstItem.attr('originalwidth') ) {
30731 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30733 // columnWidth fall back to item of first element
30734 Roo.log("set column width?");
30735 this.initialColumnWidth = this.columnWidth ;
30737 // if first elem has no width, default to size of container
30742 if (this.initialColumnWidth) {
30743 this.columnWidth = this.initialColumnWidth;
30748 // column width is fixed at the top - however if container width get's smaller we should
30751 // this bit calcs how man columns..
30753 var columnWidth = this.columnWidth += this.gutter;
30755 // calculate columns
30756 var containerWidth = this.containerWidth + this.gutter;
30758 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30759 // fix rounding errors, typically with gutters
30760 var excess = columnWidth - containerWidth % columnWidth;
30763 // if overshoot is less than a pixel, round up, otherwise floor it
30764 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30765 cols = Math[ mathMethod ]( cols );
30766 this.cols = Math.max( cols, 1 );
30767 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30769 // padding positioning..
30770 var totalColWidth = this.cols * this.columnWidth;
30771 var padavail = this.containerWidth - totalColWidth;
30772 // so for 2 columns - we need 3 'pads'
30774 var padNeeded = (1+this.cols) * this.padWidth;
30776 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30778 this.columnWidth += padExtra
30779 //this.padWidth = Math.floor(padavail / ( this.cols));
30781 // adjust colum width so that padding is fixed??
30783 // we have 3 columns ... total = width * 3
30784 // we have X left over... that should be used by
30786 //if (this.expandC) {
30794 getContainerWidth : function()
30796 /* // container is parent if fit width
30797 var container = this.isFitWidth ? this.element.parentNode : this.element;
30798 // check that this.size and size are there
30799 // IE8 triggers resize on body size change, so they might not be
30801 var size = getSize( container ); //FIXME
30802 this.containerWidth = size && size.innerWidth; //FIXME
30805 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30809 _getItemLayoutPosition : function( item ) // what is item?
30811 // we resize the item to our columnWidth..
30813 item.setWidth(this.columnWidth);
30814 item.autoBoxAdjust = false;
30816 var sz = item.getSize();
30818 // how many columns does this brick span
30819 var remainder = this.containerWidth % this.columnWidth;
30821 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30822 // round if off by 1 pixel, otherwise use ceil
30823 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30824 colSpan = Math.min( colSpan, this.cols );
30826 // normally this should be '1' as we dont' currently allow multi width columns..
30828 var colGroup = this._getColGroup( colSpan );
30829 // get the minimum Y value from the columns
30830 var minimumY = Math.min.apply( Math, colGroup );
30831 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30833 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30835 // position the brick
30837 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30838 y: this.currentSize.y + minimumY + this.padHeight
30842 // apply setHeight to necessary columns
30843 var setHeight = minimumY + sz.height + this.padHeight;
30844 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30846 var setSpan = this.cols + 1 - colGroup.length;
30847 for ( var i = 0; i < setSpan; i++ ) {
30848 this.colYs[ shortColIndex + i ] = setHeight ;
30855 * @param {Number} colSpan - number of columns the element spans
30856 * @returns {Array} colGroup
30858 _getColGroup : function( colSpan )
30860 if ( colSpan < 2 ) {
30861 // if brick spans only one column, use all the column Ys
30866 // how many different places could this brick fit horizontally
30867 var groupCount = this.cols + 1 - colSpan;
30868 // for each group potential horizontal position
30869 for ( var i = 0; i < groupCount; i++ ) {
30870 // make an array of colY values for that one group
30871 var groupColYs = this.colYs.slice( i, i + colSpan );
30872 // and get the max value of the array
30873 colGroup[i] = Math.max.apply( Math, groupColYs );
30878 _manageStamp : function( stamp )
30880 var stampSize = stamp.getSize();
30881 var offset = stamp.getBox();
30882 // get the columns that this stamp affects
30883 var firstX = this.isOriginLeft ? offset.x : offset.right;
30884 var lastX = firstX + stampSize.width;
30885 var firstCol = Math.floor( firstX / this.columnWidth );
30886 firstCol = Math.max( 0, firstCol );
30888 var lastCol = Math.floor( lastX / this.columnWidth );
30889 // lastCol should not go over if multiple of columnWidth #425
30890 lastCol -= lastX % this.columnWidth ? 0 : 1;
30891 lastCol = Math.min( this.cols - 1, lastCol );
30893 // set colYs to bottom of the stamp
30894 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30897 for ( var i = firstCol; i <= lastCol; i++ ) {
30898 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30903 _getContainerSize : function()
30905 this.maxY = Math.max.apply( Math, this.colYs );
30910 if ( this.isFitWidth ) {
30911 size.width = this._getContainerFitWidth();
30917 _getContainerFitWidth : function()
30919 var unusedCols = 0;
30920 // count unused columns
30923 if ( this.colYs[i] !== 0 ) {
30928 // fit container to columns that have been used
30929 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30932 needsResizeLayout : function()
30934 var previousWidth = this.containerWidth;
30935 this.getContainerWidth();
30936 return previousWidth !== this.containerWidth;
30951 * @class Roo.bootstrap.MasonryBrick
30952 * @extends Roo.bootstrap.Component
30953 * Bootstrap MasonryBrick class
30956 * Create a new MasonryBrick
30957 * @param {Object} config The config object
30960 Roo.bootstrap.MasonryBrick = function(config){
30961 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30967 * When a MasonryBrick is clcik
30968 * @param {Roo.bootstrap.MasonryBrick} this
30969 * @param {Roo.EventObject} e
30975 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30978 * @cfg {String} title
30982 * @cfg {String} html
30986 * @cfg {String} bgimage
30990 * @cfg {String} videourl
30994 * @cfg {String} cls
30998 * @cfg {String} href
31002 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31007 * @cfg {String} (center|bottom) placetitle
31012 * @cfg {Boolean} isFitContainer defalut true
31014 isFitContainer : true,
31017 * @cfg {Boolean} preventDefault defalut false
31019 preventDefault : false,
31021 getAutoCreate : function()
31023 if(!this.isFitContainer){
31024 return this.getSplitAutoCreate();
31027 var cls = 'masonry-brick masonry-brick-full';
31029 if(this.href.length){
31030 cls += ' masonry-brick-link';
31033 if(this.bgimage.length){
31034 cls += ' masonry-brick-image';
31037 if(!this.html.length){
31038 cls += ' enable-mask';
31042 cls += ' masonry-' + this.size + '-brick';
31045 if(this.placetitle.length){
31047 switch (this.placetitle) {
31049 cls += ' masonry-center-title';
31052 cls += ' masonry-bottom-title';
31059 if(!this.html.length && !this.bgimage.length){
31060 cls += ' masonry-center-title';
31063 if(!this.html.length && this.bgimage.length){
31064 cls += ' masonry-bottom-title';
31069 cls += ' ' + this.cls;
31073 tag: (this.href.length) ? 'a' : 'div',
31078 cls: 'masonry-brick-paragraph',
31084 if(this.href.length){
31085 cfg.href = this.href;
31088 var cn = cfg.cn[0].cn;
31090 if(this.title.length){
31093 cls: 'masonry-brick-title',
31098 if(this.html.length){
31101 cls: 'masonry-brick-text',
31105 if (!this.title.length && !this.html.length) {
31106 cfg.cn[0].cls += ' hide';
31109 if(this.bgimage.length){
31112 cls: 'masonry-brick-image-view',
31117 if(this.videourl.length){
31118 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31119 // youtube support only?
31122 cls: 'masonry-brick-image-view',
31125 allowfullscreen : true
31133 cls: 'masonry-brick-mask'
31140 getSplitAutoCreate : function()
31142 var cls = 'masonry-brick masonry-brick-split';
31144 if(this.href.length){
31145 cls += ' masonry-brick-link';
31148 if(this.bgimage.length){
31149 cls += ' masonry-brick-image';
31153 cls += ' masonry-' + this.size + '-brick';
31156 switch (this.placetitle) {
31158 cls += ' masonry-center-title';
31161 cls += ' masonry-bottom-title';
31164 if(!this.bgimage.length){
31165 cls += ' masonry-center-title';
31168 if(this.bgimage.length){
31169 cls += ' masonry-bottom-title';
31175 cls += ' ' + this.cls;
31179 tag: (this.href.length) ? 'a' : 'div',
31184 cls: 'masonry-brick-split-head',
31188 cls: 'masonry-brick-paragraph',
31195 cls: 'masonry-brick-split-body',
31201 if(this.href.length){
31202 cfg.href = this.href;
31205 if(this.title.length){
31206 cfg.cn[0].cn[0].cn.push({
31208 cls: 'masonry-brick-title',
31213 if(this.html.length){
31214 cfg.cn[1].cn.push({
31216 cls: 'masonry-brick-text',
31221 if(this.bgimage.length){
31222 cfg.cn[0].cn.push({
31224 cls: 'masonry-brick-image-view',
31229 if(this.videourl.length){
31230 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31231 // youtube support only?
31232 cfg.cn[0].cn.cn.push({
31234 cls: 'masonry-brick-image-view',
31237 allowfullscreen : true
31244 initEvents: function()
31246 switch (this.size) {
31279 this.el.on('touchstart', this.onTouchStart, this);
31280 this.el.on('touchmove', this.onTouchMove, this);
31281 this.el.on('touchend', this.onTouchEnd, this);
31282 this.el.on('contextmenu', this.onContextMenu, this);
31284 this.el.on('mouseenter' ,this.enter, this);
31285 this.el.on('mouseleave', this.leave, this);
31286 this.el.on('click', this.onClick, this);
31289 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31290 this.parent().bricks.push(this);
31295 onClick: function(e, el)
31297 var time = this.endTimer - this.startTimer;
31301 e.preventDefault();
31306 if(!this.preventDefault){
31310 e.preventDefault();
31311 this.fireEvent('click', this);
31314 enter: function(e, el)
31316 e.preventDefault();
31318 if(!this.isFitContainer){
31322 if(this.bgimage.length && this.html.length){
31323 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31327 leave: function(e, el)
31329 e.preventDefault();
31331 if(!this.isFitContainer){
31335 if(this.bgimage.length && this.html.length){
31336 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31340 onTouchStart: function(e, el)
31342 // e.preventDefault();
31344 this.touchmoved = false;
31346 if(!this.isFitContainer){
31350 if(!this.bgimage.length || !this.html.length){
31354 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31356 this.timer = new Date().getTime();
31360 onTouchMove: function(e, el)
31362 this.touchmoved = true;
31365 onContextMenu : function(e,el)
31367 e.preventDefault();
31368 e.stopPropagation();
31372 onTouchEnd: function(e, el)
31374 // e.preventDefault();
31376 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31383 if(!this.bgimage.length || !this.html.length){
31385 if(this.href.length){
31386 window.location.href = this.href;
31392 if(!this.isFitContainer){
31396 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31398 window.location.href = this.href;
31413 * @class Roo.bootstrap.Brick
31414 * @extends Roo.bootstrap.Component
31415 * Bootstrap Brick class
31418 * Create a new Brick
31419 * @param {Object} config The config object
31422 Roo.bootstrap.Brick = function(config){
31423 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31429 * When a Brick is click
31430 * @param {Roo.bootstrap.Brick} this
31431 * @param {Roo.EventObject} e
31437 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31440 * @cfg {String} title
31444 * @cfg {String} html
31448 * @cfg {String} bgimage
31452 * @cfg {String} cls
31456 * @cfg {String} href
31460 * @cfg {String} video
31464 * @cfg {Boolean} square
31468 getAutoCreate : function()
31470 var cls = 'roo-brick';
31472 if(this.href.length){
31473 cls += ' roo-brick-link';
31476 if(this.bgimage.length){
31477 cls += ' roo-brick-image';
31480 if(!this.html.length && !this.bgimage.length){
31481 cls += ' roo-brick-center-title';
31484 if(!this.html.length && this.bgimage.length){
31485 cls += ' roo-brick-bottom-title';
31489 cls += ' ' + this.cls;
31493 tag: (this.href.length) ? 'a' : 'div',
31498 cls: 'roo-brick-paragraph',
31504 if(this.href.length){
31505 cfg.href = this.href;
31508 var cn = cfg.cn[0].cn;
31510 if(this.title.length){
31513 cls: 'roo-brick-title',
31518 if(this.html.length){
31521 cls: 'roo-brick-text',
31528 if(this.bgimage.length){
31531 cls: 'roo-brick-image-view',
31539 initEvents: function()
31541 if(this.title.length || this.html.length){
31542 this.el.on('mouseenter' ,this.enter, this);
31543 this.el.on('mouseleave', this.leave, this);
31547 Roo.EventManager.onWindowResize(this.resize, this);
31552 resize : function()
31554 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31556 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31558 if(this.bgimage.length){
31559 var image = this.el.select('.roo-brick-image-view', true).first();
31560 image.setWidth(paragraph.getWidth());
31561 image.setHeight(paragraph.getWidth());
31563 this.el.setHeight(paragraph.getWidth());
31569 enter: function(e, el)
31571 e.preventDefault();
31573 if(this.bgimage.length){
31574 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31575 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31579 leave: function(e, el)
31581 e.preventDefault();
31583 if(this.bgimage.length){
31584 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31585 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31601 * @class Roo.bootstrap.NumberField
31602 * @extends Roo.bootstrap.Input
31603 * Bootstrap NumberField class
31609 * Create a new NumberField
31610 * @param {Object} config The config object
31613 Roo.bootstrap.NumberField = function(config){
31614 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31617 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31620 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31622 allowDecimals : true,
31624 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31626 decimalSeparator : ".",
31628 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31630 decimalPrecision : 2,
31632 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31634 allowNegative : true,
31636 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31638 minValue : Number.NEGATIVE_INFINITY,
31640 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31642 maxValue : Number.MAX_VALUE,
31644 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31646 minText : "The minimum value for this field is {0}",
31648 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31650 maxText : "The maximum value for this field is {0}",
31652 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31653 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31655 nanText : "{0} is not a valid number",
31657 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31662 initEvents : function()
31664 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31666 var allowed = "0123456789";
31668 if(this.allowDecimals){
31669 allowed += this.decimalSeparator;
31672 if(this.allowNegative){
31676 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31678 var keyPress = function(e){
31680 var k = e.getKey();
31682 var c = e.getCharCode();
31685 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31686 allowed.indexOf(String.fromCharCode(c)) === -1
31692 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31696 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31701 this.el.on("keypress", keyPress, this);
31704 validateValue : function(value)
31707 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31711 var num = this.parseValue(value);
31714 this.markInvalid(String.format(this.nanText, value));
31718 if(num < this.minValue){
31719 this.markInvalid(String.format(this.minText, this.minValue));
31723 if(num > this.maxValue){
31724 this.markInvalid(String.format(this.maxText, this.maxValue));
31731 getValue : function()
31733 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31736 parseValue : function(value)
31738 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31739 return isNaN(value) ? '' : value;
31742 fixPrecision : function(value)
31744 var nan = isNaN(value);
31746 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31747 return nan ? '' : value;
31749 return parseFloat(value).toFixed(this.decimalPrecision);
31752 setValue : function(v)
31754 v = this.fixPrecision(v);
31755 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31758 decimalPrecisionFcn : function(v)
31760 return Math.floor(v);
31763 beforeBlur : function()
31769 var v = this.parseValue(this.getRawValue());
31784 * @class Roo.bootstrap.DocumentSlider
31785 * @extends Roo.bootstrap.Component
31786 * Bootstrap DocumentSlider class
31789 * Create a new DocumentViewer
31790 * @param {Object} config The config object
31793 Roo.bootstrap.DocumentSlider = function(config){
31794 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31801 * Fire after initEvent
31802 * @param {Roo.bootstrap.DocumentSlider} this
31807 * Fire after update
31808 * @param {Roo.bootstrap.DocumentSlider} this
31814 * @param {Roo.bootstrap.DocumentSlider} this
31820 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31826 getAutoCreate : function()
31830 cls : 'roo-document-slider',
31834 cls : 'roo-document-slider-header',
31838 cls : 'roo-document-slider-header-title'
31844 cls : 'roo-document-slider-body',
31848 cls : 'roo-document-slider-prev',
31852 cls : 'fa fa-chevron-left'
31858 cls : 'roo-document-slider-thumb',
31862 cls : 'roo-document-slider-image'
31868 cls : 'roo-document-slider-next',
31872 cls : 'fa fa-chevron-right'
31884 initEvents : function()
31886 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31887 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31889 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31890 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31892 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31893 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31895 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31896 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31898 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31899 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31901 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31902 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31904 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31905 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31907 this.thumbEl.on('click', this.onClick, this);
31909 this.prevIndicator.on('click', this.prev, this);
31911 this.nextIndicator.on('click', this.next, this);
31915 initial : function()
31917 if(this.files.length){
31918 this.indicator = 1;
31922 this.fireEvent('initial', this);
31925 update : function()
31927 this.imageEl.attr('src', this.files[this.indicator - 1]);
31929 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31931 this.prevIndicator.show();
31933 if(this.indicator == 1){
31934 this.prevIndicator.hide();
31937 this.nextIndicator.show();
31939 if(this.indicator == this.files.length){
31940 this.nextIndicator.hide();
31943 this.thumbEl.scrollTo('top');
31945 this.fireEvent('update', this);
31948 onClick : function(e)
31950 e.preventDefault();
31952 this.fireEvent('click', this);
31957 e.preventDefault();
31959 this.indicator = Math.max(1, this.indicator - 1);
31966 e.preventDefault();
31968 this.indicator = Math.min(this.files.length, this.indicator + 1);
31982 * @class Roo.bootstrap.RadioSet
31983 * @extends Roo.bootstrap.Input
31984 * Bootstrap RadioSet class
31985 * @cfg {String} indicatorpos (left|right) default left
31986 * @cfg {Boolean} inline (true|false) inline the element (default true)
31987 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
31989 * Create a new RadioSet
31990 * @param {Object} config The config object
31993 Roo.bootstrap.RadioSet = function(config){
31995 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
31999 Roo.bootstrap.RadioSet.register(this);
32004 * Fires when the element is checked or unchecked.
32005 * @param {Roo.bootstrap.RadioSet} this This radio
32006 * @param {Roo.bootstrap.Radio} item The checked item
32013 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
32023 indicatorpos : 'left',
32025 getAutoCreate : function()
32029 cls : 'roo-radio-set-label',
32033 html : this.fieldLabel
32038 if(this.indicatorpos == 'left'){
32041 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32042 tooltip : 'This field is required'
32047 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32048 tooltip : 'This field is required'
32054 cls : 'roo-radio-set-items'
32057 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32059 if (align === 'left' && this.fieldLabel.length) {
32061 label.cls += ' col-md-' + this.labelWidth;
32064 cls : "roo-radio-set-right col-md-" + (12 - this.labelWidth),
32073 cls : 'roo-radio-set',
32077 cls : 'roo-radio-set-input',
32080 value : this.value ? this.value : ''
32087 if(this.weight.length){
32088 cfg.cls += ' roo-radio-' + this.weight;
32092 cfg.cls += ' roo-radio-set-inline';
32099 initEvents : function()
32101 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32102 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32104 if(!this.fieldLabel.length){
32105 this.labelEl.hide();
32108 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32109 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32111 this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32112 this.indicatorEl().hide();
32114 this.originalValue = this.getValue();
32118 inputEl: function ()
32120 return this.el.select('.roo-radio-set-input', true).first();
32123 getChildContainer : function()
32125 return this.itemsEl;
32128 register : function(item)
32130 this.radioes.push(item);
32134 validate : function()
32138 Roo.each(this.radioes, function(i){
32147 if(this.disabled || this.allowBlank || valid){
32152 this.markInvalid();
32157 markValid : function()
32159 if(this.labelEl.isVisible(true)){
32160 this.indicatorEl().hide();
32163 this.el.removeClass([this.invalidClass, this.validClass]);
32164 this.el.addClass(this.validClass);
32166 this.fireEvent('valid', this);
32169 markInvalid : function(msg)
32171 if(this.allowBlank || this.disabled){
32175 if(this.labelEl.isVisible(true)){
32176 this.indicatorEl().show();
32179 this.el.removeClass([this.invalidClass, this.validClass]);
32180 this.el.addClass(this.invalidClass);
32182 this.fireEvent('invalid', this, msg);
32186 setValue : function(v, suppressEvent)
32188 Roo.each(this.radioes, function(i){
32191 i.el.removeClass('checked');
32193 if(i.value === v || i.value.toString() === v.toString()){
32195 i.el.addClass('checked');
32197 if(suppressEvent !== true){
32198 this.fireEvent('check', this, i);
32204 Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32208 clearInvalid : function(){
32210 if(!this.el || this.preventMark){
32214 if(this.labelEl.isVisible(true)){
32215 this.indicatorEl().hide();
32218 this.el.removeClass([this.invalidClass]);
32220 this.fireEvent('valid', this);
32225 Roo.apply(Roo.bootstrap.RadioSet, {
32229 register : function(set)
32231 this.groups[set.name] = set;
32234 get: function(name)
32236 if (typeof(this.groups[name]) == 'undefined') {
32240 return this.groups[name] ;
32246 * Ext JS Library 1.1.1
32247 * Copyright(c) 2006-2007, Ext JS, LLC.
32249 * Originally Released Under LGPL - original licence link has changed is not relivant.
32252 * <script type="text/javascript">
32257 * @class Roo.bootstrap.SplitBar
32258 * @extends Roo.util.Observable
32259 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32263 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32264 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32265 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32266 split.minSize = 100;
32267 split.maxSize = 600;
32268 split.animate = true;
32269 split.on('moved', splitterMoved);
32272 * Create a new SplitBar
32273 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32274 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32275 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32276 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32277 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32278 position of the SplitBar).
32280 Roo.bootstrap.SplitBar = function(cfg){
32285 // dragElement : elm
32286 // resizingElement: el,
32288 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32289 // placement : Roo.bootstrap.SplitBar.LEFT ,
32290 // existingProxy ???
32293 this.el = Roo.get(cfg.dragElement, true);
32294 this.el.dom.unselectable = "on";
32296 this.resizingEl = Roo.get(cfg.resizingElement, true);
32300 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32301 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32304 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32307 * The minimum size of the resizing element. (Defaults to 0)
32313 * The maximum size of the resizing element. (Defaults to 2000)
32316 this.maxSize = 2000;
32319 * Whether to animate the transition to the new size
32322 this.animate = false;
32325 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32328 this.useShim = false;
32333 if(!cfg.existingProxy){
32335 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32337 this.proxy = Roo.get(cfg.existingProxy).dom;
32340 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32343 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32346 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32349 this.dragSpecs = {};
32352 * @private The adapter to use to positon and resize elements
32354 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32355 this.adapter.init(this);
32357 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32359 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32360 this.el.addClass("roo-splitbar-h");
32363 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32364 this.el.addClass("roo-splitbar-v");
32370 * Fires when the splitter is moved (alias for {@link #event-moved})
32371 * @param {Roo.bootstrap.SplitBar} this
32372 * @param {Number} newSize the new width or height
32377 * Fires when the splitter is moved
32378 * @param {Roo.bootstrap.SplitBar} this
32379 * @param {Number} newSize the new width or height
32383 * @event beforeresize
32384 * Fires before the splitter is dragged
32385 * @param {Roo.bootstrap.SplitBar} this
32387 "beforeresize" : true,
32389 "beforeapply" : true
32392 Roo.util.Observable.call(this);
32395 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32396 onStartProxyDrag : function(x, y){
32397 this.fireEvent("beforeresize", this);
32399 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32401 o.enableDisplayMode("block");
32402 // all splitbars share the same overlay
32403 Roo.bootstrap.SplitBar.prototype.overlay = o;
32405 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32406 this.overlay.show();
32407 Roo.get(this.proxy).setDisplayed("block");
32408 var size = this.adapter.getElementSize(this);
32409 this.activeMinSize = this.getMinimumSize();;
32410 this.activeMaxSize = this.getMaximumSize();;
32411 var c1 = size - this.activeMinSize;
32412 var c2 = Math.max(this.activeMaxSize - size, 0);
32413 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32414 this.dd.resetConstraints();
32415 this.dd.setXConstraint(
32416 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32417 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32419 this.dd.setYConstraint(0, 0);
32421 this.dd.resetConstraints();
32422 this.dd.setXConstraint(0, 0);
32423 this.dd.setYConstraint(
32424 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32425 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32428 this.dragSpecs.startSize = size;
32429 this.dragSpecs.startPoint = [x, y];
32430 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32434 * @private Called after the drag operation by the DDProxy
32436 onEndProxyDrag : function(e){
32437 Roo.get(this.proxy).setDisplayed(false);
32438 var endPoint = Roo.lib.Event.getXY(e);
32440 this.overlay.hide();
32443 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32444 newSize = this.dragSpecs.startSize +
32445 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32446 endPoint[0] - this.dragSpecs.startPoint[0] :
32447 this.dragSpecs.startPoint[0] - endPoint[0]
32450 newSize = this.dragSpecs.startSize +
32451 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32452 endPoint[1] - this.dragSpecs.startPoint[1] :
32453 this.dragSpecs.startPoint[1] - endPoint[1]
32456 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32457 if(newSize != this.dragSpecs.startSize){
32458 if(this.fireEvent('beforeapply', this, newSize) !== false){
32459 this.adapter.setElementSize(this, newSize);
32460 this.fireEvent("moved", this, newSize);
32461 this.fireEvent("resize", this, newSize);
32467 * Get the adapter this SplitBar uses
32468 * @return The adapter object
32470 getAdapter : function(){
32471 return this.adapter;
32475 * Set the adapter this SplitBar uses
32476 * @param {Object} adapter A SplitBar adapter object
32478 setAdapter : function(adapter){
32479 this.adapter = adapter;
32480 this.adapter.init(this);
32484 * Gets the minimum size for the resizing element
32485 * @return {Number} The minimum size
32487 getMinimumSize : function(){
32488 return this.minSize;
32492 * Sets the minimum size for the resizing element
32493 * @param {Number} minSize The minimum size
32495 setMinimumSize : function(minSize){
32496 this.minSize = minSize;
32500 * Gets the maximum size for the resizing element
32501 * @return {Number} The maximum size
32503 getMaximumSize : function(){
32504 return this.maxSize;
32508 * Sets the maximum size for the resizing element
32509 * @param {Number} maxSize The maximum size
32511 setMaximumSize : function(maxSize){
32512 this.maxSize = maxSize;
32516 * Sets the initialize size for the resizing element
32517 * @param {Number} size The initial size
32519 setCurrentSize : function(size){
32520 var oldAnimate = this.animate;
32521 this.animate = false;
32522 this.adapter.setElementSize(this, size);
32523 this.animate = oldAnimate;
32527 * Destroy this splitbar.
32528 * @param {Boolean} removeEl True to remove the element
32530 destroy : function(removeEl){
32532 this.shim.remove();
32535 this.proxy.parentNode.removeChild(this.proxy);
32543 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
32545 Roo.bootstrap.SplitBar.createProxy = function(dir){
32546 var proxy = new Roo.Element(document.createElement("div"));
32547 proxy.unselectable();
32548 var cls = 'roo-splitbar-proxy';
32549 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32550 document.body.appendChild(proxy.dom);
32555 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32556 * Default Adapter. It assumes the splitter and resizing element are not positioned
32557 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32559 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32562 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32563 // do nothing for now
32564 init : function(s){
32568 * Called before drag operations to get the current size of the resizing element.
32569 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32571 getElementSize : function(s){
32572 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32573 return s.resizingEl.getWidth();
32575 return s.resizingEl.getHeight();
32580 * Called after drag operations to set the size of the resizing element.
32581 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32582 * @param {Number} newSize The new size to set
32583 * @param {Function} onComplete A function to be invoked when resizing is complete
32585 setElementSize : function(s, newSize, onComplete){
32586 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32588 s.resizingEl.setWidth(newSize);
32590 onComplete(s, newSize);
32593 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32598 s.resizingEl.setHeight(newSize);
32600 onComplete(s, newSize);
32603 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32610 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32611 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32612 * Adapter that moves the splitter element to align with the resized sizing element.
32613 * Used with an absolute positioned SplitBar.
32614 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32615 * document.body, make sure you assign an id to the body element.
32617 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32618 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32619 this.container = Roo.get(container);
32622 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32623 init : function(s){
32624 this.basic.init(s);
32627 getElementSize : function(s){
32628 return this.basic.getElementSize(s);
32631 setElementSize : function(s, newSize, onComplete){
32632 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32635 moveSplitter : function(s){
32636 var yes = Roo.bootstrap.SplitBar;
32637 switch(s.placement){
32639 s.el.setX(s.resizingEl.getRight());
32642 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32645 s.el.setY(s.resizingEl.getBottom());
32648 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32655 * Orientation constant - Create a vertical SplitBar
32659 Roo.bootstrap.SplitBar.VERTICAL = 1;
32662 * Orientation constant - Create a horizontal SplitBar
32666 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32669 * Placement constant - The resizing element is to the left of the splitter element
32673 Roo.bootstrap.SplitBar.LEFT = 1;
32676 * Placement constant - The resizing element is to the right of the splitter element
32680 Roo.bootstrap.SplitBar.RIGHT = 2;
32683 * Placement constant - The resizing element is positioned above the splitter element
32687 Roo.bootstrap.SplitBar.TOP = 3;
32690 * Placement constant - The resizing element is positioned under splitter element
32694 Roo.bootstrap.SplitBar.BOTTOM = 4;
32695 Roo.namespace("Roo.bootstrap.layout");/*
32697 * Ext JS Library 1.1.1
32698 * Copyright(c) 2006-2007, Ext JS, LLC.
32700 * Originally Released Under LGPL - original licence link has changed is not relivant.
32703 * <script type="text/javascript">
32707 * @class Roo.bootstrap.layout.Manager
32708 * @extends Roo.bootstrap.Component
32709 * Base class for layout managers.
32711 Roo.bootstrap.layout.Manager = function(config)
32713 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32719 /** false to disable window resize monitoring @type Boolean */
32720 this.monitorWindowResize = true;
32725 * Fires when a layout is performed.
32726 * @param {Roo.LayoutManager} this
32730 * @event regionresized
32731 * Fires when the user resizes a region.
32732 * @param {Roo.LayoutRegion} region The resized region
32733 * @param {Number} newSize The new size (width for east/west, height for north/south)
32735 "regionresized" : true,
32737 * @event regioncollapsed
32738 * Fires when a region is collapsed.
32739 * @param {Roo.LayoutRegion} region The collapsed region
32741 "regioncollapsed" : true,
32743 * @event regionexpanded
32744 * Fires when a region is expanded.
32745 * @param {Roo.LayoutRegion} region The expanded region
32747 "regionexpanded" : true
32749 this.updating = false;
32752 this.el = Roo.get(config.el);
32758 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32763 monitorWindowResize : true,
32769 onRender : function(ct, position)
32772 this.el = Roo.get(ct);
32775 //this.fireEvent('render',this);
32779 initEvents: function()
32783 // ie scrollbar fix
32784 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32785 document.body.scroll = "no";
32786 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32787 this.el.position('relative');
32789 this.id = this.el.id;
32790 this.el.addClass("roo-layout-container");
32791 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32792 if(this.el.dom != document.body ) {
32793 this.el.on('resize', this.layout,this);
32794 this.el.on('show', this.layout,this);
32800 * Returns true if this layout is currently being updated
32801 * @return {Boolean}
32803 isUpdating : function(){
32804 return this.updating;
32808 * Suspend the LayoutManager from doing auto-layouts while
32809 * making multiple add or remove calls
32811 beginUpdate : function(){
32812 this.updating = true;
32816 * Restore auto-layouts and optionally disable the manager from performing a layout
32817 * @param {Boolean} noLayout true to disable a layout update
32819 endUpdate : function(noLayout){
32820 this.updating = false;
32826 layout: function(){
32830 onRegionResized : function(region, newSize){
32831 this.fireEvent("regionresized", region, newSize);
32835 onRegionCollapsed : function(region){
32836 this.fireEvent("regioncollapsed", region);
32839 onRegionExpanded : function(region){
32840 this.fireEvent("regionexpanded", region);
32844 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32845 * performs box-model adjustments.
32846 * @return {Object} The size as an object {width: (the width), height: (the height)}
32848 getViewSize : function()
32851 if(this.el.dom != document.body){
32852 size = this.el.getSize();
32854 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32856 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32857 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32862 * Returns the Element this layout is bound to.
32863 * @return {Roo.Element}
32865 getEl : function(){
32870 * Returns the specified region.
32871 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32872 * @return {Roo.LayoutRegion}
32874 getRegion : function(target){
32875 return this.regions[target.toLowerCase()];
32878 onWindowResize : function(){
32879 if(this.monitorWindowResize){
32886 * Ext JS Library 1.1.1
32887 * Copyright(c) 2006-2007, Ext JS, LLC.
32889 * Originally Released Under LGPL - original licence link has changed is not relivant.
32892 * <script type="text/javascript">
32895 * @class Roo.bootstrap.layout.Border
32896 * @extends Roo.bootstrap.layout.Manager
32897 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32898 * please see: examples/bootstrap/nested.html<br><br>
32900 <b>The container the layout is rendered into can be either the body element or any other element.
32901 If it is not the body element, the container needs to either be an absolute positioned element,
32902 or you will need to add "position:relative" to the css of the container. You will also need to specify
32903 the container size if it is not the body element.</b>
32906 * Create a new Border
32907 * @param {Object} config Configuration options
32909 Roo.bootstrap.layout.Border = function(config){
32910 config = config || {};
32911 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32915 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32916 if(config[region]){
32917 config[region].region = region;
32918 this.addRegion(config[region]);
32924 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32926 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32928 * Creates and adds a new region if it doesn't already exist.
32929 * @param {String} target The target region key (north, south, east, west or center).
32930 * @param {Object} config The regions config object
32931 * @return {BorderLayoutRegion} The new region
32933 addRegion : function(config)
32935 if(!this.regions[config.region]){
32936 var r = this.factory(config);
32937 this.bindRegion(r);
32939 return this.regions[config.region];
32943 bindRegion : function(r){
32944 this.regions[r.config.region] = r;
32946 r.on("visibilitychange", this.layout, this);
32947 r.on("paneladded", this.layout, this);
32948 r.on("panelremoved", this.layout, this);
32949 r.on("invalidated", this.layout, this);
32950 r.on("resized", this.onRegionResized, this);
32951 r.on("collapsed", this.onRegionCollapsed, this);
32952 r.on("expanded", this.onRegionExpanded, this);
32956 * Performs a layout update.
32958 layout : function()
32960 if(this.updating) {
32964 // render all the rebions if they have not been done alreayd?
32965 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32966 if(this.regions[region] && !this.regions[region].bodyEl){
32967 this.regions[region].onRender(this.el)
32971 var size = this.getViewSize();
32972 var w = size.width;
32973 var h = size.height;
32978 //var x = 0, y = 0;
32980 var rs = this.regions;
32981 var north = rs["north"];
32982 var south = rs["south"];
32983 var west = rs["west"];
32984 var east = rs["east"];
32985 var center = rs["center"];
32986 //if(this.hideOnLayout){ // not supported anymore
32987 //c.el.setStyle("display", "none");
32989 if(north && north.isVisible()){
32990 var b = north.getBox();
32991 var m = north.getMargins();
32992 b.width = w - (m.left+m.right);
32995 centerY = b.height + b.y + m.bottom;
32996 centerH -= centerY;
32997 north.updateBox(this.safeBox(b));
32999 if(south && south.isVisible()){
33000 var b = south.getBox();
33001 var m = south.getMargins();
33002 b.width = w - (m.left+m.right);
33004 var totalHeight = (b.height + m.top + m.bottom);
33005 b.y = h - totalHeight + m.top;
33006 centerH -= totalHeight;
33007 south.updateBox(this.safeBox(b));
33009 if(west && west.isVisible()){
33010 var b = west.getBox();
33011 var m = west.getMargins();
33012 b.height = centerH - (m.top+m.bottom);
33014 b.y = centerY + m.top;
33015 var totalWidth = (b.width + m.left + m.right);
33016 centerX += totalWidth;
33017 centerW -= totalWidth;
33018 west.updateBox(this.safeBox(b));
33020 if(east && east.isVisible()){
33021 var b = east.getBox();
33022 var m = east.getMargins();
33023 b.height = centerH - (m.top+m.bottom);
33024 var totalWidth = (b.width + m.left + m.right);
33025 b.x = w - totalWidth + m.left;
33026 b.y = centerY + m.top;
33027 centerW -= totalWidth;
33028 east.updateBox(this.safeBox(b));
33031 var m = center.getMargins();
33033 x: centerX + m.left,
33034 y: centerY + m.top,
33035 width: centerW - (m.left+m.right),
33036 height: centerH - (m.top+m.bottom)
33038 //if(this.hideOnLayout){
33039 //center.el.setStyle("display", "block");
33041 center.updateBox(this.safeBox(centerBox));
33044 this.fireEvent("layout", this);
33048 safeBox : function(box){
33049 box.width = Math.max(0, box.width);
33050 box.height = Math.max(0, box.height);
33055 * Adds a ContentPanel (or subclass) to this layout.
33056 * @param {String} target The target region key (north, south, east, west or center).
33057 * @param {Roo.ContentPanel} panel The panel to add
33058 * @return {Roo.ContentPanel} The added panel
33060 add : function(target, panel){
33062 target = target.toLowerCase();
33063 return this.regions[target].add(panel);
33067 * Remove a ContentPanel (or subclass) to this layout.
33068 * @param {String} target The target region key (north, south, east, west or center).
33069 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33070 * @return {Roo.ContentPanel} The removed panel
33072 remove : function(target, panel){
33073 target = target.toLowerCase();
33074 return this.regions[target].remove(panel);
33078 * Searches all regions for a panel with the specified id
33079 * @param {String} panelId
33080 * @return {Roo.ContentPanel} The panel or null if it wasn't found
33082 findPanel : function(panelId){
33083 var rs = this.regions;
33084 for(var target in rs){
33085 if(typeof rs[target] != "function"){
33086 var p = rs[target].getPanel(panelId);
33096 * Searches all regions for a panel with the specified id and activates (shows) it.
33097 * @param {String/ContentPanel} panelId The panels id or the panel itself
33098 * @return {Roo.ContentPanel} The shown panel or null
33100 showPanel : function(panelId) {
33101 var rs = this.regions;
33102 for(var target in rs){
33103 var r = rs[target];
33104 if(typeof r != "function"){
33105 if(r.hasPanel(panelId)){
33106 return r.showPanel(panelId);
33114 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33115 * @param {Roo.state.Provider} provider (optional) An alternate state provider
33118 restoreState : function(provider){
33120 provider = Roo.state.Manager;
33122 var sm = new Roo.LayoutStateManager();
33123 sm.init(this, provider);
33129 * Adds a xtype elements to the layout.
33133 xtype : 'ContentPanel',
33140 xtype : 'NestedLayoutPanel',
33146 items : [ ... list of content panels or nested layout panels.. ]
33150 * @param {Object} cfg Xtype definition of item to add.
33152 addxtype : function(cfg)
33154 // basically accepts a pannel...
33155 // can accept a layout region..!?!?
33156 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33159 // theory? children can only be panels??
33161 //if (!cfg.xtype.match(/Panel$/)) {
33166 if (typeof(cfg.region) == 'undefined') {
33167 Roo.log("Failed to add Panel, region was not set");
33171 var region = cfg.region;
33177 xitems = cfg.items;
33184 case 'Content': // ContentPanel (el, cfg)
33185 case 'Scroll': // ContentPanel (el, cfg)
33187 cfg.autoCreate = true;
33188 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33190 // var el = this.el.createChild();
33191 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33194 this.add(region, ret);
33198 case 'TreePanel': // our new panel!
33199 cfg.el = this.el.createChild();
33200 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33201 this.add(region, ret);
33206 // create a new Layout (which is a Border Layout...
33208 var clayout = cfg.layout;
33209 clayout.el = this.el.createChild();
33210 clayout.items = clayout.items || [];
33214 // replace this exitems with the clayout ones..
33215 xitems = clayout.items;
33217 // force background off if it's in center...
33218 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33219 cfg.background = false;
33221 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33224 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33225 //console.log('adding nested layout panel ' + cfg.toSource());
33226 this.add(region, ret);
33227 nb = {}; /// find first...
33232 // needs grid and region
33234 //var el = this.getRegion(region).el.createChild();
33236 *var el = this.el.createChild();
33237 // create the grid first...
33238 cfg.grid.container = el;
33239 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33242 if (region == 'center' && this.active ) {
33243 cfg.background = false;
33246 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33248 this.add(region, ret);
33250 if (cfg.background) {
33251 // render grid on panel activation (if panel background)
33252 ret.on('activate', function(gp) {
33253 if (!gp.grid.rendered) {
33254 // gp.grid.render(el);
33258 // cfg.grid.render(el);
33264 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33265 // it was the old xcomponent building that caused this before.
33266 // espeically if border is the top element in the tree.
33276 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33278 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33279 this.add(region, ret);
33283 throw "Can not add '" + cfg.xtype + "' to Border";
33289 this.beginUpdate();
33293 Roo.each(xitems, function(i) {
33294 region = nb && i.region ? i.region : false;
33296 var add = ret.addxtype(i);
33299 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33300 if (!i.background) {
33301 abn[region] = nb[region] ;
33308 // make the last non-background panel active..
33309 //if (nb) { Roo.log(abn); }
33312 for(var r in abn) {
33313 region = this.getRegion(r);
33315 // tried using nb[r], but it does not work..
33317 region.showPanel(abn[r]);
33328 factory : function(cfg)
33331 var validRegions = Roo.bootstrap.layout.Border.regions;
33333 var target = cfg.region;
33336 var r = Roo.bootstrap.layout;
33340 return new r.North(cfg);
33342 return new r.South(cfg);
33344 return new r.East(cfg);
33346 return new r.West(cfg);
33348 return new r.Center(cfg);
33350 throw 'Layout region "'+target+'" not supported.';
33357 * Ext JS Library 1.1.1
33358 * Copyright(c) 2006-2007, Ext JS, LLC.
33360 * Originally Released Under LGPL - original licence link has changed is not relivant.
33363 * <script type="text/javascript">
33367 * @class Roo.bootstrap.layout.Basic
33368 * @extends Roo.util.Observable
33369 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33370 * and does not have a titlebar, tabs or any other features. All it does is size and position
33371 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33372 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33373 * @cfg {string} region the region that it inhabits..
33374 * @cfg {bool} skipConfig skip config?
33378 Roo.bootstrap.layout.Basic = function(config){
33380 this.mgr = config.mgr;
33382 this.position = config.region;
33384 var skipConfig = config.skipConfig;
33388 * @scope Roo.BasicLayoutRegion
33392 * @event beforeremove
33393 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33394 * @param {Roo.LayoutRegion} this
33395 * @param {Roo.ContentPanel} panel The panel
33396 * @param {Object} e The cancel event object
33398 "beforeremove" : true,
33400 * @event invalidated
33401 * Fires when the layout for this region is changed.
33402 * @param {Roo.LayoutRegion} this
33404 "invalidated" : true,
33406 * @event visibilitychange
33407 * Fires when this region is shown or hidden
33408 * @param {Roo.LayoutRegion} this
33409 * @param {Boolean} visibility true or false
33411 "visibilitychange" : true,
33413 * @event paneladded
33414 * Fires when a panel is added.
33415 * @param {Roo.LayoutRegion} this
33416 * @param {Roo.ContentPanel} panel The panel
33418 "paneladded" : true,
33420 * @event panelremoved
33421 * Fires when a panel is removed.
33422 * @param {Roo.LayoutRegion} this
33423 * @param {Roo.ContentPanel} panel The panel
33425 "panelremoved" : true,
33427 * @event beforecollapse
33428 * Fires when this region before collapse.
33429 * @param {Roo.LayoutRegion} this
33431 "beforecollapse" : true,
33434 * Fires when this region is collapsed.
33435 * @param {Roo.LayoutRegion} this
33437 "collapsed" : true,
33440 * Fires when this region is expanded.
33441 * @param {Roo.LayoutRegion} this
33446 * Fires when this region is slid into view.
33447 * @param {Roo.LayoutRegion} this
33449 "slideshow" : true,
33452 * Fires when this region slides out of view.
33453 * @param {Roo.LayoutRegion} this
33455 "slidehide" : true,
33457 * @event panelactivated
33458 * Fires when a panel is activated.
33459 * @param {Roo.LayoutRegion} this
33460 * @param {Roo.ContentPanel} panel The activated panel
33462 "panelactivated" : true,
33465 * Fires when the user resizes this region.
33466 * @param {Roo.LayoutRegion} this
33467 * @param {Number} newSize The new size (width for east/west, height for north/south)
33471 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33472 this.panels = new Roo.util.MixedCollection();
33473 this.panels.getKey = this.getPanelId.createDelegate(this);
33475 this.activePanel = null;
33476 // ensure listeners are added...
33478 if (config.listeners || config.events) {
33479 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33480 listeners : config.listeners || {},
33481 events : config.events || {}
33485 if(skipConfig !== true){
33486 this.applyConfig(config);
33490 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33492 getPanelId : function(p){
33496 applyConfig : function(config){
33497 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33498 this.config = config;
33503 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33504 * the width, for horizontal (north, south) the height.
33505 * @param {Number} newSize The new width or height
33507 resizeTo : function(newSize){
33508 var el = this.el ? this.el :
33509 (this.activePanel ? this.activePanel.getEl() : null);
33511 switch(this.position){
33514 el.setWidth(newSize);
33515 this.fireEvent("resized", this, newSize);
33519 el.setHeight(newSize);
33520 this.fireEvent("resized", this, newSize);
33526 getBox : function(){
33527 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33530 getMargins : function(){
33531 return this.margins;
33534 updateBox : function(box){
33536 var el = this.activePanel.getEl();
33537 el.dom.style.left = box.x + "px";
33538 el.dom.style.top = box.y + "px";
33539 this.activePanel.setSize(box.width, box.height);
33543 * Returns the container element for this region.
33544 * @return {Roo.Element}
33546 getEl : function(){
33547 return this.activePanel;
33551 * Returns true if this region is currently visible.
33552 * @return {Boolean}
33554 isVisible : function(){
33555 return this.activePanel ? true : false;
33558 setActivePanel : function(panel){
33559 panel = this.getPanel(panel);
33560 if(this.activePanel && this.activePanel != panel){
33561 this.activePanel.setActiveState(false);
33562 this.activePanel.getEl().setLeftTop(-10000,-10000);
33564 this.activePanel = panel;
33565 panel.setActiveState(true);
33567 panel.setSize(this.box.width, this.box.height);
33569 this.fireEvent("panelactivated", this, panel);
33570 this.fireEvent("invalidated");
33574 * Show the specified panel.
33575 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33576 * @return {Roo.ContentPanel} The shown panel or null
33578 showPanel : function(panel){
33579 panel = this.getPanel(panel);
33581 this.setActivePanel(panel);
33587 * Get the active panel for this region.
33588 * @return {Roo.ContentPanel} The active panel or null
33590 getActivePanel : function(){
33591 return this.activePanel;
33595 * Add the passed ContentPanel(s)
33596 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33597 * @return {Roo.ContentPanel} The panel added (if only one was added)
33599 add : function(panel){
33600 if(arguments.length > 1){
33601 for(var i = 0, len = arguments.length; i < len; i++) {
33602 this.add(arguments[i]);
33606 if(this.hasPanel(panel)){
33607 this.showPanel(panel);
33610 var el = panel.getEl();
33611 if(el.dom.parentNode != this.mgr.el.dom){
33612 this.mgr.el.dom.appendChild(el.dom);
33614 if(panel.setRegion){
33615 panel.setRegion(this);
33617 this.panels.add(panel);
33618 el.setStyle("position", "absolute");
33619 if(!panel.background){
33620 this.setActivePanel(panel);
33621 if(this.config.initialSize && this.panels.getCount()==1){
33622 this.resizeTo(this.config.initialSize);
33625 this.fireEvent("paneladded", this, panel);
33630 * Returns true if the panel is in this region.
33631 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33632 * @return {Boolean}
33634 hasPanel : function(panel){
33635 if(typeof panel == "object"){ // must be panel obj
33636 panel = panel.getId();
33638 return this.getPanel(panel) ? true : false;
33642 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33643 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33644 * @param {Boolean} preservePanel Overrides the config preservePanel option
33645 * @return {Roo.ContentPanel} The panel that was removed
33647 remove : function(panel, preservePanel){
33648 panel = this.getPanel(panel);
33653 this.fireEvent("beforeremove", this, panel, e);
33654 if(e.cancel === true){
33657 var panelId = panel.getId();
33658 this.panels.removeKey(panelId);
33663 * Returns the panel specified or null if it's not in this region.
33664 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33665 * @return {Roo.ContentPanel}
33667 getPanel : function(id){
33668 if(typeof id == "object"){ // must be panel obj
33671 return this.panels.get(id);
33675 * Returns this regions position (north/south/east/west/center).
33678 getPosition: function(){
33679 return this.position;
33683 * Ext JS Library 1.1.1
33684 * Copyright(c) 2006-2007, Ext JS, LLC.
33686 * Originally Released Under LGPL - original licence link has changed is not relivant.
33689 * <script type="text/javascript">
33693 * @class Roo.bootstrap.layout.Region
33694 * @extends Roo.bootstrap.layout.Basic
33695 * This class represents a region in a layout manager.
33697 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33698 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
33699 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33700 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33701 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33702 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33703 * @cfg {String} title The title for the region (overrides panel titles)
33704 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33705 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33706 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33707 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33708 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33709 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33710 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33711 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33712 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33713 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33715 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33716 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33717 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33718 * @cfg {Number} width For East/West panels
33719 * @cfg {Number} height For North/South panels
33720 * @cfg {Boolean} split To show the splitter
33721 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33723 * @cfg {string} cls Extra CSS classes to add to region
33725 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33726 * @cfg {string} region the region that it inhabits..
33729 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33730 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33732 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33733 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33734 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33736 Roo.bootstrap.layout.Region = function(config)
33738 this.applyConfig(config);
33740 var mgr = config.mgr;
33741 var pos = config.region;
33742 config.skipConfig = true;
33743 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33746 this.onRender(mgr.el);
33749 this.visible = true;
33750 this.collapsed = false;
33751 this.unrendered_panels = [];
33754 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33756 position: '', // set by wrapper (eg. north/south etc..)
33757 unrendered_panels : null, // unrendered panels.
33758 createBody : function(){
33759 /** This region's body element
33760 * @type Roo.Element */
33761 this.bodyEl = this.el.createChild({
33763 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33767 onRender: function(ctr, pos)
33769 var dh = Roo.DomHelper;
33770 /** This region's container element
33771 * @type Roo.Element */
33772 this.el = dh.append(ctr.dom, {
33774 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33776 /** This region's title element
33777 * @type Roo.Element */
33779 this.titleEl = dh.append(this.el.dom,
33782 unselectable: "on",
33783 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33785 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33786 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33789 this.titleEl.enableDisplayMode();
33790 /** This region's title text element
33791 * @type HTMLElement */
33792 this.titleTextEl = this.titleEl.dom.firstChild;
33793 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33795 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33796 this.closeBtn.enableDisplayMode();
33797 this.closeBtn.on("click", this.closeClicked, this);
33798 this.closeBtn.hide();
33800 this.createBody(this.config);
33801 if(this.config.hideWhenEmpty){
33803 this.on("paneladded", this.validateVisibility, this);
33804 this.on("panelremoved", this.validateVisibility, this);
33806 if(this.autoScroll){
33807 this.bodyEl.setStyle("overflow", "auto");
33809 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33811 //if(c.titlebar !== false){
33812 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33813 this.titleEl.hide();
33815 this.titleEl.show();
33816 if(this.config.title){
33817 this.titleTextEl.innerHTML = this.config.title;
33821 if(this.config.collapsed){
33822 this.collapse(true);
33824 if(this.config.hidden){
33828 if (this.unrendered_panels && this.unrendered_panels.length) {
33829 for (var i =0;i< this.unrendered_panels.length; i++) {
33830 this.add(this.unrendered_panels[i]);
33832 this.unrendered_panels = null;
33838 applyConfig : function(c)
33841 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33842 var dh = Roo.DomHelper;
33843 if(c.titlebar !== false){
33844 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33845 this.collapseBtn.on("click", this.collapse, this);
33846 this.collapseBtn.enableDisplayMode();
33848 if(c.showPin === true || this.showPin){
33849 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33850 this.stickBtn.enableDisplayMode();
33851 this.stickBtn.on("click", this.expand, this);
33852 this.stickBtn.hide();
33857 /** This region's collapsed element
33858 * @type Roo.Element */
33861 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33862 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33865 if(c.floatable !== false){
33866 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33867 this.collapsedEl.on("click", this.collapseClick, this);
33870 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33871 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33872 id: "message", unselectable: "on", style:{"float":"left"}});
33873 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33875 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33876 this.expandBtn.on("click", this.expand, this);
33880 if(this.collapseBtn){
33881 this.collapseBtn.setVisible(c.collapsible == true);
33884 this.cmargins = c.cmargins || this.cmargins ||
33885 (this.position == "west" || this.position == "east" ?
33886 {top: 0, left: 2, right:2, bottom: 0} :
33887 {top: 2, left: 0, right:0, bottom: 2});
33889 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33892 this.bottomTabs = c.tabPosition != "top";
33894 this.autoScroll = c.autoScroll || false;
33899 this.duration = c.duration || .30;
33900 this.slideDuration = c.slideDuration || .45;
33905 * Returns true if this region is currently visible.
33906 * @return {Boolean}
33908 isVisible : function(){
33909 return this.visible;
33913 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33914 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33916 //setCollapsedTitle : function(title){
33917 // title = title || " ";
33918 // if(this.collapsedTitleTextEl){
33919 // this.collapsedTitleTextEl.innerHTML = title;
33923 getBox : function(){
33925 // if(!this.collapsed){
33926 b = this.el.getBox(false, true);
33928 // b = this.collapsedEl.getBox(false, true);
33933 getMargins : function(){
33934 return this.margins;
33935 //return this.collapsed ? this.cmargins : this.margins;
33938 highlight : function(){
33939 this.el.addClass("x-layout-panel-dragover");
33942 unhighlight : function(){
33943 this.el.removeClass("x-layout-panel-dragover");
33946 updateBox : function(box)
33948 if (!this.bodyEl) {
33949 return; // not rendered yet..
33953 if(!this.collapsed){
33954 this.el.dom.style.left = box.x + "px";
33955 this.el.dom.style.top = box.y + "px";
33956 this.updateBody(box.width, box.height);
33958 this.collapsedEl.dom.style.left = box.x + "px";
33959 this.collapsedEl.dom.style.top = box.y + "px";
33960 this.collapsedEl.setSize(box.width, box.height);
33963 this.tabs.autoSizeTabs();
33967 updateBody : function(w, h)
33970 this.el.setWidth(w);
33971 w -= this.el.getBorderWidth("rl");
33972 if(this.config.adjustments){
33973 w += this.config.adjustments[0];
33976 if(h !== null && h > 0){
33977 this.el.setHeight(h);
33978 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33979 h -= this.el.getBorderWidth("tb");
33980 if(this.config.adjustments){
33981 h += this.config.adjustments[1];
33983 this.bodyEl.setHeight(h);
33985 h = this.tabs.syncHeight(h);
33988 if(this.panelSize){
33989 w = w !== null ? w : this.panelSize.width;
33990 h = h !== null ? h : this.panelSize.height;
33992 if(this.activePanel){
33993 var el = this.activePanel.getEl();
33994 w = w !== null ? w : el.getWidth();
33995 h = h !== null ? h : el.getHeight();
33996 this.panelSize = {width: w, height: h};
33997 this.activePanel.setSize(w, h);
33999 if(Roo.isIE && this.tabs){
34000 this.tabs.el.repaint();
34005 * Returns the container element for this region.
34006 * @return {Roo.Element}
34008 getEl : function(){
34013 * Hides this region.
34016 //if(!this.collapsed){
34017 this.el.dom.style.left = "-2000px";
34020 // this.collapsedEl.dom.style.left = "-2000px";
34021 // this.collapsedEl.hide();
34023 this.visible = false;
34024 this.fireEvent("visibilitychange", this, false);
34028 * Shows this region if it was previously hidden.
34031 //if(!this.collapsed){
34034 // this.collapsedEl.show();
34036 this.visible = true;
34037 this.fireEvent("visibilitychange", this, true);
34040 closeClicked : function(){
34041 if(this.activePanel){
34042 this.remove(this.activePanel);
34046 collapseClick : function(e){
34048 e.stopPropagation();
34051 e.stopPropagation();
34057 * Collapses this region.
34058 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34061 collapse : function(skipAnim, skipCheck = false){
34062 if(this.collapsed) {
34066 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34068 this.collapsed = true;
34070 this.split.el.hide();
34072 if(this.config.animate && skipAnim !== true){
34073 this.fireEvent("invalidated", this);
34074 this.animateCollapse();
34076 this.el.setLocation(-20000,-20000);
34078 this.collapsedEl.show();
34079 this.fireEvent("collapsed", this);
34080 this.fireEvent("invalidated", this);
34086 animateCollapse : function(){
34091 * Expands this region if it was previously collapsed.
34092 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34093 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34096 expand : function(e, skipAnim){
34098 e.stopPropagation();
34100 if(!this.collapsed || this.el.hasActiveFx()) {
34104 this.afterSlideIn();
34107 this.collapsed = false;
34108 if(this.config.animate && skipAnim !== true){
34109 this.animateExpand();
34113 this.split.el.show();
34115 this.collapsedEl.setLocation(-2000,-2000);
34116 this.collapsedEl.hide();
34117 this.fireEvent("invalidated", this);
34118 this.fireEvent("expanded", this);
34122 animateExpand : function(){
34126 initTabs : function()
34128 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34130 var ts = new Roo.bootstrap.panel.Tabs({
34131 el: this.bodyEl.dom,
34132 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34133 disableTooltips: this.config.disableTabTips,
34134 toolbar : this.config.toolbar
34137 if(this.config.hideTabs){
34138 ts.stripWrap.setDisplayed(false);
34141 ts.resizeTabs = this.config.resizeTabs === true;
34142 ts.minTabWidth = this.config.minTabWidth || 40;
34143 ts.maxTabWidth = this.config.maxTabWidth || 250;
34144 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34145 ts.monitorResize = false;
34146 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34147 ts.bodyEl.addClass('roo-layout-tabs-body');
34148 this.panels.each(this.initPanelAsTab, this);
34151 initPanelAsTab : function(panel){
34152 var ti = this.tabs.addTab(
34156 this.config.closeOnTab && panel.isClosable(),
34159 if(panel.tabTip !== undefined){
34160 ti.setTooltip(panel.tabTip);
34162 ti.on("activate", function(){
34163 this.setActivePanel(panel);
34166 if(this.config.closeOnTab){
34167 ti.on("beforeclose", function(t, e){
34169 this.remove(panel);
34173 panel.tabItem = ti;
34178 updatePanelTitle : function(panel, title)
34180 if(this.activePanel == panel){
34181 this.updateTitle(title);
34184 var ti = this.tabs.getTab(panel.getEl().id);
34186 if(panel.tabTip !== undefined){
34187 ti.setTooltip(panel.tabTip);
34192 updateTitle : function(title){
34193 if(this.titleTextEl && !this.config.title){
34194 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
34198 setActivePanel : function(panel)
34200 panel = this.getPanel(panel);
34201 if(this.activePanel && this.activePanel != panel){
34202 this.activePanel.setActiveState(false);
34204 this.activePanel = panel;
34205 panel.setActiveState(true);
34206 if(this.panelSize){
34207 panel.setSize(this.panelSize.width, this.panelSize.height);
34210 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34212 this.updateTitle(panel.getTitle());
34214 this.fireEvent("invalidated", this);
34216 this.fireEvent("panelactivated", this, panel);
34220 * Shows the specified panel.
34221 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34222 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34224 showPanel : function(panel)
34226 panel = this.getPanel(panel);
34229 var tab = this.tabs.getTab(panel.getEl().id);
34230 if(tab.isHidden()){
34231 this.tabs.unhideTab(tab.id);
34235 this.setActivePanel(panel);
34242 * Get the active panel for this region.
34243 * @return {Roo.ContentPanel} The active panel or null
34245 getActivePanel : function(){
34246 return this.activePanel;
34249 validateVisibility : function(){
34250 if(this.panels.getCount() < 1){
34251 this.updateTitle(" ");
34252 this.closeBtn.hide();
34255 if(!this.isVisible()){
34262 * Adds the passed ContentPanel(s) to this region.
34263 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34264 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34266 add : function(panel)
34268 if(arguments.length > 1){
34269 for(var i = 0, len = arguments.length; i < len; i++) {
34270 this.add(arguments[i]);
34275 // if we have not been rendered yet, then we can not really do much of this..
34276 if (!this.bodyEl) {
34277 this.unrendered_panels.push(panel);
34284 if(this.hasPanel(panel)){
34285 this.showPanel(panel);
34288 panel.setRegion(this);
34289 this.panels.add(panel);
34290 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34291 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34292 // and hide them... ???
34293 this.bodyEl.dom.appendChild(panel.getEl().dom);
34294 if(panel.background !== true){
34295 this.setActivePanel(panel);
34297 this.fireEvent("paneladded", this, panel);
34304 this.initPanelAsTab(panel);
34308 if(panel.background !== true){
34309 this.tabs.activate(panel.getEl().id);
34311 this.fireEvent("paneladded", this, panel);
34316 * Hides the tab for the specified panel.
34317 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34319 hidePanel : function(panel){
34320 if(this.tabs && (panel = this.getPanel(panel))){
34321 this.tabs.hideTab(panel.getEl().id);
34326 * Unhides the tab for a previously hidden panel.
34327 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34329 unhidePanel : function(panel){
34330 if(this.tabs && (panel = this.getPanel(panel))){
34331 this.tabs.unhideTab(panel.getEl().id);
34335 clearPanels : function(){
34336 while(this.panels.getCount() > 0){
34337 this.remove(this.panels.first());
34342 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34343 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34344 * @param {Boolean} preservePanel Overrides the config preservePanel option
34345 * @return {Roo.ContentPanel} The panel that was removed
34347 remove : function(panel, preservePanel)
34349 panel = this.getPanel(panel);
34354 this.fireEvent("beforeremove", this, panel, e);
34355 if(e.cancel === true){
34358 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34359 var panelId = panel.getId();
34360 this.panels.removeKey(panelId);
34362 document.body.appendChild(panel.getEl().dom);
34365 this.tabs.removeTab(panel.getEl().id);
34366 }else if (!preservePanel){
34367 this.bodyEl.dom.removeChild(panel.getEl().dom);
34369 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34370 var p = this.panels.first();
34371 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34372 tempEl.appendChild(p.getEl().dom);
34373 this.bodyEl.update("");
34374 this.bodyEl.dom.appendChild(p.getEl().dom);
34376 this.updateTitle(p.getTitle());
34378 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34379 this.setActivePanel(p);
34381 panel.setRegion(null);
34382 if(this.activePanel == panel){
34383 this.activePanel = null;
34385 if(this.config.autoDestroy !== false && preservePanel !== true){
34386 try{panel.destroy();}catch(e){}
34388 this.fireEvent("panelremoved", this, panel);
34393 * Returns the TabPanel component used by this region
34394 * @return {Roo.TabPanel}
34396 getTabs : function(){
34400 createTool : function(parentEl, className){
34401 var btn = Roo.DomHelper.append(parentEl, {
34403 cls: "x-layout-tools-button",
34406 cls: "roo-layout-tools-button-inner " + className,
34410 btn.addClassOnOver("roo-layout-tools-button-over");
34415 * Ext JS Library 1.1.1
34416 * Copyright(c) 2006-2007, Ext JS, LLC.
34418 * Originally Released Under LGPL - original licence link has changed is not relivant.
34421 * <script type="text/javascript">
34427 * @class Roo.SplitLayoutRegion
34428 * @extends Roo.LayoutRegion
34429 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34431 Roo.bootstrap.layout.Split = function(config){
34432 this.cursor = config.cursor;
34433 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34436 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34438 splitTip : "Drag to resize.",
34439 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34440 useSplitTips : false,
34442 applyConfig : function(config){
34443 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34446 onRender : function(ctr,pos) {
34448 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34449 if(!this.config.split){
34454 var splitEl = Roo.DomHelper.append(ctr.dom, {
34456 id: this.el.id + "-split",
34457 cls: "roo-layout-split roo-layout-split-"+this.position,
34460 /** The SplitBar for this region
34461 * @type Roo.SplitBar */
34462 // does not exist yet...
34463 Roo.log([this.position, this.orientation]);
34465 this.split = new Roo.bootstrap.SplitBar({
34466 dragElement : splitEl,
34467 resizingElement: this.el,
34468 orientation : this.orientation
34471 this.split.on("moved", this.onSplitMove, this);
34472 this.split.useShim = this.config.useShim === true;
34473 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34474 if(this.useSplitTips){
34475 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34477 //if(config.collapsible){
34478 // this.split.el.on("dblclick", this.collapse, this);
34481 if(typeof this.config.minSize != "undefined"){
34482 this.split.minSize = this.config.minSize;
34484 if(typeof this.config.maxSize != "undefined"){
34485 this.split.maxSize = this.config.maxSize;
34487 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34488 this.hideSplitter();
34493 getHMaxSize : function(){
34494 var cmax = this.config.maxSize || 10000;
34495 var center = this.mgr.getRegion("center");
34496 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34499 getVMaxSize : function(){
34500 var cmax = this.config.maxSize || 10000;
34501 var center = this.mgr.getRegion("center");
34502 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34505 onSplitMove : function(split, newSize){
34506 this.fireEvent("resized", this, newSize);
34510 * Returns the {@link Roo.SplitBar} for this region.
34511 * @return {Roo.SplitBar}
34513 getSplitBar : function(){
34518 this.hideSplitter();
34519 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34522 hideSplitter : function(){
34524 this.split.el.setLocation(-2000,-2000);
34525 this.split.el.hide();
34531 this.split.el.show();
34533 Roo.bootstrap.layout.Split.superclass.show.call(this);
34536 beforeSlide: function(){
34537 if(Roo.isGecko){// firefox overflow auto bug workaround
34538 this.bodyEl.clip();
34540 this.tabs.bodyEl.clip();
34542 if(this.activePanel){
34543 this.activePanel.getEl().clip();
34545 if(this.activePanel.beforeSlide){
34546 this.activePanel.beforeSlide();
34552 afterSlide : function(){
34553 if(Roo.isGecko){// firefox overflow auto bug workaround
34554 this.bodyEl.unclip();
34556 this.tabs.bodyEl.unclip();
34558 if(this.activePanel){
34559 this.activePanel.getEl().unclip();
34560 if(this.activePanel.afterSlide){
34561 this.activePanel.afterSlide();
34567 initAutoHide : function(){
34568 if(this.autoHide !== false){
34569 if(!this.autoHideHd){
34570 var st = new Roo.util.DelayedTask(this.slideIn, this);
34571 this.autoHideHd = {
34572 "mouseout": function(e){
34573 if(!e.within(this.el, true)){
34577 "mouseover" : function(e){
34583 this.el.on(this.autoHideHd);
34587 clearAutoHide : function(){
34588 if(this.autoHide !== false){
34589 this.el.un("mouseout", this.autoHideHd.mouseout);
34590 this.el.un("mouseover", this.autoHideHd.mouseover);
34594 clearMonitor : function(){
34595 Roo.get(document).un("click", this.slideInIf, this);
34598 // these names are backwards but not changed for compat
34599 slideOut : function(){
34600 if(this.isSlid || this.el.hasActiveFx()){
34603 this.isSlid = true;
34604 if(this.collapseBtn){
34605 this.collapseBtn.hide();
34607 this.closeBtnState = this.closeBtn.getStyle('display');
34608 this.closeBtn.hide();
34610 this.stickBtn.show();
34613 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34614 this.beforeSlide();
34615 this.el.setStyle("z-index", 10001);
34616 this.el.slideIn(this.getSlideAnchor(), {
34617 callback: function(){
34619 this.initAutoHide();
34620 Roo.get(document).on("click", this.slideInIf, this);
34621 this.fireEvent("slideshow", this);
34628 afterSlideIn : function(){
34629 this.clearAutoHide();
34630 this.isSlid = false;
34631 this.clearMonitor();
34632 this.el.setStyle("z-index", "");
34633 if(this.collapseBtn){
34634 this.collapseBtn.show();
34636 this.closeBtn.setStyle('display', this.closeBtnState);
34638 this.stickBtn.hide();
34640 this.fireEvent("slidehide", this);
34643 slideIn : function(cb){
34644 if(!this.isSlid || this.el.hasActiveFx()){
34648 this.isSlid = false;
34649 this.beforeSlide();
34650 this.el.slideOut(this.getSlideAnchor(), {
34651 callback: function(){
34652 this.el.setLeftTop(-10000, -10000);
34654 this.afterSlideIn();
34662 slideInIf : function(e){
34663 if(!e.within(this.el)){
34668 animateCollapse : function(){
34669 this.beforeSlide();
34670 this.el.setStyle("z-index", 20000);
34671 var anchor = this.getSlideAnchor();
34672 this.el.slideOut(anchor, {
34673 callback : function(){
34674 this.el.setStyle("z-index", "");
34675 this.collapsedEl.slideIn(anchor, {duration:.3});
34677 this.el.setLocation(-10000,-10000);
34679 this.fireEvent("collapsed", this);
34686 animateExpand : function(){
34687 this.beforeSlide();
34688 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34689 this.el.setStyle("z-index", 20000);
34690 this.collapsedEl.hide({
34693 this.el.slideIn(this.getSlideAnchor(), {
34694 callback : function(){
34695 this.el.setStyle("z-index", "");
34698 this.split.el.show();
34700 this.fireEvent("invalidated", this);
34701 this.fireEvent("expanded", this);
34729 getAnchor : function(){
34730 return this.anchors[this.position];
34733 getCollapseAnchor : function(){
34734 return this.canchors[this.position];
34737 getSlideAnchor : function(){
34738 return this.sanchors[this.position];
34741 getAlignAdj : function(){
34742 var cm = this.cmargins;
34743 switch(this.position){
34759 getExpandAdj : function(){
34760 var c = this.collapsedEl, cm = this.cmargins;
34761 switch(this.position){
34763 return [-(cm.right+c.getWidth()+cm.left), 0];
34766 return [cm.right+c.getWidth()+cm.left, 0];
34769 return [0, -(cm.top+cm.bottom+c.getHeight())];
34772 return [0, cm.top+cm.bottom+c.getHeight()];
34778 * Ext JS Library 1.1.1
34779 * Copyright(c) 2006-2007, Ext JS, LLC.
34781 * Originally Released Under LGPL - original licence link has changed is not relivant.
34784 * <script type="text/javascript">
34787 * These classes are private internal classes
34789 Roo.bootstrap.layout.Center = function(config){
34790 config.region = "center";
34791 Roo.bootstrap.layout.Region.call(this, config);
34792 this.visible = true;
34793 this.minWidth = config.minWidth || 20;
34794 this.minHeight = config.minHeight || 20;
34797 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34799 // center panel can't be hidden
34803 // center panel can't be hidden
34806 getMinWidth: function(){
34807 return this.minWidth;
34810 getMinHeight: function(){
34811 return this.minHeight;
34824 Roo.bootstrap.layout.North = function(config)
34826 config.region = 'north';
34827 config.cursor = 'n-resize';
34829 Roo.bootstrap.layout.Split.call(this, config);
34833 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34834 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34835 this.split.el.addClass("roo-layout-split-v");
34837 var size = config.initialSize || config.height;
34838 if(typeof size != "undefined"){
34839 this.el.setHeight(size);
34842 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34844 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34848 getBox : function(){
34849 if(this.collapsed){
34850 return this.collapsedEl.getBox();
34852 var box = this.el.getBox();
34854 box.height += this.split.el.getHeight();
34859 updateBox : function(box){
34860 if(this.split && !this.collapsed){
34861 box.height -= this.split.el.getHeight();
34862 this.split.el.setLeft(box.x);
34863 this.split.el.setTop(box.y+box.height);
34864 this.split.el.setWidth(box.width);
34866 if(this.collapsed){
34867 this.updateBody(box.width, null);
34869 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34877 Roo.bootstrap.layout.South = function(config){
34878 config.region = 'south';
34879 config.cursor = 's-resize';
34880 Roo.bootstrap.layout.Split.call(this, config);
34882 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34883 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34884 this.split.el.addClass("roo-layout-split-v");
34886 var size = config.initialSize || config.height;
34887 if(typeof size != "undefined"){
34888 this.el.setHeight(size);
34892 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34893 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34894 getBox : function(){
34895 if(this.collapsed){
34896 return this.collapsedEl.getBox();
34898 var box = this.el.getBox();
34900 var sh = this.split.el.getHeight();
34907 updateBox : function(box){
34908 if(this.split && !this.collapsed){
34909 var sh = this.split.el.getHeight();
34912 this.split.el.setLeft(box.x);
34913 this.split.el.setTop(box.y-sh);
34914 this.split.el.setWidth(box.width);
34916 if(this.collapsed){
34917 this.updateBody(box.width, null);
34919 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34923 Roo.bootstrap.layout.East = function(config){
34924 config.region = "east";
34925 config.cursor = "e-resize";
34926 Roo.bootstrap.layout.Split.call(this, config);
34928 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34929 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34930 this.split.el.addClass("roo-layout-split-h");
34932 var size = config.initialSize || config.width;
34933 if(typeof size != "undefined"){
34934 this.el.setWidth(size);
34937 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34938 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34939 getBox : function(){
34940 if(this.collapsed){
34941 return this.collapsedEl.getBox();
34943 var box = this.el.getBox();
34945 var sw = this.split.el.getWidth();
34952 updateBox : function(box){
34953 if(this.split && !this.collapsed){
34954 var sw = this.split.el.getWidth();
34956 this.split.el.setLeft(box.x);
34957 this.split.el.setTop(box.y);
34958 this.split.el.setHeight(box.height);
34961 if(this.collapsed){
34962 this.updateBody(null, box.height);
34964 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34968 Roo.bootstrap.layout.West = function(config){
34969 config.region = "west";
34970 config.cursor = "w-resize";
34972 Roo.bootstrap.layout.Split.call(this, config);
34974 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34975 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34976 this.split.el.addClass("roo-layout-split-h");
34980 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34981 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34983 onRender: function(ctr, pos)
34985 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34986 var size = this.config.initialSize || this.config.width;
34987 if(typeof size != "undefined"){
34988 this.el.setWidth(size);
34992 getBox : function(){
34993 if(this.collapsed){
34994 return this.collapsedEl.getBox();
34996 var box = this.el.getBox();
34998 box.width += this.split.el.getWidth();
35003 updateBox : function(box){
35004 if(this.split && !this.collapsed){
35005 var sw = this.split.el.getWidth();
35007 this.split.el.setLeft(box.x+box.width);
35008 this.split.el.setTop(box.y);
35009 this.split.el.setHeight(box.height);
35011 if(this.collapsed){
35012 this.updateBody(null, box.height);
35014 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35017 Roo.namespace("Roo.bootstrap.panel");/*
35019 * Ext JS Library 1.1.1
35020 * Copyright(c) 2006-2007, Ext JS, LLC.
35022 * Originally Released Under LGPL - original licence link has changed is not relivant.
35025 * <script type="text/javascript">
35028 * @class Roo.ContentPanel
35029 * @extends Roo.util.Observable
35030 * A basic ContentPanel element.
35031 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
35032 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
35033 * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
35034 * @cfg {Boolean} closable True if the panel can be closed/removed
35035 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
35036 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35037 * @cfg {Toolbar} toolbar A toolbar for this panel
35038 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
35039 * @cfg {String} title The title for this panel
35040 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35041 * @cfg {String} url Calls {@link #setUrl} with this value
35042 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35043 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
35044 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
35045 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
35046 * @cfg {Boolean} badges render the badges
35049 * Create a new ContentPanel.
35050 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35051 * @param {String/Object} config A string to set only the title or a config object
35052 * @param {String} content (optional) Set the HTML content for this panel
35053 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35055 Roo.bootstrap.panel.Content = function( config){
35057 this.tpl = config.tpl || false;
35059 var el = config.el;
35060 var content = config.content;
35062 if(config.autoCreate){ // xtype is available if this is called from factory
35065 this.el = Roo.get(el);
35066 if(!this.el && config && config.autoCreate){
35067 if(typeof config.autoCreate == "object"){
35068 if(!config.autoCreate.id){
35069 config.autoCreate.id = config.id||el;
35071 this.el = Roo.DomHelper.append(document.body,
35072 config.autoCreate, true);
35074 var elcfg = { tag: "div",
35075 cls: "roo-layout-inactive-content",
35079 elcfg.html = config.html;
35083 this.el = Roo.DomHelper.append(document.body, elcfg , true);
35086 this.closable = false;
35087 this.loaded = false;
35088 this.active = false;
35091 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35093 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35095 this.wrapEl = this.el; //this.el.wrap();
35097 if (config.toolbar.items) {
35098 ti = config.toolbar.items ;
35099 delete config.toolbar.items ;
35103 this.toolbar.render(this.wrapEl, 'before');
35104 for(var i =0;i < ti.length;i++) {
35105 // Roo.log(['add child', items[i]]);
35106 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35108 this.toolbar.items = nitems;
35109 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35110 delete config.toolbar;
35114 // xtype created footer. - not sure if will work as we normally have to render first..
35115 if (this.footer && !this.footer.el && this.footer.xtype) {
35116 if (!this.wrapEl) {
35117 this.wrapEl = this.el.wrap();
35120 this.footer.container = this.wrapEl.createChild();
35122 this.footer = Roo.factory(this.footer, Roo);
35127 if(typeof config == "string"){
35128 this.title = config;
35130 Roo.apply(this, config);
35134 this.resizeEl = Roo.get(this.resizeEl, true);
35136 this.resizeEl = this.el;
35138 // handle view.xtype
35146 * Fires when this panel is activated.
35147 * @param {Roo.ContentPanel} this
35151 * @event deactivate
35152 * Fires when this panel is activated.
35153 * @param {Roo.ContentPanel} this
35155 "deactivate" : true,
35159 * Fires when this panel is resized if fitToFrame is true.
35160 * @param {Roo.ContentPanel} this
35161 * @param {Number} width The width after any component adjustments
35162 * @param {Number} height The height after any component adjustments
35168 * Fires when this tab is created
35169 * @param {Roo.ContentPanel} this
35180 if(this.autoScroll){
35181 this.resizeEl.setStyle("overflow", "auto");
35183 // fix randome scrolling
35184 //this.el.on('scroll', function() {
35185 // Roo.log('fix random scolling');
35186 // this.scrollTo('top',0);
35189 content = content || this.content;
35191 this.setContent(content);
35193 if(config && config.url){
35194 this.setUrl(this.url, this.params, this.loadOnce);
35199 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35201 if (this.view && typeof(this.view.xtype) != 'undefined') {
35202 this.view.el = this.el.appendChild(document.createElement("div"));
35203 this.view = Roo.factory(this.view);
35204 this.view.render && this.view.render(false, '');
35208 this.fireEvent('render', this);
35211 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35215 setRegion : function(region){
35216 this.region = region;
35217 this.setActiveClass(region && !this.background);
35221 setActiveClass: function(state)
35224 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35225 this.el.setStyle('position','relative');
35227 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35228 this.el.setStyle('position', 'absolute');
35233 * Returns the toolbar for this Panel if one was configured.
35234 * @return {Roo.Toolbar}
35236 getToolbar : function(){
35237 return this.toolbar;
35240 setActiveState : function(active)
35242 this.active = active;
35243 this.setActiveClass(active);
35245 this.fireEvent("deactivate", this);
35247 this.fireEvent("activate", this);
35251 * Updates this panel's element
35252 * @param {String} content The new content
35253 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35255 setContent : function(content, loadScripts){
35256 this.el.update(content, loadScripts);
35259 ignoreResize : function(w, h){
35260 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35263 this.lastSize = {width: w, height: h};
35268 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35269 * @return {Roo.UpdateManager} The UpdateManager
35271 getUpdateManager : function(){
35272 return this.el.getUpdateManager();
35275 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35276 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
35279 url: "your-url.php",
35280 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35281 callback: yourFunction,
35282 scope: yourObject, //(optional scope)
35285 text: "Loading...",
35290 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35291 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
35292 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
35293 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35294 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
35295 * @return {Roo.ContentPanel} this
35298 var um = this.el.getUpdateManager();
35299 um.update.apply(um, arguments);
35305 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
35306 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35307 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
35308 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
35309 * @return {Roo.UpdateManager} The UpdateManager
35311 setUrl : function(url, params, loadOnce){
35312 if(this.refreshDelegate){
35313 this.removeListener("activate", this.refreshDelegate);
35315 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35316 this.on("activate", this.refreshDelegate);
35317 return this.el.getUpdateManager();
35320 _handleRefresh : function(url, params, loadOnce){
35321 if(!loadOnce || !this.loaded){
35322 var updater = this.el.getUpdateManager();
35323 updater.update(url, params, this._setLoaded.createDelegate(this));
35327 _setLoaded : function(){
35328 this.loaded = true;
35332 * Returns this panel's id
35335 getId : function(){
35340 * Returns this panel's element - used by regiosn to add.
35341 * @return {Roo.Element}
35343 getEl : function(){
35344 return this.wrapEl || this.el;
35349 adjustForComponents : function(width, height)
35351 //Roo.log('adjustForComponents ');
35352 if(this.resizeEl != this.el){
35353 width -= this.el.getFrameWidth('lr');
35354 height -= this.el.getFrameWidth('tb');
35357 var te = this.toolbar.getEl();
35358 height -= te.getHeight();
35359 te.setWidth(width);
35362 var te = this.footer.getEl();
35363 Roo.log("footer:" + te.getHeight());
35365 height -= te.getHeight();
35366 te.setWidth(width);
35370 if(this.adjustments){
35371 width += this.adjustments[0];
35372 height += this.adjustments[1];
35374 return {"width": width, "height": height};
35377 setSize : function(width, height){
35378 if(this.fitToFrame && !this.ignoreResize(width, height)){
35379 if(this.fitContainer && this.resizeEl != this.el){
35380 this.el.setSize(width, height);
35382 var size = this.adjustForComponents(width, height);
35383 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35384 this.fireEvent('resize', this, size.width, size.height);
35389 * Returns this panel's title
35392 getTitle : function(){
35397 * Set this panel's title
35398 * @param {String} title
35400 setTitle : function(title){
35401 this.title = title;
35403 this.region.updatePanelTitle(this, title);
35408 * Returns true is this panel was configured to be closable
35409 * @return {Boolean}
35411 isClosable : function(){
35412 return this.closable;
35415 beforeSlide : function(){
35417 this.resizeEl.clip();
35420 afterSlide : function(){
35422 this.resizeEl.unclip();
35426 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35427 * Will fail silently if the {@link #setUrl} method has not been called.
35428 * This does not activate the panel, just updates its content.
35430 refresh : function(){
35431 if(this.refreshDelegate){
35432 this.loaded = false;
35433 this.refreshDelegate();
35438 * Destroys this panel
35440 destroy : function(){
35441 this.el.removeAllListeners();
35442 var tempEl = document.createElement("span");
35443 tempEl.appendChild(this.el.dom);
35444 tempEl.innerHTML = "";
35450 * form - if the content panel contains a form - this is a reference to it.
35451 * @type {Roo.form.Form}
35455 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35456 * This contains a reference to it.
35462 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35472 * @param {Object} cfg Xtype definition of item to add.
35476 getChildContainer: function () {
35477 return this.getEl();
35482 var ret = new Roo.factory(cfg);
35487 if (cfg.xtype.match(/^Form$/)) {
35490 //if (this.footer) {
35491 // el = this.footer.container.insertSibling(false, 'before');
35493 el = this.el.createChild();
35496 this.form = new Roo.form.Form(cfg);
35499 if ( this.form.allItems.length) {
35500 this.form.render(el.dom);
35504 // should only have one of theses..
35505 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35506 // views.. should not be just added - used named prop 'view''
35508 cfg.el = this.el.appendChild(document.createElement("div"));
35511 var ret = new Roo.factory(cfg);
35513 ret.render && ret.render(false, ''); // render blank..
35523 * @class Roo.bootstrap.panel.Grid
35524 * @extends Roo.bootstrap.panel.Content
35526 * Create a new GridPanel.
35527 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35528 * @param {Object} config A the config object
35534 Roo.bootstrap.panel.Grid = function(config)
35538 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35539 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35541 config.el = this.wrapper;
35542 //this.el = this.wrapper;
35544 if (config.container) {
35545 // ctor'ed from a Border/panel.grid
35548 this.wrapper.setStyle("overflow", "hidden");
35549 this.wrapper.addClass('roo-grid-container');
35554 if(config.toolbar){
35555 var tool_el = this.wrapper.createChild();
35556 this.toolbar = Roo.factory(config.toolbar);
35558 if (config.toolbar.items) {
35559 ti = config.toolbar.items ;
35560 delete config.toolbar.items ;
35564 this.toolbar.render(tool_el);
35565 for(var i =0;i < ti.length;i++) {
35566 // Roo.log(['add child', items[i]]);
35567 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35569 this.toolbar.items = nitems;
35571 delete config.toolbar;
35574 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35575 config.grid.scrollBody = true;;
35576 config.grid.monitorWindowResize = false; // turn off autosizing
35577 config.grid.autoHeight = false;
35578 config.grid.autoWidth = false;
35580 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35582 if (config.background) {
35583 // render grid on panel activation (if panel background)
35584 this.on('activate', function(gp) {
35585 if (!gp.grid.rendered) {
35586 gp.grid.render(this.wrapper);
35587 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35592 this.grid.render(this.wrapper);
35593 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35596 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35597 // ??? needed ??? config.el = this.wrapper;
35602 // xtype created footer. - not sure if will work as we normally have to render first..
35603 if (this.footer && !this.footer.el && this.footer.xtype) {
35605 var ctr = this.grid.getView().getFooterPanel(true);
35606 this.footer.dataSource = this.grid.dataSource;
35607 this.footer = Roo.factory(this.footer, Roo);
35608 this.footer.render(ctr);
35618 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35619 getId : function(){
35620 return this.grid.id;
35624 * Returns the grid for this panel
35625 * @return {Roo.bootstrap.Table}
35627 getGrid : function(){
35631 setSize : function(width, height){
35632 if(!this.ignoreResize(width, height)){
35633 var grid = this.grid;
35634 var size = this.adjustForComponents(width, height);
35635 var gridel = grid.getGridEl();
35636 gridel.setSize(size.width, size.height);
35638 var thd = grid.getGridEl().select('thead',true).first();
35639 var tbd = grid.getGridEl().select('tbody', true).first();
35641 tbd.setSize(width, height - thd.getHeight());
35650 beforeSlide : function(){
35651 this.grid.getView().scroller.clip();
35654 afterSlide : function(){
35655 this.grid.getView().scroller.unclip();
35658 destroy : function(){
35659 this.grid.destroy();
35661 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35666 * @class Roo.bootstrap.panel.Nest
35667 * @extends Roo.bootstrap.panel.Content
35669 * Create a new Panel, that can contain a layout.Border.
35672 * @param {Roo.BorderLayout} layout The layout for this panel
35673 * @param {String/Object} config A string to set only the title or a config object
35675 Roo.bootstrap.panel.Nest = function(config)
35677 // construct with only one argument..
35678 /* FIXME - implement nicer consturctors
35679 if (layout.layout) {
35681 layout = config.layout;
35682 delete config.layout;
35684 if (layout.xtype && !layout.getEl) {
35685 // then layout needs constructing..
35686 layout = Roo.factory(layout, Roo);
35690 config.el = config.layout.getEl();
35692 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35694 config.layout.monitorWindowResize = false; // turn off autosizing
35695 this.layout = config.layout;
35696 this.layout.getEl().addClass("roo-layout-nested-layout");
35703 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35705 setSize : function(width, height){
35706 if(!this.ignoreResize(width, height)){
35707 var size = this.adjustForComponents(width, height);
35708 var el = this.layout.getEl();
35709 if (size.height < 1) {
35710 el.setWidth(size.width);
35712 el.setSize(size.width, size.height);
35714 var touch = el.dom.offsetWidth;
35715 this.layout.layout();
35716 // ie requires a double layout on the first pass
35717 if(Roo.isIE && !this.initialized){
35718 this.initialized = true;
35719 this.layout.layout();
35724 // activate all subpanels if not currently active..
35726 setActiveState : function(active){
35727 this.active = active;
35728 this.setActiveClass(active);
35731 this.fireEvent("deactivate", this);
35735 this.fireEvent("activate", this);
35736 // not sure if this should happen before or after..
35737 if (!this.layout) {
35738 return; // should not happen..
35741 for (var r in this.layout.regions) {
35742 reg = this.layout.getRegion(r);
35743 if (reg.getActivePanel()) {
35744 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35745 reg.setActivePanel(reg.getActivePanel());
35748 if (!reg.panels.length) {
35751 reg.showPanel(reg.getPanel(0));
35760 * Returns the nested BorderLayout for this panel
35761 * @return {Roo.BorderLayout}
35763 getLayout : function(){
35764 return this.layout;
35768 * Adds a xtype elements to the layout of the nested panel
35772 xtype : 'ContentPanel',
35779 xtype : 'NestedLayoutPanel',
35785 items : [ ... list of content panels or nested layout panels.. ]
35789 * @param {Object} cfg Xtype definition of item to add.
35791 addxtype : function(cfg) {
35792 return this.layout.addxtype(cfg);
35797 * Ext JS Library 1.1.1
35798 * Copyright(c) 2006-2007, Ext JS, LLC.
35800 * Originally Released Under LGPL - original licence link has changed is not relivant.
35803 * <script type="text/javascript">
35806 * @class Roo.TabPanel
35807 * @extends Roo.util.Observable
35808 * A lightweight tab container.
35812 // basic tabs 1, built from existing content
35813 var tabs = new Roo.TabPanel("tabs1");
35814 tabs.addTab("script", "View Script");
35815 tabs.addTab("markup", "View Markup");
35816 tabs.activate("script");
35818 // more advanced tabs, built from javascript
35819 var jtabs = new Roo.TabPanel("jtabs");
35820 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35822 // set up the UpdateManager
35823 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35824 var updater = tab2.getUpdateManager();
35825 updater.setDefaultUrl("ajax1.htm");
35826 tab2.on('activate', updater.refresh, updater, true);
35828 // Use setUrl for Ajax loading
35829 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35830 tab3.setUrl("ajax2.htm", null, true);
35833 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35836 jtabs.activate("jtabs-1");
35839 * Create a new TabPanel.
35840 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35841 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35843 Roo.bootstrap.panel.Tabs = function(config){
35845 * The container element for this TabPanel.
35846 * @type Roo.Element
35848 this.el = Roo.get(config.el);
35851 if(typeof config == "boolean"){
35852 this.tabPosition = config ? "bottom" : "top";
35854 Roo.apply(this, config);
35858 if(this.tabPosition == "bottom"){
35859 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35860 this.el.addClass("roo-tabs-bottom");
35862 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35863 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35864 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35866 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35868 if(this.tabPosition != "bottom"){
35869 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35870 * @type Roo.Element
35872 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35873 this.el.addClass("roo-tabs-top");
35877 this.bodyEl.setStyle("position", "relative");
35879 this.active = null;
35880 this.activateDelegate = this.activate.createDelegate(this);
35885 * Fires when the active tab changes
35886 * @param {Roo.TabPanel} this
35887 * @param {Roo.TabPanelItem} activePanel The new active tab
35891 * @event beforetabchange
35892 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35893 * @param {Roo.TabPanel} this
35894 * @param {Object} e Set cancel to true on this object to cancel the tab change
35895 * @param {Roo.TabPanelItem} tab The tab being changed to
35897 "beforetabchange" : true
35900 Roo.EventManager.onWindowResize(this.onResize, this);
35901 this.cpad = this.el.getPadding("lr");
35902 this.hiddenCount = 0;
35905 // toolbar on the tabbar support...
35906 if (this.toolbar) {
35907 alert("no toolbar support yet");
35908 this.toolbar = false;
35910 var tcfg = this.toolbar;
35911 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35912 this.toolbar = new Roo.Toolbar(tcfg);
35913 if (Roo.isSafari) {
35914 var tbl = tcfg.container.child('table', true);
35915 tbl.setAttribute('width', '100%');
35923 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35926 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35928 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35930 tabPosition : "top",
35932 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35934 currentTabWidth : 0,
35936 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35940 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35944 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35946 preferredTabWidth : 175,
35948 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35950 resizeTabs : false,
35952 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35954 monitorResize : true,
35956 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35961 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35962 * @param {String} id The id of the div to use <b>or create</b>
35963 * @param {String} text The text for the tab
35964 * @param {String} content (optional) Content to put in the TabPanelItem body
35965 * @param {Boolean} closable (optional) True to create a close icon on the tab
35966 * @return {Roo.TabPanelItem} The created TabPanelItem
35968 addTab : function(id, text, content, closable, tpl)
35970 var item = new Roo.bootstrap.panel.TabItem({
35974 closable : closable,
35977 this.addTabItem(item);
35979 item.setContent(content);
35985 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35986 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35987 * @return {Roo.TabPanelItem}
35989 getTab : function(id){
35990 return this.items[id];
35994 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35995 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35997 hideTab : function(id){
35998 var t = this.items[id];
36001 this.hiddenCount++;
36002 this.autoSizeTabs();
36007 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36008 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36010 unhideTab : function(id){
36011 var t = this.items[id];
36013 t.setHidden(false);
36014 this.hiddenCount--;
36015 this.autoSizeTabs();
36020 * Adds an existing {@link Roo.TabPanelItem}.
36021 * @param {Roo.TabPanelItem} item The TabPanelItem to add
36023 addTabItem : function(item){
36024 this.items[item.id] = item;
36025 this.items.push(item);
36026 // if(this.resizeTabs){
36027 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36028 // this.autoSizeTabs();
36030 // item.autoSize();
36035 * Removes a {@link Roo.TabPanelItem}.
36036 * @param {String/Number} id The id or index of the TabPanelItem to remove.
36038 removeTab : function(id){
36039 var items = this.items;
36040 var tab = items[id];
36041 if(!tab) { return; }
36042 var index = items.indexOf(tab);
36043 if(this.active == tab && items.length > 1){
36044 var newTab = this.getNextAvailable(index);
36049 this.stripEl.dom.removeChild(tab.pnode.dom);
36050 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36051 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36053 items.splice(index, 1);
36054 delete this.items[tab.id];
36055 tab.fireEvent("close", tab);
36056 tab.purgeListeners();
36057 this.autoSizeTabs();
36060 getNextAvailable : function(start){
36061 var items = this.items;
36063 // look for a next tab that will slide over to
36064 // replace the one being removed
36065 while(index < items.length){
36066 var item = items[++index];
36067 if(item && !item.isHidden()){
36071 // if one isn't found select the previous tab (on the left)
36074 var item = items[--index];
36075 if(item && !item.isHidden()){
36083 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36084 * @param {String/Number} id The id or index of the TabPanelItem to disable.
36086 disableTab : function(id){
36087 var tab = this.items[id];
36088 if(tab && this.active != tab){
36094 * Enables a {@link Roo.TabPanelItem} that is disabled.
36095 * @param {String/Number} id The id or index of the TabPanelItem to enable.
36097 enableTab : function(id){
36098 var tab = this.items[id];
36103 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36104 * @param {String/Number} id The id or index of the TabPanelItem to activate.
36105 * @return {Roo.TabPanelItem} The TabPanelItem.
36107 activate : function(id){
36108 var tab = this.items[id];
36112 if(tab == this.active || tab.disabled){
36116 this.fireEvent("beforetabchange", this, e, tab);
36117 if(e.cancel !== true && !tab.disabled){
36119 this.active.hide();
36121 this.active = this.items[id];
36122 this.active.show();
36123 this.fireEvent("tabchange", this, this.active);
36129 * Gets the active {@link Roo.TabPanelItem}.
36130 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36132 getActiveTab : function(){
36133 return this.active;
36137 * Updates the tab body element to fit the height of the container element
36138 * for overflow scrolling
36139 * @param {Number} targetHeight (optional) Override the starting height from the elements height
36141 syncHeight : function(targetHeight){
36142 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36143 var bm = this.bodyEl.getMargins();
36144 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36145 this.bodyEl.setHeight(newHeight);
36149 onResize : function(){
36150 if(this.monitorResize){
36151 this.autoSizeTabs();
36156 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36158 beginUpdate : function(){
36159 this.updating = true;
36163 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36165 endUpdate : function(){
36166 this.updating = false;
36167 this.autoSizeTabs();
36171 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36173 autoSizeTabs : function(){
36174 var count = this.items.length;
36175 var vcount = count - this.hiddenCount;
36176 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36179 var w = Math.max(this.el.getWidth() - this.cpad, 10);
36180 var availWidth = Math.floor(w / vcount);
36181 var b = this.stripBody;
36182 if(b.getWidth() > w){
36183 var tabs = this.items;
36184 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36185 if(availWidth < this.minTabWidth){
36186 /*if(!this.sleft){ // incomplete scrolling code
36187 this.createScrollButtons();
36190 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36193 if(this.currentTabWidth < this.preferredTabWidth){
36194 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36200 * Returns the number of tabs in this TabPanel.
36203 getCount : function(){
36204 return this.items.length;
36208 * Resizes all the tabs to the passed width
36209 * @param {Number} The new width
36211 setTabWidth : function(width){
36212 this.currentTabWidth = width;
36213 for(var i = 0, len = this.items.length; i < len; i++) {
36214 if(!this.items[i].isHidden()) {
36215 this.items[i].setWidth(width);
36221 * Destroys this TabPanel
36222 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36224 destroy : function(removeEl){
36225 Roo.EventManager.removeResizeListener(this.onResize, this);
36226 for(var i = 0, len = this.items.length; i < len; i++){
36227 this.items[i].purgeListeners();
36229 if(removeEl === true){
36230 this.el.update("");
36235 createStrip : function(container)
36237 var strip = document.createElement("nav");
36238 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36239 container.appendChild(strip);
36243 createStripList : function(strip)
36245 // div wrapper for retard IE
36246 // returns the "tr" element.
36247 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36248 //'<div class="x-tabs-strip-wrap">'+
36249 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36250 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36251 return strip.firstChild; //.firstChild.firstChild.firstChild;
36253 createBody : function(container)
36255 var body = document.createElement("div");
36256 Roo.id(body, "tab-body");
36257 //Roo.fly(body).addClass("x-tabs-body");
36258 Roo.fly(body).addClass("tab-content");
36259 container.appendChild(body);
36262 createItemBody :function(bodyEl, id){
36263 var body = Roo.getDom(id);
36265 body = document.createElement("div");
36268 //Roo.fly(body).addClass("x-tabs-item-body");
36269 Roo.fly(body).addClass("tab-pane");
36270 bodyEl.insertBefore(body, bodyEl.firstChild);
36274 createStripElements : function(stripEl, text, closable, tpl)
36276 var td = document.createElement("li"); // was td..
36279 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36282 stripEl.appendChild(td);
36284 td.className = "x-tabs-closable";
36285 if(!this.closeTpl){
36286 this.closeTpl = new Roo.Template(
36287 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36288 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36289 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36292 var el = this.closeTpl.overwrite(td, {"text": text});
36293 var close = el.getElementsByTagName("div")[0];
36294 var inner = el.getElementsByTagName("em")[0];
36295 return {"el": el, "close": close, "inner": inner};
36298 // not sure what this is..
36299 // if(!this.tabTpl){
36300 //this.tabTpl = new Roo.Template(
36301 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36302 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36304 // this.tabTpl = new Roo.Template(
36305 // '<a href="#">' +
36306 // '<span unselectable="on"' +
36307 // (this.disableTooltips ? '' : ' title="{text}"') +
36308 // ' >{text}</span></a>'
36314 var template = tpl || this.tabTpl || false;
36318 template = new Roo.Template(
36320 '<span unselectable="on"' +
36321 (this.disableTooltips ? '' : ' title="{text}"') +
36322 ' >{text}</span></a>'
36326 switch (typeof(template)) {
36330 template = new Roo.Template(template);
36336 var el = template.overwrite(td, {"text": text});
36338 var inner = el.getElementsByTagName("span")[0];
36340 return {"el": el, "inner": inner};
36348 * @class Roo.TabPanelItem
36349 * @extends Roo.util.Observable
36350 * Represents an individual item (tab plus body) in a TabPanel.
36351 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36352 * @param {String} id The id of this TabPanelItem
36353 * @param {String} text The text for the tab of this TabPanelItem
36354 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36356 Roo.bootstrap.panel.TabItem = function(config){
36358 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36359 * @type Roo.TabPanel
36361 this.tabPanel = config.panel;
36363 * The id for this TabPanelItem
36366 this.id = config.id;
36368 this.disabled = false;
36370 this.text = config.text;
36372 this.loaded = false;
36373 this.closable = config.closable;
36376 * The body element for this TabPanelItem.
36377 * @type Roo.Element
36379 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36380 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36381 this.bodyEl.setStyle("display", "block");
36382 this.bodyEl.setStyle("zoom", "1");
36383 //this.hideAction();
36385 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36387 this.el = Roo.get(els.el);
36388 this.inner = Roo.get(els.inner, true);
36389 this.textEl = Roo.get(this.el.dom.firstChild, true);
36390 this.pnode = Roo.get(els.el.parentNode, true);
36391 this.el.on("mousedown", this.onTabMouseDown, this);
36392 this.el.on("click", this.onTabClick, this);
36394 if(config.closable){
36395 var c = Roo.get(els.close, true);
36396 c.dom.title = this.closeText;
36397 c.addClassOnOver("close-over");
36398 c.on("click", this.closeClick, this);
36404 * Fires when this tab becomes the active tab.
36405 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36406 * @param {Roo.TabPanelItem} this
36410 * @event beforeclose
36411 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36412 * @param {Roo.TabPanelItem} this
36413 * @param {Object} e Set cancel to true on this object to cancel the close.
36415 "beforeclose": true,
36418 * Fires when this tab is closed.
36419 * @param {Roo.TabPanelItem} this
36423 * @event deactivate
36424 * Fires when this tab is no longer the active tab.
36425 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36426 * @param {Roo.TabPanelItem} this
36428 "deactivate" : true
36430 this.hidden = false;
36432 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36435 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36437 purgeListeners : function(){
36438 Roo.util.Observable.prototype.purgeListeners.call(this);
36439 this.el.removeAllListeners();
36442 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36445 this.pnode.addClass("active");
36448 this.tabPanel.stripWrap.repaint();
36450 this.fireEvent("activate", this.tabPanel, this);
36454 * Returns true if this tab is the active tab.
36455 * @return {Boolean}
36457 isActive : function(){
36458 return this.tabPanel.getActiveTab() == this;
36462 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36465 this.pnode.removeClass("active");
36467 this.fireEvent("deactivate", this.tabPanel, this);
36470 hideAction : function(){
36471 this.bodyEl.hide();
36472 this.bodyEl.setStyle("position", "absolute");
36473 this.bodyEl.setLeft("-20000px");
36474 this.bodyEl.setTop("-20000px");
36477 showAction : function(){
36478 this.bodyEl.setStyle("position", "relative");
36479 this.bodyEl.setTop("");
36480 this.bodyEl.setLeft("");
36481 this.bodyEl.show();
36485 * Set the tooltip for the tab.
36486 * @param {String} tooltip The tab's tooltip
36488 setTooltip : function(text){
36489 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36490 this.textEl.dom.qtip = text;
36491 this.textEl.dom.removeAttribute('title');
36493 this.textEl.dom.title = text;
36497 onTabClick : function(e){
36498 e.preventDefault();
36499 this.tabPanel.activate(this.id);
36502 onTabMouseDown : function(e){
36503 e.preventDefault();
36504 this.tabPanel.activate(this.id);
36507 getWidth : function(){
36508 return this.inner.getWidth();
36511 setWidth : function(width){
36512 var iwidth = width - this.pnode.getPadding("lr");
36513 this.inner.setWidth(iwidth);
36514 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36515 this.pnode.setWidth(width);
36519 * Show or hide the tab
36520 * @param {Boolean} hidden True to hide or false to show.
36522 setHidden : function(hidden){
36523 this.hidden = hidden;
36524 this.pnode.setStyle("display", hidden ? "none" : "");
36528 * Returns true if this tab is "hidden"
36529 * @return {Boolean}
36531 isHidden : function(){
36532 return this.hidden;
36536 * Returns the text for this tab
36539 getText : function(){
36543 autoSize : function(){
36544 //this.el.beginMeasure();
36545 this.textEl.setWidth(1);
36547 * #2804 [new] Tabs in Roojs
36548 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36550 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36551 //this.el.endMeasure();
36555 * Sets the text for the tab (Note: this also sets the tooltip text)
36556 * @param {String} text The tab's text and tooltip
36558 setText : function(text){
36560 this.textEl.update(text);
36561 this.setTooltip(text);
36562 //if(!this.tabPanel.resizeTabs){
36563 // this.autoSize();
36567 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36569 activate : function(){
36570 this.tabPanel.activate(this.id);
36574 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36576 disable : function(){
36577 if(this.tabPanel.active != this){
36578 this.disabled = true;
36579 this.pnode.addClass("disabled");
36584 * Enables this TabPanelItem if it was previously disabled.
36586 enable : function(){
36587 this.disabled = false;
36588 this.pnode.removeClass("disabled");
36592 * Sets the content for this TabPanelItem.
36593 * @param {String} content The content
36594 * @param {Boolean} loadScripts true to look for and load scripts
36596 setContent : function(content, loadScripts){
36597 this.bodyEl.update(content, loadScripts);
36601 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36602 * @return {Roo.UpdateManager} The UpdateManager
36604 getUpdateManager : function(){
36605 return this.bodyEl.getUpdateManager();
36609 * Set a URL to be used to load the content for this TabPanelItem.
36610 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36611 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36612 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
36613 * @return {Roo.UpdateManager} The UpdateManager
36615 setUrl : function(url, params, loadOnce){
36616 if(this.refreshDelegate){
36617 this.un('activate', this.refreshDelegate);
36619 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36620 this.on("activate", this.refreshDelegate);
36621 return this.bodyEl.getUpdateManager();
36625 _handleRefresh : function(url, params, loadOnce){
36626 if(!loadOnce || !this.loaded){
36627 var updater = this.bodyEl.getUpdateManager();
36628 updater.update(url, params, this._setLoaded.createDelegate(this));
36633 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36634 * Will fail silently if the setUrl method has not been called.
36635 * This does not activate the panel, just updates its content.
36637 refresh : function(){
36638 if(this.refreshDelegate){
36639 this.loaded = false;
36640 this.refreshDelegate();
36645 _setLoaded : function(){
36646 this.loaded = true;
36650 closeClick : function(e){
36653 this.fireEvent("beforeclose", this, o);
36654 if(o.cancel !== true){
36655 this.tabPanel.removeTab(this.id);
36659 * The text displayed in the tooltip for the close icon.
36662 closeText : "Close this tab"