2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
65 allowDomMove : false, // to stop relocations in parent onRender...
75 * Initialize Events for the element
77 initEvents : function() { },
83 can_build_overlaid : true,
85 container_method : false,
92 // returns the parent component..
93 return Roo.ComponentMgr.get(this.parentId)
99 onRender : function(ct, position)
101 // Roo.log("Call onRender: " + this.xtype);
103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
106 if (this.el.attr('xtype')) {
107 this.el.attr('xtypex', this.el.attr('xtype'));
108 this.el.dom.removeAttribute('xtype');
118 var cfg = Roo.apply({}, this.getAutoCreate());
120 cfg.id = this.id || Roo.id();
122 // fill in the extra attributes
123 if (this.xattr && typeof(this.xattr) =='object') {
124 for (var i in this.xattr) {
125 cfg[i] = this.xattr[i];
130 cfg.dataId = this.dataId;
134 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
137 if (this.style) { // fixme needs to support more complex style data.
138 cfg.style = this.style;
142 cfg.name = this.name;
145 this.el = ct.createChild(cfg, position);
148 this.tooltipEl().attr('tooltip', this.tooltip);
151 if(this.tabIndex !== undefined){
152 this.el.dom.setAttribute('tabIndex', this.tabIndex);
159 * Fetch the element to add children to
160 * @return {Roo.Element} defaults to this.el
162 getChildContainer : function()
167 * Fetch the element to display the tooltip on.
168 * @return {Roo.Element} defaults to this.el
170 tooltipEl : function()
175 addxtype : function(tree,cntr)
179 cn = Roo.factory(tree);
180 //Roo.log(['addxtype', cn]);
182 cn.parentType = this.xtype; //??
183 cn.parentId = this.id;
185 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 if (typeof(cn.container_method) == 'string') {
187 cntr = cn.container_method;
191 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
193 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
195 var build_from_html = Roo.XComponent.build_from_html;
197 var is_body = (tree.xtype == 'Body') ;
199 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
201 var self_cntr_el = Roo.get(this[cntr](false));
203 // do not try and build conditional elements
204 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
208 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210 return this.addxtypeChild(tree,cntr, is_body);
213 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
216 return this.addxtypeChild(Roo.apply({}, tree),cntr);
219 Roo.log('skipping render');
225 if (!build_from_html) {
229 // this i think handles overlaying multiple children of the same type
230 // with the sam eelement.. - which might be buggy..
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
238 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
242 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
249 addxtypeChild : function (tree, cntr, is_body)
251 Roo.debug && Roo.log('addxtypeChild:' + cntr);
253 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
256 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257 (typeof(tree['flexy:foreach']) != 'undefined');
261 skip_children = false;
262 // render the element if it's not BODY.
265 // if parent was disabled, then do not try and create the children..
266 if(!this[cntr](true)){
271 cn = Roo.factory(tree);
273 cn.parentType = this.xtype; //??
274 cn.parentId = this.id;
276 var build_from_html = Roo.XComponent.build_from_html;
279 // does the container contain child eleemnts with 'xtype' attributes.
280 // that match this xtype..
281 // note - when we render we create these as well..
282 // so we should check to see if body has xtype set.
283 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
285 var self_cntr_el = Roo.get(this[cntr](false));
286 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
288 //Roo.log(Roo.XComponent.build_from_html);
289 //Roo.log("got echild:");
292 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293 // and are not displayed -this causes this to use up the wrong element when matching.
294 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
297 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
304 //echild.dom.removeAttribute('xtype');
306 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307 Roo.debug && Roo.log(self_cntr_el);
308 Roo.debug && Roo.log(echild);
309 Roo.debug && Roo.log(cn);
315 // if object has flexy:if - then it may or may not be rendered.
316 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
317 // skip a flexy if element.
318 Roo.debug && Roo.log('skipping render');
319 Roo.debug && Roo.log(tree);
321 Roo.debug && Roo.log('skipping all children');
322 skip_children = true;
327 // actually if flexy:foreach is found, we really want to create
328 // multiple copies here...
330 //Roo.log(this[cntr]());
331 // some elements do not have render methods.. like the layouts...
333 if(this[cntr](true) === false){
338 cn.render && cn.render(this[cntr](true));
341 // then add the element..
348 if (typeof (tree.menu) != 'undefined') {
349 tree.menu.parentType = cn.xtype;
350 tree.menu.triggerEl = cn.el;
351 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
355 if (!tree.items || !tree.items.length) {
357 //Roo.log(["no children", this]);
362 var items = tree.items;
365 //Roo.log(items.length);
367 if (!skip_children) {
368 for(var i =0;i < items.length;i++) {
369 // Roo.log(['add child', items[i]]);
370 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
376 //Roo.log("fire childrenrendered");
378 cn.fireEvent('childrenrendered', this);
384 * Set the element that will be used to show or hide
386 setVisibilityEl : function(el)
388 this.visibilityEl = el;
392 * Get the element that will be used to show or hide
394 getVisibilityEl : function()
396 if (typeof(this.visibilityEl) == 'object') {
397 return this.visibilityEl;
400 if (typeof(this.visibilityEl) == 'string') {
401 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
408 * Show a component - removes 'hidden' class
412 if(!this.getVisibilityEl()){
416 this.getVisibilityEl().removeClass(['hidden','d-none']);
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass(['hidden','d-none']);
433 this.fireEvent('hide', this);
446 * @class Roo.bootstrap.Body
447 * @extends Roo.bootstrap.Component
448 * Bootstrap Body class
452 * @param {Object} config The config object
455 Roo.bootstrap.Body = function(config){
457 config = config || {};
459 Roo.bootstrap.Body.superclass.constructor.call(this, config);
460 this.el = Roo.get(config.el ? config.el : document.body );
461 if (this.cls && this.cls.length) {
462 Roo.get(document.body).addClass(this.cls);
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
468 is_body : true,// just to make sure it's constructed?
473 onRender : function(ct, position)
475 /* Roo.log("Roo.bootstrap.Body - onRender");
476 if (this.cls && this.cls.length) {
477 Roo.get(document.body).addClass(this.cls);
496 * @class Roo.bootstrap.ButtonGroup
497 * @extends Roo.bootstrap.Component
498 * Bootstrap ButtonGroup class
499 * @cfg {String} size lg | sm | xs (default empty normal)
500 * @cfg {String} align vertical | justified (default none)
501 * @cfg {String} direction up | down (default down)
502 * @cfg {Boolean} toolbar false | true
503 * @cfg {Boolean} btn true | false
508 * @param {Object} config The config object
511 Roo.bootstrap.ButtonGroup = function(config){
512 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
523 getAutoCreate : function(){
529 cfg.html = this.html || cfg.html;
540 if (['vertical','justified'].indexOf(this.align)!==-1) {
541 cfg.cls = 'btn-group-' + this.align;
543 if (this.align == 'justified') {
544 console.log(this.items);
548 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549 cfg.cls += ' btn-group-' + this.size;
552 if (this.direction == 'up') {
553 cfg.cls += ' dropup' ;
559 * Add a button to the group (similar to NavItem API.)
561 addItem : function(cfg)
563 var cn = new Roo.bootstrap.Button(cfg);
565 cn.parentId = this.id;
566 cn.onRender(this.el, null);
580 * @class Roo.bootstrap.Button
581 * @extends Roo.bootstrap.Component
582 * Bootstrap Button class
583 * @cfg {String} html The button content
584 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587 * @cfg {String} size ( lg | sm | xs)
588 * @cfg {String} tag ( a | input | submit)
589 * @cfg {String} href empty or href
590 * @cfg {Boolean} disabled default false;
591 * @cfg {Boolean} isClose default false;
592 * @cfg {String} glyphicon depricated - use fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1078 cfg.cls += ' col-' + size + '-' + settings[size] + (
1079 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1085 cfg.cls += ' hidden';
1088 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089 cfg.cls +=' alert alert-' + this.alert;
1093 if (this.html.length) {
1094 cfg.html = this.html;
1098 if (this.fasize > 1) {
1099 fasize = ' fa-' + this.fasize + 'x';
1101 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1106 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1125 * @class Roo.bootstrap.Container
1126 * @extends Roo.bootstrap.Component
1127 * Bootstrap Container class
1128 * @cfg {Boolean} jumbotron is it a jumbotron element
1129 * @cfg {String} html content of element
1130 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1132 * @cfg {String} header content of header (for panel)
1133 * @cfg {String} footer content of footer (for panel)
1134 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135 * @cfg {String} tag (header|aside|section) type of HTML tag.
1136 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137 * @cfg {String} fa font awesome icon
1138 * @cfg {String} icon (info-sign|check|...) glyphicon name
1139 * @cfg {Boolean} hidden (true|false) hide the element
1140 * @cfg {Boolean} expandable (true|false) default false
1141 * @cfg {Boolean} expanded (true|false) default true
1142 * @cfg {String} rheader contet on the right of header
1143 * @cfg {Boolean} clickable (true|false) default false
1147 * Create a new Container
1148 * @param {Object} config The config object
1151 Roo.bootstrap.Container = function(config){
1152 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1158 * After the panel has been expand
1160 * @param {Roo.bootstrap.Container} this
1165 * After the panel has been collapsed
1167 * @param {Roo.bootstrap.Container} this
1172 * When a element is chick
1173 * @param {Roo.bootstrap.Container} this
1174 * @param {Roo.EventObject} e
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1198 getChildContainer : function() {
1204 if (this.panel.length) {
1205 return this.el.select('.panel-body',true).first();
1212 getAutoCreate : function(){
1215 tag : this.tag || 'div',
1219 if (this.jumbotron) {
1220 cfg.cls = 'jumbotron';
1225 // - this is applied by the parent..
1227 // cfg.cls = this.cls + '';
1230 if (this.sticky.length) {
1232 var bd = Roo.get(document.body);
1233 if (!bd.hasClass('bootstrap-sticky')) {
1234 bd.addClass('bootstrap-sticky');
1235 Roo.select('html',true).setStyle('height', '100%');
1238 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1242 if (this.well.length) {
1243 switch (this.well) {
1246 cfg.cls +=' well well-' +this.well;
1255 cfg.cls += ' hidden';
1259 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260 cfg.cls +=' alert alert-' + this.alert;
1265 if (this.panel.length) {
1266 cfg.cls += ' panel panel-' + this.panel;
1268 if (this.header.length) {
1272 if(this.expandable){
1274 cfg.cls = cfg.cls + ' expandable';
1278 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1286 cls : 'panel-title',
1287 html : (this.expandable ? ' ' : '') + this.header
1291 cls: 'panel-header-right',
1297 cls : 'panel-heading',
1298 style : this.expandable ? 'cursor: pointer' : '',
1306 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1311 if (this.footer.length) {
1313 cls : 'panel-footer',
1322 body.html = this.html || cfg.html;
1323 // prefix with the icons..
1325 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1328 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1333 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334 cfg.cls = 'container';
1340 initEvents: function()
1342 if(this.expandable){
1343 var headerEl = this.headerEl();
1346 headerEl.on('click', this.onToggleClick, this);
1351 this.el.on('click', this.onClick, this);
1356 onToggleClick : function()
1358 var headerEl = this.headerEl();
1374 if(this.fireEvent('expand', this)) {
1376 this.expanded = true;
1378 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1380 this.el.select('.panel-body',true).first().removeClass('hide');
1382 var toggleEl = this.toggleEl();
1388 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1393 collapse : function()
1395 if(this.fireEvent('collapse', this)) {
1397 this.expanded = false;
1399 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400 this.el.select('.panel-body',true).first().addClass('hide');
1402 var toggleEl = this.toggleEl();
1408 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1412 toggleEl : function()
1414 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1418 return this.el.select('.panel-heading .fa',true).first();
1421 headerEl : function()
1423 if(!this.el || !this.panel.length || !this.header.length){
1427 return this.el.select('.panel-heading',true).first()
1432 if(!this.el || !this.panel.length){
1436 return this.el.select('.panel-body',true).first()
1439 titleEl : function()
1441 if(!this.el || !this.panel.length || !this.header.length){
1445 return this.el.select('.panel-title',true).first();
1448 setTitle : function(v)
1450 var titleEl = this.titleEl();
1456 titleEl.dom.innerHTML = v;
1459 getTitle : function()
1462 var titleEl = this.titleEl();
1468 return titleEl.dom.innerHTML;
1471 setRightTitle : function(v)
1473 var t = this.el.select('.panel-header-right',true).first();
1479 t.dom.innerHTML = v;
1482 onClick : function(e)
1486 this.fireEvent('click', this, e);
1499 * @class Roo.bootstrap.Img
1500 * @extends Roo.bootstrap.Component
1501 * Bootstrap Img class
1502 * @cfg {Boolean} imgResponsive false | true
1503 * @cfg {String} border rounded | circle | thumbnail
1504 * @cfg {String} src image source
1505 * @cfg {String} alt image alternative text
1506 * @cfg {String} href a tag href
1507 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508 * @cfg {String} xsUrl xs image source
1509 * @cfg {String} smUrl sm image source
1510 * @cfg {String} mdUrl md image source
1511 * @cfg {String} lgUrl lg image source
1514 * Create a new Input
1515 * @param {Object} config The config object
1518 Roo.bootstrap.Img = function(config){
1519 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1525 * The img click event for the img.
1526 * @param {Roo.EventObject} e
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1534 imgResponsive: true,
1544 getAutoCreate : function()
1546 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547 return this.createSingleImg();
1552 cls: 'roo-image-responsive-group',
1557 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1559 if(!_this[size + 'Url']){
1565 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566 html: _this.html || cfg.html,
1567 src: _this[size + 'Url']
1570 img.cls += ' roo-image-responsive-' + size;
1572 var s = ['xs', 'sm', 'md', 'lg'];
1574 s.splice(s.indexOf(size), 1);
1576 Roo.each(s, function(ss){
1577 img.cls += ' hidden-' + ss;
1580 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581 cfg.cls += ' img-' + _this.border;
1585 cfg.alt = _this.alt;
1598 a.target = _this.target;
1602 cfg.cn.push((_this.href) ? a : img);
1609 createSingleImg : function()
1613 cls: (this.imgResponsive) ? 'img-responsive' : '',
1615 src : 'about:blank' // just incase src get's set to undefined?!?
1618 cfg.html = this.html || cfg.html;
1620 cfg.src = this.src || cfg.src;
1622 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623 cfg.cls += ' img-' + this.border;
1640 a.target = this.target;
1645 return (this.href) ? a : cfg;
1648 initEvents: function()
1651 this.el.on('click', this.onClick, this);
1656 onClick : function(e)
1658 Roo.log('img onclick');
1659 this.fireEvent('click', this, e);
1662 * Sets the url of the image - used to update it
1663 * @param {String} url the url of the image
1666 setSrc : function(url)
1670 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671 this.el.dom.src = url;
1675 this.el.select('img', true).first().dom.src = url;
1691 * @class Roo.bootstrap.Link
1692 * @extends Roo.bootstrap.Component
1693 * Bootstrap Link Class
1694 * @cfg {String} alt image alternative text
1695 * @cfg {String} href a tag href
1696 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697 * @cfg {String} html the content of the link.
1698 * @cfg {String} anchor name for the anchor link
1699 * @cfg {String} fa - favicon
1701 * @cfg {Boolean} preventDefault (true | false) default false
1705 * Create a new Input
1706 * @param {Object} config The config object
1709 Roo.bootstrap.Link = function(config){
1710 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1716 * The img click event for the img.
1717 * @param {Roo.EventObject} e
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1727 preventDefault: false,
1733 getAutoCreate : function()
1735 var html = this.html || '';
1737 if (this.fa !== false) {
1738 html = '<i class="fa fa-' + this.fa + '"></i>';
1743 // anchor's do not require html/href...
1744 if (this.anchor === false) {
1746 cfg.href = this.href || '#';
1748 cfg.name = this.anchor;
1749 if (this.html !== false || this.fa !== false) {
1752 if (this.href !== false) {
1753 cfg.href = this.href;
1757 if(this.alt !== false){
1762 if(this.target !== false) {
1763 cfg.target = this.target;
1769 initEvents: function() {
1771 if(!this.href || this.preventDefault){
1772 this.el.on('click', this.onClick, this);
1776 onClick : function(e)
1778 if(this.preventDefault){
1781 //Roo.log('img onclick');
1782 this.fireEvent('click', this, e);
1795 * @class Roo.bootstrap.Header
1796 * @extends Roo.bootstrap.Component
1797 * Bootstrap Header class
1798 * @cfg {String} html content of header
1799 * @cfg {Number} level (1|2|3|4|5|6) default 1
1802 * Create a new Header
1803 * @param {Object} config The config object
1807 Roo.bootstrap.Header = function(config){
1808 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1819 getAutoCreate : function(){
1824 tag: 'h' + (1 *this.level),
1825 html: this.html || ''
1837 * Ext JS Library 1.1.1
1838 * Copyright(c) 2006-2007, Ext JS, LLC.
1840 * Originally Released Under LGPL - original licence link has changed is not relivant.
1843 * <script type="text/javascript">
1847 * @class Roo.bootstrap.MenuMgr
1848 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1851 Roo.bootstrap.MenuMgr = function(){
1852 var menus, active, groups = {}, attached = false, lastShow = new Date();
1854 // private - called when first menu is created
1857 active = new Roo.util.MixedCollection();
1858 Roo.get(document).addKeyListener(27, function(){
1859 if(active.length > 0){
1867 if(active && active.length > 0){
1868 var c = active.clone();
1878 if(active.length < 1){
1879 Roo.get(document).un("mouseup", onMouseDown);
1887 var last = active.last();
1888 lastShow = new Date();
1891 Roo.get(document).on("mouseup", onMouseDown);
1896 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897 m.parentMenu.activeChild = m;
1898 }else if(last && last.isVisible()){
1899 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1904 function onBeforeHide(m){
1906 m.activeChild.hide();
1908 if(m.autoHideTimer){
1909 clearTimeout(m.autoHideTimer);
1910 delete m.autoHideTimer;
1915 function onBeforeShow(m){
1916 var pm = m.parentMenu;
1917 if(!pm && !m.allowOtherMenus){
1919 }else if(pm && pm.activeChild && active != m){
1920 pm.activeChild.hide();
1924 // private this should really trigger on mouseup..
1925 function onMouseDown(e){
1926 Roo.log("on Mouse Up");
1928 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929 Roo.log("MenuManager hideAll");
1938 function onBeforeCheck(mi, state){
1940 var g = groups[mi.group];
1941 for(var i = 0, l = g.length; i < l; i++){
1943 g[i].setChecked(false);
1952 * Hides all menus that are currently visible
1954 hideAll : function(){
1959 register : function(menu){
1963 menus[menu.id] = menu;
1964 menu.on("beforehide", onBeforeHide);
1965 menu.on("hide", onHide);
1966 menu.on("beforeshow", onBeforeShow);
1967 menu.on("show", onShow);
1969 if(g && menu.events["checkchange"]){
1973 groups[g].push(menu);
1974 menu.on("checkchange", onCheck);
1979 * Returns a {@link Roo.menu.Menu} object
1980 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981 * be used to generate and return a new Menu instance.
1983 get : function(menu){
1984 if(typeof menu == "string"){ // menu id
1986 }else if(menu.events){ // menu instance
1989 /*else if(typeof menu.length == 'number'){ // array of menu items?
1990 return new Roo.bootstrap.Menu({items:menu});
1991 }else{ // otherwise, must be a config
1992 return new Roo.bootstrap.Menu(menu);
1999 unregister : function(menu){
2000 delete menus[menu.id];
2001 menu.un("beforehide", onBeforeHide);
2002 menu.un("hide", onHide);
2003 menu.un("beforeshow", onBeforeShow);
2004 menu.un("show", onShow);
2006 if(g && menu.events["checkchange"]){
2007 groups[g].remove(menu);
2008 menu.un("checkchange", onCheck);
2013 registerCheckable : function(menuItem){
2014 var g = menuItem.group;
2019 groups[g].push(menuItem);
2020 menuItem.on("beforecheckchange", onBeforeCheck);
2025 unregisterCheckable : function(menuItem){
2026 var g = menuItem.group;
2028 groups[g].remove(menuItem);
2029 menuItem.un("beforecheckchange", onBeforeCheck);
2041 * @class Roo.bootstrap.Menu
2042 * @extends Roo.bootstrap.Component
2043 * Bootstrap Menu class - container for MenuItems
2044 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045 * @cfg {bool} hidden if the menu should be hidden when rendered.
2046 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2047 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2051 * @param {Object} config The config object
2055 Roo.bootstrap.Menu = function(config){
2056 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057 if (this.registerMenu && this.type != 'treeview') {
2058 Roo.bootstrap.MenuMgr.register(this);
2065 * Fires before this menu is displayed (return false to block)
2066 * @param {Roo.menu.Menu} this
2071 * Fires before this menu is hidden (return false to block)
2072 * @param {Roo.menu.Menu} this
2077 * Fires after this menu is displayed
2078 * @param {Roo.menu.Menu} this
2083 * Fires after this menu is hidden
2084 * @param {Roo.menu.Menu} this
2089 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090 * @param {Roo.menu.Menu} this
2091 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092 * @param {Roo.EventObject} e
2097 * Fires when the mouse is hovering over this menu
2098 * @param {Roo.menu.Menu} this
2099 * @param {Roo.EventObject} e
2100 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2105 * Fires when the mouse exits this menu
2106 * @param {Roo.menu.Menu} this
2107 * @param {Roo.EventObject} e
2108 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2113 * Fires when a menu item contained in this menu is clicked
2114 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115 * @param {Roo.EventObject} e
2119 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2126 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2129 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2131 registerMenu : true,
2133 menuItems :false, // stores the menu items..
2143 getChildContainer : function() {
2147 getAutoCreate : function(){
2149 //if (['right'].indexOf(this.align)!==-1) {
2150 // cfg.cn[1].cls += ' pull-right'
2156 cls : 'dropdown-menu' ,
2157 style : 'z-index:1000'
2161 if (this.type === 'submenu') {
2162 cfg.cls = 'submenu active';
2164 if (this.type === 'treeview') {
2165 cfg.cls = 'treeview-menu';
2170 initEvents : function() {
2172 // Roo.log("ADD event");
2173 // Roo.log(this.triggerEl.dom);
2175 this.triggerEl.on('click', this.onTriggerClick, this);
2177 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2180 if (this.triggerEl.hasClass('nav-item')) {
2181 // dropdown toggle on the 'a' in BS4?
2182 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2184 this.triggerEl.addClass('dropdown-toggle');
2187 this.el.on('touchstart' , this.onTouch, this);
2189 this.el.on('click' , this.onClick, this);
2191 this.el.on("mouseover", this.onMouseOver, this);
2192 this.el.on("mouseout", this.onMouseOut, this);
2196 findTargetItem : function(e)
2198 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2202 //Roo.log(t); Roo.log(t.id);
2204 //Roo.log(this.menuitems);
2205 return this.menuitems.get(t.id);
2207 //return this.items.get(t.menuItemId);
2213 onTouch : function(e)
2215 Roo.log("menu.onTouch");
2216 //e.stopEvent(); this make the user popdown broken
2220 onClick : function(e)
2222 Roo.log("menu.onClick");
2224 var t = this.findTargetItem(e);
2225 if(!t || t.isContainer){
2230 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2231 if(t == this.activeItem && t.shouldDeactivate(e)){
2232 this.activeItem.deactivate();
2233 delete this.activeItem;
2237 this.setActiveItem(t, true);
2245 Roo.log('pass click event');
2249 this.fireEvent("click", this, t, e);
2253 if(!t.href.length || t.href == '#'){
2254 (function() { _this.hide(); }).defer(100);
2259 onMouseOver : function(e){
2260 var t = this.findTargetItem(e);
2263 // if(t.canActivate && !t.disabled){
2264 // this.setActiveItem(t, true);
2268 this.fireEvent("mouseover", this, e, t);
2270 isVisible : function(){
2271 return !this.hidden;
2273 onMouseOut : function(e){
2274 var t = this.findTargetItem(e);
2277 // if(t == this.activeItem && t.shouldDeactivate(e)){
2278 // this.activeItem.deactivate();
2279 // delete this.activeItem;
2282 this.fireEvent("mouseout", this, e, t);
2287 * Displays this menu relative to another element
2288 * @param {String/HTMLElement/Roo.Element} element The element to align to
2289 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290 * the element (defaults to this.defaultAlign)
2291 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2293 show : function(el, pos, parentMenu)
2295 if (false === this.fireEvent("beforeshow", this)) {
2296 Roo.log("show canceled");
2299 this.parentMenu = parentMenu;
2304 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2307 * Displays this menu at a specific xy position
2308 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2311 showAt : function(xy, parentMenu, /* private: */_e){
2312 this.parentMenu = parentMenu;
2317 this.fireEvent("beforeshow", this);
2318 //xy = this.el.adjustForConstraints(xy);
2322 this.hideMenuItems();
2323 this.hidden = false;
2324 this.triggerEl.addClass('open');
2325 this.el.addClass('show');
2327 // reassign x when hitting right
2328 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2332 // reassign y when hitting bottom
2333 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2337 // but the list may align on trigger left or trigger top... should it be a properity?
2339 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2344 this.fireEvent("show", this);
2350 this.doFocus.defer(50, this);
2354 doFocus : function(){
2356 this.focusEl.focus();
2361 * Hides this menu and optionally all parent menus
2362 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2364 hide : function(deep)
2366 if (false === this.fireEvent("beforehide", this)) {
2367 Roo.log("hide canceled");
2370 this.hideMenuItems();
2371 if(this.el && this.isVisible()){
2373 if(this.activeItem){
2374 this.activeItem.deactivate();
2375 this.activeItem = null;
2377 this.triggerEl.removeClass('open');;
2378 this.el.removeClass('show');
2380 this.fireEvent("hide", this);
2382 if(deep === true && this.parentMenu){
2383 this.parentMenu.hide(true);
2387 onTriggerClick : function(e)
2389 Roo.log('trigger click');
2391 var target = e.getTarget();
2393 Roo.log(target.nodeName.toLowerCase());
2395 if(target.nodeName.toLowerCase() === 'i'){
2401 onTriggerPress : function(e)
2403 Roo.log('trigger press');
2404 //Roo.log(e.getTarget());
2405 // Roo.log(this.triggerEl.dom);
2407 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408 var pel = Roo.get(e.getTarget());
2409 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410 Roo.log('is treeview or dropdown?');
2414 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2418 if (this.isVisible()) {
2423 this.show(this.triggerEl, '?', false);
2426 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2433 hideMenuItems : function()
2435 Roo.log("hide Menu Items");
2440 this.el.select('.open',true).each(function(aa) {
2442 aa.removeClass('open');
2446 addxtypeChild : function (tree, cntr) {
2447 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2449 this.menuitems.add(comp);
2461 this.getEl().dom.innerHTML = '';
2462 this.menuitems.clear();
2476 * @class Roo.bootstrap.MenuItem
2477 * @extends Roo.bootstrap.Component
2478 * Bootstrap MenuItem class
2479 * @cfg {String} html the menu label
2480 * @cfg {String} href the link
2481 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483 * @cfg {Boolean} active used on sidebars to highlight active itesm
2484 * @cfg {String} fa favicon to show on left of menu item.
2485 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2489 * Create a new MenuItem
2490 * @param {Object} config The config object
2494 Roo.bootstrap.MenuItem = function(config){
2495 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2500 * The raw click event for the entire grid.
2501 * @param {Roo.bootstrap.MenuItem} this
2502 * @param {Roo.EventObject} e
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2512 preventDefault: false,
2513 isContainer : false,
2517 getAutoCreate : function(){
2519 if(this.isContainer){
2522 cls: 'dropdown-menu-item '
2532 cls : 'dropdown-item',
2537 if (this.fa !== false) {
2540 cls : 'fa fa-' + this.fa
2549 cls: 'dropdown-menu-item',
2552 if (this.parent().type == 'treeview') {
2553 cfg.cls = 'treeview-menu';
2556 cfg.cls += ' active';
2561 anc.href = this.href || cfg.cn[0].href ;
2562 ctag.html = this.html || cfg.cn[0].html ;
2566 initEvents: function()
2568 if (this.parent().type == 'treeview') {
2569 this.el.select('a').on('click', this.onClick, this);
2573 this.menu.parentType = this.xtype;
2574 this.menu.triggerEl = this.el;
2575 this.menu = this.addxtype(Roo.apply({}, this.menu));
2579 onClick : function(e)
2581 Roo.log('item on click ');
2583 if(this.preventDefault){
2586 //this.parent().hideMenuItems();
2588 this.fireEvent('click', this, e);
2607 * @class Roo.bootstrap.MenuSeparator
2608 * @extends Roo.bootstrap.Component
2609 * Bootstrap MenuSeparator class
2612 * Create a new MenuItem
2613 * @param {Object} config The config object
2617 Roo.bootstrap.MenuSeparator = function(config){
2618 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2623 getAutoCreate : function(){
2642 * @class Roo.bootstrap.Modal
2643 * @extends Roo.bootstrap.Component
2644 * Bootstrap Modal class
2645 * @cfg {String} title Title of dialog
2646 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2648 * @cfg {Boolean} specificTitle default false
2649 * @cfg {Array} buttons Array of buttons or standard button set..
2650 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651 * @cfg {Boolean} animate default true
2652 * @cfg {Boolean} allow_close default true
2653 * @cfg {Boolean} fitwindow default false
2654 * @cfg {String} size (sm|lg) default empty
2655 * @cfg {Number} max_width set the max width of modal
2659 * Create a new Modal Dialog
2660 * @param {Object} config The config object
2663 Roo.bootstrap.Modal = function(config){
2664 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2669 * The raw btnclick event for the button
2670 * @param {Roo.EventObject} e
2675 * Fire when dialog resize
2676 * @param {Roo.bootstrap.Modal} this
2677 * @param {Roo.EventObject} e
2681 this.buttons = this.buttons || [];
2684 this.tmpl = Roo.factory(this.tmpl);
2689 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2691 title : 'test dialog',
2701 specificTitle: false,
2703 buttonPosition: 'right',
2726 onRender : function(ct, position)
2728 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2731 var cfg = Roo.apply({}, this.getAutoCreate());
2734 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2736 //if (!cfg.name.length) {
2740 cfg.cls += ' ' + this.cls;
2743 cfg.style = this.style;
2745 this.el = Roo.get(document.body).createChild(cfg, position);
2747 //var type = this.el.dom.type;
2750 if(this.tabIndex !== undefined){
2751 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2754 this.dialogEl = this.el.select('.modal-dialog',true).first();
2755 this.bodyEl = this.el.select('.modal-body',true).first();
2756 this.closeEl = this.el.select('.modal-header .close', true).first();
2757 this.headerEl = this.el.select('.modal-header',true).first();
2758 this.titleEl = this.el.select('.modal-title',true).first();
2759 this.footerEl = this.el.select('.modal-footer',true).first();
2761 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2763 //this.el.addClass("x-dlg-modal");
2765 if (this.buttons.length) {
2766 Roo.each(this.buttons, function(bb) {
2767 var b = Roo.apply({}, bb);
2768 b.xns = b.xns || Roo.bootstrap;
2769 b.xtype = b.xtype || 'Button';
2770 if (typeof(b.listeners) == 'undefined') {
2771 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2774 var btn = Roo.factory(b);
2776 btn.render(this.getButtonContainer());
2780 // render the children.
2783 if(typeof(this.items) != 'undefined'){
2784 var items = this.items;
2787 for(var i =0;i < items.length;i++) {
2788 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2792 this.items = nitems;
2794 // where are these used - they used to be body/close/footer
2798 //this.el.addClass([this.fieldClass, this.cls]);
2802 getAutoCreate : function()
2806 html : this.html || ''
2811 cls : 'modal-title',
2815 if(this.specificTitle){
2821 if (this.allow_close && Roo.bootstrap.version == 3) {
2831 if (this.allow_close && Roo.bootstrap.version == 4) {
2841 if(this.size.length){
2842 size = 'modal-' + this.size;
2845 var footer = Roo.bootstrap.version == 3 ?
2847 cls : 'modal-footer',
2851 cls: 'btn-' + this.buttonPosition
2856 { // BS4 uses mr-auto on left buttons....
2857 cls : 'modal-footer'
2868 cls: "modal-dialog " + size,
2871 cls : "modal-content",
2874 cls : 'modal-header',
2889 modal.cls += ' fade';
2895 getChildContainer : function() {
2900 getButtonContainer : function() {
2902 return Roo.bootstrap.version == 4 ?
2903 this.el.select('.modal-footer',true).first()
2904 : this.el.select('.modal-footer div',true).first();
2907 initEvents : function()
2909 if (this.allow_close) {
2910 this.closeEl.on('click', this.hide, this);
2912 Roo.EventManager.onWindowResize(this.resize, this, true);
2920 this.maskEl.setSize(
2921 Roo.lib.Dom.getViewWidth(true),
2922 Roo.lib.Dom.getViewHeight(true)
2925 if (this.fitwindow) {
2929 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2930 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2935 if(this.max_width !== 0) {
2937 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2940 this.setSize(w, this.height);
2944 if(this.max_height) {
2945 this.setSize(w,Math.min(
2947 Roo.lib.Dom.getViewportHeight(true) - 60
2953 if(!this.fit_content) {
2954 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2958 this.setSize(w, Math.min(
2960 this.headerEl.getHeight() +
2961 this.footerEl.getHeight() +
2962 this.getChildHeight(this.bodyEl.dom.childNodes),
2963 Roo.lib.Dom.getViewportHeight(true) - 60)
2969 setSize : function(w,h)
2980 if (!this.rendered) {
2984 //this.el.setStyle('display', 'block');
2985 this.el.removeClass('hideing');
2986 this.el.dom.style.display='block';
2988 Roo.get(document.body).addClass('modal-open');
2990 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2993 this.el.addClass('show');
2994 this.el.addClass('in');
2997 this.el.addClass('show');
2998 this.el.addClass('in');
3001 // not sure how we can show data in here..
3003 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3006 Roo.get(document.body).addClass("x-body-masked");
3008 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3009 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3010 this.maskEl.dom.style.display = 'block';
3011 this.maskEl.addClass('show');
3016 this.fireEvent('show', this);
3018 // set zindex here - otherwise it appears to be ignored...
3019 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3022 this.items.forEach( function(e) {
3023 e.layout ? e.layout() : false;
3031 if(this.fireEvent("beforehide", this) !== false){
3033 this.maskEl.removeClass('show');
3035 this.maskEl.dom.style.display = '';
3036 Roo.get(document.body).removeClass("x-body-masked");
3037 this.el.removeClass('in');
3038 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3040 if(this.animate){ // why
3041 this.el.addClass('hideing');
3042 this.el.removeClass('show');
3044 if (!this.el.hasClass('hideing')) {
3045 return; // it's been shown again...
3048 this.el.dom.style.display='';
3050 Roo.get(document.body).removeClass('modal-open');
3051 this.el.removeClass('hideing');
3055 this.el.removeClass('show');
3056 this.el.dom.style.display='';
3057 Roo.get(document.body).removeClass('modal-open');
3060 this.fireEvent('hide', this);
3063 isVisible : function()
3066 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3070 addButton : function(str, cb)
3074 var b = Roo.apply({}, { html : str } );
3075 b.xns = b.xns || Roo.bootstrap;
3076 b.xtype = b.xtype || 'Button';
3077 if (typeof(b.listeners) == 'undefined') {
3078 b.listeners = { click : cb.createDelegate(this) };
3081 var btn = Roo.factory(b);
3083 btn.render(this.getButtonContainer());
3089 setDefaultButton : function(btn)
3091 //this.el.select('.modal-footer').()
3094 resizeTo: function(w,h)
3096 this.dialogEl.setWidth(w);
3098 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3100 this.bodyEl.setHeight(h - diff);
3102 this.fireEvent('resize', this);
3105 setContentSize : function(w, h)
3109 onButtonClick: function(btn,e)
3112 this.fireEvent('btnclick', btn.name, e);
3115 * Set the title of the Dialog
3116 * @param {String} str new Title
3118 setTitle: function(str) {
3119 this.titleEl.dom.innerHTML = str;
3122 * Set the body of the Dialog
3123 * @param {String} str new Title
3125 setBody: function(str) {
3126 this.bodyEl.dom.innerHTML = str;
3129 * Set the body of the Dialog using the template
3130 * @param {Obj} data - apply this data to the template and replace the body contents.
3132 applyBody: function(obj)
3135 Roo.log("Error - using apply Body without a template");
3138 this.tmpl.overwrite(this.bodyEl, obj);
3141 getChildHeight : function(child_nodes)
3145 child_nodes.length == 0
3150 var child_height = 0;
3152 for(var i = 0; i < child_nodes.length; i++) {
3155 * for modal with tabs...
3156 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3158 var layout_childs = child_nodes[i].childNodes;
3160 for(var j = 0; j < layout_childs.length; j++) {
3162 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3164 var layout_body_childs = layout_childs[j].childNodes;
3166 for(var k = 0; k < layout_body_childs.length; k++) {
3168 if(layout_body_childs[k].classList.contains('navbar')) {
3169 child_height += layout_body_childs[k].offsetHeight;
3173 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3175 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3177 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3179 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3180 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3195 child_height += child_nodes[i].offsetHeight;
3196 // Roo.log(child_nodes[i].offsetHeight);
3199 return child_height;
3205 Roo.apply(Roo.bootstrap.Modal, {
3207 * Button config that displays a single OK button
3216 * Button config that displays Yes and No buttons
3232 * Button config that displays OK and Cancel buttons
3247 * Button config that displays Yes, No and Cancel buttons
3271 * messagebox - can be used as a replace
3275 * @class Roo.MessageBox
3276 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3280 Roo.Msg.alert('Status', 'Changes saved successfully.');
3282 // Prompt for user data:
3283 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3285 // process text value...
3289 // Show a dialog using config options:
3291 title:'Save Changes?',
3292 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3293 buttons: Roo.Msg.YESNOCANCEL,
3300 Roo.bootstrap.MessageBox = function(){
3301 var dlg, opt, mask, waitTimer;
3302 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3303 var buttons, activeTextEl, bwidth;
3307 var handleButton = function(button){
3309 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3313 var handleHide = function(){
3315 dlg.el.removeClass(opt.cls);
3318 // Roo.TaskMgr.stop(waitTimer);
3319 // waitTimer = null;
3324 var updateButtons = function(b){
3327 buttons["ok"].hide();
3328 buttons["cancel"].hide();
3329 buttons["yes"].hide();
3330 buttons["no"].hide();
3331 dlg.footerEl.hide();
3335 dlg.footerEl.show();
3336 for(var k in buttons){
3337 if(typeof buttons[k] != "function"){
3340 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3341 width += buttons[k].el.getWidth()+15;
3351 var handleEsc = function(d, k, e){
3352 if(opt && opt.closable !== false){
3362 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3363 * @return {Roo.BasicDialog} The BasicDialog element
3365 getDialog : function(){
3367 dlg = new Roo.bootstrap.Modal( {
3370 //constraintoviewport:false,
3372 //collapsible : false,
3377 //buttonAlign:"center",
3378 closeClick : function(){
3379 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3382 handleButton("cancel");
3387 dlg.on("hide", handleHide);
3389 //dlg.addKeyListener(27, handleEsc);
3391 this.buttons = buttons;
3392 var bt = this.buttonText;
3393 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3394 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3395 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3396 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3398 bodyEl = dlg.bodyEl.createChild({
3400 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3401 '<textarea class="roo-mb-textarea"></textarea>' +
3402 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3404 msgEl = bodyEl.dom.firstChild;
3405 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3406 textboxEl.enableDisplayMode();
3407 textboxEl.addKeyListener([10,13], function(){
3408 if(dlg.isVisible() && opt && opt.buttons){
3411 }else if(opt.buttons.yes){
3412 handleButton("yes");
3416 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3417 textareaEl.enableDisplayMode();
3418 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3419 progressEl.enableDisplayMode();
3421 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3422 var pf = progressEl.dom.firstChild;
3424 pp = Roo.get(pf.firstChild);
3425 pp.setHeight(pf.offsetHeight);
3433 * Updates the message box body text
3434 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3435 * the XHTML-compliant non-breaking space character '&#160;')
3436 * @return {Roo.MessageBox} This message box
3438 updateText : function(text)
3440 if(!dlg.isVisible() && !opt.width){
3441 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3442 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3444 msgEl.innerHTML = text || ' ';
3446 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3447 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3449 Math.min(opt.width || cw , this.maxWidth),
3450 Math.max(opt.minWidth || this.minWidth, bwidth)
3453 activeTextEl.setWidth(w);
3455 if(dlg.isVisible()){
3456 dlg.fixedcenter = false;
3458 // to big, make it scroll. = But as usual stupid IE does not support
3461 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3462 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3463 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3465 bodyEl.dom.style.height = '';
3466 bodyEl.dom.style.overflowY = '';
3469 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3471 bodyEl.dom.style.overflowX = '';
3474 dlg.setContentSize(w, bodyEl.getHeight());
3475 if(dlg.isVisible()){
3476 dlg.fixedcenter = true;
3482 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3483 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3484 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3485 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3486 * @return {Roo.MessageBox} This message box
3488 updateProgress : function(value, text){
3490 this.updateText(text);
3493 if (pp) { // weird bug on my firefox - for some reason this is not defined
3494 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3495 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3501 * Returns true if the message box is currently displayed
3502 * @return {Boolean} True if the message box is visible, else false
3504 isVisible : function(){
3505 return dlg && dlg.isVisible();
3509 * Hides the message box if it is displayed
3512 if(this.isVisible()){
3518 * Displays a new message box, or reinitializes an existing message box, based on the config options
3519 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3520 * The following config object properties are supported:
3522 Property Type Description
3523 ---------- --------------- ------------------------------------------------------------------------------------
3524 animEl String/Element An id or Element from which the message box should animate as it opens and
3525 closes (defaults to undefined)
3526 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3527 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3528 closable Boolean False to hide the top-right close button (defaults to true). Note that
3529 progress and wait dialogs will ignore this property and always hide the
3530 close button as they can only be closed programmatically.
3531 cls String A custom CSS class to apply to the message box element
3532 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3533 displayed (defaults to 75)
3534 fn Function A callback function to execute after closing the dialog. The arguments to the
3535 function will be btn (the name of the button that was clicked, if applicable,
3536 e.g. "ok"), and text (the value of the active text field, if applicable).
3537 Progress and wait dialogs will ignore this option since they do not respond to
3538 user actions and can only be closed programmatically, so any required function
3539 should be called by the same code after it closes the dialog.
3540 icon String A CSS class that provides a background image to be used as an icon for
3541 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3542 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3543 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3544 modal Boolean False to allow user interaction with the page while the message box is
3545 displayed (defaults to true)
3546 msg String A string that will replace the existing message box body text (defaults
3547 to the XHTML-compliant non-breaking space character ' ')
3548 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3549 progress Boolean True to display a progress bar (defaults to false)
3550 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3551 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3552 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3553 title String The title text
3554 value String The string value to set into the active textbox element if displayed
3555 wait Boolean True to display a progress bar (defaults to false)
3556 width Number The width of the dialog in pixels
3563 msg: 'Please enter your address:',
3565 buttons: Roo.MessageBox.OKCANCEL,
3568 animEl: 'addAddressBtn'
3571 * @param {Object} config Configuration options
3572 * @return {Roo.MessageBox} This message box
3574 show : function(options)
3577 // this causes nightmares if you show one dialog after another
3578 // especially on callbacks..
3580 if(this.isVisible()){
3583 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3584 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3585 Roo.log("New Dialog Message:" + options.msg )
3586 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3587 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3590 var d = this.getDialog();
3592 d.setTitle(opt.title || " ");
3593 d.closeEl.setDisplayed(opt.closable !== false);
3594 activeTextEl = textboxEl;
3595 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3600 textareaEl.setHeight(typeof opt.multiline == "number" ?
3601 opt.multiline : this.defaultTextHeight);
3602 activeTextEl = textareaEl;
3611 progressEl.setDisplayed(opt.progress === true);
3613 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3615 this.updateProgress(0);
3616 activeTextEl.dom.value = opt.value || "";
3618 dlg.setDefaultButton(activeTextEl);
3620 var bs = opt.buttons;
3624 }else if(bs && bs.yes){
3625 db = buttons["yes"];
3627 dlg.setDefaultButton(db);
3629 bwidth = updateButtons(opt.buttons);
3630 this.updateText(opt.msg);
3632 d.el.addClass(opt.cls);
3634 d.proxyDrag = opt.proxyDrag === true;
3635 d.modal = opt.modal !== false;
3636 d.mask = opt.modal !== false ? mask : false;
3638 // force it to the end of the z-index stack so it gets a cursor in FF
3639 document.body.appendChild(dlg.el.dom);
3640 d.animateTarget = null;
3641 d.show(options.animEl);
3647 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3648 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3649 * and closing the message box when the process is complete.
3650 * @param {String} title The title bar text
3651 * @param {String} msg The message box body text
3652 * @return {Roo.MessageBox} This message box
3654 progress : function(title, msg){
3661 minWidth: this.minProgressWidth,
3668 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3669 * If a callback function is passed it will be called after the user clicks the button, and the
3670 * id of the button that was clicked will be passed as the only parameter to the callback
3671 * (could also be the top-right close button).
3672 * @param {String} title The title bar text
3673 * @param {String} msg The message box body text
3674 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3675 * @param {Object} scope (optional) The scope of the callback function
3676 * @return {Roo.MessageBox} This message box
3678 alert : function(title, msg, fn, scope)
3693 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3694 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3695 * You are responsible for closing the message box when the process is complete.
3696 * @param {String} msg The message box body text
3697 * @param {String} title (optional) The title bar text
3698 * @return {Roo.MessageBox} This message box
3700 wait : function(msg, title){
3711 waitTimer = Roo.TaskMgr.start({
3713 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3721 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3722 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3723 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3724 * @param {String} title The title bar text
3725 * @param {String} msg The message box body text
3726 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3727 * @param {Object} scope (optional) The scope of the callback function
3728 * @return {Roo.MessageBox} This message box
3730 confirm : function(title, msg, fn, scope){
3734 buttons: this.YESNO,
3743 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3744 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3745 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3746 * (could also be the top-right close button) and the text that was entered will be passed as the two
3747 * parameters to the callback.
3748 * @param {String} title The title bar text
3749 * @param {String} msg The message box body text
3750 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3751 * @param {Object} scope (optional) The scope of the callback function
3752 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3753 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3754 * @return {Roo.MessageBox} This message box
3756 prompt : function(title, msg, fn, scope, multiline){
3760 buttons: this.OKCANCEL,
3765 multiline: multiline,
3772 * Button config that displays a single OK button
3777 * Button config that displays Yes and No buttons
3780 YESNO : {yes:true, no:true},
3782 * Button config that displays OK and Cancel buttons
3785 OKCANCEL : {ok:true, cancel:true},
3787 * Button config that displays Yes, No and Cancel buttons
3790 YESNOCANCEL : {yes:true, no:true, cancel:true},
3793 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3796 defaultTextHeight : 75,
3798 * The maximum width in pixels of the message box (defaults to 600)
3803 * The minimum width in pixels of the message box (defaults to 100)
3808 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3809 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3812 minProgressWidth : 250,
3814 * An object containing the default button text strings that can be overriden for localized language support.
3815 * Supported properties are: ok, cancel, yes and no.
3816 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3829 * Shorthand for {@link Roo.MessageBox}
3831 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3832 Roo.Msg = Roo.Msg || Roo.MessageBox;
3841 * @class Roo.bootstrap.Navbar
3842 * @extends Roo.bootstrap.Component
3843 * Bootstrap Navbar class
3846 * Create a new Navbar
3847 * @param {Object} config The config object
3851 Roo.bootstrap.Navbar = function(config){
3852 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3856 * @event beforetoggle
3857 * Fire before toggle the menu
3858 * @param {Roo.EventObject} e
3860 "beforetoggle" : true
3864 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3873 getAutoCreate : function(){
3876 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3880 initEvents :function ()
3882 //Roo.log(this.el.select('.navbar-toggle',true));
3883 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3890 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3892 var size = this.el.getSize();
3893 this.maskEl.setSize(size.width, size.height);
3894 this.maskEl.enableDisplayMode("block");
3903 getChildContainer : function()
3905 if (this.el && this.el.select('.collapse').getCount()) {
3906 return this.el.select('.collapse',true).first();
3921 onToggle : function()
3924 if(this.fireEvent('beforetoggle', this) === false){
3927 var ce = this.el.select('.navbar-collapse',true).first();
3929 if (!ce.hasClass('show')) {
3939 * Expand the navbar pulldown
3941 expand : function ()
3944 var ce = this.el.select('.navbar-collapse',true).first();
3945 if (ce.hasClass('collapsing')) {
3948 ce.dom.style.height = '';
3950 ce.addClass('in'); // old...
3951 ce.removeClass('collapse');
3952 ce.addClass('show');
3953 var h = ce.getHeight();
3955 ce.removeClass('show');
3956 // at this point we should be able to see it..
3957 ce.addClass('collapsing');
3959 ce.setHeight(0); // resize it ...
3960 ce.on('transitionend', function() {
3961 //Roo.log('done transition');
3962 ce.removeClass('collapsing');
3963 ce.addClass('show');
3964 ce.removeClass('collapse');
3966 ce.dom.style.height = '';
3967 }, this, { single: true} );
3969 ce.dom.scrollTop = 0;
3972 * Collapse the navbar pulldown
3974 collapse : function()
3976 var ce = this.el.select('.navbar-collapse',true).first();
3978 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3979 // it's collapsed or collapsing..
3982 ce.removeClass('in'); // old...
3983 ce.setHeight(ce.getHeight());
3984 ce.removeClass('show');
3985 ce.addClass('collapsing');
3987 ce.on('transitionend', function() {
3988 ce.dom.style.height = '';
3989 ce.removeClass('collapsing');
3990 ce.addClass('collapse');
3991 }, this, { single: true} );
4011 * @class Roo.bootstrap.NavSimplebar
4012 * @extends Roo.bootstrap.Navbar
4013 * Bootstrap Sidebar class
4015 * @cfg {Boolean} inverse is inverted color
4017 * @cfg {String} type (nav | pills | tabs)
4018 * @cfg {Boolean} arrangement stacked | justified
4019 * @cfg {String} align (left | right) alignment
4021 * @cfg {Boolean} main (true|false) main nav bar? default false
4022 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4024 * @cfg {String} tag (header|footer|nav|div) default is nav
4026 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4030 * Create a new Sidebar
4031 * @param {Object} config The config object
4035 Roo.bootstrap.NavSimplebar = function(config){
4036 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4039 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4055 getAutoCreate : function(){
4059 tag : this.tag || 'div',
4060 cls : 'navbar navbar-expand-lg roo-navbar-simple'
4062 if (['light','white'].indexOf(this.weight) > -1) {
4063 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4065 cfg.cls += ' bg-' + this.weight;
4068 cfg.cls += ' navbar-inverse';
4072 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4074 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4083 cls: 'nav nav-' + this.xtype,
4089 this.type = this.type || 'nav';
4090 if (['tabs','pills'].indexOf(this.type) != -1) {
4091 cfg.cn[0].cls += ' nav-' + this.type
4095 if (this.type!=='nav') {
4096 Roo.log('nav type must be nav/tabs/pills')
4098 cfg.cn[0].cls += ' navbar-nav'
4104 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4105 cfg.cn[0].cls += ' nav-' + this.arrangement;
4109 if (this.align === 'right') {
4110 cfg.cn[0].cls += ' navbar-right';
4135 * navbar-expand-md fixed-top
4139 * @class Roo.bootstrap.NavHeaderbar
4140 * @extends Roo.bootstrap.NavSimplebar
4141 * Bootstrap Sidebar class
4143 * @cfg {String} brand what is brand
4144 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4145 * @cfg {String} brand_href href of the brand
4146 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4147 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4148 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4149 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4152 * Create a new Sidebar
4153 * @param {Object} config The config object
4157 Roo.bootstrap.NavHeaderbar = function(config){
4158 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4162 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4169 desktopCenter : false,
4172 getAutoCreate : function(){
4175 tag: this.nav || 'nav',
4176 cls: 'navbar navbar-expand-md',
4182 if (this.desktopCenter) {
4183 cn.push({cls : 'container', cn : []});
4191 cls: 'navbar-toggle navbar-toggler',
4192 'data-toggle': 'collapse',
4197 html: 'Toggle navigation'
4201 cls: 'icon-bar navbar-toggler-icon'
4214 cn.push( Roo.bootstrap.version == 4 ? btn : {
4216 cls: 'navbar-header',
4225 cls: 'collapse navbar-collapse',
4229 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4231 if (['light','white'].indexOf(this.weight) > -1) {
4232 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4234 cfg.cls += ' bg-' + this.weight;
4237 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4238 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4240 // tag can override this..
4242 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4245 if (this.brand !== '') {
4246 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4247 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4249 href: this.brand_href ? this.brand_href : '#',
4250 cls: 'navbar-brand',
4258 cfg.cls += ' main-nav';
4266 getHeaderChildContainer : function()
4268 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4269 return this.el.select('.navbar-header',true).first();
4272 return this.getChildContainer();
4276 initEvents : function()
4278 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4280 if (this.autohide) {
4285 Roo.get(document).on('scroll',function(e) {
4286 var ns = Roo.get(document).getScroll().top;
4287 var os = prevScroll;
4291 ft.removeClass('slideDown');
4292 ft.addClass('slideUp');
4295 ft.removeClass('slideUp');
4296 ft.addClass('slideDown');
4317 * @class Roo.bootstrap.NavSidebar
4318 * @extends Roo.bootstrap.Navbar
4319 * Bootstrap Sidebar class
4322 * Create a new Sidebar
4323 * @param {Object} config The config object
4327 Roo.bootstrap.NavSidebar = function(config){
4328 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4331 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4333 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4335 getAutoCreate : function(){
4340 cls: 'sidebar sidebar-nav'
4362 * @class Roo.bootstrap.NavGroup
4363 * @extends Roo.bootstrap.Component
4364 * Bootstrap NavGroup class
4365 * @cfg {String} align (left|right)
4366 * @cfg {Boolean} inverse
4367 * @cfg {String} type (nav|pills|tab) default nav
4368 * @cfg {String} navId - reference Id for navbar.
4372 * Create a new nav group
4373 * @param {Object} config The config object
4376 Roo.bootstrap.NavGroup = function(config){
4377 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4380 Roo.bootstrap.NavGroup.register(this);
4384 * Fires when the active item changes
4385 * @param {Roo.bootstrap.NavGroup} this
4386 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4387 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4394 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4405 getAutoCreate : function()
4407 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4413 if (Roo.bootstrap.version == 4) {
4414 if (['tabs','pills'].indexOf(this.type) != -1) {
4415 cfg.cls += ' nav-' + this.type;
4417 cfg.cls += ' navbar-nav';
4420 if (['tabs','pills'].indexOf(this.type) != -1) {
4421 cfg.cls += ' nav-' + this.type
4423 if (this.type !== 'nav') {
4424 Roo.log('nav type must be nav/tabs/pills')
4426 cfg.cls += ' navbar-nav'
4430 if (this.parent() && this.parent().sidebar) {
4433 cls: 'dashboard-menu sidebar-menu'
4439 if (this.form === true) {
4442 cls: 'navbar-form form-inline'
4445 if (this.align === 'right') {
4446 cfg.cls += ' navbar-right ml-md-auto';
4448 cfg.cls += ' navbar-left';
4452 if (this.align === 'right') {
4453 cfg.cls += ' navbar-right ml-md-auto';
4455 cfg.cls += ' mr-auto';
4459 cfg.cls += ' navbar-inverse';
4467 * sets the active Navigation item
4468 * @param {Roo.bootstrap.NavItem} the new current navitem
4470 setActiveItem : function(item)
4473 Roo.each(this.navItems, function(v){
4478 v.setActive(false, true);
4485 item.setActive(true, true);
4486 this.fireEvent('changed', this, item, prev);
4491 * gets the active Navigation item
4492 * @return {Roo.bootstrap.NavItem} the current navitem
4494 getActive : function()
4498 Roo.each(this.navItems, function(v){
4509 indexOfNav : function()
4513 Roo.each(this.navItems, function(v,i){
4524 * adds a Navigation item
4525 * @param {Roo.bootstrap.NavItem} the navitem to add
4527 addItem : function(cfg)
4529 if (this.form && Roo.bootstrap.version == 4) {
4532 var cn = new Roo.bootstrap.NavItem(cfg);
4534 cn.parentId = this.id;
4535 cn.onRender(this.el, null);
4539 * register a Navigation item
4540 * @param {Roo.bootstrap.NavItem} the navitem to add
4542 register : function(item)
4544 this.navItems.push( item);
4545 item.navId = this.navId;
4550 * clear all the Navigation item
4553 clearAll : function()
4556 this.el.dom.innerHTML = '';
4559 getNavItem: function(tabId)
4562 Roo.each(this.navItems, function(e) {
4563 if (e.tabId == tabId) {
4573 setActiveNext : function()
4575 var i = this.indexOfNav(this.getActive());
4576 if (i > this.navItems.length) {
4579 this.setActiveItem(this.navItems[i+1]);
4581 setActivePrev : function()
4583 var i = this.indexOfNav(this.getActive());
4587 this.setActiveItem(this.navItems[i-1]);
4589 clearWasActive : function(except) {
4590 Roo.each(this.navItems, function(e) {
4591 if (e.tabId != except.tabId && e.was_active) {
4592 e.was_active = false;
4599 getWasActive : function ()
4602 Roo.each(this.navItems, function(e) {
4617 Roo.apply(Roo.bootstrap.NavGroup, {
4621 * register a Navigation Group
4622 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4624 register : function(navgrp)
4626 this.groups[navgrp.navId] = navgrp;
4630 * fetch a Navigation Group based on the navigation ID
4631 * @param {string} the navgroup to add
4632 * @returns {Roo.bootstrap.NavGroup} the navgroup
4634 get: function(navId) {
4635 if (typeof(this.groups[navId]) == 'undefined') {
4637 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4639 return this.groups[navId] ;
4654 * @class Roo.bootstrap.NavItem
4655 * @extends Roo.bootstrap.Component
4656 * Bootstrap Navbar.NavItem class
4657 * @cfg {String} href link to
4658 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4660 * @cfg {String} html content of button
4661 * @cfg {String} badge text inside badge
4662 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4663 * @cfg {String} glyphicon DEPRICATED - use fa
4664 * @cfg {String} icon DEPRICATED - use fa
4665 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4666 * @cfg {Boolean} active Is item active
4667 * @cfg {Boolean} disabled Is item disabled
4669 * @cfg {Boolean} preventDefault (true | false) default false
4670 * @cfg {String} tabId the tab that this item activates.
4671 * @cfg {String} tagtype (a|span) render as a href or span?
4672 * @cfg {Boolean} animateRef (true|false) link to element default false
4675 * Create a new Navbar Item
4676 * @param {Object} config The config object
4678 Roo.bootstrap.NavItem = function(config){
4679 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4684 * The raw click event for the entire grid.
4685 * @param {Roo.EventObject} e
4690 * Fires when the active item active state changes
4691 * @param {Roo.bootstrap.NavItem} this
4692 * @param {boolean} state the new state
4698 * Fires when scroll to element
4699 * @param {Roo.bootstrap.NavItem} this
4700 * @param {Object} options
4701 * @param {Roo.EventObject} e
4709 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4718 preventDefault : false,
4726 button_outline : false,
4730 getAutoCreate : function(){
4738 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4740 if (this.disabled) {
4741 cfg.cls += ' disabled';
4745 if (this.button_weight.length) {
4746 cfg.tag = this.href ? 'a' : 'button';
4747 cfg.html = this.html || '';
4748 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4750 cfg.href = this.href;
4753 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4756 // menu .. should add dropdown-menu class - so no need for carat..
4758 if (this.badge !== '') {
4760 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4765 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4769 href : this.href || "#",
4770 html: this.html || ''
4773 if (this.tagtype == 'a') {
4774 cfg.cn[0].cls = 'nav-link';
4777 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4780 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4782 if(this.glyphicon) {
4783 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4788 cfg.cn[0].html += " <span class='caret'></span>";
4792 if (this.badge !== '') {
4794 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4802 onRender : function(ct, position)
4804 // Roo.log("Call onRender: " + this.xtype);
4805 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4809 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4810 this.navLink = this.el.select('.nav-link',true).first();
4815 initEvents: function()
4817 if (typeof (this.menu) != 'undefined') {
4818 this.menu.parentType = this.xtype;
4819 this.menu.triggerEl = this.el;
4820 this.menu = this.addxtype(Roo.apply({}, this.menu));
4823 this.el.select('a',true).on('click', this.onClick, this);
4825 if(this.tagtype == 'span'){
4826 this.el.select('span',true).on('click', this.onClick, this);
4829 // at this point parent should be available..
4830 this.parent().register(this);
4833 onClick : function(e)
4835 if (e.getTarget('.dropdown-menu-item')) {
4836 // did you click on a menu itemm.... - then don't trigger onclick..
4841 this.preventDefault ||
4844 Roo.log("NavItem - prevent Default?");
4848 if (this.disabled) {
4852 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4853 if (tg && tg.transition) {
4854 Roo.log("waiting for the transitionend");
4860 //Roo.log("fire event clicked");
4861 if(this.fireEvent('click', this, e) === false){
4865 if(this.tagtype == 'span'){
4869 //Roo.log(this.href);
4870 var ael = this.el.select('a',true).first();
4873 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4874 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4875 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4876 return; // ignore... - it's a 'hash' to another page.
4878 Roo.log("NavItem - prevent Default?");
4880 this.scrollToElement(e);
4884 var p = this.parent();
4886 if (['tabs','pills'].indexOf(p.type)!==-1) {
4887 if (typeof(p.setActiveItem) !== 'undefined') {
4888 p.setActiveItem(this);
4892 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4893 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4894 // remove the collapsed menu expand...
4895 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4899 isActive: function () {
4902 setActive : function(state, fire, is_was_active)
4904 if (this.active && !state && this.navId) {
4905 this.was_active = true;
4906 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4908 nv.clearWasActive(this);
4912 this.active = state;
4915 this.el.removeClass('active');
4916 this.navLink ? this.navLink.removeClass('active') : false;
4917 } else if (!this.el.hasClass('active')) {
4919 this.el.addClass('active');
4920 if (Roo.bootstrap.version == 4 && this.navLink ) {
4921 this.navLink.addClass('active');
4926 this.fireEvent('changed', this, state);
4929 // show a panel if it's registered and related..
4931 if (!this.navId || !this.tabId || !state || is_was_active) {
4935 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4939 var pan = tg.getPanelByName(this.tabId);
4943 // if we can not flip to new panel - go back to old nav highlight..
4944 if (false == tg.showPanel(pan)) {
4945 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4947 var onav = nv.getWasActive();
4949 onav.setActive(true, false, true);
4958 // this should not be here...
4959 setDisabled : function(state)
4961 this.disabled = state;
4963 this.el.removeClass('disabled');
4964 } else if (!this.el.hasClass('disabled')) {
4965 this.el.addClass('disabled');
4971 * Fetch the element to display the tooltip on.
4972 * @return {Roo.Element} defaults to this.el
4974 tooltipEl : function()
4976 return this.el.select('' + this.tagtype + '', true).first();
4979 scrollToElement : function(e)
4981 var c = document.body;
4984 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4986 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4987 c = document.documentElement;
4990 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4996 var o = target.calcOffsetsTo(c);
5003 this.fireEvent('scrollto', this, options, e);
5005 Roo.get(c).scrollTo('top', options.value, true);
5018 * <span> icon </span>
5019 * <span> text </span>
5020 * <span>badge </span>
5024 * @class Roo.bootstrap.NavSidebarItem
5025 * @extends Roo.bootstrap.NavItem
5026 * Bootstrap Navbar.NavSidebarItem class
5027 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5028 * {Boolean} open is the menu open
5029 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5030 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5031 * {String} buttonSize (sm|md|lg)the extra classes for the button
5032 * {Boolean} showArrow show arrow next to the text (default true)
5034 * Create a new Navbar Button
5035 * @param {Object} config The config object
5037 Roo.bootstrap.NavSidebarItem = function(config){
5038 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5043 * The raw click event for the entire grid.
5044 * @param {Roo.EventObject} e
5049 * Fires when the active item active state changes
5050 * @param {Roo.bootstrap.NavSidebarItem} this
5051 * @param {boolean} state the new state
5059 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5061 badgeWeight : 'default',
5067 buttonWeight : 'default',
5073 getAutoCreate : function(){
5078 href : this.href || '#',
5084 if(this.buttonView){
5087 href : this.href || '#',
5088 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5101 cfg.cls += ' active';
5104 if (this.disabled) {
5105 cfg.cls += ' disabled';
5108 cfg.cls += ' open x-open';
5111 if (this.glyphicon || this.icon) {
5112 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5113 a.cn.push({ tag : 'i', cls : c }) ;
5116 if(!this.buttonView){
5119 html : this.html || ''
5126 if (this.badge !== '') {
5127 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5133 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5136 a.cls += ' dropdown-toggle treeview' ;
5142 initEvents : function()
5144 if (typeof (this.menu) != 'undefined') {
5145 this.menu.parentType = this.xtype;
5146 this.menu.triggerEl = this.el;
5147 this.menu = this.addxtype(Roo.apply({}, this.menu));
5150 this.el.on('click', this.onClick, this);
5152 if(this.badge !== ''){
5153 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5158 onClick : function(e)
5165 if(this.preventDefault){
5169 this.fireEvent('click', this, e);
5172 disable : function()
5174 this.setDisabled(true);
5179 this.setDisabled(false);
5182 setDisabled : function(state)
5184 if(this.disabled == state){
5188 this.disabled = state;
5191 this.el.addClass('disabled');
5195 this.el.removeClass('disabled');
5200 setActive : function(state)
5202 if(this.active == state){
5206 this.active = state;
5209 this.el.addClass('active');
5213 this.el.removeClass('active');
5218 isActive: function ()
5223 setBadge : function(str)
5229 this.badgeEl.dom.innerHTML = str;
5246 * @class Roo.bootstrap.Row
5247 * @extends Roo.bootstrap.Component
5248 * Bootstrap Row class (contains columns...)
5252 * @param {Object} config The config object
5255 Roo.bootstrap.Row = function(config){
5256 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5259 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5261 getAutoCreate : function(){
5280 * @class Roo.bootstrap.Element
5281 * @extends Roo.bootstrap.Component
5282 * Bootstrap Element class
5283 * @cfg {String} html contents of the element
5284 * @cfg {String} tag tag of the element
5285 * @cfg {String} cls class of the element
5286 * @cfg {Boolean} preventDefault (true|false) default false
5287 * @cfg {Boolean} clickable (true|false) default false
5290 * Create a new Element
5291 * @param {Object} config The config object
5294 Roo.bootstrap.Element = function(config){
5295 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5301 * When a element is chick
5302 * @param {Roo.bootstrap.Element} this
5303 * @param {Roo.EventObject} e
5309 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5314 preventDefault: false,
5317 getAutoCreate : function(){
5321 // cls: this.cls, double assign in parent class Component.js :: onRender
5328 initEvents: function()
5330 Roo.bootstrap.Element.superclass.initEvents.call(this);
5333 this.el.on('click', this.onClick, this);
5338 onClick : function(e)
5340 if(this.preventDefault){
5344 this.fireEvent('click', this, e);
5347 getValue : function()
5349 return this.el.dom.innerHTML;
5352 setValue : function(value)
5354 this.el.dom.innerHTML = value;
5369 * @class Roo.bootstrap.Pagination
5370 * @extends Roo.bootstrap.Component
5371 * Bootstrap Pagination class
5372 * @cfg {String} size xs | sm | md | lg
5373 * @cfg {Boolean} inverse false | true
5376 * Create a new Pagination
5377 * @param {Object} config The config object
5380 Roo.bootstrap.Pagination = function(config){
5381 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5384 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5390 getAutoCreate : function(){
5396 cfg.cls += ' inverse';
5402 cfg.cls += " " + this.cls;
5420 * @class Roo.bootstrap.PaginationItem
5421 * @extends Roo.bootstrap.Component
5422 * Bootstrap PaginationItem class
5423 * @cfg {String} html text
5424 * @cfg {String} href the link
5425 * @cfg {Boolean} preventDefault (true | false) default true
5426 * @cfg {Boolean} active (true | false) default false
5427 * @cfg {Boolean} disabled default false
5431 * Create a new PaginationItem
5432 * @param {Object} config The config object
5436 Roo.bootstrap.PaginationItem = function(config){
5437 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5442 * The raw click event for the entire grid.
5443 * @param {Roo.EventObject} e
5449 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5453 preventDefault: true,
5458 getAutoCreate : function(){
5464 href : this.href ? this.href : '#',
5465 html : this.html ? this.html : ''
5475 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5479 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5485 initEvents: function() {
5487 this.el.on('click', this.onClick, this);
5490 onClick : function(e)
5492 Roo.log('PaginationItem on click ');
5493 if(this.preventDefault){
5501 this.fireEvent('click', this, e);
5517 * @class Roo.bootstrap.Slider
5518 * @extends Roo.bootstrap.Component
5519 * Bootstrap Slider class
5522 * Create a new Slider
5523 * @param {Object} config The config object
5526 Roo.bootstrap.Slider = function(config){
5527 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5530 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5532 getAutoCreate : function(){
5536 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5540 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5552 * Ext JS Library 1.1.1
5553 * Copyright(c) 2006-2007, Ext JS, LLC.
5555 * Originally Released Under LGPL - original licence link has changed is not relivant.
5558 * <script type="text/javascript">
5563 * @class Roo.grid.ColumnModel
5564 * @extends Roo.util.Observable
5565 * This is the default implementation of a ColumnModel used by the Grid. It defines
5566 * the columns in the grid.
5569 var colModel = new Roo.grid.ColumnModel([
5570 {header: "Ticker", width: 60, sortable: true, locked: true},
5571 {header: "Company Name", width: 150, sortable: true},
5572 {header: "Market Cap.", width: 100, sortable: true},
5573 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5574 {header: "Employees", width: 100, sortable: true, resizable: false}
5579 * The config options listed for this class are options which may appear in each
5580 * individual column definition.
5581 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5583 * @param {Object} config An Array of column config objects. See this class's
5584 * config objects for details.
5586 Roo.grid.ColumnModel = function(config){
5588 * The config passed into the constructor
5590 this.config = config;
5593 // if no id, create one
5594 // if the column does not have a dataIndex mapping,
5595 // map it to the order it is in the config
5596 for(var i = 0, len = config.length; i < len; i++){
5598 if(typeof c.dataIndex == "undefined"){
5601 if(typeof c.renderer == "string"){
5602 c.renderer = Roo.util.Format[c.renderer];
5604 if(typeof c.id == "undefined"){
5607 if(c.editor && c.editor.xtype){
5608 c.editor = Roo.factory(c.editor, Roo.grid);
5610 if(c.editor && c.editor.isFormField){
5611 c.editor = new Roo.grid.GridEditor(c.editor);
5613 this.lookup[c.id] = c;
5617 * The width of columns which have no width specified (defaults to 100)
5620 this.defaultWidth = 100;
5623 * Default sortable of columns which have no sortable specified (defaults to false)
5626 this.defaultSortable = false;
5630 * @event widthchange
5631 * Fires when the width of a column changes.
5632 * @param {ColumnModel} this
5633 * @param {Number} columnIndex The column index
5634 * @param {Number} newWidth The new width
5636 "widthchange": true,
5638 * @event headerchange
5639 * Fires when the text of a header changes.
5640 * @param {ColumnModel} this
5641 * @param {Number} columnIndex The column index
5642 * @param {Number} newText The new header text
5644 "headerchange": true,
5646 * @event hiddenchange
5647 * Fires when a column is hidden or "unhidden".
5648 * @param {ColumnModel} this
5649 * @param {Number} columnIndex The column index
5650 * @param {Boolean} hidden true if hidden, false otherwise
5652 "hiddenchange": true,
5654 * @event columnmoved
5655 * Fires when a column is moved.
5656 * @param {ColumnModel} this
5657 * @param {Number} oldIndex
5658 * @param {Number} newIndex
5660 "columnmoved" : true,
5662 * @event columlockchange
5663 * Fires when a column's locked state is changed
5664 * @param {ColumnModel} this
5665 * @param {Number} colIndex
5666 * @param {Boolean} locked true if locked
5668 "columnlockchange" : true
5670 Roo.grid.ColumnModel.superclass.constructor.call(this);
5672 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5674 * @cfg {String} header The header text to display in the Grid view.
5677 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5678 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5679 * specified, the column's index is used as an index into the Record's data Array.
5682 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5683 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5686 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5687 * Defaults to the value of the {@link #defaultSortable} property.
5688 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5691 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5694 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5697 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5700 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5703 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5704 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5705 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5706 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5709 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5712 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5715 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5718 * @cfg {String} cursor (Optional)
5721 * @cfg {String} tooltip (Optional)
5724 * @cfg {Number} xs (Optional)
5727 * @cfg {Number} sm (Optional)
5730 * @cfg {Number} md (Optional)
5733 * @cfg {Number} lg (Optional)
5736 * Returns the id of the column at the specified index.
5737 * @param {Number} index The column index
5738 * @return {String} the id
5740 getColumnId : function(index){
5741 return this.config[index].id;
5745 * Returns the column for a specified id.
5746 * @param {String} id The column id
5747 * @return {Object} the column
5749 getColumnById : function(id){
5750 return this.lookup[id];
5755 * Returns the column for a specified dataIndex.
5756 * @param {String} dataIndex The column dataIndex
5757 * @return {Object|Boolean} the column or false if not found
5759 getColumnByDataIndex: function(dataIndex){
5760 var index = this.findColumnIndex(dataIndex);
5761 return index > -1 ? this.config[index] : false;
5765 * Returns the index for a specified column id.
5766 * @param {String} id The column id
5767 * @return {Number} the index, or -1 if not found
5769 getIndexById : function(id){
5770 for(var i = 0, len = this.config.length; i < len; i++){
5771 if(this.config[i].id == id){
5779 * Returns the index for a specified column dataIndex.
5780 * @param {String} dataIndex The column dataIndex
5781 * @return {Number} the index, or -1 if not found
5784 findColumnIndex : function(dataIndex){
5785 for(var i = 0, len = this.config.length; i < len; i++){
5786 if(this.config[i].dataIndex == dataIndex){
5794 moveColumn : function(oldIndex, newIndex){
5795 var c = this.config[oldIndex];
5796 this.config.splice(oldIndex, 1);
5797 this.config.splice(newIndex, 0, c);
5798 this.dataMap = null;
5799 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5802 isLocked : function(colIndex){
5803 return this.config[colIndex].locked === true;
5806 setLocked : function(colIndex, value, suppressEvent){
5807 if(this.isLocked(colIndex) == value){
5810 this.config[colIndex].locked = value;
5812 this.fireEvent("columnlockchange", this, colIndex, value);
5816 getTotalLockedWidth : function(){
5818 for(var i = 0; i < this.config.length; i++){
5819 if(this.isLocked(i) && !this.isHidden(i)){
5820 this.totalWidth += this.getColumnWidth(i);
5826 getLockedCount : function(){
5827 for(var i = 0, len = this.config.length; i < len; i++){
5828 if(!this.isLocked(i)){
5833 return this.config.length;
5837 * Returns the number of columns.
5840 getColumnCount : function(visibleOnly){
5841 if(visibleOnly === true){
5843 for(var i = 0, len = this.config.length; i < len; i++){
5844 if(!this.isHidden(i)){
5850 return this.config.length;
5854 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5855 * @param {Function} fn
5856 * @param {Object} scope (optional)
5857 * @return {Array} result
5859 getColumnsBy : function(fn, scope){
5861 for(var i = 0, len = this.config.length; i < len; i++){
5862 var c = this.config[i];
5863 if(fn.call(scope||this, c, i) === true){
5871 * Returns true if the specified column is sortable.
5872 * @param {Number} col The column index
5875 isSortable : function(col){
5876 if(typeof this.config[col].sortable == "undefined"){
5877 return this.defaultSortable;
5879 return this.config[col].sortable;
5883 * Returns the rendering (formatting) function defined for the column.
5884 * @param {Number} col The column index.
5885 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5887 getRenderer : function(col){
5888 if(!this.config[col].renderer){
5889 return Roo.grid.ColumnModel.defaultRenderer;
5891 return this.config[col].renderer;
5895 * Sets the rendering (formatting) function for a column.
5896 * @param {Number} col The column index
5897 * @param {Function} fn The function to use to process the cell's raw data
5898 * to return HTML markup for the grid view. The render function is called with
5899 * the following parameters:<ul>
5900 * <li>Data value.</li>
5901 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5902 * <li>css A CSS style string to apply to the table cell.</li>
5903 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5904 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5905 * <li>Row index</li>
5906 * <li>Column index</li>
5907 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5909 setRenderer : function(col, fn){
5910 this.config[col].renderer = fn;
5914 * Returns the width for the specified column.
5915 * @param {Number} col The column index
5918 getColumnWidth : function(col){
5919 return this.config[col].width * 1 || this.defaultWidth;
5923 * Sets the width for a column.
5924 * @param {Number} col The column index
5925 * @param {Number} width The new width
5927 setColumnWidth : function(col, width, suppressEvent){
5928 this.config[col].width = width;
5929 this.totalWidth = null;
5931 this.fireEvent("widthchange", this, col, width);
5936 * Returns the total width of all columns.
5937 * @param {Boolean} includeHidden True to include hidden column widths
5940 getTotalWidth : function(includeHidden){
5941 if(!this.totalWidth){
5942 this.totalWidth = 0;
5943 for(var i = 0, len = this.config.length; i < len; i++){
5944 if(includeHidden || !this.isHidden(i)){
5945 this.totalWidth += this.getColumnWidth(i);
5949 return this.totalWidth;
5953 * Returns the header for the specified column.
5954 * @param {Number} col The column index
5957 getColumnHeader : function(col){
5958 return this.config[col].header;
5962 * Sets the header for a column.
5963 * @param {Number} col The column index
5964 * @param {String} header The new header
5966 setColumnHeader : function(col, header){
5967 this.config[col].header = header;
5968 this.fireEvent("headerchange", this, col, header);
5972 * Returns the tooltip for the specified column.
5973 * @param {Number} col The column index
5976 getColumnTooltip : function(col){
5977 return this.config[col].tooltip;
5980 * Sets the tooltip for a column.
5981 * @param {Number} col The column index
5982 * @param {String} tooltip The new tooltip
5984 setColumnTooltip : function(col, tooltip){
5985 this.config[col].tooltip = tooltip;
5989 * Returns the dataIndex for the specified column.
5990 * @param {Number} col The column index
5993 getDataIndex : function(col){
5994 return this.config[col].dataIndex;
5998 * Sets the dataIndex for a column.
5999 * @param {Number} col The column index
6000 * @param {Number} dataIndex The new dataIndex
6002 setDataIndex : function(col, dataIndex){
6003 this.config[col].dataIndex = dataIndex;
6009 * Returns true if the cell is editable.
6010 * @param {Number} colIndex The column index
6011 * @param {Number} rowIndex The row index - this is nto actually used..?
6014 isCellEditable : function(colIndex, rowIndex){
6015 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6019 * Returns the editor defined for the cell/column.
6020 * return false or null to disable editing.
6021 * @param {Number} colIndex The column index
6022 * @param {Number} rowIndex The row index
6025 getCellEditor : function(colIndex, rowIndex){
6026 return this.config[colIndex].editor;
6030 * Sets if a column is editable.
6031 * @param {Number} col The column index
6032 * @param {Boolean} editable True if the column is editable
6034 setEditable : function(col, editable){
6035 this.config[col].editable = editable;
6040 * Returns true if the column is hidden.
6041 * @param {Number} colIndex The column index
6044 isHidden : function(colIndex){
6045 return this.config[colIndex].hidden;
6050 * Returns true if the column width cannot be changed
6052 isFixed : function(colIndex){
6053 return this.config[colIndex].fixed;
6057 * Returns true if the column can be resized
6060 isResizable : function(colIndex){
6061 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6064 * Sets if a column is hidden.
6065 * @param {Number} colIndex The column index
6066 * @param {Boolean} hidden True if the column is hidden
6068 setHidden : function(colIndex, hidden){
6069 this.config[colIndex].hidden = hidden;
6070 this.totalWidth = null;
6071 this.fireEvent("hiddenchange", this, colIndex, hidden);
6075 * Sets the editor for a column.
6076 * @param {Number} col The column index
6077 * @param {Object} editor The editor object
6079 setEditor : function(col, editor){
6080 this.config[col].editor = editor;
6084 Roo.grid.ColumnModel.defaultRenderer = function(value)
6086 if(typeof value == "object") {
6089 if(typeof value == "string" && value.length < 1){
6093 return String.format("{0}", value);
6096 // Alias for backwards compatibility
6097 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6100 * Ext JS Library 1.1.1
6101 * Copyright(c) 2006-2007, Ext JS, LLC.
6103 * Originally Released Under LGPL - original licence link has changed is not relivant.
6106 * <script type="text/javascript">
6110 * @class Roo.LoadMask
6111 * A simple utility class for generically masking elements while loading data. If the element being masked has
6112 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6113 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6114 * element's UpdateManager load indicator and will be destroyed after the initial load.
6116 * Create a new LoadMask
6117 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6118 * @param {Object} config The config object
6120 Roo.LoadMask = function(el, config){
6121 this.el = Roo.get(el);
6122 Roo.apply(this, config);
6124 this.store.on('beforeload', this.onBeforeLoad, this);
6125 this.store.on('load', this.onLoad, this);
6126 this.store.on('loadexception', this.onLoadException, this);
6127 this.removeMask = false;
6129 var um = this.el.getUpdateManager();
6130 um.showLoadIndicator = false; // disable the default indicator
6131 um.on('beforeupdate', this.onBeforeLoad, this);
6132 um.on('update', this.onLoad, this);
6133 um.on('failure', this.onLoad, this);
6134 this.removeMask = true;
6138 Roo.LoadMask.prototype = {
6140 * @cfg {Boolean} removeMask
6141 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6142 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6146 * The text to display in a centered loading message box (defaults to 'Loading...')
6150 * @cfg {String} msgCls
6151 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6153 msgCls : 'x-mask-loading',
6156 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6162 * Disables the mask to prevent it from being displayed
6164 disable : function(){
6165 this.disabled = true;
6169 * Enables the mask so that it can be displayed
6171 enable : function(){
6172 this.disabled = false;
6175 onLoadException : function()
6179 if (typeof(arguments[3]) != 'undefined') {
6180 Roo.MessageBox.alert("Error loading",arguments[3]);
6184 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6185 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6192 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6197 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6201 onBeforeLoad : function(){
6203 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6208 destroy : function(){
6210 this.store.un('beforeload', this.onBeforeLoad, this);
6211 this.store.un('load', this.onLoad, this);
6212 this.store.un('loadexception', this.onLoadException, this);
6214 var um = this.el.getUpdateManager();
6215 um.un('beforeupdate', this.onBeforeLoad, this);
6216 um.un('update', this.onLoad, this);
6217 um.un('failure', this.onLoad, this);
6228 * @class Roo.bootstrap.Table
6229 * @extends Roo.bootstrap.Component
6230 * Bootstrap Table class
6231 * @cfg {String} cls table class
6232 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6233 * @cfg {String} bgcolor Specifies the background color for a table
6234 * @cfg {Number} border Specifies whether the table cells should have borders or not
6235 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6236 * @cfg {Number} cellspacing Specifies the space between cells
6237 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6238 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6239 * @cfg {String} sortable Specifies that the table should be sortable
6240 * @cfg {String} summary Specifies a summary of the content of a table
6241 * @cfg {Number} width Specifies the width of a table
6242 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6244 * @cfg {boolean} striped Should the rows be alternative striped
6245 * @cfg {boolean} bordered Add borders to the table
6246 * @cfg {boolean} hover Add hover highlighting
6247 * @cfg {boolean} condensed Format condensed
6248 * @cfg {boolean} responsive Format condensed
6249 * @cfg {Boolean} loadMask (true|false) default false
6250 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6251 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6252 * @cfg {Boolean} rowSelection (true|false) default false
6253 * @cfg {Boolean} cellSelection (true|false) default false
6254 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6255 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6256 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6257 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6261 * Create a new Table
6262 * @param {Object} config The config object
6265 Roo.bootstrap.Table = function(config){
6266 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6271 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6272 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6273 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6274 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6276 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6278 this.sm.grid = this;
6279 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6280 this.sm = this.selModel;
6281 this.sm.xmodule = this.xmodule || false;
6284 if (this.cm && typeof(this.cm.config) == 'undefined') {
6285 this.colModel = new Roo.grid.ColumnModel(this.cm);
6286 this.cm = this.colModel;
6287 this.cm.xmodule = this.xmodule || false;
6290 this.store= Roo.factory(this.store, Roo.data);
6291 this.ds = this.store;
6292 this.ds.xmodule = this.xmodule || false;
6295 if (this.footer && this.store) {
6296 this.footer.dataSource = this.ds;
6297 this.footer = Roo.factory(this.footer);
6304 * Fires when a cell is clicked
6305 * @param {Roo.bootstrap.Table} this
6306 * @param {Roo.Element} el
6307 * @param {Number} rowIndex
6308 * @param {Number} columnIndex
6309 * @param {Roo.EventObject} e
6313 * @event celldblclick
6314 * Fires when a cell is double clicked
6315 * @param {Roo.bootstrap.Table} this
6316 * @param {Roo.Element} el
6317 * @param {Number} rowIndex
6318 * @param {Number} columnIndex
6319 * @param {Roo.EventObject} e
6321 "celldblclick" : true,
6324 * Fires when a row is clicked
6325 * @param {Roo.bootstrap.Table} this
6326 * @param {Roo.Element} el
6327 * @param {Number} rowIndex
6328 * @param {Roo.EventObject} e
6332 * @event rowdblclick
6333 * Fires when a row is double clicked
6334 * @param {Roo.bootstrap.Table} this
6335 * @param {Roo.Element} el
6336 * @param {Number} rowIndex
6337 * @param {Roo.EventObject} e
6339 "rowdblclick" : true,
6342 * Fires when a mouseover occur
6343 * @param {Roo.bootstrap.Table} this
6344 * @param {Roo.Element} el
6345 * @param {Number} rowIndex
6346 * @param {Number} columnIndex
6347 * @param {Roo.EventObject} e
6352 * Fires when a mouseout occur
6353 * @param {Roo.bootstrap.Table} this
6354 * @param {Roo.Element} el
6355 * @param {Number} rowIndex
6356 * @param {Number} columnIndex
6357 * @param {Roo.EventObject} e
6362 * Fires when a row is rendered, so you can change add a style to it.
6363 * @param {Roo.bootstrap.Table} this
6364 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6368 * @event rowsrendered
6369 * Fires when all the rows have been rendered
6370 * @param {Roo.bootstrap.Table} this
6372 'rowsrendered' : true,
6374 * @event contextmenu
6375 * The raw contextmenu event for the entire grid.
6376 * @param {Roo.EventObject} e
6378 "contextmenu" : true,
6380 * @event rowcontextmenu
6381 * Fires when a row is right clicked
6382 * @param {Roo.bootstrap.Table} this
6383 * @param {Number} rowIndex
6384 * @param {Roo.EventObject} e
6386 "rowcontextmenu" : true,
6388 * @event cellcontextmenu
6389 * Fires when a cell is right clicked
6390 * @param {Roo.bootstrap.Table} this
6391 * @param {Number} rowIndex
6392 * @param {Number} cellIndex
6393 * @param {Roo.EventObject} e
6395 "cellcontextmenu" : true,
6397 * @event headercontextmenu
6398 * Fires when a header is right clicked
6399 * @param {Roo.bootstrap.Table} this
6400 * @param {Number} columnIndex
6401 * @param {Roo.EventObject} e
6403 "headercontextmenu" : true
6407 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6433 rowSelection : false,
6434 cellSelection : false,
6437 // Roo.Element - the tbody
6439 // Roo.Element - thead element
6442 container: false, // used by gridpanel...
6448 auto_hide_footer : false,
6450 getAutoCreate : function()
6452 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6459 if (this.scrollBody) {
6460 cfg.cls += ' table-body-fixed';
6463 cfg.cls += ' table-striped';
6467 cfg.cls += ' table-hover';
6469 if (this.bordered) {
6470 cfg.cls += ' table-bordered';
6472 if (this.condensed) {
6473 cfg.cls += ' table-condensed';
6475 if (this.responsive) {
6476 cfg.cls += ' table-responsive';
6480 cfg.cls+= ' ' +this.cls;
6483 // this lot should be simplifed...
6496 ].forEach(function(k) {
6504 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6507 if(this.store || this.cm){
6508 if(this.headerShow){
6509 cfg.cn.push(this.renderHeader());
6512 cfg.cn.push(this.renderBody());
6514 if(this.footerShow){
6515 cfg.cn.push(this.renderFooter());
6517 // where does this come from?
6518 //cfg.cls+= ' TableGrid';
6521 return { cn : [ cfg ] };
6524 initEvents : function()
6526 if(!this.store || !this.cm){
6529 if (this.selModel) {
6530 this.selModel.initEvents();
6534 //Roo.log('initEvents with ds!!!!');
6536 this.mainBody = this.el.select('tbody', true).first();
6537 this.mainHead = this.el.select('thead', true).first();
6538 this.mainFoot = this.el.select('tfoot', true).first();
6544 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6545 e.on('click', _this.sort, _this);
6548 this.mainBody.on("click", this.onClick, this);
6549 this.mainBody.on("dblclick", this.onDblClick, this);
6551 // why is this done????? = it breaks dialogs??
6552 //this.parent().el.setStyle('position', 'relative');
6556 this.footer.parentId = this.id;
6557 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6560 this.el.select('tfoot tr td').first().addClass('hide');
6565 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6568 this.store.on('load', this.onLoad, this);
6569 this.store.on('beforeload', this.onBeforeLoad, this);
6570 this.store.on('update', this.onUpdate, this);
6571 this.store.on('add', this.onAdd, this);
6572 this.store.on("clear", this.clear, this);
6574 this.el.on("contextmenu", this.onContextMenu, this);
6576 this.mainBody.on('scroll', this.onBodyScroll, this);
6578 this.cm.on("headerchange", this.onHeaderChange, this);
6580 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6584 onContextMenu : function(e, t)
6586 this.processEvent("contextmenu", e);
6589 processEvent : function(name, e)
6591 if (name != 'touchstart' ) {
6592 this.fireEvent(name, e);
6595 var t = e.getTarget();
6597 var cell = Roo.get(t);
6603 if(cell.findParent('tfoot', false, true)){
6607 if(cell.findParent('thead', false, true)){
6609 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6610 cell = Roo.get(t).findParent('th', false, true);
6612 Roo.log("failed to find th in thead?");
6613 Roo.log(e.getTarget());
6618 var cellIndex = cell.dom.cellIndex;
6620 var ename = name == 'touchstart' ? 'click' : name;
6621 this.fireEvent("header" + ename, this, cellIndex, e);
6626 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6627 cell = Roo.get(t).findParent('td', false, true);
6629 Roo.log("failed to find th in tbody?");
6630 Roo.log(e.getTarget());
6635 var row = cell.findParent('tr', false, true);
6636 var cellIndex = cell.dom.cellIndex;
6637 var rowIndex = row.dom.rowIndex - 1;
6641 this.fireEvent("row" + name, this, rowIndex, e);
6645 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6651 onMouseover : function(e, el)
6653 var cell = Roo.get(el);
6659 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6660 cell = cell.findParent('td', false, true);
6663 var row = cell.findParent('tr', false, true);
6664 var cellIndex = cell.dom.cellIndex;
6665 var rowIndex = row.dom.rowIndex - 1; // start from 0
6667 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6671 onMouseout : function(e, el)
6673 var cell = Roo.get(el);
6679 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6680 cell = cell.findParent('td', false, true);
6683 var row = cell.findParent('tr', false, true);
6684 var cellIndex = cell.dom.cellIndex;
6685 var rowIndex = row.dom.rowIndex - 1; // start from 0
6687 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6691 onClick : function(e, el)
6693 var cell = Roo.get(el);
6695 if(!cell || (!this.cellSelection && !this.rowSelection)){
6699 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6700 cell = cell.findParent('td', false, true);
6703 if(!cell || typeof(cell) == 'undefined'){
6707 var row = cell.findParent('tr', false, true);
6709 if(!row || typeof(row) == 'undefined'){
6713 var cellIndex = cell.dom.cellIndex;
6714 var rowIndex = this.getRowIndex(row);
6716 // why??? - should these not be based on SelectionModel?
6717 if(this.cellSelection){
6718 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6721 if(this.rowSelection){
6722 this.fireEvent('rowclick', this, row, rowIndex, e);
6728 onDblClick : function(e,el)
6730 var cell = Roo.get(el);
6732 if(!cell || (!this.cellSelection && !this.rowSelection)){
6736 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6737 cell = cell.findParent('td', false, true);
6740 if(!cell || typeof(cell) == 'undefined'){
6744 var row = cell.findParent('tr', false, true);
6746 if(!row || typeof(row) == 'undefined'){
6750 var cellIndex = cell.dom.cellIndex;
6751 var rowIndex = this.getRowIndex(row);
6753 if(this.cellSelection){
6754 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6757 if(this.rowSelection){
6758 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6762 sort : function(e,el)
6764 var col = Roo.get(el);
6766 if(!col.hasClass('sortable')){
6770 var sort = col.attr('sort');
6773 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6777 this.store.sortInfo = {field : sort, direction : dir};
6780 Roo.log("calling footer first");
6781 this.footer.onClick('first');
6784 this.store.load({ params : { start : 0 } });
6788 renderHeader : function()
6796 this.totalWidth = 0;
6798 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6800 var config = cm.config[i];
6804 cls : 'x-hcol-' + i,
6806 html: cm.getColumnHeader(i)
6811 if(typeof(config.sortable) != 'undefined' && config.sortable){
6813 c.html = '<i class="glyphicon"></i>' + c.html;
6816 // could use BS4 hidden-..-down
6818 if(typeof(config.lgHeader) != 'undefined'){
6819 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6822 if(typeof(config.mdHeader) != 'undefined'){
6823 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6826 if(typeof(config.smHeader) != 'undefined'){
6827 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6830 if(typeof(config.xsHeader) != 'undefined'){
6831 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6838 if(typeof(config.tooltip) != 'undefined'){
6839 c.tooltip = config.tooltip;
6842 if(typeof(config.colspan) != 'undefined'){
6843 c.colspan = config.colspan;
6846 if(typeof(config.hidden) != 'undefined' && config.hidden){
6847 c.style += ' display:none;';
6850 if(typeof(config.dataIndex) != 'undefined'){
6851 c.sort = config.dataIndex;
6856 if(typeof(config.align) != 'undefined' && config.align.length){
6857 c.style += ' text-align:' + config.align + ';';
6860 if(typeof(config.width) != 'undefined'){
6861 c.style += ' width:' + config.width + 'px;';
6862 this.totalWidth += config.width;
6864 this.totalWidth += 100; // assume minimum of 100 per column?
6867 if(typeof(config.cls) != 'undefined'){
6868 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6871 ['xs','sm','md','lg'].map(function(size){
6873 if(typeof(config[size]) == 'undefined'){
6877 if (!config[size]) { // 0 = hidden
6878 // BS 4 '0' is treated as hide that column and below.
6879 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6883 c.cls += ' col-' + size + '-' + config[size] + (
6884 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6896 renderBody : function()
6906 colspan : this.cm.getColumnCount()
6916 renderFooter : function()
6926 colspan : this.cm.getColumnCount()
6940 // Roo.log('ds onload');
6945 var ds = this.store;
6947 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6948 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6949 if (_this.store.sortInfo) {
6951 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6952 e.select('i', true).addClass(['glyphicon-arrow-up']);
6955 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6956 e.select('i', true).addClass(['glyphicon-arrow-down']);
6961 var tbody = this.mainBody;
6963 if(ds.getCount() > 0){
6964 ds.data.each(function(d,rowIndex){
6965 var row = this.renderRow(cm, ds, rowIndex);
6967 tbody.createChild(row);
6971 if(row.cellObjects.length){
6972 Roo.each(row.cellObjects, function(r){
6973 _this.renderCellObject(r);
6980 var tfoot = this.el.select('tfoot', true).first();
6982 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6984 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6986 var total = this.ds.getTotalCount();
6988 if(this.footer.pageSize < total){
6989 this.mainFoot.show();
6993 Roo.each(this.el.select('tbody td', true).elements, function(e){
6994 e.on('mouseover', _this.onMouseover, _this);
6997 Roo.each(this.el.select('tbody td', true).elements, function(e){
6998 e.on('mouseout', _this.onMouseout, _this);
7000 this.fireEvent('rowsrendered', this);
7006 onUpdate : function(ds,record)
7008 this.refreshRow(record);
7012 onRemove : function(ds, record, index, isUpdate){
7013 if(isUpdate !== true){
7014 this.fireEvent("beforerowremoved", this, index, record);
7016 var bt = this.mainBody.dom;
7018 var rows = this.el.select('tbody > tr', true).elements;
7020 if(typeof(rows[index]) != 'undefined'){
7021 bt.removeChild(rows[index].dom);
7024 // if(bt.rows[index]){
7025 // bt.removeChild(bt.rows[index]);
7028 if(isUpdate !== true){
7029 //this.stripeRows(index);
7030 //this.syncRowHeights(index, index);
7032 this.fireEvent("rowremoved", this, index, record);
7036 onAdd : function(ds, records, rowIndex)
7038 //Roo.log('on Add called');
7039 // - note this does not handle multiple adding very well..
7040 var bt = this.mainBody.dom;
7041 for (var i =0 ; i < records.length;i++) {
7042 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7043 //Roo.log(records[i]);
7044 //Roo.log(this.store.getAt(rowIndex+i));
7045 this.insertRow(this.store, rowIndex + i, false);
7052 refreshRow : function(record){
7053 var ds = this.store, index;
7054 if(typeof record == 'number'){
7056 record = ds.getAt(index);
7058 index = ds.indexOf(record);
7060 this.insertRow(ds, index, true);
7062 this.onRemove(ds, record, index+1, true);
7064 //this.syncRowHeights(index, index);
7066 this.fireEvent("rowupdated", this, index, record);
7069 insertRow : function(dm, rowIndex, isUpdate){
7072 this.fireEvent("beforerowsinserted", this, rowIndex);
7074 //var s = this.getScrollState();
7075 var row = this.renderRow(this.cm, this.store, rowIndex);
7076 // insert before rowIndex..
7077 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7081 if(row.cellObjects.length){
7082 Roo.each(row.cellObjects, function(r){
7083 _this.renderCellObject(r);
7088 this.fireEvent("rowsinserted", this, rowIndex);
7089 //this.syncRowHeights(firstRow, lastRow);
7090 //this.stripeRows(firstRow);
7097 getRowDom : function(rowIndex)
7099 var rows = this.el.select('tbody > tr', true).elements;
7101 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7104 // returns the object tree for a tr..
7107 renderRow : function(cm, ds, rowIndex)
7109 var d = ds.getAt(rowIndex);
7113 cls : 'x-row-' + rowIndex,
7117 var cellObjects = [];
7119 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7120 var config = cm.config[i];
7122 var renderer = cm.getRenderer(i);
7126 if(typeof(renderer) !== 'undefined'){
7127 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7129 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7130 // and are rendered into the cells after the row is rendered - using the id for the element.
7132 if(typeof(value) === 'object'){
7142 rowIndex : rowIndex,
7147 this.fireEvent('rowclass', this, rowcfg);
7151 cls : rowcfg.rowClass + ' x-col-' + i,
7153 html: (typeof(value) === 'object') ? '' : value
7160 if(typeof(config.colspan) != 'undefined'){
7161 td.colspan = config.colspan;
7164 if(typeof(config.hidden) != 'undefined' && config.hidden){
7165 td.style += ' display:none;';
7168 if(typeof(config.align) != 'undefined' && config.align.length){
7169 td.style += ' text-align:' + config.align + ';';
7171 if(typeof(config.valign) != 'undefined' && config.valign.length){
7172 td.style += ' vertical-align:' + config.valign + ';';
7175 if(typeof(config.width) != 'undefined'){
7176 td.style += ' width:' + config.width + 'px;';
7179 if(typeof(config.cursor) != 'undefined'){
7180 td.style += ' cursor:' + config.cursor + ';';
7183 if(typeof(config.cls) != 'undefined'){
7184 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7187 ['xs','sm','md','lg'].map(function(size){
7189 if(typeof(config[size]) == 'undefined'){
7195 if (!config[size]) { // 0 = hidden
7196 // BS 4 '0' is treated as hide that column and below.
7197 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7201 td.cls += ' col-' + size + '-' + config[size] + (
7202 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7212 row.cellObjects = cellObjects;
7220 onBeforeLoad : function()
7229 this.el.select('tbody', true).first().dom.innerHTML = '';
7232 * Show or hide a row.
7233 * @param {Number} rowIndex to show or hide
7234 * @param {Boolean} state hide
7236 setRowVisibility : function(rowIndex, state)
7238 var bt = this.mainBody.dom;
7240 var rows = this.el.select('tbody > tr', true).elements;
7242 if(typeof(rows[rowIndex]) == 'undefined'){
7245 rows[rowIndex].dom.style.display = state ? '' : 'none';
7249 getSelectionModel : function(){
7251 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7253 return this.selModel;
7256 * Render the Roo.bootstrap object from renderder
7258 renderCellObject : function(r)
7262 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7264 var t = r.cfg.render(r.container);
7267 Roo.each(r.cfg.cn, function(c){
7269 container: t.getChildContainer(),
7272 _this.renderCellObject(child);
7277 getRowIndex : function(row)
7281 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7292 * Returns the grid's underlying element = used by panel.Grid
7293 * @return {Element} The element
7295 getGridEl : function(){
7299 * Forces a resize - used by panel.Grid
7300 * @return {Element} The element
7302 autoSize : function()
7304 //var ctr = Roo.get(this.container.dom.parentElement);
7305 var ctr = Roo.get(this.el.dom);
7307 var thd = this.getGridEl().select('thead',true).first();
7308 var tbd = this.getGridEl().select('tbody', true).first();
7309 var tfd = this.getGridEl().select('tfoot', true).first();
7311 var cw = ctr.getWidth();
7315 tbd.setSize(ctr.getWidth(),
7316 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7318 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7321 cw = Math.max(cw, this.totalWidth);
7322 this.getGridEl().select('tr',true).setWidth(cw);
7323 // resize 'expandable coloumn?
7325 return; // we doe not have a view in this design..
7328 onBodyScroll: function()
7330 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7332 this.mainHead.setStyle({
7333 'position' : 'relative',
7334 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7340 var scrollHeight = this.mainBody.dom.scrollHeight;
7342 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7344 var height = this.mainBody.getHeight();
7346 if(scrollHeight - height == scrollTop) {
7348 var total = this.ds.getTotalCount();
7350 if(this.footer.cursor + this.footer.pageSize < total){
7352 this.footer.ds.load({
7354 start : this.footer.cursor + this.footer.pageSize,
7355 limit : this.footer.pageSize
7365 onHeaderChange : function()
7367 var header = this.renderHeader();
7368 var table = this.el.select('table', true).first();
7370 this.mainHead.remove();
7371 this.mainHead = table.createChild(header, this.mainBody, false);
7374 onHiddenChange : function(colModel, colIndex, hidden)
7376 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7377 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7379 this.CSS.updateRule(thSelector, "display", "");
7380 this.CSS.updateRule(tdSelector, "display", "");
7383 this.CSS.updateRule(thSelector, "display", "none");
7384 this.CSS.updateRule(tdSelector, "display", "none");
7387 this.onHeaderChange();
7391 setColumnWidth: function(col_index, width)
7393 // width = "md-2 xs-2..."
7394 if(!this.colModel.config[col_index]) {
7398 var w = width.split(" ");
7400 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7402 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7405 for(var j = 0; j < w.length; j++) {
7411 var size_cls = w[j].split("-");
7413 if(!Number.isInteger(size_cls[1] * 1)) {
7417 if(!this.colModel.config[col_index][size_cls[0]]) {
7421 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7425 h_row[0].classList.replace(
7426 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7427 "col-"+size_cls[0]+"-"+size_cls[1]
7430 for(var i = 0; i < rows.length; i++) {
7432 var size_cls = w[j].split("-");
7434 if(!Number.isInteger(size_cls[1] * 1)) {
7438 if(!this.colModel.config[col_index][size_cls[0]]) {
7442 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7446 rows[i].classList.replace(
7447 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7448 "col-"+size_cls[0]+"-"+size_cls[1]
7452 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7467 * @class Roo.bootstrap.TableCell
7468 * @extends Roo.bootstrap.Component
7469 * Bootstrap TableCell class
7470 * @cfg {String} html cell contain text
7471 * @cfg {String} cls cell class
7472 * @cfg {String} tag cell tag (td|th) default td
7473 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7474 * @cfg {String} align Aligns the content in a cell
7475 * @cfg {String} axis Categorizes cells
7476 * @cfg {String} bgcolor Specifies the background color of a cell
7477 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7478 * @cfg {Number} colspan Specifies the number of columns a cell should span
7479 * @cfg {String} headers Specifies one or more header cells a cell is related to
7480 * @cfg {Number} height Sets the height of a cell
7481 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7482 * @cfg {Number} rowspan Sets the number of rows a cell should span
7483 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7484 * @cfg {String} valign Vertical aligns the content in a cell
7485 * @cfg {Number} width Specifies the width of a cell
7488 * Create a new TableCell
7489 * @param {Object} config The config object
7492 Roo.bootstrap.TableCell = function(config){
7493 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7496 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7516 getAutoCreate : function(){
7517 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7537 cfg.align=this.align
7543 cfg.bgcolor=this.bgcolor
7546 cfg.charoff=this.charoff
7549 cfg.colspan=this.colspan
7552 cfg.headers=this.headers
7555 cfg.height=this.height
7558 cfg.nowrap=this.nowrap
7561 cfg.rowspan=this.rowspan
7564 cfg.scope=this.scope
7567 cfg.valign=this.valign
7570 cfg.width=this.width
7589 * @class Roo.bootstrap.TableRow
7590 * @extends Roo.bootstrap.Component
7591 * Bootstrap TableRow class
7592 * @cfg {String} cls row class
7593 * @cfg {String} align Aligns the content in a table row
7594 * @cfg {String} bgcolor Specifies a background color for a table row
7595 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7596 * @cfg {String} valign Vertical aligns the content in a table row
7599 * Create a new TableRow
7600 * @param {Object} config The config object
7603 Roo.bootstrap.TableRow = function(config){
7604 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7607 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7615 getAutoCreate : function(){
7616 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7626 cfg.align = this.align;
7629 cfg.bgcolor = this.bgcolor;
7632 cfg.charoff = this.charoff;
7635 cfg.valign = this.valign;
7653 * @class Roo.bootstrap.TableBody
7654 * @extends Roo.bootstrap.Component
7655 * Bootstrap TableBody class
7656 * @cfg {String} cls element class
7657 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7658 * @cfg {String} align Aligns the content inside the element
7659 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7660 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7663 * Create a new TableBody
7664 * @param {Object} config The config object
7667 Roo.bootstrap.TableBody = function(config){
7668 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7671 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7679 getAutoCreate : function(){
7680 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7694 cfg.align = this.align;
7697 cfg.charoff = this.charoff;
7700 cfg.valign = this.valign;
7707 // initEvents : function()
7714 // this.store = Roo.factory(this.store, Roo.data);
7715 // this.store.on('load', this.onLoad, this);
7717 // this.store.load();
7721 // onLoad: function ()
7723 // this.fireEvent('load', this);
7733 * Ext JS Library 1.1.1
7734 * Copyright(c) 2006-2007, Ext JS, LLC.
7736 * Originally Released Under LGPL - original licence link has changed is not relivant.
7739 * <script type="text/javascript">
7742 // as we use this in bootstrap.
7743 Roo.namespace('Roo.form');
7745 * @class Roo.form.Action
7746 * Internal Class used to handle form actions
7748 * @param {Roo.form.BasicForm} el The form element or its id
7749 * @param {Object} config Configuration options
7754 // define the action interface
7755 Roo.form.Action = function(form, options){
7757 this.options = options || {};
7760 * Client Validation Failed
7763 Roo.form.Action.CLIENT_INVALID = 'client';
7765 * Server Validation Failed
7768 Roo.form.Action.SERVER_INVALID = 'server';
7770 * Connect to Server Failed
7773 Roo.form.Action.CONNECT_FAILURE = 'connect';
7775 * Reading Data from Server Failed
7778 Roo.form.Action.LOAD_FAILURE = 'load';
7780 Roo.form.Action.prototype = {
7782 failureType : undefined,
7783 response : undefined,
7787 run : function(options){
7792 success : function(response){
7797 handleResponse : function(response){
7801 // default connection failure
7802 failure : function(response){
7804 this.response = response;
7805 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7806 this.form.afterAction(this, false);
7809 processResponse : function(response){
7810 this.response = response;
7811 if(!response.responseText){
7814 this.result = this.handleResponse(response);
7818 // utility functions used internally
7819 getUrl : function(appendParams){
7820 var url = this.options.url || this.form.url || this.form.el.dom.action;
7822 var p = this.getParams();
7824 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7830 getMethod : function(){
7831 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7834 getParams : function(){
7835 var bp = this.form.baseParams;
7836 var p = this.options.params;
7838 if(typeof p == "object"){
7839 p = Roo.urlEncode(Roo.applyIf(p, bp));
7840 }else if(typeof p == 'string' && bp){
7841 p += '&' + Roo.urlEncode(bp);
7844 p = Roo.urlEncode(bp);
7849 createCallback : function(){
7851 success: this.success,
7852 failure: this.failure,
7854 timeout: (this.form.timeout*1000),
7855 upload: this.form.fileUpload ? this.success : undefined
7860 Roo.form.Action.Submit = function(form, options){
7861 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7864 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7867 haveProgress : false,
7868 uploadComplete : false,
7870 // uploadProgress indicator.
7871 uploadProgress : function()
7873 if (!this.form.progressUrl) {
7877 if (!this.haveProgress) {
7878 Roo.MessageBox.progress("Uploading", "Uploading");
7880 if (this.uploadComplete) {
7881 Roo.MessageBox.hide();
7885 this.haveProgress = true;
7887 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7889 var c = new Roo.data.Connection();
7891 url : this.form.progressUrl,
7896 success : function(req){
7897 //console.log(data);
7901 rdata = Roo.decode(req.responseText)
7903 Roo.log("Invalid data from server..");
7907 if (!rdata || !rdata.success) {
7909 Roo.MessageBox.alert(Roo.encode(rdata));
7912 var data = rdata.data;
7914 if (this.uploadComplete) {
7915 Roo.MessageBox.hide();
7920 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7921 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7924 this.uploadProgress.defer(2000,this);
7927 failure: function(data) {
7928 Roo.log('progress url failed ');
7939 // run get Values on the form, so it syncs any secondary forms.
7940 this.form.getValues();
7942 var o = this.options;
7943 var method = this.getMethod();
7944 var isPost = method == 'POST';
7945 if(o.clientValidation === false || this.form.isValid()){
7947 if (this.form.progressUrl) {
7948 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7949 (new Date() * 1) + '' + Math.random());
7954 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7955 form:this.form.el.dom,
7956 url:this.getUrl(!isPost),
7958 params:isPost ? this.getParams() : null,
7959 isUpload: this.form.fileUpload,
7960 formData : this.form.formData
7963 this.uploadProgress();
7965 }else if (o.clientValidation !== false){ // client validation failed
7966 this.failureType = Roo.form.Action.CLIENT_INVALID;
7967 this.form.afterAction(this, false);
7971 success : function(response)
7973 this.uploadComplete= true;
7974 if (this.haveProgress) {
7975 Roo.MessageBox.hide();
7979 var result = this.processResponse(response);
7980 if(result === true || result.success){
7981 this.form.afterAction(this, true);
7985 this.form.markInvalid(result.errors);
7986 this.failureType = Roo.form.Action.SERVER_INVALID;
7988 this.form.afterAction(this, false);
7990 failure : function(response)
7992 this.uploadComplete= true;
7993 if (this.haveProgress) {
7994 Roo.MessageBox.hide();
7997 this.response = response;
7998 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7999 this.form.afterAction(this, false);
8002 handleResponse : function(response){
8003 if(this.form.errorReader){
8004 var rs = this.form.errorReader.read(response);
8007 for(var i = 0, len = rs.records.length; i < len; i++) {
8008 var r = rs.records[i];
8012 if(errors.length < 1){
8016 success : rs.success,
8022 ret = Roo.decode(response.responseText);
8026 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8036 Roo.form.Action.Load = function(form, options){
8037 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8038 this.reader = this.form.reader;
8041 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8046 Roo.Ajax.request(Roo.apply(
8047 this.createCallback(), {
8048 method:this.getMethod(),
8049 url:this.getUrl(false),
8050 params:this.getParams()
8054 success : function(response){
8056 var result = this.processResponse(response);
8057 if(result === true || !result.success || !result.data){
8058 this.failureType = Roo.form.Action.LOAD_FAILURE;
8059 this.form.afterAction(this, false);
8062 this.form.clearInvalid();
8063 this.form.setValues(result.data);
8064 this.form.afterAction(this, true);
8067 handleResponse : function(response){
8068 if(this.form.reader){
8069 var rs = this.form.reader.read(response);
8070 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8072 success : rs.success,
8076 return Roo.decode(response.responseText);
8080 Roo.form.Action.ACTION_TYPES = {
8081 'load' : Roo.form.Action.Load,
8082 'submit' : Roo.form.Action.Submit
8091 * @class Roo.bootstrap.Form
8092 * @extends Roo.bootstrap.Component
8093 * Bootstrap Form class
8094 * @cfg {String} method GET | POST (default POST)
8095 * @cfg {String} labelAlign top | left (default top)
8096 * @cfg {String} align left | right - for navbars
8097 * @cfg {Boolean} loadMask load mask when submit (default true)
8102 * @param {Object} config The config object
8106 Roo.bootstrap.Form = function(config){
8108 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8110 Roo.bootstrap.Form.popover.apply();
8114 * @event clientvalidation
8115 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8116 * @param {Form} this
8117 * @param {Boolean} valid true if the form has passed client-side validation
8119 clientvalidation: true,
8121 * @event beforeaction
8122 * Fires before any action is performed. Return false to cancel the action.
8123 * @param {Form} this
8124 * @param {Action} action The action to be performed
8128 * @event actionfailed
8129 * Fires when an action fails.
8130 * @param {Form} this
8131 * @param {Action} action The action that failed
8133 actionfailed : true,
8135 * @event actioncomplete
8136 * Fires when an action is completed.
8137 * @param {Form} this
8138 * @param {Action} action The action that completed
8140 actioncomplete : true
8144 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8147 * @cfg {String} method
8148 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8153 * The URL to use for form actions if one isn't supplied in the action options.
8156 * @cfg {Boolean} fileUpload
8157 * Set to true if this form is a file upload.
8161 * @cfg {Object} baseParams
8162 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8166 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8170 * @cfg {Sting} align (left|right) for navbar forms
8175 activeAction : null,
8178 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8179 * element by passing it or its id or mask the form itself by passing in true.
8182 waitMsgTarget : false,
8187 * @cfg {Boolean} errorMask (true|false) default false
8192 * @cfg {Number} maskOffset Default 100
8197 * @cfg {Boolean} maskBody
8201 getAutoCreate : function(){
8205 method : this.method || 'POST',
8206 id : this.id || Roo.id(),
8209 if (this.parent().xtype.match(/^Nav/)) {
8210 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8214 if (this.labelAlign == 'left' ) {
8215 cfg.cls += ' form-horizontal';
8221 initEvents : function()
8223 this.el.on('submit', this.onSubmit, this);
8224 // this was added as random key presses on the form where triggering form submit.
8225 this.el.on('keypress', function(e) {
8226 if (e.getCharCode() != 13) {
8229 // we might need to allow it for textareas.. and some other items.
8230 // check e.getTarget().
8232 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8236 Roo.log("keypress blocked");
8244 onSubmit : function(e){
8249 * Returns true if client-side validation on the form is successful.
8252 isValid : function(){
8253 var items = this.getItems();
8257 items.each(function(f){
8263 Roo.log('invalid field: ' + f.name);
8267 if(!target && f.el.isVisible(true)){
8273 if(this.errorMask && !valid){
8274 Roo.bootstrap.Form.popover.mask(this, target);
8281 * Returns true if any fields in this form have changed since their original load.
8284 isDirty : function(){
8286 var items = this.getItems();
8287 items.each(function(f){
8297 * Performs a predefined action (submit or load) or custom actions you define on this form.
8298 * @param {String} actionName The name of the action type
8299 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8300 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8301 * accept other config options):
8303 Property Type Description
8304 ---------------- --------------- ----------------------------------------------------------------------------------
8305 url String The url for the action (defaults to the form's url)
8306 method String The form method to use (defaults to the form's method, or POST if not defined)
8307 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8308 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8309 validate the form on the client (defaults to false)
8311 * @return {BasicForm} this
8313 doAction : function(action, options){
8314 if(typeof action == 'string'){
8315 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8317 if(this.fireEvent('beforeaction', this, action) !== false){
8318 this.beforeAction(action);
8319 action.run.defer(100, action);
8325 beforeAction : function(action){
8326 var o = action.options;
8331 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8333 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8336 // not really supported yet.. ??
8338 //if(this.waitMsgTarget === true){
8339 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8340 //}else if(this.waitMsgTarget){
8341 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8342 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8344 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8350 afterAction : function(action, success){
8351 this.activeAction = null;
8352 var o = action.options;
8357 Roo.get(document.body).unmask();
8363 //if(this.waitMsgTarget === true){
8364 // this.el.unmask();
8365 //}else if(this.waitMsgTarget){
8366 // this.waitMsgTarget.unmask();
8368 // Roo.MessageBox.updateProgress(1);
8369 // Roo.MessageBox.hide();
8376 Roo.callback(o.success, o.scope, [this, action]);
8377 this.fireEvent('actioncomplete', this, action);
8381 // failure condition..
8382 // we have a scenario where updates need confirming.
8383 // eg. if a locking scenario exists..
8384 // we look for { errors : { needs_confirm : true }} in the response.
8386 (typeof(action.result) != 'undefined') &&
8387 (typeof(action.result.errors) != 'undefined') &&
8388 (typeof(action.result.errors.needs_confirm) != 'undefined')
8391 Roo.log("not supported yet");
8394 Roo.MessageBox.confirm(
8395 "Change requires confirmation",
8396 action.result.errorMsg,
8401 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8411 Roo.callback(o.failure, o.scope, [this, action]);
8412 // show an error message if no failed handler is set..
8413 if (!this.hasListener('actionfailed')) {
8414 Roo.log("need to add dialog support");
8416 Roo.MessageBox.alert("Error",
8417 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8418 action.result.errorMsg :
8419 "Saving Failed, please check your entries or try again"
8424 this.fireEvent('actionfailed', this, action);
8429 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8430 * @param {String} id The value to search for
8433 findField : function(id){
8434 var items = this.getItems();
8435 var field = items.get(id);
8437 items.each(function(f){
8438 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8445 return field || null;
8448 * Mark fields in this form invalid in bulk.
8449 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8450 * @return {BasicForm} this
8452 markInvalid : function(errors){
8453 if(errors instanceof Array){
8454 for(var i = 0, len = errors.length; i < len; i++){
8455 var fieldError = errors[i];
8456 var f = this.findField(fieldError.id);
8458 f.markInvalid(fieldError.msg);
8464 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8465 field.markInvalid(errors[id]);
8469 //Roo.each(this.childForms || [], function (f) {
8470 // f.markInvalid(errors);
8477 * Set values for fields in this form in bulk.
8478 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8479 * @return {BasicForm} this
8481 setValues : function(values){
8482 if(values instanceof Array){ // array of objects
8483 for(var i = 0, len = values.length; i < len; i++){
8485 var f = this.findField(v.id);
8487 f.setValue(v.value);
8488 if(this.trackResetOnLoad){
8489 f.originalValue = f.getValue();
8493 }else{ // object hash
8496 if(typeof values[id] != 'function' && (field = this.findField(id))){
8498 if (field.setFromData &&
8500 field.displayField &&
8501 // combos' with local stores can
8502 // be queried via setValue()
8503 // to set their value..
8504 (field.store && !field.store.isLocal)
8508 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8509 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8510 field.setFromData(sd);
8512 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8514 field.setFromData(values);
8517 field.setValue(values[id]);
8521 if(this.trackResetOnLoad){
8522 field.originalValue = field.getValue();
8528 //Roo.each(this.childForms || [], function (f) {
8529 // f.setValues(values);
8536 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8537 * they are returned as an array.
8538 * @param {Boolean} asString
8541 getValues : function(asString){
8542 //if (this.childForms) {
8543 // copy values from the child forms
8544 // Roo.each(this.childForms, function (f) {
8545 // this.setValues(f.getValues());
8551 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8552 if(asString === true){
8555 return Roo.urlDecode(fs);
8559 * Returns the fields in this form as an object with key/value pairs.
8560 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8563 getFieldValues : function(with_hidden)
8565 var items = this.getItems();
8567 items.each(function(f){
8573 var v = f.getValue();
8575 if (f.inputType =='radio') {
8576 if (typeof(ret[f.getName()]) == 'undefined') {
8577 ret[f.getName()] = ''; // empty..
8580 if (!f.el.dom.checked) {
8588 if(f.xtype == 'MoneyField'){
8589 ret[f.currencyName] = f.getCurrency();
8592 // not sure if this supported any more..
8593 if ((typeof(v) == 'object') && f.getRawValue) {
8594 v = f.getRawValue() ; // dates..
8596 // combo boxes where name != hiddenName...
8597 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8598 ret[f.name] = f.getRawValue();
8600 ret[f.getName()] = v;
8607 * Clears all invalid messages in this form.
8608 * @return {BasicForm} this
8610 clearInvalid : function(){
8611 var items = this.getItems();
8613 items.each(function(f){
8622 * @return {BasicForm} this
8625 var items = this.getItems();
8626 items.each(function(f){
8630 Roo.each(this.childForms || [], function (f) {
8638 getItems : function()
8640 var r=new Roo.util.MixedCollection(false, function(o){
8641 return o.id || (o.id = Roo.id());
8643 var iter = function(el) {
8650 Roo.each(el.items,function(e) {
8659 hideFields : function(items)
8661 Roo.each(items, function(i){
8663 var f = this.findField(i);
8674 showFields : function(items)
8676 Roo.each(items, function(i){
8678 var f = this.findField(i);
8691 Roo.apply(Roo.bootstrap.Form, {
8718 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8719 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8720 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8721 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8724 this.maskEl.top.enableDisplayMode("block");
8725 this.maskEl.left.enableDisplayMode("block");
8726 this.maskEl.bottom.enableDisplayMode("block");
8727 this.maskEl.right.enableDisplayMode("block");
8729 this.toolTip = new Roo.bootstrap.Tooltip({
8730 cls : 'roo-form-error-popover',
8732 'left' : ['r-l', [-2,0], 'right'],
8733 'right' : ['l-r', [2,0], 'left'],
8734 'bottom' : ['tl-bl', [0,2], 'top'],
8735 'top' : [ 'bl-tl', [0,-2], 'bottom']
8739 this.toolTip.render(Roo.get(document.body));
8741 this.toolTip.el.enableDisplayMode("block");
8743 Roo.get(document.body).on('click', function(){
8747 Roo.get(document.body).on('touchstart', function(){
8751 this.isApplied = true
8754 mask : function(form, target)
8758 this.target = target;
8760 if(!this.form.errorMask || !target.el){
8764 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8766 Roo.log(scrollable);
8768 var ot = this.target.el.calcOffsetsTo(scrollable);
8770 var scrollTo = ot[1] - this.form.maskOffset;
8772 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8774 scrollable.scrollTo('top', scrollTo);
8776 var box = this.target.el.getBox();
8778 var zIndex = Roo.bootstrap.Modal.zIndex++;
8781 this.maskEl.top.setStyle('position', 'absolute');
8782 this.maskEl.top.setStyle('z-index', zIndex);
8783 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8784 this.maskEl.top.setLeft(0);
8785 this.maskEl.top.setTop(0);
8786 this.maskEl.top.show();
8788 this.maskEl.left.setStyle('position', 'absolute');
8789 this.maskEl.left.setStyle('z-index', zIndex);
8790 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8791 this.maskEl.left.setLeft(0);
8792 this.maskEl.left.setTop(box.y - this.padding);
8793 this.maskEl.left.show();
8795 this.maskEl.bottom.setStyle('position', 'absolute');
8796 this.maskEl.bottom.setStyle('z-index', zIndex);
8797 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8798 this.maskEl.bottom.setLeft(0);
8799 this.maskEl.bottom.setTop(box.bottom + this.padding);
8800 this.maskEl.bottom.show();
8802 this.maskEl.right.setStyle('position', 'absolute');
8803 this.maskEl.right.setStyle('z-index', zIndex);
8804 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8805 this.maskEl.right.setLeft(box.right + this.padding);
8806 this.maskEl.right.setTop(box.y - this.padding);
8807 this.maskEl.right.show();
8809 this.toolTip.bindEl = this.target.el;
8811 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8813 var tip = this.target.blankText;
8815 if(this.target.getValue() !== '' ) {
8817 if (this.target.invalidText.length) {
8818 tip = this.target.invalidText;
8819 } else if (this.target.regexText.length){
8820 tip = this.target.regexText;
8824 this.toolTip.show(tip);
8826 this.intervalID = window.setInterval(function() {
8827 Roo.bootstrap.Form.popover.unmask();
8830 window.onwheel = function(){ return false;};
8832 (function(){ this.isMasked = true; }).defer(500, this);
8838 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8842 this.maskEl.top.setStyle('position', 'absolute');
8843 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8844 this.maskEl.top.hide();
8846 this.maskEl.left.setStyle('position', 'absolute');
8847 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8848 this.maskEl.left.hide();
8850 this.maskEl.bottom.setStyle('position', 'absolute');
8851 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8852 this.maskEl.bottom.hide();
8854 this.maskEl.right.setStyle('position', 'absolute');
8855 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8856 this.maskEl.right.hide();
8858 this.toolTip.hide();
8860 this.toolTip.el.hide();
8862 window.onwheel = function(){ return true;};
8864 if(this.intervalID){
8865 window.clearInterval(this.intervalID);
8866 this.intervalID = false;
8869 this.isMasked = false;
8879 * Ext JS Library 1.1.1
8880 * Copyright(c) 2006-2007, Ext JS, LLC.
8882 * Originally Released Under LGPL - original licence link has changed is not relivant.
8885 * <script type="text/javascript">
8888 * @class Roo.form.VTypes
8889 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8892 Roo.form.VTypes = function(){
8893 // closure these in so they are only created once.
8894 var alpha = /^[a-zA-Z_]+$/;
8895 var alphanum = /^[a-zA-Z0-9_]+$/;
8896 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8897 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8899 // All these messages and functions are configurable
8902 * The function used to validate email addresses
8903 * @param {String} value The email address
8905 'email' : function(v){
8906 return email.test(v);
8909 * The error text to display when the email validation function returns false
8912 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8914 * The keystroke filter mask to be applied on email input
8917 'emailMask' : /[a-z0-9_\.\-@]/i,
8920 * The function used to validate URLs
8921 * @param {String} value The URL
8923 'url' : function(v){
8927 * The error text to display when the url validation function returns false
8930 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8933 * The function used to validate alpha values
8934 * @param {String} value The value
8936 'alpha' : function(v){
8937 return alpha.test(v);
8940 * The error text to display when the alpha validation function returns false
8943 'alphaText' : 'This field should only contain letters and _',
8945 * The keystroke filter mask to be applied on alpha input
8948 'alphaMask' : /[a-z_]/i,
8951 * The function used to validate alphanumeric values
8952 * @param {String} value The value
8954 'alphanum' : function(v){
8955 return alphanum.test(v);
8958 * The error text to display when the alphanumeric validation function returns false
8961 'alphanumText' : 'This field should only contain letters, numbers and _',
8963 * The keystroke filter mask to be applied on alphanumeric input
8966 'alphanumMask' : /[a-z0-9_]/i
8976 * @class Roo.bootstrap.Input
8977 * @extends Roo.bootstrap.Component
8978 * Bootstrap Input class
8979 * @cfg {Boolean} disabled is it disabled
8980 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8981 * @cfg {String} name name of the input
8982 * @cfg {string} fieldLabel - the label associated
8983 * @cfg {string} placeholder - placeholder to put in text.
8984 * @cfg {string} before - input group add on before
8985 * @cfg {string} after - input group add on after
8986 * @cfg {string} size - (lg|sm) or leave empty..
8987 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8988 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8989 * @cfg {Number} md colspan out of 12 for computer-sized screens
8990 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8991 * @cfg {string} value default value of the input
8992 * @cfg {Number} labelWidth set the width of label
8993 * @cfg {Number} labellg set the width of label (1-12)
8994 * @cfg {Number} labelmd set the width of label (1-12)
8995 * @cfg {Number} labelsm set the width of label (1-12)
8996 * @cfg {Number} labelxs set the width of label (1-12)
8997 * @cfg {String} labelAlign (top|left)
8998 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8999 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9000 * @cfg {String} indicatorpos (left|right) default left
9001 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9002 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9004 * @cfg {String} align (left|center|right) Default left
9005 * @cfg {Boolean} forceFeedback (true|false) Default false
9008 * Create a new Input
9009 * @param {Object} config The config object
9012 Roo.bootstrap.Input = function(config){
9014 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9019 * Fires when this field receives input focus.
9020 * @param {Roo.form.Field} this
9025 * Fires when this field loses input focus.
9026 * @param {Roo.form.Field} this
9031 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9032 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9033 * @param {Roo.form.Field} this
9034 * @param {Roo.EventObject} e The event object
9039 * Fires just before the field blurs if the field value has changed.
9040 * @param {Roo.form.Field} this
9041 * @param {Mixed} newValue The new value
9042 * @param {Mixed} oldValue The original value
9047 * Fires after the field has been marked as invalid.
9048 * @param {Roo.form.Field} this
9049 * @param {String} msg The validation message
9054 * Fires after the field has been validated with no errors.
9055 * @param {Roo.form.Field} this
9060 * Fires after the key up
9061 * @param {Roo.form.Field} this
9062 * @param {Roo.EventObject} e The event Object
9068 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9070 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9071 automatic validation (defaults to "keyup").
9073 validationEvent : "keyup",
9075 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9077 validateOnBlur : true,
9079 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9081 validationDelay : 250,
9083 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9085 focusClass : "x-form-focus", // not needed???
9089 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9091 invalidClass : "has-warning",
9094 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9096 validClass : "has-success",
9099 * @cfg {Boolean} hasFeedback (true|false) default true
9104 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9106 invalidFeedbackClass : "glyphicon-warning-sign",
9109 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9111 validFeedbackClass : "glyphicon-ok",
9114 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9116 selectOnFocus : false,
9119 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9123 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9128 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9130 disableKeyFilter : false,
9133 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9137 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9141 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9143 blankText : "Please complete this mandatory field",
9146 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9150 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9152 maxLength : Number.MAX_VALUE,
9154 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9156 minLengthText : "The minimum length for this field is {0}",
9158 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9160 maxLengthText : "The maximum length for this field is {0}",
9164 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9165 * If available, this function will be called only after the basic validators all return true, and will be passed the
9166 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9170 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9171 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9172 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9176 * @cfg {String} regexText -- Depricated - use Invalid Text
9181 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9187 autocomplete: false,
9206 formatedValue : false,
9207 forceFeedback : false,
9209 indicatorpos : 'left',
9219 parentLabelAlign : function()
9222 while (parent.parent()) {
9223 parent = parent.parent();
9224 if (typeof(parent.labelAlign) !='undefined') {
9225 return parent.labelAlign;
9232 getAutoCreate : function()
9234 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9240 if(this.inputType != 'hidden'){
9241 cfg.cls = 'form-group' //input-group
9247 type : this.inputType,
9249 cls : 'form-control',
9250 placeholder : this.placeholder || '',
9251 autocomplete : this.autocomplete || 'new-password'
9254 if(this.capture.length){
9255 input.capture = this.capture;
9258 if(this.accept.length){
9259 input.accept = this.accept + "/*";
9263 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9266 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9267 input.maxLength = this.maxLength;
9270 if (this.disabled) {
9271 input.disabled=true;
9274 if (this.readOnly) {
9275 input.readonly=true;
9279 input.name = this.name;
9283 input.cls += ' input-' + this.size;
9287 ['xs','sm','md','lg'].map(function(size){
9288 if (settings[size]) {
9289 cfg.cls += ' col-' + size + '-' + settings[size];
9293 var inputblock = input;
9297 cls: 'glyphicon form-control-feedback'
9300 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9303 cls : 'has-feedback',
9311 if (this.before || this.after) {
9314 cls : 'input-group',
9318 if (this.before && typeof(this.before) == 'string') {
9320 inputblock.cn.push({
9322 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9326 if (this.before && typeof(this.before) == 'object') {
9327 this.before = Roo.factory(this.before);
9329 inputblock.cn.push({
9331 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9332 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9336 inputblock.cn.push(input);
9338 if (this.after && typeof(this.after) == 'string') {
9339 inputblock.cn.push({
9341 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9345 if (this.after && typeof(this.after) == 'object') {
9346 this.after = Roo.factory(this.after);
9348 inputblock.cn.push({
9350 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9351 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9355 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9356 inputblock.cls += ' has-feedback';
9357 inputblock.cn.push(feedback);
9362 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9363 tooltip : 'This field is required'
9365 if (Roo.bootstrap.version == 4) {
9368 style : 'display-none'
9371 if (align ==='left' && this.fieldLabel.length) {
9373 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9380 cls : 'control-label col-form-label',
9381 html : this.fieldLabel
9392 var labelCfg = cfg.cn[1];
9393 var contentCfg = cfg.cn[2];
9395 if(this.indicatorpos == 'right'){
9400 cls : 'control-label col-form-label',
9404 html : this.fieldLabel
9418 labelCfg = cfg.cn[0];
9419 contentCfg = cfg.cn[1];
9423 if(this.labelWidth > 12){
9424 labelCfg.style = "width: " + this.labelWidth + 'px';
9427 if(this.labelWidth < 13 && this.labelmd == 0){
9428 this.labelmd = this.labelWidth;
9431 if(this.labellg > 0){
9432 labelCfg.cls += ' col-lg-' + this.labellg;
9433 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9436 if(this.labelmd > 0){
9437 labelCfg.cls += ' col-md-' + this.labelmd;
9438 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9441 if(this.labelsm > 0){
9442 labelCfg.cls += ' col-sm-' + this.labelsm;
9443 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9446 if(this.labelxs > 0){
9447 labelCfg.cls += ' col-xs-' + this.labelxs;
9448 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9452 } else if ( this.fieldLabel.length) {
9457 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9458 tooltip : 'This field is required'
9462 //cls : 'input-group-addon',
9463 html : this.fieldLabel
9471 if(this.indicatorpos == 'right'){
9476 //cls : 'input-group-addon',
9477 html : this.fieldLabel
9482 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9483 tooltip : 'This field is required'
9503 if (this.parentType === 'Navbar' && this.parent().bar) {
9504 cfg.cls += ' navbar-form';
9507 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9508 // on BS4 we do this only if not form
9509 cfg.cls += ' navbar-form';
9517 * return the real input element.
9519 inputEl: function ()
9521 return this.el.select('input.form-control',true).first();
9524 tooltipEl : function()
9526 return this.inputEl();
9529 indicatorEl : function()
9531 if (Roo.bootstrap.version == 4) {
9532 return false; // not enabled in v4 yet.
9535 var indicator = this.el.select('i.roo-required-indicator',true).first();
9545 setDisabled : function(v)
9547 var i = this.inputEl().dom;
9549 i.removeAttribute('disabled');
9553 i.setAttribute('disabled','true');
9555 initEvents : function()
9558 this.inputEl().on("keydown" , this.fireKey, this);
9559 this.inputEl().on("focus", this.onFocus, this);
9560 this.inputEl().on("blur", this.onBlur, this);
9562 this.inputEl().relayEvent('keyup', this);
9564 this.indicator = this.indicatorEl();
9567 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9570 // reference to original value for reset
9571 this.originalValue = this.getValue();
9572 //Roo.form.TextField.superclass.initEvents.call(this);
9573 if(this.validationEvent == 'keyup'){
9574 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9575 this.inputEl().on('keyup', this.filterValidation, this);
9577 else if(this.validationEvent !== false){
9578 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9581 if(this.selectOnFocus){
9582 this.on("focus", this.preFocus, this);
9585 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9586 this.inputEl().on("keypress", this.filterKeys, this);
9588 this.inputEl().relayEvent('keypress', this);
9591 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9592 this.el.on("click", this.autoSize, this);
9595 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9596 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9599 if (typeof(this.before) == 'object') {
9600 this.before.render(this.el.select('.roo-input-before',true).first());
9602 if (typeof(this.after) == 'object') {
9603 this.after.render(this.el.select('.roo-input-after',true).first());
9606 this.inputEl().on('change', this.onChange, this);
9609 filterValidation : function(e){
9610 if(!e.isNavKeyPress()){
9611 this.validationTask.delay(this.validationDelay);
9615 * Validates the field value
9616 * @return {Boolean} True if the value is valid, else false
9618 validate : function(){
9619 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9620 if(this.disabled || this.validateValue(this.getRawValue())){
9631 * Validates a value according to the field's validation rules and marks the field as invalid
9632 * if the validation fails
9633 * @param {Mixed} value The value to validate
9634 * @return {Boolean} True if the value is valid, else false
9636 validateValue : function(value)
9638 if(this.getVisibilityEl().hasClass('hidden')){
9642 if(value.length < 1) { // if it's blank
9643 if(this.allowBlank){
9649 if(value.length < this.minLength){
9652 if(value.length > this.maxLength){
9656 var vt = Roo.form.VTypes;
9657 if(!vt[this.vtype](value, this)){
9661 if(typeof this.validator == "function"){
9662 var msg = this.validator(value);
9666 if (typeof(msg) == 'string') {
9667 this.invalidText = msg;
9671 if(this.regex && !this.regex.test(value)){
9679 fireKey : function(e){
9680 //Roo.log('field ' + e.getKey());
9681 if(e.isNavKeyPress()){
9682 this.fireEvent("specialkey", this, e);
9685 focus : function (selectText){
9687 this.inputEl().focus();
9688 if(selectText === true){
9689 this.inputEl().dom.select();
9695 onFocus : function(){
9696 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9697 // this.el.addClass(this.focusClass);
9700 this.hasFocus = true;
9701 this.startValue = this.getValue();
9702 this.fireEvent("focus", this);
9706 beforeBlur : Roo.emptyFn,
9710 onBlur : function(){
9712 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9713 //this.el.removeClass(this.focusClass);
9715 this.hasFocus = false;
9716 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9719 var v = this.getValue();
9720 if(String(v) !== String(this.startValue)){
9721 this.fireEvent('change', this, v, this.startValue);
9723 this.fireEvent("blur", this);
9726 onChange : function(e)
9728 var v = this.getValue();
9729 if(String(v) !== String(this.startValue)){
9730 this.fireEvent('change', this, v, this.startValue);
9736 * Resets the current field value to the originally loaded value and clears any validation messages
9739 this.setValue(this.originalValue);
9743 * Returns the name of the field
9744 * @return {Mixed} name The name field
9746 getName: function(){
9750 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9751 * @return {Mixed} value The field value
9753 getValue : function(){
9755 var v = this.inputEl().getValue();
9760 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9761 * @return {Mixed} value The field value
9763 getRawValue : function(){
9764 var v = this.inputEl().getValue();
9770 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9771 * @param {Mixed} value The value to set
9773 setRawValue : function(v){
9774 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9777 selectText : function(start, end){
9778 var v = this.getRawValue();
9780 start = start === undefined ? 0 : start;
9781 end = end === undefined ? v.length : end;
9782 var d = this.inputEl().dom;
9783 if(d.setSelectionRange){
9784 d.setSelectionRange(start, end);
9785 }else if(d.createTextRange){
9786 var range = d.createTextRange();
9787 range.moveStart("character", start);
9788 range.moveEnd("character", v.length-end);
9795 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9796 * @param {Mixed} value The value to set
9798 setValue : function(v){
9801 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9807 processValue : function(value){
9808 if(this.stripCharsRe){
9809 var newValue = value.replace(this.stripCharsRe, '');
9810 if(newValue !== value){
9811 this.setRawValue(newValue);
9818 preFocus : function(){
9820 if(this.selectOnFocus){
9821 this.inputEl().dom.select();
9824 filterKeys : function(e){
9826 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9829 var c = e.getCharCode(), cc = String.fromCharCode(c);
9830 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9833 if(!this.maskRe.test(cc)){
9838 * Clear any invalid styles/messages for this field
9840 clearInvalid : function(){
9842 if(!this.el || this.preventMark){ // not rendered
9847 this.el.removeClass([this.invalidClass, 'is-invalid']);
9849 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9851 var feedback = this.el.select('.form-control-feedback', true).first();
9854 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9860 this.indicator.removeClass('visible');
9861 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9864 this.fireEvent('valid', this);
9868 * Mark this field as valid
9870 markValid : function()
9872 if(!this.el || this.preventMark){ // not rendered...
9876 this.el.removeClass([this.invalidClass, this.validClass]);
9877 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9879 var feedback = this.el.select('.form-control-feedback', true).first();
9882 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9886 this.indicator.removeClass('visible');
9887 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9894 if(this.allowBlank && !this.getRawValue().length){
9897 if (Roo.bootstrap.version == 3) {
9898 this.el.addClass(this.validClass);
9900 this.inputEl().addClass('is-valid');
9903 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9905 var feedback = this.el.select('.form-control-feedback', true).first();
9908 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9909 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9914 this.fireEvent('valid', this);
9918 * Mark this field as invalid
9919 * @param {String} msg The validation message
9921 markInvalid : function(msg)
9923 if(!this.el || this.preventMark){ // not rendered
9927 this.el.removeClass([this.invalidClass, this.validClass]);
9928 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9930 var feedback = this.el.select('.form-control-feedback', true).first();
9933 this.el.select('.form-control-feedback', true).first().removeClass(
9934 [this.invalidFeedbackClass, this.validFeedbackClass]);
9941 if(this.allowBlank && !this.getRawValue().length){
9946 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9947 this.indicator.addClass('visible');
9949 if (Roo.bootstrap.version == 3) {
9950 this.el.addClass(this.invalidClass);
9952 this.inputEl().addClass('is-invalid');
9957 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9959 var feedback = this.el.select('.form-control-feedback', true).first();
9962 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9964 if(this.getValue().length || this.forceFeedback){
9965 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9972 this.fireEvent('invalid', this, msg);
9975 SafariOnKeyDown : function(event)
9977 // this is a workaround for a password hang bug on chrome/ webkit.
9978 if (this.inputEl().dom.type != 'password') {
9982 var isSelectAll = false;
9984 if(this.inputEl().dom.selectionEnd > 0){
9985 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9987 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9988 event.preventDefault();
9993 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9995 event.preventDefault();
9996 // this is very hacky as keydown always get's upper case.
9998 var cc = String.fromCharCode(event.getCharCode());
9999 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10003 adjustWidth : function(tag, w){
10004 tag = tag.toLowerCase();
10005 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10006 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10007 if(tag == 'input'){
10010 if(tag == 'textarea'){
10013 }else if(Roo.isOpera){
10014 if(tag == 'input'){
10017 if(tag == 'textarea'){
10025 setFieldLabel : function(v)
10027 if(!this.rendered){
10031 if(this.indicatorEl()){
10032 var ar = this.el.select('label > span',true);
10034 if (ar.elements.length) {
10035 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036 this.fieldLabel = v;
10040 var br = this.el.select('label',true);
10042 if(br.elements.length) {
10043 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044 this.fieldLabel = v;
10048 Roo.log('Cannot Found any of label > span || label in input');
10052 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10053 this.fieldLabel = v;
10068 * @class Roo.bootstrap.TextArea
10069 * @extends Roo.bootstrap.Input
10070 * Bootstrap TextArea class
10071 * @cfg {Number} cols Specifies the visible width of a text area
10072 * @cfg {Number} rows Specifies the visible number of lines in a text area
10073 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10074 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10075 * @cfg {string} html text
10078 * Create a new TextArea
10079 * @param {Object} config The config object
10082 Roo.bootstrap.TextArea = function(config){
10083 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10087 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10097 getAutoCreate : function(){
10099 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10105 if(this.inputType != 'hidden'){
10106 cfg.cls = 'form-group' //input-group
10114 value : this.value || '',
10115 html: this.html || '',
10116 cls : 'form-control',
10117 placeholder : this.placeholder || ''
10121 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10122 input.maxLength = this.maxLength;
10126 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10130 input.cols = this.cols;
10133 if (this.readOnly) {
10134 input.readonly = true;
10138 input.name = this.name;
10142 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10146 ['xs','sm','md','lg'].map(function(size){
10147 if (settings[size]) {
10148 cfg.cls += ' col-' + size + '-' + settings[size];
10152 var inputblock = input;
10154 if(this.hasFeedback && !this.allowBlank){
10158 cls: 'glyphicon form-control-feedback'
10162 cls : 'has-feedback',
10171 if (this.before || this.after) {
10174 cls : 'input-group',
10178 inputblock.cn.push({
10180 cls : 'input-group-addon',
10185 inputblock.cn.push(input);
10187 if(this.hasFeedback && !this.allowBlank){
10188 inputblock.cls += ' has-feedback';
10189 inputblock.cn.push(feedback);
10193 inputblock.cn.push({
10195 cls : 'input-group-addon',
10202 if (align ==='left' && this.fieldLabel.length) {
10207 cls : 'control-label',
10208 html : this.fieldLabel
10219 if(this.labelWidth > 12){
10220 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10223 if(this.labelWidth < 13 && this.labelmd == 0){
10224 this.labelmd = this.labelWidth;
10227 if(this.labellg > 0){
10228 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10229 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10232 if(this.labelmd > 0){
10233 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10234 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10237 if(this.labelsm > 0){
10238 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10239 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10242 if(this.labelxs > 0){
10243 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10244 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10247 } else if ( this.fieldLabel.length) {
10252 //cls : 'input-group-addon',
10253 html : this.fieldLabel
10271 if (this.disabled) {
10272 input.disabled=true;
10279 * return the real textarea element.
10281 inputEl: function ()
10283 return this.el.select('textarea.form-control',true).first();
10287 * Clear any invalid styles/messages for this field
10289 clearInvalid : function()
10292 if(!this.el || this.preventMark){ // not rendered
10296 var label = this.el.select('label', true).first();
10297 var icon = this.el.select('i.fa-star', true).first();
10302 this.el.removeClass( this.validClass);
10303 this.inputEl().removeClass('is-invalid');
10305 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10307 var feedback = this.el.select('.form-control-feedback', true).first();
10310 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10315 this.fireEvent('valid', this);
10319 * Mark this field as valid
10321 markValid : function()
10323 if(!this.el || this.preventMark){ // not rendered
10327 this.el.removeClass([this.invalidClass, this.validClass]);
10328 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10330 var feedback = this.el.select('.form-control-feedback', true).first();
10333 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10336 if(this.disabled || this.allowBlank){
10340 var label = this.el.select('label', true).first();
10341 var icon = this.el.select('i.fa-star', true).first();
10346 if (Roo.bootstrap.version == 3) {
10347 this.el.addClass(this.validClass);
10349 this.inputEl().addClass('is-valid');
10353 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10355 var feedback = this.el.select('.form-control-feedback', true).first();
10358 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10359 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10364 this.fireEvent('valid', this);
10368 * Mark this field as invalid
10369 * @param {String} msg The validation message
10371 markInvalid : function(msg)
10373 if(!this.el || this.preventMark){ // not rendered
10377 this.el.removeClass([this.invalidClass, this.validClass]);
10378 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10380 var feedback = this.el.select('.form-control-feedback', true).first();
10383 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10386 if(this.disabled || this.allowBlank){
10390 var label = this.el.select('label', true).first();
10391 var icon = this.el.select('i.fa-star', true).first();
10393 if(!this.getValue().length && label && !icon){
10394 this.el.createChild({
10396 cls : 'text-danger fa fa-lg fa-star',
10397 tooltip : 'This field is required',
10398 style : 'margin-right:5px;'
10402 if (Roo.bootstrap.version == 3) {
10403 this.el.addClass(this.invalidClass);
10405 this.inputEl().addClass('is-invalid');
10408 // fixme ... this may be depricated need to test..
10409 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10411 var feedback = this.el.select('.form-control-feedback', true).first();
10414 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10416 if(this.getValue().length || this.forceFeedback){
10417 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10424 this.fireEvent('invalid', this, msg);
10432 * trigger field - base class for combo..
10437 * @class Roo.bootstrap.TriggerField
10438 * @extends Roo.bootstrap.Input
10439 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10440 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10441 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10442 * for which you can provide a custom implementation. For example:
10444 var trigger = new Roo.bootstrap.TriggerField();
10445 trigger.onTriggerClick = myTriggerFn;
10446 trigger.applyTo('my-field');
10449 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10450 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10451 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10452 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10453 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10456 * Create a new TriggerField.
10457 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10458 * to the base TextField)
10460 Roo.bootstrap.TriggerField = function(config){
10461 this.mimicing = false;
10462 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10465 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10467 * @cfg {String} triggerClass A CSS class to apply to the trigger
10470 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10475 * @cfg {Boolean} removable (true|false) special filter default false
10479 /** @cfg {Boolean} grow @hide */
10480 /** @cfg {Number} growMin @hide */
10481 /** @cfg {Number} growMax @hide */
10487 autoSize: Roo.emptyFn,
10491 deferHeight : true,
10494 actionMode : 'wrap',
10499 getAutoCreate : function(){
10501 var align = this.labelAlign || this.parentLabelAlign();
10506 cls: 'form-group' //input-group
10513 type : this.inputType,
10514 cls : 'form-control',
10515 autocomplete: 'new-password',
10516 placeholder : this.placeholder || ''
10520 input.name = this.name;
10523 input.cls += ' input-' + this.size;
10526 if (this.disabled) {
10527 input.disabled=true;
10530 var inputblock = input;
10532 if(this.hasFeedback && !this.allowBlank){
10536 cls: 'glyphicon form-control-feedback'
10539 if(this.removable && !this.editable && !this.tickable){
10541 cls : 'has-feedback',
10547 cls : 'roo-combo-removable-btn close'
10554 cls : 'has-feedback',
10563 if(this.removable && !this.editable && !this.tickable){
10565 cls : 'roo-removable',
10571 cls : 'roo-combo-removable-btn close'
10578 if (this.before || this.after) {
10581 cls : 'input-group',
10585 inputblock.cn.push({
10587 cls : 'input-group-addon input-group-prepend input-group-text',
10592 inputblock.cn.push(input);
10594 if(this.hasFeedback && !this.allowBlank){
10595 inputblock.cls += ' has-feedback';
10596 inputblock.cn.push(feedback);
10600 inputblock.cn.push({
10602 cls : 'input-group-addon input-group-append input-group-text',
10611 var ibwrap = inputblock;
10616 cls: 'roo-select2-choices',
10620 cls: 'roo-select2-search-field',
10632 cls: 'roo-select2-container input-group',
10637 cls: 'form-hidden-field'
10643 if(!this.multiple && this.showToggleBtn){
10649 if (this.caret != false) {
10652 cls: 'fa fa-' + this.caret
10659 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10664 cls: 'combobox-clear',
10678 combobox.cls += ' roo-select2-container-multi';
10682 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10683 tooltip : 'This field is required'
10685 if (Roo.bootstrap.version == 4) {
10688 style : 'display:none'
10693 if (align ==='left' && this.fieldLabel.length) {
10695 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10702 cls : 'control-label',
10703 html : this.fieldLabel
10715 var labelCfg = cfg.cn[1];
10716 var contentCfg = cfg.cn[2];
10718 if(this.indicatorpos == 'right'){
10723 cls : 'control-label',
10727 html : this.fieldLabel
10741 labelCfg = cfg.cn[0];
10742 contentCfg = cfg.cn[1];
10745 if(this.labelWidth > 12){
10746 labelCfg.style = "width: " + this.labelWidth + 'px';
10749 if(this.labelWidth < 13 && this.labelmd == 0){
10750 this.labelmd = this.labelWidth;
10753 if(this.labellg > 0){
10754 labelCfg.cls += ' col-lg-' + this.labellg;
10755 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10758 if(this.labelmd > 0){
10759 labelCfg.cls += ' col-md-' + this.labelmd;
10760 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10763 if(this.labelsm > 0){
10764 labelCfg.cls += ' col-sm-' + this.labelsm;
10765 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10768 if(this.labelxs > 0){
10769 labelCfg.cls += ' col-xs-' + this.labelxs;
10770 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10773 } else if ( this.fieldLabel.length) {
10774 // Roo.log(" label");
10779 //cls : 'input-group-addon',
10780 html : this.fieldLabel
10788 if(this.indicatorpos == 'right'){
10796 html : this.fieldLabel
10810 // Roo.log(" no label && no align");
10817 ['xs','sm','md','lg'].map(function(size){
10818 if (settings[size]) {
10819 cfg.cls += ' col-' + size + '-' + settings[size];
10830 onResize : function(w, h){
10831 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10832 // if(typeof w == 'number'){
10833 // var x = w - this.trigger.getWidth();
10834 // this.inputEl().setWidth(this.adjustWidth('input', x));
10835 // this.trigger.setStyle('left', x+'px');
10840 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10843 getResizeEl : function(){
10844 return this.inputEl();
10848 getPositionEl : function(){
10849 return this.inputEl();
10853 alignErrorIcon : function(){
10854 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10858 initEvents : function(){
10862 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10863 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10864 if(!this.multiple && this.showToggleBtn){
10865 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10866 if(this.hideTrigger){
10867 this.trigger.setDisplayed(false);
10869 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10873 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10876 if(this.removable && !this.editable && !this.tickable){
10877 var close = this.closeTriggerEl();
10880 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10881 close.on('click', this.removeBtnClick, this, close);
10885 //this.trigger.addClassOnOver('x-form-trigger-over');
10886 //this.trigger.addClassOnClick('x-form-trigger-click');
10889 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10893 closeTriggerEl : function()
10895 var close = this.el.select('.roo-combo-removable-btn', true).first();
10896 return close ? close : false;
10899 removeBtnClick : function(e, h, el)
10901 e.preventDefault();
10903 if(this.fireEvent("remove", this) !== false){
10905 this.fireEvent("afterremove", this)
10909 createList : function()
10911 this.list = Roo.get(document.body).createChild({
10912 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10913 cls: 'typeahead typeahead-long dropdown-menu',
10914 style: 'display:none'
10917 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10922 initTrigger : function(){
10927 onDestroy : function(){
10929 this.trigger.removeAllListeners();
10930 // this.trigger.remove();
10933 // this.wrap.remove();
10935 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10939 onFocus : function(){
10940 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10942 if(!this.mimicing){
10943 this.wrap.addClass('x-trigger-wrap-focus');
10944 this.mimicing = true;
10945 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10946 if(this.monitorTab){
10947 this.el.on("keydown", this.checkTab, this);
10954 checkTab : function(e){
10955 if(e.getKey() == e.TAB){
10956 this.triggerBlur();
10961 onBlur : function(){
10966 mimicBlur : function(e, t){
10968 if(!this.wrap.contains(t) && this.validateBlur()){
10969 this.triggerBlur();
10975 triggerBlur : function(){
10976 this.mimicing = false;
10977 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10978 if(this.monitorTab){
10979 this.el.un("keydown", this.checkTab, this);
10981 //this.wrap.removeClass('x-trigger-wrap-focus');
10982 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10986 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10987 validateBlur : function(e, t){
10992 onDisable : function(){
10993 this.inputEl().dom.disabled = true;
10994 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10996 // this.wrap.addClass('x-item-disabled');
11001 onEnable : function(){
11002 this.inputEl().dom.disabled = false;
11003 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11005 // this.el.removeClass('x-item-disabled');
11010 onShow : function(){
11011 var ae = this.getActionEl();
11014 ae.dom.style.display = '';
11015 ae.dom.style.visibility = 'visible';
11021 onHide : function(){
11022 var ae = this.getActionEl();
11023 ae.dom.style.display = 'none';
11027 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11028 * by an implementing function.
11030 * @param {EventObject} e
11032 onTriggerClick : Roo.emptyFn
11036 * Ext JS Library 1.1.1
11037 * Copyright(c) 2006-2007, Ext JS, LLC.
11039 * Originally Released Under LGPL - original licence link has changed is not relivant.
11042 * <script type="text/javascript">
11047 * @class Roo.data.SortTypes
11049 * Defines the default sorting (casting?) comparison functions used when sorting data.
11051 Roo.data.SortTypes = {
11053 * Default sort that does nothing
11054 * @param {Mixed} s The value being converted
11055 * @return {Mixed} The comparison value
11057 none : function(s){
11062 * The regular expression used to strip tags
11066 stripTagsRE : /<\/?[^>]+>/gi,
11069 * Strips all HTML tags to sort on text only
11070 * @param {Mixed} s The value being converted
11071 * @return {String} The comparison value
11073 asText : function(s){
11074 return String(s).replace(this.stripTagsRE, "");
11078 * Strips all HTML tags to sort on text only - Case insensitive
11079 * @param {Mixed} s The value being converted
11080 * @return {String} The comparison value
11082 asUCText : function(s){
11083 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11087 * Case insensitive string
11088 * @param {Mixed} s The value being converted
11089 * @return {String} The comparison value
11091 asUCString : function(s) {
11092 return String(s).toUpperCase();
11097 * @param {Mixed} s The value being converted
11098 * @return {Number} The comparison value
11100 asDate : function(s) {
11104 if(s instanceof Date){
11105 return s.getTime();
11107 return Date.parse(String(s));
11112 * @param {Mixed} s The value being converted
11113 * @return {Float} The comparison value
11115 asFloat : function(s) {
11116 var val = parseFloat(String(s).replace(/,/g, ""));
11125 * @param {Mixed} s The value being converted
11126 * @return {Number} The comparison value
11128 asInt : function(s) {
11129 var val = parseInt(String(s).replace(/,/g, ""));
11137 * Ext JS Library 1.1.1
11138 * Copyright(c) 2006-2007, Ext JS, LLC.
11140 * Originally Released Under LGPL - original licence link has changed is not relivant.
11143 * <script type="text/javascript">
11147 * @class Roo.data.Record
11148 * Instances of this class encapsulate both record <em>definition</em> information, and record
11149 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11150 * to access Records cached in an {@link Roo.data.Store} object.<br>
11152 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11153 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11156 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11158 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11159 * {@link #create}. The parameters are the same.
11160 * @param {Array} data An associative Array of data values keyed by the field name.
11161 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11162 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11163 * not specified an integer id is generated.
11165 Roo.data.Record = function(data, id){
11166 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11171 * Generate a constructor for a specific record layout.
11172 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11173 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11174 * Each field definition object may contain the following properties: <ul>
11175 * <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,
11176 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11177 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11178 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11179 * is being used, then this is a string containing the javascript expression to reference the data relative to
11180 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11181 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11182 * this may be omitted.</p></li>
11183 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11184 * <ul><li>auto (Default, implies no conversion)</li>
11189 * <li>date</li></ul></p></li>
11190 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11191 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11192 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11193 * by the Reader into an object that will be stored in the Record. It is passed the
11194 * following parameters:<ul>
11195 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11197 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11199 * <br>usage:<br><pre><code>
11200 var TopicRecord = Roo.data.Record.create(
11201 {name: 'title', mapping: 'topic_title'},
11202 {name: 'author', mapping: 'username'},
11203 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11204 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11205 {name: 'lastPoster', mapping: 'user2'},
11206 {name: 'excerpt', mapping: 'post_text'}
11209 var myNewRecord = new TopicRecord({
11210 title: 'Do my job please',
11213 lastPost: new Date(),
11214 lastPoster: 'Animal',
11215 excerpt: 'No way dude!'
11217 myStore.add(myNewRecord);
11222 Roo.data.Record.create = function(o){
11223 var f = function(){
11224 f.superclass.constructor.apply(this, arguments);
11226 Roo.extend(f, Roo.data.Record);
11227 var p = f.prototype;
11228 p.fields = new Roo.util.MixedCollection(false, function(field){
11231 for(var i = 0, len = o.length; i < len; i++){
11232 p.fields.add(new Roo.data.Field(o[i]));
11234 f.getField = function(name){
11235 return p.fields.get(name);
11240 Roo.data.Record.AUTO_ID = 1000;
11241 Roo.data.Record.EDIT = 'edit';
11242 Roo.data.Record.REJECT = 'reject';
11243 Roo.data.Record.COMMIT = 'commit';
11245 Roo.data.Record.prototype = {
11247 * Readonly flag - true if this record has been modified.
11256 join : function(store){
11257 this.store = store;
11261 * Set the named field to the specified value.
11262 * @param {String} name The name of the field to set.
11263 * @param {Object} value The value to set the field to.
11265 set : function(name, value){
11266 if(this.data[name] == value){
11270 if(!this.modified){
11271 this.modified = {};
11273 if(typeof this.modified[name] == 'undefined'){
11274 this.modified[name] = this.data[name];
11276 this.data[name] = value;
11277 if(!this.editing && this.store){
11278 this.store.afterEdit(this);
11283 * Get the value of the named field.
11284 * @param {String} name The name of the field to get the value of.
11285 * @return {Object} The value of the field.
11287 get : function(name){
11288 return this.data[name];
11292 beginEdit : function(){
11293 this.editing = true;
11294 this.modified = {};
11298 cancelEdit : function(){
11299 this.editing = false;
11300 delete this.modified;
11304 endEdit : function(){
11305 this.editing = false;
11306 if(this.dirty && this.store){
11307 this.store.afterEdit(this);
11312 * Usually called by the {@link Roo.data.Store} which owns the Record.
11313 * Rejects all changes made to the Record since either creation, or the last commit operation.
11314 * Modified fields are reverted to their original values.
11316 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11317 * of reject operations.
11319 reject : function(){
11320 var m = this.modified;
11322 if(typeof m[n] != "function"){
11323 this.data[n] = m[n];
11326 this.dirty = false;
11327 delete this.modified;
11328 this.editing = false;
11330 this.store.afterReject(this);
11335 * Usually called by the {@link Roo.data.Store} which owns the Record.
11336 * Commits all changes made to the Record since either creation, or the last commit operation.
11338 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11339 * of commit operations.
11341 commit : function(){
11342 this.dirty = false;
11343 delete this.modified;
11344 this.editing = false;
11346 this.store.afterCommit(this);
11351 hasError : function(){
11352 return this.error != null;
11356 clearError : function(){
11361 * Creates a copy of this record.
11362 * @param {String} id (optional) A new record id if you don't want to use this record's id
11365 copy : function(newId) {
11366 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11370 * Ext JS Library 1.1.1
11371 * Copyright(c) 2006-2007, Ext JS, LLC.
11373 * Originally Released Under LGPL - original licence link has changed is not relivant.
11376 * <script type="text/javascript">
11382 * @class Roo.data.Store
11383 * @extends Roo.util.Observable
11384 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11385 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11387 * 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
11388 * has no knowledge of the format of the data returned by the Proxy.<br>
11390 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11391 * instances from the data object. These records are cached and made available through accessor functions.
11393 * Creates a new Store.
11394 * @param {Object} config A config object containing the objects needed for the Store to access data,
11395 * and read the data into Records.
11397 Roo.data.Store = function(config){
11398 this.data = new Roo.util.MixedCollection(false);
11399 this.data.getKey = function(o){
11402 this.baseParams = {};
11404 this.paramNames = {
11409 "multisort" : "_multisort"
11412 if(config && config.data){
11413 this.inlineData = config.data;
11414 delete config.data;
11417 Roo.apply(this, config);
11419 if(this.reader){ // reader passed
11420 this.reader = Roo.factory(this.reader, Roo.data);
11421 this.reader.xmodule = this.xmodule || false;
11422 if(!this.recordType){
11423 this.recordType = this.reader.recordType;
11425 if(this.reader.onMetaChange){
11426 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11430 if(this.recordType){
11431 this.fields = this.recordType.prototype.fields;
11433 this.modified = [];
11437 * @event datachanged
11438 * Fires when the data cache has changed, and a widget which is using this Store
11439 * as a Record cache should refresh its view.
11440 * @param {Store} this
11442 datachanged : true,
11444 * @event metachange
11445 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11446 * @param {Store} this
11447 * @param {Object} meta The JSON metadata
11452 * Fires when Records have been added to the Store
11453 * @param {Store} this
11454 * @param {Roo.data.Record[]} records The array of Records added
11455 * @param {Number} index The index at which the record(s) were added
11460 * Fires when a Record has been removed from the Store
11461 * @param {Store} this
11462 * @param {Roo.data.Record} record The Record that was removed
11463 * @param {Number} index The index at which the record was removed
11468 * Fires when a Record has been updated
11469 * @param {Store} this
11470 * @param {Roo.data.Record} record The Record that was updated
11471 * @param {String} operation The update operation being performed. Value may be one of:
11473 Roo.data.Record.EDIT
11474 Roo.data.Record.REJECT
11475 Roo.data.Record.COMMIT
11481 * Fires when the data cache has been cleared.
11482 * @param {Store} this
11486 * @event beforeload
11487 * Fires before a request is made for a new data object. If the beforeload handler returns false
11488 * the load action will be canceled.
11489 * @param {Store} this
11490 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11494 * @event beforeloadadd
11495 * Fires after a new set of Records has been loaded.
11496 * @param {Store} this
11497 * @param {Roo.data.Record[]} records The Records that were loaded
11498 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11500 beforeloadadd : true,
11503 * Fires after a new set of Records has been loaded, before they are added to the store.
11504 * @param {Store} this
11505 * @param {Roo.data.Record[]} records The Records that were loaded
11506 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11507 * @params {Object} return from reader
11511 * @event loadexception
11512 * Fires if an exception occurs in the Proxy during loading.
11513 * Called with the signature of the Proxy's "loadexception" event.
11514 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11517 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11518 * @param {Object} load options
11519 * @param {Object} jsonData from your request (normally this contains the Exception)
11521 loadexception : true
11525 this.proxy = Roo.factory(this.proxy, Roo.data);
11526 this.proxy.xmodule = this.xmodule || false;
11527 this.relayEvents(this.proxy, ["loadexception"]);
11529 this.sortToggle = {};
11530 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11532 Roo.data.Store.superclass.constructor.call(this);
11534 if(this.inlineData){
11535 this.loadData(this.inlineData);
11536 delete this.inlineData;
11540 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11542 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11543 * without a remote query - used by combo/forms at present.
11547 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11550 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11553 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11554 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11557 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11558 * on any HTTP request
11561 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11564 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11568 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11569 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11571 remoteSort : false,
11574 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11575 * loaded or when a record is removed. (defaults to false).
11577 pruneModifiedRecords : false,
11580 lastOptions : null,
11583 * Add Records to the Store and fires the add event.
11584 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11586 add : function(records){
11587 records = [].concat(records);
11588 for(var i = 0, len = records.length; i < len; i++){
11589 records[i].join(this);
11591 var index = this.data.length;
11592 this.data.addAll(records);
11593 this.fireEvent("add", this, records, index);
11597 * Remove a Record from the Store and fires the remove event.
11598 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11600 remove : function(record){
11601 var index = this.data.indexOf(record);
11602 this.data.removeAt(index);
11604 if(this.pruneModifiedRecords){
11605 this.modified.remove(record);
11607 this.fireEvent("remove", this, record, index);
11611 * Remove all Records from the Store and fires the clear event.
11613 removeAll : function(){
11615 if(this.pruneModifiedRecords){
11616 this.modified = [];
11618 this.fireEvent("clear", this);
11622 * Inserts Records to the Store at the given index and fires the add event.
11623 * @param {Number} index The start index at which to insert the passed Records.
11624 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11626 insert : function(index, records){
11627 records = [].concat(records);
11628 for(var i = 0, len = records.length; i < len; i++){
11629 this.data.insert(index, records[i]);
11630 records[i].join(this);
11632 this.fireEvent("add", this, records, index);
11636 * Get the index within the cache of the passed Record.
11637 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11638 * @return {Number} The index of the passed Record. Returns -1 if not found.
11640 indexOf : function(record){
11641 return this.data.indexOf(record);
11645 * Get the index within the cache of the Record with the passed id.
11646 * @param {String} id The id of the Record to find.
11647 * @return {Number} The index of the Record. Returns -1 if not found.
11649 indexOfId : function(id){
11650 return this.data.indexOfKey(id);
11654 * Get the Record with the specified id.
11655 * @param {String} id The id of the Record to find.
11656 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11658 getById : function(id){
11659 return this.data.key(id);
11663 * Get the Record at the specified index.
11664 * @param {Number} index The index of the Record to find.
11665 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11667 getAt : function(index){
11668 return this.data.itemAt(index);
11672 * Returns a range of Records between specified indices.
11673 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11674 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11675 * @return {Roo.data.Record[]} An array of Records
11677 getRange : function(start, end){
11678 return this.data.getRange(start, end);
11682 storeOptions : function(o){
11683 o = Roo.apply({}, o);
11686 this.lastOptions = o;
11690 * Loads the Record cache from the configured Proxy using the configured Reader.
11692 * If using remote paging, then the first load call must specify the <em>start</em>
11693 * and <em>limit</em> properties in the options.params property to establish the initial
11694 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11696 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11697 * and this call will return before the new data has been loaded. Perform any post-processing
11698 * in a callback function, or in a "load" event handler.</strong>
11700 * @param {Object} options An object containing properties which control loading options:<ul>
11701 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11702 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11703 * passed the following arguments:<ul>
11704 * <li>r : Roo.data.Record[]</li>
11705 * <li>options: Options object from the load call</li>
11706 * <li>success: Boolean success indicator</li></ul></li>
11707 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11708 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11711 load : function(options){
11712 options = options || {};
11713 if(this.fireEvent("beforeload", this, options) !== false){
11714 this.storeOptions(options);
11715 var p = Roo.apply(options.params || {}, this.baseParams);
11716 // if meta was not loaded from remote source.. try requesting it.
11717 if (!this.reader.metaFromRemote) {
11718 p._requestMeta = 1;
11720 if(this.sortInfo && this.remoteSort){
11721 var pn = this.paramNames;
11722 p[pn["sort"]] = this.sortInfo.field;
11723 p[pn["dir"]] = this.sortInfo.direction;
11725 if (this.multiSort) {
11726 var pn = this.paramNames;
11727 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11730 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11735 * Reloads the Record cache from the configured Proxy using the configured Reader and
11736 * the options from the last load operation performed.
11737 * @param {Object} options (optional) An object containing properties which may override the options
11738 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11739 * the most recently used options are reused).
11741 reload : function(options){
11742 this.load(Roo.applyIf(options||{}, this.lastOptions));
11746 // Called as a callback by the Reader during a load operation.
11747 loadRecords : function(o, options, success){
11748 if(!o || success === false){
11749 if(success !== false){
11750 this.fireEvent("load", this, [], options, o);
11752 if(options.callback){
11753 options.callback.call(options.scope || this, [], options, false);
11757 // if data returned failure - throw an exception.
11758 if (o.success === false) {
11759 // show a message if no listener is registered.
11760 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11761 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11763 // loadmask wil be hooked into this..
11764 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11767 var r = o.records, t = o.totalRecords || r.length;
11769 this.fireEvent("beforeloadadd", this, r, options, o);
11771 if(!options || options.add !== true){
11772 if(this.pruneModifiedRecords){
11773 this.modified = [];
11775 for(var i = 0, len = r.length; i < len; i++){
11779 this.data = this.snapshot;
11780 delete this.snapshot;
11783 this.data.addAll(r);
11784 this.totalLength = t;
11786 this.fireEvent("datachanged", this);
11788 this.totalLength = Math.max(t, this.data.length+r.length);
11792 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11794 var e = new Roo.data.Record({});
11796 e.set(this.parent.displayField, this.parent.emptyTitle);
11797 e.set(this.parent.valueField, '');
11802 this.fireEvent("load", this, r, options, o);
11803 if(options.callback){
11804 options.callback.call(options.scope || this, r, options, true);
11810 * Loads data from a passed data block. A Reader which understands the format of the data
11811 * must have been configured in the constructor.
11812 * @param {Object} data The data block from which to read the Records. The format of the data expected
11813 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11814 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11816 loadData : function(o, append){
11817 var r = this.reader.readRecords(o);
11818 this.loadRecords(r, {add: append}, true);
11822 * Gets the number of cached records.
11824 * <em>If using paging, this may not be the total size of the dataset. If the data object
11825 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11826 * the data set size</em>
11828 getCount : function(){
11829 return this.data.length || 0;
11833 * Gets the total number of records in the dataset as returned by the server.
11835 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11836 * the dataset size</em>
11838 getTotalCount : function(){
11839 return this.totalLength || 0;
11843 * Returns the sort state of the Store as an object with two properties:
11845 field {String} The name of the field by which the Records are sorted
11846 direction {String} The sort order, "ASC" or "DESC"
11849 getSortState : function(){
11850 return this.sortInfo;
11854 applySort : function(){
11855 if(this.sortInfo && !this.remoteSort){
11856 var s = this.sortInfo, f = s.field;
11857 var st = this.fields.get(f).sortType;
11858 var fn = function(r1, r2){
11859 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11860 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11862 this.data.sort(s.direction, fn);
11863 if(this.snapshot && this.snapshot != this.data){
11864 this.snapshot.sort(s.direction, fn);
11870 * Sets the default sort column and order to be used by the next load operation.
11871 * @param {String} fieldName The name of the field to sort by.
11872 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11874 setDefaultSort : function(field, dir){
11875 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11879 * Sort the Records.
11880 * If remote sorting is used, the sort is performed on the server, and the cache is
11881 * reloaded. If local sorting is used, the cache is sorted internally.
11882 * @param {String} fieldName The name of the field to sort by.
11883 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11885 sort : function(fieldName, dir){
11886 var f = this.fields.get(fieldName);
11888 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11890 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11891 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11896 this.sortToggle[f.name] = dir;
11897 this.sortInfo = {field: f.name, direction: dir};
11898 if(!this.remoteSort){
11900 this.fireEvent("datachanged", this);
11902 this.load(this.lastOptions);
11907 * Calls the specified function for each of the Records in the cache.
11908 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11909 * Returning <em>false</em> aborts and exits the iteration.
11910 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11912 each : function(fn, scope){
11913 this.data.each(fn, scope);
11917 * Gets all records modified since the last commit. Modified records are persisted across load operations
11918 * (e.g., during paging).
11919 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11921 getModifiedRecords : function(){
11922 return this.modified;
11926 createFilterFn : function(property, value, anyMatch){
11927 if(!value.exec){ // not a regex
11928 value = String(value);
11929 if(value.length == 0){
11932 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11934 return function(r){
11935 return value.test(r.data[property]);
11940 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11941 * @param {String} property A field on your records
11942 * @param {Number} start The record index to start at (defaults to 0)
11943 * @param {Number} end The last record index to include (defaults to length - 1)
11944 * @return {Number} The sum
11946 sum : function(property, start, end){
11947 var rs = this.data.items, v = 0;
11948 start = start || 0;
11949 end = (end || end === 0) ? end : rs.length-1;
11951 for(var i = start; i <= end; i++){
11952 v += (rs[i].data[property] || 0);
11958 * Filter the records by a specified property.
11959 * @param {String} field A field on your records
11960 * @param {String/RegExp} value Either a string that the field
11961 * should start with or a RegExp to test against the field
11962 * @param {Boolean} anyMatch True to match any part not just the beginning
11964 filter : function(property, value, anyMatch){
11965 var fn = this.createFilterFn(property, value, anyMatch);
11966 return fn ? this.filterBy(fn) : this.clearFilter();
11970 * Filter by a function. The specified function will be called with each
11971 * record in this data source. If the function returns true the record is included,
11972 * otherwise it is filtered.
11973 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11974 * @param {Object} scope (optional) The scope of the function (defaults to this)
11976 filterBy : function(fn, scope){
11977 this.snapshot = this.snapshot || this.data;
11978 this.data = this.queryBy(fn, scope||this);
11979 this.fireEvent("datachanged", this);
11983 * Query the records by a specified property.
11984 * @param {String} field A field on your records
11985 * @param {String/RegExp} value Either a string that the field
11986 * should start with or a RegExp to test against the field
11987 * @param {Boolean} anyMatch True to match any part not just the beginning
11988 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11990 query : function(property, value, anyMatch){
11991 var fn = this.createFilterFn(property, value, anyMatch);
11992 return fn ? this.queryBy(fn) : this.data.clone();
11996 * Query by a function. The specified function will be called with each
11997 * record in this data source. If the function returns true the record is included
11999 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12000 * @param {Object} scope (optional) The scope of the function (defaults to this)
12001 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12003 queryBy : function(fn, scope){
12004 var data = this.snapshot || this.data;
12005 return data.filterBy(fn, scope||this);
12009 * Collects unique values for a particular dataIndex from this store.
12010 * @param {String} dataIndex The property to collect
12011 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12012 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12013 * @return {Array} An array of the unique values
12015 collect : function(dataIndex, allowNull, bypassFilter){
12016 var d = (bypassFilter === true && this.snapshot) ?
12017 this.snapshot.items : this.data.items;
12018 var v, sv, r = [], l = {};
12019 for(var i = 0, len = d.length; i < len; i++){
12020 v = d[i].data[dataIndex];
12022 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12031 * Revert to a view of the Record cache with no filtering applied.
12032 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12034 clearFilter : function(suppressEvent){
12035 if(this.snapshot && this.snapshot != this.data){
12036 this.data = this.snapshot;
12037 delete this.snapshot;
12038 if(suppressEvent !== true){
12039 this.fireEvent("datachanged", this);
12045 afterEdit : function(record){
12046 if(this.modified.indexOf(record) == -1){
12047 this.modified.push(record);
12049 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12053 afterReject : function(record){
12054 this.modified.remove(record);
12055 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12059 afterCommit : function(record){
12060 this.modified.remove(record);
12061 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12065 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12066 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12068 commitChanges : function(){
12069 var m = this.modified.slice(0);
12070 this.modified = [];
12071 for(var i = 0, len = m.length; i < len; i++){
12077 * Cancel outstanding changes on all changed records.
12079 rejectChanges : function(){
12080 var m = this.modified.slice(0);
12081 this.modified = [];
12082 for(var i = 0, len = m.length; i < len; i++){
12087 onMetaChange : function(meta, rtype, o){
12088 this.recordType = rtype;
12089 this.fields = rtype.prototype.fields;
12090 delete this.snapshot;
12091 this.sortInfo = meta.sortInfo || this.sortInfo;
12092 this.modified = [];
12093 this.fireEvent('metachange', this, this.reader.meta);
12096 moveIndex : function(data, type)
12098 var index = this.indexOf(data);
12100 var newIndex = index + type;
12104 this.insert(newIndex, data);
12109 * Ext JS Library 1.1.1
12110 * Copyright(c) 2006-2007, Ext JS, LLC.
12112 * Originally Released Under LGPL - original licence link has changed is not relivant.
12115 * <script type="text/javascript">
12119 * @class Roo.data.SimpleStore
12120 * @extends Roo.data.Store
12121 * Small helper class to make creating Stores from Array data easier.
12122 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12123 * @cfg {Array} fields An array of field definition objects, or field name strings.
12124 * @cfg {Array} data The multi-dimensional array of data
12126 * @param {Object} config
12128 Roo.data.SimpleStore = function(config){
12129 Roo.data.SimpleStore.superclass.constructor.call(this, {
12131 reader: new Roo.data.ArrayReader({
12134 Roo.data.Record.create(config.fields)
12136 proxy : new Roo.data.MemoryProxy(config.data)
12140 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12142 * Ext JS Library 1.1.1
12143 * Copyright(c) 2006-2007, Ext JS, LLC.
12145 * Originally Released Under LGPL - original licence link has changed is not relivant.
12148 * <script type="text/javascript">
12153 * @extends Roo.data.Store
12154 * @class Roo.data.JsonStore
12155 * Small helper class to make creating Stores for JSON data easier. <br/>
12157 var store = new Roo.data.JsonStore({
12158 url: 'get-images.php',
12160 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12163 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12164 * JsonReader and HttpProxy (unless inline data is provided).</b>
12165 * @cfg {Array} fields An array of field definition objects, or field name strings.
12167 * @param {Object} config
12169 Roo.data.JsonStore = function(c){
12170 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12171 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12172 reader: new Roo.data.JsonReader(c, c.fields)
12175 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12177 * Ext JS Library 1.1.1
12178 * Copyright(c) 2006-2007, Ext JS, LLC.
12180 * Originally Released Under LGPL - original licence link has changed is not relivant.
12183 * <script type="text/javascript">
12187 Roo.data.Field = function(config){
12188 if(typeof config == "string"){
12189 config = {name: config};
12191 Roo.apply(this, config);
12194 this.type = "auto";
12197 var st = Roo.data.SortTypes;
12198 // named sortTypes are supported, here we look them up
12199 if(typeof this.sortType == "string"){
12200 this.sortType = st[this.sortType];
12203 // set default sortType for strings and dates
12204 if(!this.sortType){
12207 this.sortType = st.asUCString;
12210 this.sortType = st.asDate;
12213 this.sortType = st.none;
12218 var stripRe = /[\$,%]/g;
12220 // prebuilt conversion function for this field, instead of
12221 // switching every time we're reading a value
12223 var cv, dateFormat = this.dateFormat;
12228 cv = function(v){ return v; };
12231 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12235 return v !== undefined && v !== null && v !== '' ?
12236 parseInt(String(v).replace(stripRe, ""), 10) : '';
12241 return v !== undefined && v !== null && v !== '' ?
12242 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12247 cv = function(v){ return v === true || v === "true" || v == 1; };
12254 if(v instanceof Date){
12258 if(dateFormat == "timestamp"){
12259 return new Date(v*1000);
12261 return Date.parseDate(v, dateFormat);
12263 var parsed = Date.parse(v);
12264 return parsed ? new Date(parsed) : null;
12273 Roo.data.Field.prototype = {
12281 * Ext JS Library 1.1.1
12282 * Copyright(c) 2006-2007, Ext JS, LLC.
12284 * Originally Released Under LGPL - original licence link has changed is not relivant.
12287 * <script type="text/javascript">
12290 // Base class for reading structured data from a data source. This class is intended to be
12291 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12294 * @class Roo.data.DataReader
12295 * Base class for reading structured data from a data source. This class is intended to be
12296 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12299 Roo.data.DataReader = function(meta, recordType){
12303 this.recordType = recordType instanceof Array ?
12304 Roo.data.Record.create(recordType) : recordType;
12307 Roo.data.DataReader.prototype = {
12309 * Create an empty record
12310 * @param {Object} data (optional) - overlay some values
12311 * @return {Roo.data.Record} record created.
12313 newRow : function(d) {
12315 this.recordType.prototype.fields.each(function(c) {
12317 case 'int' : da[c.name] = 0; break;
12318 case 'date' : da[c.name] = new Date(); break;
12319 case 'float' : da[c.name] = 0.0; break;
12320 case 'boolean' : da[c.name] = false; break;
12321 default : da[c.name] = ""; break;
12325 return new this.recordType(Roo.apply(da, d));
12330 * Ext JS Library 1.1.1
12331 * Copyright(c) 2006-2007, Ext JS, LLC.
12333 * Originally Released Under LGPL - original licence link has changed is not relivant.
12336 * <script type="text/javascript">
12340 * @class Roo.data.DataProxy
12341 * @extends Roo.data.Observable
12342 * This class is an abstract base class for implementations which provide retrieval of
12343 * unformatted data objects.<br>
12345 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12346 * (of the appropriate type which knows how to parse the data object) to provide a block of
12347 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12349 * Custom implementations must implement the load method as described in
12350 * {@link Roo.data.HttpProxy#load}.
12352 Roo.data.DataProxy = function(){
12355 * @event beforeload
12356 * Fires before a network request is made to retrieve a data object.
12357 * @param {Object} This DataProxy object.
12358 * @param {Object} params The params parameter to the load function.
12363 * Fires before the load method's callback is called.
12364 * @param {Object} This DataProxy object.
12365 * @param {Object} o The data object.
12366 * @param {Object} arg The callback argument object passed to the load function.
12370 * @event loadexception
12371 * Fires if an Exception occurs during data retrieval.
12372 * @param {Object} This DataProxy object.
12373 * @param {Object} o The data object.
12374 * @param {Object} arg The callback argument object passed to the load function.
12375 * @param {Object} e The Exception.
12377 loadexception : true
12379 Roo.data.DataProxy.superclass.constructor.call(this);
12382 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12385 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12389 * Ext JS Library 1.1.1
12390 * Copyright(c) 2006-2007, Ext JS, LLC.
12392 * Originally Released Under LGPL - original licence link has changed is not relivant.
12395 * <script type="text/javascript">
12398 * @class Roo.data.MemoryProxy
12399 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12400 * to the Reader when its load method is called.
12402 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12404 Roo.data.MemoryProxy = function(data){
12408 Roo.data.MemoryProxy.superclass.constructor.call(this);
12412 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12415 * Load data from the requested source (in this case an in-memory
12416 * data object passed to the constructor), read the data object into
12417 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12418 * process that block using the passed callback.
12419 * @param {Object} params This parameter is not used by the MemoryProxy class.
12420 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12421 * object into a block of Roo.data.Records.
12422 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12423 * The function must be passed <ul>
12424 * <li>The Record block object</li>
12425 * <li>The "arg" argument from the load function</li>
12426 * <li>A boolean success indicator</li>
12428 * @param {Object} scope The scope in which to call the callback
12429 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12431 load : function(params, reader, callback, scope, arg){
12432 params = params || {};
12435 result = reader.readRecords(params.data ? params.data :this.data);
12437 this.fireEvent("loadexception", this, arg, null, e);
12438 callback.call(scope, null, arg, false);
12441 callback.call(scope, result, arg, true);
12445 update : function(params, records){
12450 * Ext JS Library 1.1.1
12451 * Copyright(c) 2006-2007, Ext JS, LLC.
12453 * Originally Released Under LGPL - original licence link has changed is not relivant.
12456 * <script type="text/javascript">
12459 * @class Roo.data.HttpProxy
12460 * @extends Roo.data.DataProxy
12461 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12462 * configured to reference a certain URL.<br><br>
12464 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12465 * from which the running page was served.<br><br>
12467 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12469 * Be aware that to enable the browser to parse an XML document, the server must set
12470 * the Content-Type header in the HTTP response to "text/xml".
12472 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12473 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12474 * will be used to make the request.
12476 Roo.data.HttpProxy = function(conn){
12477 Roo.data.HttpProxy.superclass.constructor.call(this);
12478 // is conn a conn config or a real conn?
12480 this.useAjax = !conn || !conn.events;
12484 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12485 // thse are take from connection...
12488 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12491 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12492 * extra parameters to each request made by this object. (defaults to undefined)
12495 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12496 * to each request made by this object. (defaults to undefined)
12499 * @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)
12502 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12505 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12511 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12515 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12516 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12517 * a finer-grained basis than the DataProxy events.
12519 getConnection : function(){
12520 return this.useAjax ? Roo.Ajax : this.conn;
12524 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12525 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12526 * process that block using the passed callback.
12527 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12528 * for the request to the remote server.
12529 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12530 * object into a block of Roo.data.Records.
12531 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12532 * The function must be passed <ul>
12533 * <li>The Record block object</li>
12534 * <li>The "arg" argument from the load function</li>
12535 * <li>A boolean success indicator</li>
12537 * @param {Object} scope The scope in which to call the callback
12538 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12540 load : function(params, reader, callback, scope, arg){
12541 if(this.fireEvent("beforeload", this, params) !== false){
12543 params : params || {},
12545 callback : callback,
12550 callback : this.loadResponse,
12554 Roo.applyIf(o, this.conn);
12555 if(this.activeRequest){
12556 Roo.Ajax.abort(this.activeRequest);
12558 this.activeRequest = Roo.Ajax.request(o);
12560 this.conn.request(o);
12563 callback.call(scope||this, null, arg, false);
12568 loadResponse : function(o, success, response){
12569 delete this.activeRequest;
12571 this.fireEvent("loadexception", this, o, response);
12572 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12577 result = o.reader.read(response);
12579 this.fireEvent("loadexception", this, o, response, e);
12580 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12584 this.fireEvent("load", this, o, o.request.arg);
12585 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12589 update : function(dataSet){
12594 updateResponse : function(dataSet){
12599 * Ext JS Library 1.1.1
12600 * Copyright(c) 2006-2007, Ext JS, LLC.
12602 * Originally Released Under LGPL - original licence link has changed is not relivant.
12605 * <script type="text/javascript">
12609 * @class Roo.data.ScriptTagProxy
12610 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12611 * other than the originating domain of the running page.<br><br>
12613 * <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
12614 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12616 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12617 * source code that is used as the source inside a <script> tag.<br><br>
12619 * In order for the browser to process the returned data, the server must wrap the data object
12620 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12621 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12622 * depending on whether the callback name was passed:
12625 boolean scriptTag = false;
12626 String cb = request.getParameter("callback");
12629 response.setContentType("text/javascript");
12631 response.setContentType("application/x-json");
12633 Writer out = response.getWriter();
12635 out.write(cb + "(");
12637 out.print(dataBlock.toJsonString());
12644 * @param {Object} config A configuration object.
12646 Roo.data.ScriptTagProxy = function(config){
12647 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12648 Roo.apply(this, config);
12649 this.head = document.getElementsByTagName("head")[0];
12652 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12654 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12656 * @cfg {String} url The URL from which to request the data object.
12659 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12663 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12664 * the server the name of the callback function set up by the load call to process the returned data object.
12665 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12666 * javascript output which calls this named function passing the data object as its only parameter.
12668 callbackParam : "callback",
12670 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12671 * name to the request.
12676 * Load data from the configured URL, read the data object into
12677 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12678 * process that block using the passed callback.
12679 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12680 * for the request to the remote server.
12681 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12682 * object into a block of Roo.data.Records.
12683 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12684 * The function must be passed <ul>
12685 * <li>The Record block object</li>
12686 * <li>The "arg" argument from the load function</li>
12687 * <li>A boolean success indicator</li>
12689 * @param {Object} scope The scope in which to call the callback
12690 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12692 load : function(params, reader, callback, scope, arg){
12693 if(this.fireEvent("beforeload", this, params) !== false){
12695 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12697 var url = this.url;
12698 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12700 url += "&_dc=" + (new Date().getTime());
12702 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12705 cb : "stcCallback"+transId,
12706 scriptId : "stcScript"+transId,
12710 callback : callback,
12716 window[trans.cb] = function(o){
12717 conn.handleResponse(o, trans);
12720 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12722 if(this.autoAbort !== false){
12726 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12728 var script = document.createElement("script");
12729 script.setAttribute("src", url);
12730 script.setAttribute("type", "text/javascript");
12731 script.setAttribute("id", trans.scriptId);
12732 this.head.appendChild(script);
12734 this.trans = trans;
12736 callback.call(scope||this, null, arg, false);
12741 isLoading : function(){
12742 return this.trans ? true : false;
12746 * Abort the current server request.
12748 abort : function(){
12749 if(this.isLoading()){
12750 this.destroyTrans(this.trans);
12755 destroyTrans : function(trans, isLoaded){
12756 this.head.removeChild(document.getElementById(trans.scriptId));
12757 clearTimeout(trans.timeoutId);
12759 window[trans.cb] = undefined;
12761 delete window[trans.cb];
12764 // if hasn't been loaded, wait for load to remove it to prevent script error
12765 window[trans.cb] = function(){
12766 window[trans.cb] = undefined;
12768 delete window[trans.cb];
12775 handleResponse : function(o, trans){
12776 this.trans = false;
12777 this.destroyTrans(trans, true);
12780 result = trans.reader.readRecords(o);
12782 this.fireEvent("loadexception", this, o, trans.arg, e);
12783 trans.callback.call(trans.scope||window, null, trans.arg, false);
12786 this.fireEvent("load", this, o, trans.arg);
12787 trans.callback.call(trans.scope||window, result, trans.arg, true);
12791 handleFailure : function(trans){
12792 this.trans = false;
12793 this.destroyTrans(trans, false);
12794 this.fireEvent("loadexception", this, null, trans.arg);
12795 trans.callback.call(trans.scope||window, null, trans.arg, false);
12799 * Ext JS Library 1.1.1
12800 * Copyright(c) 2006-2007, Ext JS, LLC.
12802 * Originally Released Under LGPL - original licence link has changed is not relivant.
12805 * <script type="text/javascript">
12809 * @class Roo.data.JsonReader
12810 * @extends Roo.data.DataReader
12811 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12812 * based on mappings in a provided Roo.data.Record constructor.
12814 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12815 * in the reply previously.
12820 var RecordDef = Roo.data.Record.create([
12821 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12822 {name: 'occupation'} // This field will use "occupation" as the mapping.
12824 var myReader = new Roo.data.JsonReader({
12825 totalProperty: "results", // The property which contains the total dataset size (optional)
12826 root: "rows", // The property which contains an Array of row objects
12827 id: "id" // The property within each row object that provides an ID for the record (optional)
12831 * This would consume a JSON file like this:
12833 { 'results': 2, 'rows': [
12834 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12835 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12838 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12839 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12840 * paged from the remote server.
12841 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12842 * @cfg {String} root name of the property which contains the Array of row objects.
12843 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12844 * @cfg {Array} fields Array of field definition objects
12846 * Create a new JsonReader
12847 * @param {Object} meta Metadata configuration options
12848 * @param {Object} recordType Either an Array of field definition objects,
12849 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12851 Roo.data.JsonReader = function(meta, recordType){
12854 // set some defaults:
12855 Roo.applyIf(meta, {
12856 totalProperty: 'total',
12857 successProperty : 'success',
12862 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12864 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12867 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12868 * Used by Store query builder to append _requestMeta to params.
12871 metaFromRemote : false,
12873 * This method is only used by a DataProxy which has retrieved data from a remote server.
12874 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12875 * @return {Object} data A data block which is used by an Roo.data.Store object as
12876 * a cache of Roo.data.Records.
12878 read : function(response){
12879 var json = response.responseText;
12881 var o = /* eval:var:o */ eval("("+json+")");
12883 throw {message: "JsonReader.read: Json object not found"};
12889 this.metaFromRemote = true;
12890 this.meta = o.metaData;
12891 this.recordType = Roo.data.Record.create(o.metaData.fields);
12892 this.onMetaChange(this.meta, this.recordType, o);
12894 return this.readRecords(o);
12897 // private function a store will implement
12898 onMetaChange : function(meta, recordType, o){
12905 simpleAccess: function(obj, subsc) {
12912 getJsonAccessor: function(){
12914 return function(expr) {
12916 return(re.test(expr))
12917 ? new Function("obj", "return obj." + expr)
12922 return Roo.emptyFn;
12927 * Create a data block containing Roo.data.Records from an XML document.
12928 * @param {Object} o An object which contains an Array of row objects in the property specified
12929 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12930 * which contains the total size of the dataset.
12931 * @return {Object} data A data block which is used by an Roo.data.Store object as
12932 * a cache of Roo.data.Records.
12934 readRecords : function(o){
12936 * After any data loads, the raw JSON data is available for further custom processing.
12940 var s = this.meta, Record = this.recordType,
12941 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12943 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12945 if(s.totalProperty) {
12946 this.getTotal = this.getJsonAccessor(s.totalProperty);
12948 if(s.successProperty) {
12949 this.getSuccess = this.getJsonAccessor(s.successProperty);
12951 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12953 var g = this.getJsonAccessor(s.id);
12954 this.getId = function(rec) {
12956 return (r === undefined || r === "") ? null : r;
12959 this.getId = function(){return null;};
12962 for(var jj = 0; jj < fl; jj++){
12964 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12965 this.ef[jj] = this.getJsonAccessor(map);
12969 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12970 if(s.totalProperty){
12971 var vt = parseInt(this.getTotal(o), 10);
12976 if(s.successProperty){
12977 var vs = this.getSuccess(o);
12978 if(vs === false || vs === 'false'){
12983 for(var i = 0; i < c; i++){
12986 var id = this.getId(n);
12987 for(var j = 0; j < fl; j++){
12989 var v = this.ef[j](n);
12991 Roo.log('missing convert for ' + f.name);
12995 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12997 var record = new Record(values, id);
12999 records[i] = record;
13005 totalRecords : totalRecords
13010 * Ext JS Library 1.1.1
13011 * Copyright(c) 2006-2007, Ext JS, LLC.
13013 * Originally Released Under LGPL - original licence link has changed is not relivant.
13016 * <script type="text/javascript">
13020 * @class Roo.data.ArrayReader
13021 * @extends Roo.data.DataReader
13022 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13023 * Each element of that Array represents a row of data fields. The
13024 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13025 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13029 var RecordDef = Roo.data.Record.create([
13030 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13031 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13033 var myReader = new Roo.data.ArrayReader({
13034 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13038 * This would consume an Array like this:
13040 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13044 * Create a new JsonReader
13045 * @param {Object} meta Metadata configuration options.
13046 * @param {Object|Array} recordType Either an Array of field definition objects
13048 * @cfg {Array} fields Array of field definition objects
13049 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13050 * as specified to {@link Roo.data.Record#create},
13051 * or an {@link Roo.data.Record} object
13054 * created using {@link Roo.data.Record#create}.
13056 Roo.data.ArrayReader = function(meta, recordType){
13059 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13062 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13064 * Create a data block containing Roo.data.Records from an XML document.
13065 * @param {Object} o An Array of row objects which represents the dataset.
13066 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13067 * a cache of Roo.data.Records.
13069 readRecords : function(o){
13070 var sid = this.meta ? this.meta.id : null;
13071 var recordType = this.recordType, fields = recordType.prototype.fields;
13074 for(var i = 0; i < root.length; i++){
13077 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13078 for(var j = 0, jlen = fields.length; j < jlen; j++){
13079 var f = fields.items[j];
13080 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13081 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13083 values[f.name] = v;
13085 var record = new recordType(values, id);
13087 records[records.length] = record;
13091 totalRecords : records.length
13100 * @class Roo.bootstrap.ComboBox
13101 * @extends Roo.bootstrap.TriggerField
13102 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13103 * @cfg {Boolean} append (true|false) default false
13104 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13105 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13106 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13107 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13108 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13109 * @cfg {Boolean} animate default true
13110 * @cfg {Boolean} emptyResultText only for touch device
13111 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13112 * @cfg {String} emptyTitle default ''
13114 * Create a new ComboBox.
13115 * @param {Object} config Configuration options
13117 Roo.bootstrap.ComboBox = function(config){
13118 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13122 * Fires when the dropdown list is expanded
13123 * @param {Roo.bootstrap.ComboBox} combo This combo box
13128 * Fires when the dropdown list is collapsed
13129 * @param {Roo.bootstrap.ComboBox} combo This combo box
13133 * @event beforeselect
13134 * Fires before a list item is selected. Return false to cancel the selection.
13135 * @param {Roo.bootstrap.ComboBox} combo This combo box
13136 * @param {Roo.data.Record} record The data record returned from the underlying store
13137 * @param {Number} index The index of the selected item in the dropdown list
13139 'beforeselect' : true,
13142 * Fires when a list item is selected
13143 * @param {Roo.bootstrap.ComboBox} combo This combo box
13144 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13145 * @param {Number} index The index of the selected item in the dropdown list
13149 * @event beforequery
13150 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13151 * The event object passed has these properties:
13152 * @param {Roo.bootstrap.ComboBox} combo This combo box
13153 * @param {String} query The query
13154 * @param {Boolean} forceAll true to force "all" query
13155 * @param {Boolean} cancel true to cancel the query
13156 * @param {Object} e The query event object
13158 'beforequery': true,
13161 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13162 * @param {Roo.bootstrap.ComboBox} combo This combo box
13167 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13168 * @param {Roo.bootstrap.ComboBox} combo This combo box
13169 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13174 * Fires when the remove value from the combobox array
13175 * @param {Roo.bootstrap.ComboBox} combo This combo box
13179 * @event afterremove
13180 * Fires when the remove value from the combobox array
13181 * @param {Roo.bootstrap.ComboBox} combo This combo box
13183 'afterremove' : true,
13185 * @event specialfilter
13186 * Fires when specialfilter
13187 * @param {Roo.bootstrap.ComboBox} combo This combo box
13189 'specialfilter' : true,
13192 * Fires when tick the element
13193 * @param {Roo.bootstrap.ComboBox} combo This combo box
13197 * @event touchviewdisplay
13198 * Fires when touch view require special display (default is using displayField)
13199 * @param {Roo.bootstrap.ComboBox} combo This combo box
13200 * @param {Object} cfg set html .
13202 'touchviewdisplay' : true
13207 this.tickItems = [];
13209 this.selectedIndex = -1;
13210 if(this.mode == 'local'){
13211 if(config.queryDelay === undefined){
13212 this.queryDelay = 10;
13214 if(config.minChars === undefined){
13220 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13223 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13224 * rendering into an Roo.Editor, defaults to false)
13227 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13228 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13231 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13234 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13235 * the dropdown list (defaults to undefined, with no header element)
13239 * @cfg {String/Roo.Template} tpl The template to use to render the output
13243 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13245 listWidth: undefined,
13247 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13248 * mode = 'remote' or 'text' if mode = 'local')
13250 displayField: undefined,
13253 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13254 * mode = 'remote' or 'value' if mode = 'local').
13255 * Note: use of a valueField requires the user make a selection
13256 * in order for a value to be mapped.
13258 valueField: undefined,
13260 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13265 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13266 * field's data value (defaults to the underlying DOM element's name)
13268 hiddenName: undefined,
13270 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13274 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13276 selectedClass: 'active',
13279 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13283 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13284 * anchor positions (defaults to 'tl-bl')
13286 listAlign: 'tl-bl?',
13288 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13292 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13293 * query specified by the allQuery config option (defaults to 'query')
13295 triggerAction: 'query',
13297 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13298 * (defaults to 4, does not apply if editable = false)
13302 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13303 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13307 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13308 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13312 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13313 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13317 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13318 * when editable = true (defaults to false)
13320 selectOnFocus:false,
13322 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13324 queryParam: 'query',
13326 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13327 * when mode = 'remote' (defaults to 'Loading...')
13329 loadingText: 'Loading...',
13331 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13335 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13339 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13340 * traditional select (defaults to true)
13344 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13348 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13352 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13353 * listWidth has a higher value)
13357 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13358 * allow the user to set arbitrary text into the field (defaults to false)
13360 forceSelection:false,
13362 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13363 * if typeAhead = true (defaults to 250)
13365 typeAheadDelay : 250,
13367 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13368 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13370 valueNotFoundText : undefined,
13372 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13374 blockFocus : false,
13377 * @cfg {Boolean} disableClear Disable showing of clear button.
13379 disableClear : false,
13381 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13383 alwaysQuery : false,
13386 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13391 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13393 invalidClass : "has-warning",
13396 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13398 validClass : "has-success",
13401 * @cfg {Boolean} specialFilter (true|false) special filter default false
13403 specialFilter : false,
13406 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13408 mobileTouchView : true,
13411 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13413 useNativeIOS : false,
13416 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13418 mobile_restrict_height : false,
13420 ios_options : false,
13432 btnPosition : 'right',
13433 triggerList : true,
13434 showToggleBtn : true,
13436 emptyResultText: 'Empty',
13437 triggerText : 'Select',
13440 // element that contains real text value.. (when hidden is used..)
13442 getAutoCreate : function()
13447 * Render classic select for iso
13450 if(Roo.isIOS && this.useNativeIOS){
13451 cfg = this.getAutoCreateNativeIOS();
13459 if(Roo.isTouch && this.mobileTouchView){
13460 cfg = this.getAutoCreateTouchView();
13467 if(!this.tickable){
13468 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13473 * ComboBox with tickable selections
13476 var align = this.labelAlign || this.parentLabelAlign();
13479 cls : 'form-group roo-combobox-tickable' //input-group
13482 var btn_text_select = '';
13483 var btn_text_done = '';
13484 var btn_text_cancel = '';
13486 if (this.btn_text_show) {
13487 btn_text_select = 'Select';
13488 btn_text_done = 'Done';
13489 btn_text_cancel = 'Cancel';
13494 cls : 'tickable-buttons',
13499 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13500 //html : this.triggerText
13501 html: btn_text_select
13507 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13509 html: btn_text_done
13515 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13517 html: btn_text_cancel
13523 buttons.cn.unshift({
13525 cls: 'roo-select2-search-field-input'
13531 Roo.each(buttons.cn, function(c){
13533 c.cls += ' btn-' + _this.size;
13536 if (_this.disabled) {
13543 style : 'display: contents',
13548 cls: 'form-hidden-field'
13552 cls: 'roo-select2-choices',
13556 cls: 'roo-select2-search-field',
13567 cls: 'roo-select2-container input-group roo-select2-container-multi',
13573 // cls: 'typeahead typeahead-long dropdown-menu',
13574 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13579 if(this.hasFeedback && !this.allowBlank){
13583 cls: 'glyphicon form-control-feedback'
13586 combobox.cn.push(feedback);
13591 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13592 tooltip : 'This field is required'
13594 if (Roo.bootstrap.version == 4) {
13597 style : 'display:none'
13600 if (align ==='left' && this.fieldLabel.length) {
13602 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13609 cls : 'control-label col-form-label',
13610 html : this.fieldLabel
13622 var labelCfg = cfg.cn[1];
13623 var contentCfg = cfg.cn[2];
13626 if(this.indicatorpos == 'right'){
13632 cls : 'control-label col-form-label',
13636 html : this.fieldLabel
13652 labelCfg = cfg.cn[0];
13653 contentCfg = cfg.cn[1];
13657 if(this.labelWidth > 12){
13658 labelCfg.style = "width: " + this.labelWidth + 'px';
13661 if(this.labelWidth < 13 && this.labelmd == 0){
13662 this.labelmd = this.labelWidth;
13665 if(this.labellg > 0){
13666 labelCfg.cls += ' col-lg-' + this.labellg;
13667 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13670 if(this.labelmd > 0){
13671 labelCfg.cls += ' col-md-' + this.labelmd;
13672 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13675 if(this.labelsm > 0){
13676 labelCfg.cls += ' col-sm-' + this.labelsm;
13677 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13680 if(this.labelxs > 0){
13681 labelCfg.cls += ' col-xs-' + this.labelxs;
13682 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13686 } else if ( this.fieldLabel.length) {
13687 // Roo.log(" label");
13692 //cls : 'input-group-addon',
13693 html : this.fieldLabel
13698 if(this.indicatorpos == 'right'){
13702 //cls : 'input-group-addon',
13703 html : this.fieldLabel
13713 // Roo.log(" no label && no align");
13720 ['xs','sm','md','lg'].map(function(size){
13721 if (settings[size]) {
13722 cfg.cls += ' col-' + size + '-' + settings[size];
13730 _initEventsCalled : false,
13733 initEvents: function()
13735 if (this._initEventsCalled) { // as we call render... prevent looping...
13738 this._initEventsCalled = true;
13741 throw "can not find store for combo";
13744 this.indicator = this.indicatorEl();
13746 this.store = Roo.factory(this.store, Roo.data);
13747 this.store.parent = this;
13749 // if we are building from html. then this element is so complex, that we can not really
13750 // use the rendered HTML.
13751 // so we have to trash and replace the previous code.
13752 if (Roo.XComponent.build_from_html) {
13753 // remove this element....
13754 var e = this.el.dom, k=0;
13755 while (e ) { e = e.previousSibling; ++k;}
13760 this.rendered = false;
13762 this.render(this.parent().getChildContainer(true), k);
13765 if(Roo.isIOS && this.useNativeIOS){
13766 this.initIOSView();
13774 if(Roo.isTouch && this.mobileTouchView){
13775 this.initTouchView();
13780 this.initTickableEvents();
13784 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13786 if(this.hiddenName){
13788 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13790 this.hiddenField.dom.value =
13791 this.hiddenValue !== undefined ? this.hiddenValue :
13792 this.value !== undefined ? this.value : '';
13794 // prevent input submission
13795 this.el.dom.removeAttribute('name');
13796 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13801 // this.el.dom.setAttribute('autocomplete', 'off');
13804 var cls = 'x-combo-list';
13806 //this.list = new Roo.Layer({
13807 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13813 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13814 _this.list.setWidth(lw);
13817 this.list.on('mouseover', this.onViewOver, this);
13818 this.list.on('mousemove', this.onViewMove, this);
13819 this.list.on('scroll', this.onViewScroll, this);
13822 this.list.swallowEvent('mousewheel');
13823 this.assetHeight = 0;
13826 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13827 this.assetHeight += this.header.getHeight();
13830 this.innerList = this.list.createChild({cls:cls+'-inner'});
13831 this.innerList.on('mouseover', this.onViewOver, this);
13832 this.innerList.on('mousemove', this.onViewMove, this);
13833 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13835 if(this.allowBlank && !this.pageSize && !this.disableClear){
13836 this.footer = this.list.createChild({cls:cls+'-ft'});
13837 this.pageTb = new Roo.Toolbar(this.footer);
13841 this.footer = this.list.createChild({cls:cls+'-ft'});
13842 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13843 {pageSize: this.pageSize});
13847 if (this.pageTb && this.allowBlank && !this.disableClear) {
13849 this.pageTb.add(new Roo.Toolbar.Fill(), {
13850 cls: 'x-btn-icon x-btn-clear',
13852 handler: function()
13855 _this.clearValue();
13856 _this.onSelect(false, -1);
13861 this.assetHeight += this.footer.getHeight();
13866 this.tpl = Roo.bootstrap.version == 4 ?
13867 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13868 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13871 this.view = new Roo.View(this.list, this.tpl, {
13872 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13874 //this.view.wrapEl.setDisplayed(false);
13875 this.view.on('click', this.onViewClick, this);
13878 this.store.on('beforeload', this.onBeforeLoad, this);
13879 this.store.on('load', this.onLoad, this);
13880 this.store.on('loadexception', this.onLoadException, this);
13882 if(this.resizable){
13883 this.resizer = new Roo.Resizable(this.list, {
13884 pinned:true, handles:'se'
13886 this.resizer.on('resize', function(r, w, h){
13887 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13888 this.listWidth = w;
13889 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13890 this.restrictHeight();
13892 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13895 if(!this.editable){
13896 this.editable = true;
13897 this.setEditable(false);
13902 if (typeof(this.events.add.listeners) != 'undefined') {
13904 this.addicon = this.wrap.createChild(
13905 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13907 this.addicon.on('click', function(e) {
13908 this.fireEvent('add', this);
13911 if (typeof(this.events.edit.listeners) != 'undefined') {
13913 this.editicon = this.wrap.createChild(
13914 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13915 if (this.addicon) {
13916 this.editicon.setStyle('margin-left', '40px');
13918 this.editicon.on('click', function(e) {
13920 // we fire even if inothing is selected..
13921 this.fireEvent('edit', this, this.lastData );
13927 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13928 "up" : function(e){
13929 this.inKeyMode = true;
13933 "down" : function(e){
13934 if(!this.isExpanded()){
13935 this.onTriggerClick();
13937 this.inKeyMode = true;
13942 "enter" : function(e){
13943 // this.onViewClick();
13947 if(this.fireEvent("specialkey", this, e)){
13948 this.onViewClick(false);
13954 "esc" : function(e){
13958 "tab" : function(e){
13961 if(this.fireEvent("specialkey", this, e)){
13962 this.onViewClick(false);
13970 doRelay : function(foo, bar, hname){
13971 if(hname == 'down' || this.scope.isExpanded()){
13972 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13981 this.queryDelay = Math.max(this.queryDelay || 10,
13982 this.mode == 'local' ? 10 : 250);
13985 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13987 if(this.typeAhead){
13988 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13990 if(this.editable !== false){
13991 this.inputEl().on("keyup", this.onKeyUp, this);
13993 if(this.forceSelection){
13994 this.inputEl().on('blur', this.doForce, this);
13998 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13999 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14003 initTickableEvents: function()
14007 if(this.hiddenName){
14009 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14011 this.hiddenField.dom.value =
14012 this.hiddenValue !== undefined ? this.hiddenValue :
14013 this.value !== undefined ? this.value : '';
14015 // prevent input submission
14016 this.el.dom.removeAttribute('name');
14017 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14022 // this.list = this.el.select('ul.dropdown-menu',true).first();
14024 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14025 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14026 if(this.triggerList){
14027 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14030 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14031 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14033 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14034 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14036 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14037 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14039 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14040 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14041 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14044 this.cancelBtn.hide();
14049 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14050 _this.list.setWidth(lw);
14053 this.list.on('mouseover', this.onViewOver, this);
14054 this.list.on('mousemove', this.onViewMove, this);
14056 this.list.on('scroll', this.onViewScroll, this);
14059 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14060 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14063 this.view = new Roo.View(this.list, this.tpl, {
14068 selectedClass: this.selectedClass
14071 //this.view.wrapEl.setDisplayed(false);
14072 this.view.on('click', this.onViewClick, this);
14076 this.store.on('beforeload', this.onBeforeLoad, this);
14077 this.store.on('load', this.onLoad, this);
14078 this.store.on('loadexception', this.onLoadException, this);
14081 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14082 "up" : function(e){
14083 this.inKeyMode = true;
14087 "down" : function(e){
14088 this.inKeyMode = true;
14092 "enter" : function(e){
14093 if(this.fireEvent("specialkey", this, e)){
14094 this.onViewClick(false);
14100 "esc" : function(e){
14101 this.onTickableFooterButtonClick(e, false, false);
14104 "tab" : function(e){
14105 this.fireEvent("specialkey", this, e);
14107 this.onTickableFooterButtonClick(e, false, false);
14114 doRelay : function(e, fn, key){
14115 if(this.scope.isExpanded()){
14116 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14125 this.queryDelay = Math.max(this.queryDelay || 10,
14126 this.mode == 'local' ? 10 : 250);
14129 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14131 if(this.typeAhead){
14132 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14135 if(this.editable !== false){
14136 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14139 this.indicator = this.indicatorEl();
14141 if(this.indicator){
14142 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14143 this.indicator.hide();
14148 onDestroy : function(){
14150 this.view.setStore(null);
14151 this.view.el.removeAllListeners();
14152 this.view.el.remove();
14153 this.view.purgeListeners();
14156 this.list.dom.innerHTML = '';
14160 this.store.un('beforeload', this.onBeforeLoad, this);
14161 this.store.un('load', this.onLoad, this);
14162 this.store.un('loadexception', this.onLoadException, this);
14164 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14168 fireKey : function(e){
14169 if(e.isNavKeyPress() && !this.list.isVisible()){
14170 this.fireEvent("specialkey", this, e);
14175 onResize: function(w, h){
14176 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14178 // if(typeof w != 'number'){
14179 // // we do not handle it!?!?
14182 // var tw = this.trigger.getWidth();
14183 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14184 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14186 // this.inputEl().setWidth( this.adjustWidth('input', x));
14188 // //this.trigger.setStyle('left', x+'px');
14190 // if(this.list && this.listWidth === undefined){
14191 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14192 // this.list.setWidth(lw);
14193 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14201 * Allow or prevent the user from directly editing the field text. If false is passed,
14202 * the user will only be able to select from the items defined in the dropdown list. This method
14203 * is the runtime equivalent of setting the 'editable' config option at config time.
14204 * @param {Boolean} value True to allow the user to directly edit the field text
14206 setEditable : function(value){
14207 if(value == this.editable){
14210 this.editable = value;
14212 this.inputEl().dom.setAttribute('readOnly', true);
14213 this.inputEl().on('mousedown', this.onTriggerClick, this);
14214 this.inputEl().addClass('x-combo-noedit');
14216 this.inputEl().dom.setAttribute('readOnly', false);
14217 this.inputEl().un('mousedown', this.onTriggerClick, this);
14218 this.inputEl().removeClass('x-combo-noedit');
14224 onBeforeLoad : function(combo,opts){
14225 if(!this.hasFocus){
14229 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14231 this.restrictHeight();
14232 this.selectedIndex = -1;
14236 onLoad : function(){
14238 this.hasQuery = false;
14240 if(!this.hasFocus){
14244 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14245 this.loading.hide();
14248 if(this.store.getCount() > 0){
14251 this.restrictHeight();
14252 if(this.lastQuery == this.allQuery){
14253 if(this.editable && !this.tickable){
14254 this.inputEl().dom.select();
14258 !this.selectByValue(this.value, true) &&
14261 !this.store.lastOptions ||
14262 typeof(this.store.lastOptions.add) == 'undefined' ||
14263 this.store.lastOptions.add != true
14266 this.select(0, true);
14269 if(this.autoFocus){
14272 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14273 this.taTask.delay(this.typeAheadDelay);
14277 this.onEmptyResults();
14283 onLoadException : function()
14285 this.hasQuery = false;
14287 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14288 this.loading.hide();
14291 if(this.tickable && this.editable){
14296 // only causes errors at present
14297 //Roo.log(this.store.reader.jsonData);
14298 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14300 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14306 onTypeAhead : function(){
14307 if(this.store.getCount() > 0){
14308 var r = this.store.getAt(0);
14309 var newValue = r.data[this.displayField];
14310 var len = newValue.length;
14311 var selStart = this.getRawValue().length;
14313 if(selStart != len){
14314 this.setRawValue(newValue);
14315 this.selectText(selStart, newValue.length);
14321 onSelect : function(record, index){
14323 if(this.fireEvent('beforeselect', this, record, index) !== false){
14325 this.setFromData(index > -1 ? record.data : false);
14328 this.fireEvent('select', this, record, index);
14333 * Returns the currently selected field value or empty string if no value is set.
14334 * @return {String} value The selected value
14336 getValue : function()
14338 if(Roo.isIOS && this.useNativeIOS){
14339 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14343 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14346 if(this.valueField){
14347 return typeof this.value != 'undefined' ? this.value : '';
14349 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14353 getRawValue : function()
14355 if(Roo.isIOS && this.useNativeIOS){
14356 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14359 var v = this.inputEl().getValue();
14365 * Clears any text/value currently set in the field
14367 clearValue : function(){
14369 if(this.hiddenField){
14370 this.hiddenField.dom.value = '';
14373 this.setRawValue('');
14374 this.lastSelectionText = '';
14375 this.lastData = false;
14377 var close = this.closeTriggerEl();
14388 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14389 * will be displayed in the field. If the value does not match the data value of an existing item,
14390 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14391 * Otherwise the field will be blank (although the value will still be set).
14392 * @param {String} value The value to match
14394 setValue : function(v)
14396 if(Roo.isIOS && this.useNativeIOS){
14397 this.setIOSValue(v);
14407 if(this.valueField){
14408 var r = this.findRecord(this.valueField, v);
14410 text = r.data[this.displayField];
14411 }else if(this.valueNotFoundText !== undefined){
14412 text = this.valueNotFoundText;
14415 this.lastSelectionText = text;
14416 if(this.hiddenField){
14417 this.hiddenField.dom.value = v;
14419 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14422 var close = this.closeTriggerEl();
14425 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14431 * @property {Object} the last set data for the element
14436 * Sets the value of the field based on a object which is related to the record format for the store.
14437 * @param {Object} value the value to set as. or false on reset?
14439 setFromData : function(o){
14446 var dv = ''; // display value
14447 var vv = ''; // value value..
14449 if (this.displayField) {
14450 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14452 // this is an error condition!!!
14453 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14456 if(this.valueField){
14457 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14460 var close = this.closeTriggerEl();
14463 if(dv.length || vv * 1 > 0){
14465 this.blockFocus=true;
14471 if(this.hiddenField){
14472 this.hiddenField.dom.value = vv;
14474 this.lastSelectionText = dv;
14475 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14479 // no hidden field.. - we store the value in 'value', but still display
14480 // display field!!!!
14481 this.lastSelectionText = dv;
14482 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14489 reset : function(){
14490 // overridden so that last data is reset..
14497 this.setValue(this.originalValue);
14498 //this.clearInvalid();
14499 this.lastData = false;
14501 this.view.clearSelections();
14507 findRecord : function(prop, value){
14509 if(this.store.getCount() > 0){
14510 this.store.each(function(r){
14511 if(r.data[prop] == value){
14521 getName: function()
14523 // returns hidden if it's set..
14524 if (!this.rendered) {return ''};
14525 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14529 onViewMove : function(e, t){
14530 this.inKeyMode = false;
14534 onViewOver : function(e, t){
14535 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14538 var item = this.view.findItemFromChild(t);
14541 var index = this.view.indexOf(item);
14542 this.select(index, false);
14547 onViewClick : function(view, doFocus, el, e)
14549 var index = this.view.getSelectedIndexes()[0];
14551 var r = this.store.getAt(index);
14555 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14562 Roo.each(this.tickItems, function(v,k){
14564 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14566 _this.tickItems.splice(k, 1);
14568 if(typeof(e) == 'undefined' && view == false){
14569 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14581 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14582 this.tickItems.push(r.data);
14585 if(typeof(e) == 'undefined' && view == false){
14586 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14593 this.onSelect(r, index);
14595 if(doFocus !== false && !this.blockFocus){
14596 this.inputEl().focus();
14601 restrictHeight : function(){
14602 //this.innerList.dom.style.height = '';
14603 //var inner = this.innerList.dom;
14604 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14605 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14606 //this.list.beginUpdate();
14607 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14608 this.list.alignTo(this.inputEl(), this.listAlign);
14609 this.list.alignTo(this.inputEl(), this.listAlign);
14610 //this.list.endUpdate();
14614 onEmptyResults : function(){
14616 if(this.tickable && this.editable){
14617 this.hasFocus = false;
14618 this.restrictHeight();
14626 * Returns true if the dropdown list is expanded, else false.
14628 isExpanded : function(){
14629 return this.list.isVisible();
14633 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14634 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14635 * @param {String} value The data value of the item to select
14636 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14637 * selected item if it is not currently in view (defaults to true)
14638 * @return {Boolean} True if the value matched an item in the list, else false
14640 selectByValue : function(v, scrollIntoView){
14641 if(v !== undefined && v !== null){
14642 var r = this.findRecord(this.valueField || this.displayField, v);
14644 this.select(this.store.indexOf(r), scrollIntoView);
14652 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14653 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14654 * @param {Number} index The zero-based index of the list item to select
14655 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14656 * selected item if it is not currently in view (defaults to true)
14658 select : function(index, scrollIntoView){
14659 this.selectedIndex = index;
14660 this.view.select(index);
14661 if(scrollIntoView !== false){
14662 var el = this.view.getNode(index);
14664 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14667 this.list.scrollChildIntoView(el, false);
14673 selectNext : function(){
14674 var ct = this.store.getCount();
14676 if(this.selectedIndex == -1){
14678 }else if(this.selectedIndex < ct-1){
14679 this.select(this.selectedIndex+1);
14685 selectPrev : function(){
14686 var ct = this.store.getCount();
14688 if(this.selectedIndex == -1){
14690 }else if(this.selectedIndex != 0){
14691 this.select(this.selectedIndex-1);
14697 onKeyUp : function(e){
14698 if(this.editable !== false && !e.isSpecialKey()){
14699 this.lastKey = e.getKey();
14700 this.dqTask.delay(this.queryDelay);
14705 validateBlur : function(){
14706 return !this.list || !this.list.isVisible();
14710 initQuery : function(){
14712 var v = this.getRawValue();
14714 if(this.tickable && this.editable){
14715 v = this.tickableInputEl().getValue();
14722 doForce : function(){
14723 if(this.inputEl().dom.value.length > 0){
14724 this.inputEl().dom.value =
14725 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14731 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14732 * query allowing the query action to be canceled if needed.
14733 * @param {String} query The SQL query to execute
14734 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14735 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14736 * saved in the current store (defaults to false)
14738 doQuery : function(q, forceAll){
14740 if(q === undefined || q === null){
14745 forceAll: forceAll,
14749 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14754 forceAll = qe.forceAll;
14755 if(forceAll === true || (q.length >= this.minChars)){
14757 this.hasQuery = true;
14759 if(this.lastQuery != q || this.alwaysQuery){
14760 this.lastQuery = q;
14761 if(this.mode == 'local'){
14762 this.selectedIndex = -1;
14764 this.store.clearFilter();
14767 if(this.specialFilter){
14768 this.fireEvent('specialfilter', this);
14773 this.store.filter(this.displayField, q);
14776 this.store.fireEvent("datachanged", this.store);
14783 this.store.baseParams[this.queryParam] = q;
14785 var options = {params : this.getParams(q)};
14788 options.add = true;
14789 options.params.start = this.page * this.pageSize;
14792 this.store.load(options);
14795 * this code will make the page width larger, at the beginning, the list not align correctly,
14796 * we should expand the list on onLoad
14797 * so command out it
14802 this.selectedIndex = -1;
14807 this.loadNext = false;
14811 getParams : function(q){
14813 //p[this.queryParam] = q;
14817 p.limit = this.pageSize;
14823 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14825 collapse : function(){
14826 if(!this.isExpanded()){
14832 this.hasFocus = false;
14836 this.cancelBtn.hide();
14837 this.trigger.show();
14840 this.tickableInputEl().dom.value = '';
14841 this.tickableInputEl().blur();
14846 Roo.get(document).un('mousedown', this.collapseIf, this);
14847 Roo.get(document).un('mousewheel', this.collapseIf, this);
14848 if (!this.editable) {
14849 Roo.get(document).un('keydown', this.listKeyPress, this);
14851 this.fireEvent('collapse', this);
14857 collapseIf : function(e){
14858 var in_combo = e.within(this.el);
14859 var in_list = e.within(this.list);
14860 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14862 if (in_combo || in_list || is_list) {
14863 //e.stopPropagation();
14868 this.onTickableFooterButtonClick(e, false, false);
14876 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14878 expand : function(){
14880 if(this.isExpanded() || !this.hasFocus){
14884 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14885 this.list.setWidth(lw);
14891 this.restrictHeight();
14895 this.tickItems = Roo.apply([], this.item);
14898 this.cancelBtn.show();
14899 this.trigger.hide();
14902 this.tickableInputEl().focus();
14907 Roo.get(document).on('mousedown', this.collapseIf, this);
14908 Roo.get(document).on('mousewheel', this.collapseIf, this);
14909 if (!this.editable) {
14910 Roo.get(document).on('keydown', this.listKeyPress, this);
14913 this.fireEvent('expand', this);
14917 // Implements the default empty TriggerField.onTriggerClick function
14918 onTriggerClick : function(e)
14920 Roo.log('trigger click');
14922 if(this.disabled || !this.triggerList){
14927 this.loadNext = false;
14929 if(this.isExpanded()){
14931 if (!this.blockFocus) {
14932 this.inputEl().focus();
14936 this.hasFocus = true;
14937 if(this.triggerAction == 'all') {
14938 this.doQuery(this.allQuery, true);
14940 this.doQuery(this.getRawValue());
14942 if (!this.blockFocus) {
14943 this.inputEl().focus();
14948 onTickableTriggerClick : function(e)
14955 this.loadNext = false;
14956 this.hasFocus = true;
14958 if(this.triggerAction == 'all') {
14959 this.doQuery(this.allQuery, true);
14961 this.doQuery(this.getRawValue());
14965 onSearchFieldClick : function(e)
14967 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14968 this.onTickableFooterButtonClick(e, false, false);
14972 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14977 this.loadNext = false;
14978 this.hasFocus = true;
14980 if(this.triggerAction == 'all') {
14981 this.doQuery(this.allQuery, true);
14983 this.doQuery(this.getRawValue());
14987 listKeyPress : function(e)
14989 //Roo.log('listkeypress');
14990 // scroll to first matching element based on key pres..
14991 if (e.isSpecialKey()) {
14994 var k = String.fromCharCode(e.getKey()).toUpperCase();
14997 var csel = this.view.getSelectedNodes();
14998 var cselitem = false;
15000 var ix = this.view.indexOf(csel[0]);
15001 cselitem = this.store.getAt(ix);
15002 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15008 this.store.each(function(v) {
15010 // start at existing selection.
15011 if (cselitem.id == v.id) {
15017 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15018 match = this.store.indexOf(v);
15024 if (match === false) {
15025 return true; // no more action?
15028 this.view.select(match);
15029 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15030 sn.scrollIntoView(sn.dom.parentNode, false);
15033 onViewScroll : function(e, t){
15035 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){
15039 this.hasQuery = true;
15041 this.loading = this.list.select('.loading', true).first();
15043 if(this.loading === null){
15044 this.list.createChild({
15046 cls: 'loading roo-select2-more-results roo-select2-active',
15047 html: 'Loading more results...'
15050 this.loading = this.list.select('.loading', true).first();
15052 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15054 this.loading.hide();
15057 this.loading.show();
15062 this.loadNext = true;
15064 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15069 addItem : function(o)
15071 var dv = ''; // display value
15073 if (this.displayField) {
15074 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15076 // this is an error condition!!!
15077 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15084 var choice = this.choices.createChild({
15086 cls: 'roo-select2-search-choice',
15095 cls: 'roo-select2-search-choice-close fa fa-times',
15100 }, this.searchField);
15102 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15104 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15112 this.inputEl().dom.value = '';
15117 onRemoveItem : function(e, _self, o)
15119 e.preventDefault();
15121 this.lastItem = Roo.apply([], this.item);
15123 var index = this.item.indexOf(o.data) * 1;
15126 Roo.log('not this item?!');
15130 this.item.splice(index, 1);
15135 this.fireEvent('remove', this, e);
15141 syncValue : function()
15143 if(!this.item.length){
15150 Roo.each(this.item, function(i){
15151 if(_this.valueField){
15152 value.push(i[_this.valueField]);
15159 this.value = value.join(',');
15161 if(this.hiddenField){
15162 this.hiddenField.dom.value = this.value;
15165 this.store.fireEvent("datachanged", this.store);
15170 clearItem : function()
15172 if(!this.multiple){
15178 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15186 if(this.tickable && !Roo.isTouch){
15187 this.view.refresh();
15191 inputEl: function ()
15193 if(Roo.isIOS && this.useNativeIOS){
15194 return this.el.select('select.roo-ios-select', true).first();
15197 if(Roo.isTouch && this.mobileTouchView){
15198 return this.el.select('input.form-control',true).first();
15202 return this.searchField;
15205 return this.el.select('input.form-control',true).first();
15208 onTickableFooterButtonClick : function(e, btn, el)
15210 e.preventDefault();
15212 this.lastItem = Roo.apply([], this.item);
15214 if(btn && btn.name == 'cancel'){
15215 this.tickItems = Roo.apply([], this.item);
15224 Roo.each(this.tickItems, function(o){
15232 validate : function()
15234 if(this.getVisibilityEl().hasClass('hidden')){
15238 var v = this.getRawValue();
15241 v = this.getValue();
15244 if(this.disabled || this.allowBlank || v.length){
15249 this.markInvalid();
15253 tickableInputEl : function()
15255 if(!this.tickable || !this.editable){
15256 return this.inputEl();
15259 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15263 getAutoCreateTouchView : function()
15268 cls: 'form-group' //input-group
15274 type : this.inputType,
15275 cls : 'form-control x-combo-noedit',
15276 autocomplete: 'new-password',
15277 placeholder : this.placeholder || '',
15282 input.name = this.name;
15286 input.cls += ' input-' + this.size;
15289 if (this.disabled) {
15290 input.disabled = true;
15301 inputblock.cls += ' input-group';
15303 inputblock.cn.unshift({
15305 cls : 'input-group-addon input-group-prepend input-group-text',
15310 if(this.removable && !this.multiple){
15311 inputblock.cls += ' roo-removable';
15313 inputblock.cn.push({
15316 cls : 'roo-combo-removable-btn close'
15320 if(this.hasFeedback && !this.allowBlank){
15322 inputblock.cls += ' has-feedback';
15324 inputblock.cn.push({
15326 cls: 'glyphicon form-control-feedback'
15333 inputblock.cls += (this.before) ? '' : ' input-group';
15335 inputblock.cn.push({
15337 cls : 'input-group-addon input-group-append input-group-text',
15343 var ibwrap = inputblock;
15348 cls: 'roo-select2-choices',
15352 cls: 'roo-select2-search-field',
15365 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15370 cls: 'form-hidden-field'
15376 if(!this.multiple && this.showToggleBtn){
15383 if (this.caret != false) {
15386 cls: 'fa fa-' + this.caret
15393 cls : 'input-group-addon input-group-append input-group-text btn' +
15394 (Roo.bootstrap.version == 3 ? ' dropdown-toggle' : ''),
15399 cls: 'combobox-clear',
15413 combobox.cls += ' roo-select2-container-multi';
15416 var align = this.labelAlign || this.parentLabelAlign();
15418 if (align ==='left' && this.fieldLabel.length) {
15423 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15424 tooltip : 'This field is required'
15428 cls : 'control-label col-form-label',
15429 html : this.fieldLabel
15440 var labelCfg = cfg.cn[1];
15441 var contentCfg = cfg.cn[2];
15444 if(this.indicatorpos == 'right'){
15449 cls : 'control-label col-form-label',
15453 html : this.fieldLabel
15457 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15458 tooltip : 'This field is required'
15471 labelCfg = cfg.cn[0];
15472 contentCfg = cfg.cn[1];
15477 if(this.labelWidth > 12){
15478 labelCfg.style = "width: " + this.labelWidth + 'px';
15481 if(this.labelWidth < 13 && this.labelmd == 0){
15482 this.labelmd = this.labelWidth;
15485 if(this.labellg > 0){
15486 labelCfg.cls += ' col-lg-' + this.labellg;
15487 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15490 if(this.labelmd > 0){
15491 labelCfg.cls += ' col-md-' + this.labelmd;
15492 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15495 if(this.labelsm > 0){
15496 labelCfg.cls += ' col-sm-' + this.labelsm;
15497 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15500 if(this.labelxs > 0){
15501 labelCfg.cls += ' col-xs-' + this.labelxs;
15502 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15506 } else if ( this.fieldLabel.length) {
15510 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15511 tooltip : 'This field is required'
15515 cls : 'control-label',
15516 html : this.fieldLabel
15527 if(this.indicatorpos == 'right'){
15531 cls : 'control-label',
15532 html : this.fieldLabel,
15536 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15537 tooltip : 'This field is required'
15554 var settings = this;
15556 ['xs','sm','md','lg'].map(function(size){
15557 if (settings[size]) {
15558 cfg.cls += ' col-' + size + '-' + settings[size];
15565 initTouchView : function()
15567 this.renderTouchView();
15569 this.touchViewEl.on('scroll', function(){
15570 this.el.dom.scrollTop = 0;
15573 this.originalValue = this.getValue();
15575 this.triggerEl = this.el.select('span.input-group-append',true).first();
15577 this.inputEl().on("click", this.showTouchView, this);
15578 if (this.triggerEl) {
15579 this.triggerEl.on("click", this.showTouchView, this);
15583 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15584 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15586 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15588 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15589 this.store.on('load', this.onTouchViewLoad, this);
15590 this.store.on('loadexception', this.onTouchViewLoadException, this);
15592 if(this.hiddenName){
15594 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15596 this.hiddenField.dom.value =
15597 this.hiddenValue !== undefined ? this.hiddenValue :
15598 this.value !== undefined ? this.value : '';
15600 this.el.dom.removeAttribute('name');
15601 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15605 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15606 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15609 if(this.removable && !this.multiple){
15610 var close = this.closeTriggerEl();
15612 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15613 close.on('click', this.removeBtnClick, this, close);
15617 * fix the bug in Safari iOS8
15619 this.inputEl().on("focus", function(e){
15620 document.activeElement.blur();
15623 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15630 renderTouchView : function()
15632 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15633 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15635 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15636 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15638 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15639 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15640 this.touchViewBodyEl.setStyle('overflow', 'auto');
15642 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15643 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15645 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15646 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15650 showTouchView : function()
15656 this.touchViewHeaderEl.hide();
15658 if(this.modalTitle.length){
15659 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15660 this.touchViewHeaderEl.show();
15663 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15664 this.touchViewEl.show();
15666 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15668 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15669 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15671 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15673 if(this.modalTitle.length){
15674 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15677 this.touchViewBodyEl.setHeight(bodyHeight);
15681 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15683 this.touchViewEl.addClass('in');
15686 if(this._touchViewMask){
15687 Roo.get(document.body).addClass("x-body-masked");
15688 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15689 this._touchViewMask.setStyle('z-index', 10000);
15690 this._touchViewMask.addClass('show');
15693 this.doTouchViewQuery();
15697 hideTouchView : function()
15699 this.touchViewEl.removeClass('in');
15703 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15705 this.touchViewEl.setStyle('display', 'none');
15708 if(this._touchViewMask){
15709 this._touchViewMask.removeClass('show');
15710 Roo.get(document.body).removeClass("x-body-masked");
15714 setTouchViewValue : function()
15721 Roo.each(this.tickItems, function(o){
15726 this.hideTouchView();
15729 doTouchViewQuery : function()
15738 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15742 if(!this.alwaysQuery || this.mode == 'local'){
15743 this.onTouchViewLoad();
15750 onTouchViewBeforeLoad : function(combo,opts)
15756 onTouchViewLoad : function()
15758 if(this.store.getCount() < 1){
15759 this.onTouchViewEmptyResults();
15763 this.clearTouchView();
15765 var rawValue = this.getRawValue();
15767 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15769 this.tickItems = [];
15771 this.store.data.each(function(d, rowIndex){
15772 var row = this.touchViewListGroup.createChild(template);
15774 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15775 row.addClass(d.data.cls);
15778 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15781 html : d.data[this.displayField]
15784 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15785 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15788 row.removeClass('selected');
15789 if(!this.multiple && this.valueField &&
15790 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15793 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15794 row.addClass('selected');
15797 if(this.multiple && this.valueField &&
15798 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15802 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15803 this.tickItems.push(d.data);
15806 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15810 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15812 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15814 if(this.modalTitle.length){
15815 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15818 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15820 if(this.mobile_restrict_height && listHeight < bodyHeight){
15821 this.touchViewBodyEl.setHeight(listHeight);
15826 if(firstChecked && listHeight > bodyHeight){
15827 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15832 onTouchViewLoadException : function()
15834 this.hideTouchView();
15837 onTouchViewEmptyResults : function()
15839 this.clearTouchView();
15841 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15843 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15847 clearTouchView : function()
15849 this.touchViewListGroup.dom.innerHTML = '';
15852 onTouchViewClick : function(e, el, o)
15854 e.preventDefault();
15857 var rowIndex = o.rowIndex;
15859 var r = this.store.getAt(rowIndex);
15861 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15863 if(!this.multiple){
15864 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15865 c.dom.removeAttribute('checked');
15868 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15870 this.setFromData(r.data);
15872 var close = this.closeTriggerEl();
15878 this.hideTouchView();
15880 this.fireEvent('select', this, r, rowIndex);
15885 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15886 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15887 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15891 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15892 this.addItem(r.data);
15893 this.tickItems.push(r.data);
15897 getAutoCreateNativeIOS : function()
15900 cls: 'form-group' //input-group,
15905 cls : 'roo-ios-select'
15909 combobox.name = this.name;
15912 if (this.disabled) {
15913 combobox.disabled = true;
15916 var settings = this;
15918 ['xs','sm','md','lg'].map(function(size){
15919 if (settings[size]) {
15920 cfg.cls += ' col-' + size + '-' + settings[size];
15930 initIOSView : function()
15932 this.store.on('load', this.onIOSViewLoad, this);
15937 onIOSViewLoad : function()
15939 if(this.store.getCount() < 1){
15943 this.clearIOSView();
15945 if(this.allowBlank) {
15947 var default_text = '-- SELECT --';
15949 if(this.placeholder.length){
15950 default_text = this.placeholder;
15953 if(this.emptyTitle.length){
15954 default_text += ' - ' + this.emptyTitle + ' -';
15957 var opt = this.inputEl().createChild({
15960 html : default_text
15964 o[this.valueField] = 0;
15965 o[this.displayField] = default_text;
15967 this.ios_options.push({
15974 this.store.data.each(function(d, rowIndex){
15978 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15979 html = d.data[this.displayField];
15984 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15985 value = d.data[this.valueField];
15994 if(this.value == d.data[this.valueField]){
15995 option['selected'] = true;
15998 var opt = this.inputEl().createChild(option);
16000 this.ios_options.push({
16007 this.inputEl().on('change', function(){
16008 this.fireEvent('select', this);
16013 clearIOSView: function()
16015 this.inputEl().dom.innerHTML = '';
16017 this.ios_options = [];
16020 setIOSValue: function(v)
16024 if(!this.ios_options){
16028 Roo.each(this.ios_options, function(opts){
16030 opts.el.dom.removeAttribute('selected');
16032 if(opts.data[this.valueField] != v){
16036 opts.el.dom.setAttribute('selected', true);
16042 * @cfg {Boolean} grow
16046 * @cfg {Number} growMin
16050 * @cfg {Number} growMax
16059 Roo.apply(Roo.bootstrap.ComboBox, {
16063 cls: 'modal-header',
16085 cls: 'list-group-item',
16089 cls: 'roo-combobox-list-group-item-value'
16093 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16107 listItemCheckbox : {
16109 cls: 'list-group-item',
16113 cls: 'roo-combobox-list-group-item-value'
16117 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16133 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16138 cls: 'modal-footer',
16146 cls: 'col-xs-6 text-left',
16149 cls: 'btn btn-danger roo-touch-view-cancel',
16155 cls: 'col-xs-6 text-right',
16158 cls: 'btn btn-success roo-touch-view-ok',
16169 Roo.apply(Roo.bootstrap.ComboBox, {
16171 touchViewTemplate : {
16173 cls: 'modal fade roo-combobox-touch-view',
16177 cls: 'modal-dialog',
16178 style : 'position:fixed', // we have to fix position....
16182 cls: 'modal-content',
16184 Roo.bootstrap.ComboBox.header,
16185 Roo.bootstrap.ComboBox.body,
16186 Roo.bootstrap.ComboBox.footer
16195 * Ext JS Library 1.1.1
16196 * Copyright(c) 2006-2007, Ext JS, LLC.
16198 * Originally Released Under LGPL - original licence link has changed is not relivant.
16201 * <script type="text/javascript">
16206 * @extends Roo.util.Observable
16207 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16208 * This class also supports single and multi selection modes. <br>
16209 * Create a data model bound view:
16211 var store = new Roo.data.Store(...);
16213 var view = new Roo.View({
16215 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16217 singleSelect: true,
16218 selectedClass: "ydataview-selected",
16222 // listen for node click?
16223 view.on("click", function(vw, index, node, e){
16224 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16228 dataModel.load("foobar.xml");
16230 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16232 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16233 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16235 * Note: old style constructor is still suported (container, template, config)
16238 * Create a new View
16239 * @param {Object} config The config object
16242 Roo.View = function(config, depreciated_tpl, depreciated_config){
16244 this.parent = false;
16246 if (typeof(depreciated_tpl) == 'undefined') {
16247 // new way.. - universal constructor.
16248 Roo.apply(this, config);
16249 this.el = Roo.get(this.el);
16252 this.el = Roo.get(config);
16253 this.tpl = depreciated_tpl;
16254 Roo.apply(this, depreciated_config);
16256 this.wrapEl = this.el.wrap().wrap();
16257 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16260 if(typeof(this.tpl) == "string"){
16261 this.tpl = new Roo.Template(this.tpl);
16263 // support xtype ctors..
16264 this.tpl = new Roo.factory(this.tpl, Roo);
16268 this.tpl.compile();
16273 * @event beforeclick
16274 * Fires before a click is processed. Returns false to cancel the default action.
16275 * @param {Roo.View} this
16276 * @param {Number} index The index of the target node
16277 * @param {HTMLElement} node The target node
16278 * @param {Roo.EventObject} e The raw event object
16280 "beforeclick" : true,
16283 * Fires when a template node is clicked.
16284 * @param {Roo.View} this
16285 * @param {Number} index The index of the target node
16286 * @param {HTMLElement} node The target node
16287 * @param {Roo.EventObject} e The raw event object
16292 * Fires when a template node is double clicked.
16293 * @param {Roo.View} this
16294 * @param {Number} index The index of the target node
16295 * @param {HTMLElement} node The target node
16296 * @param {Roo.EventObject} e The raw event object
16300 * @event contextmenu
16301 * Fires when a template node is right clicked.
16302 * @param {Roo.View} this
16303 * @param {Number} index The index of the target node
16304 * @param {HTMLElement} node The target node
16305 * @param {Roo.EventObject} e The raw event object
16307 "contextmenu" : true,
16309 * @event selectionchange
16310 * Fires when the selected nodes change.
16311 * @param {Roo.View} this
16312 * @param {Array} selections Array of the selected nodes
16314 "selectionchange" : true,
16317 * @event beforeselect
16318 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16319 * @param {Roo.View} this
16320 * @param {HTMLElement} node The node to be selected
16321 * @param {Array} selections Array of currently selected nodes
16323 "beforeselect" : true,
16325 * @event preparedata
16326 * Fires on every row to render, to allow you to change the data.
16327 * @param {Roo.View} this
16328 * @param {Object} data to be rendered (change this)
16330 "preparedata" : true
16338 "click": this.onClick,
16339 "dblclick": this.onDblClick,
16340 "contextmenu": this.onContextMenu,
16344 this.selections = [];
16346 this.cmp = new Roo.CompositeElementLite([]);
16348 this.store = Roo.factory(this.store, Roo.data);
16349 this.setStore(this.store, true);
16352 if ( this.footer && this.footer.xtype) {
16354 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16356 this.footer.dataSource = this.store;
16357 this.footer.container = fctr;
16358 this.footer = Roo.factory(this.footer, Roo);
16359 fctr.insertFirst(this.el);
16361 // this is a bit insane - as the paging toolbar seems to detach the el..
16362 // dom.parentNode.parentNode.parentNode
16363 // they get detached?
16367 Roo.View.superclass.constructor.call(this);
16372 Roo.extend(Roo.View, Roo.util.Observable, {
16375 * @cfg {Roo.data.Store} store Data store to load data from.
16380 * @cfg {String|Roo.Element} el The container element.
16385 * @cfg {String|Roo.Template} tpl The template used by this View
16389 * @cfg {String} dataName the named area of the template to use as the data area
16390 * Works with domtemplates roo-name="name"
16394 * @cfg {String} selectedClass The css class to add to selected nodes
16396 selectedClass : "x-view-selected",
16398 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16403 * @cfg {String} text to display on mask (default Loading)
16407 * @cfg {Boolean} multiSelect Allow multiple selection
16409 multiSelect : false,
16411 * @cfg {Boolean} singleSelect Allow single selection
16413 singleSelect: false,
16416 * @cfg {Boolean} toggleSelect - selecting
16418 toggleSelect : false,
16421 * @cfg {Boolean} tickable - selecting
16426 * Returns the element this view is bound to.
16427 * @return {Roo.Element}
16429 getEl : function(){
16430 return this.wrapEl;
16436 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16438 refresh : function(){
16439 //Roo.log('refresh');
16442 // if we are using something like 'domtemplate', then
16443 // the what gets used is:
16444 // t.applySubtemplate(NAME, data, wrapping data..)
16445 // the outer template then get' applied with
16446 // the store 'extra data'
16447 // and the body get's added to the
16448 // roo-name="data" node?
16449 // <span class='roo-tpl-{name}'></span> ?????
16453 this.clearSelections();
16454 this.el.update("");
16456 var records = this.store.getRange();
16457 if(records.length < 1) {
16459 // is this valid?? = should it render a template??
16461 this.el.update(this.emptyText);
16465 if (this.dataName) {
16466 this.el.update(t.apply(this.store.meta)); //????
16467 el = this.el.child('.roo-tpl-' + this.dataName);
16470 for(var i = 0, len = records.length; i < len; i++){
16471 var data = this.prepareData(records[i].data, i, records[i]);
16472 this.fireEvent("preparedata", this, data, i, records[i]);
16474 var d = Roo.apply({}, data);
16477 Roo.apply(d, {'roo-id' : Roo.id()});
16481 Roo.each(this.parent.item, function(item){
16482 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16485 Roo.apply(d, {'roo-data-checked' : 'checked'});
16489 html[html.length] = Roo.util.Format.trim(
16491 t.applySubtemplate(this.dataName, d, this.store.meta) :
16498 el.update(html.join(""));
16499 this.nodes = el.dom.childNodes;
16500 this.updateIndexes(0);
16505 * Function to override to reformat the data that is sent to
16506 * the template for each node.
16507 * DEPRICATED - use the preparedata event handler.
16508 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16509 * a JSON object for an UpdateManager bound view).
16511 prepareData : function(data, index, record)
16513 this.fireEvent("preparedata", this, data, index, record);
16517 onUpdate : function(ds, record){
16518 // Roo.log('on update');
16519 this.clearSelections();
16520 var index = this.store.indexOf(record);
16521 var n = this.nodes[index];
16522 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16523 n.parentNode.removeChild(n);
16524 this.updateIndexes(index, index);
16530 onAdd : function(ds, records, index)
16532 //Roo.log(['on Add', ds, records, index] );
16533 this.clearSelections();
16534 if(this.nodes.length == 0){
16538 var n = this.nodes[index];
16539 for(var i = 0, len = records.length; i < len; i++){
16540 var d = this.prepareData(records[i].data, i, records[i]);
16542 this.tpl.insertBefore(n, d);
16545 this.tpl.append(this.el, d);
16548 this.updateIndexes(index);
16551 onRemove : function(ds, record, index){
16552 // Roo.log('onRemove');
16553 this.clearSelections();
16554 var el = this.dataName ?
16555 this.el.child('.roo-tpl-' + this.dataName) :
16558 el.dom.removeChild(this.nodes[index]);
16559 this.updateIndexes(index);
16563 * Refresh an individual node.
16564 * @param {Number} index
16566 refreshNode : function(index){
16567 this.onUpdate(this.store, this.store.getAt(index));
16570 updateIndexes : function(startIndex, endIndex){
16571 var ns = this.nodes;
16572 startIndex = startIndex || 0;
16573 endIndex = endIndex || ns.length - 1;
16574 for(var i = startIndex; i <= endIndex; i++){
16575 ns[i].nodeIndex = i;
16580 * Changes the data store this view uses and refresh the view.
16581 * @param {Store} store
16583 setStore : function(store, initial){
16584 if(!initial && this.store){
16585 this.store.un("datachanged", this.refresh);
16586 this.store.un("add", this.onAdd);
16587 this.store.un("remove", this.onRemove);
16588 this.store.un("update", this.onUpdate);
16589 this.store.un("clear", this.refresh);
16590 this.store.un("beforeload", this.onBeforeLoad);
16591 this.store.un("load", this.onLoad);
16592 this.store.un("loadexception", this.onLoad);
16596 store.on("datachanged", this.refresh, this);
16597 store.on("add", this.onAdd, this);
16598 store.on("remove", this.onRemove, this);
16599 store.on("update", this.onUpdate, this);
16600 store.on("clear", this.refresh, this);
16601 store.on("beforeload", this.onBeforeLoad, this);
16602 store.on("load", this.onLoad, this);
16603 store.on("loadexception", this.onLoad, this);
16611 * onbeforeLoad - masks the loading area.
16614 onBeforeLoad : function(store,opts)
16616 //Roo.log('onBeforeLoad');
16618 this.el.update("");
16620 this.el.mask(this.mask ? this.mask : "Loading" );
16622 onLoad : function ()
16629 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16630 * @param {HTMLElement} node
16631 * @return {HTMLElement} The template node
16633 findItemFromChild : function(node){
16634 var el = this.dataName ?
16635 this.el.child('.roo-tpl-' + this.dataName,true) :
16638 if(!node || node.parentNode == el){
16641 var p = node.parentNode;
16642 while(p && p != el){
16643 if(p.parentNode == el){
16652 onClick : function(e){
16653 var item = this.findItemFromChild(e.getTarget());
16655 var index = this.indexOf(item);
16656 if(this.onItemClick(item, index, e) !== false){
16657 this.fireEvent("click", this, index, item, e);
16660 this.clearSelections();
16665 onContextMenu : function(e){
16666 var item = this.findItemFromChild(e.getTarget());
16668 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16673 onDblClick : function(e){
16674 var item = this.findItemFromChild(e.getTarget());
16676 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16680 onItemClick : function(item, index, e)
16682 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16685 if (this.toggleSelect) {
16686 var m = this.isSelected(item) ? 'unselect' : 'select';
16689 _t[m](item, true, false);
16692 if(this.multiSelect || this.singleSelect){
16693 if(this.multiSelect && e.shiftKey && this.lastSelection){
16694 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16696 this.select(item, this.multiSelect && e.ctrlKey);
16697 this.lastSelection = item;
16700 if(!this.tickable){
16701 e.preventDefault();
16709 * Get the number of selected nodes.
16712 getSelectionCount : function(){
16713 return this.selections.length;
16717 * Get the currently selected nodes.
16718 * @return {Array} An array of HTMLElements
16720 getSelectedNodes : function(){
16721 return this.selections;
16725 * Get the indexes of the selected nodes.
16728 getSelectedIndexes : function(){
16729 var indexes = [], s = this.selections;
16730 for(var i = 0, len = s.length; i < len; i++){
16731 indexes.push(s[i].nodeIndex);
16737 * Clear all selections
16738 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16740 clearSelections : function(suppressEvent){
16741 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16742 this.cmp.elements = this.selections;
16743 this.cmp.removeClass(this.selectedClass);
16744 this.selections = [];
16745 if(!suppressEvent){
16746 this.fireEvent("selectionchange", this, this.selections);
16752 * Returns true if the passed node is selected
16753 * @param {HTMLElement/Number} node The node or node index
16754 * @return {Boolean}
16756 isSelected : function(node){
16757 var s = this.selections;
16761 node = this.getNode(node);
16762 return s.indexOf(node) !== -1;
16767 * @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
16768 * @param {Boolean} keepExisting (optional) true to keep existing selections
16769 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16771 select : function(nodeInfo, keepExisting, suppressEvent){
16772 if(nodeInfo instanceof Array){
16774 this.clearSelections(true);
16776 for(var i = 0, len = nodeInfo.length; i < len; i++){
16777 this.select(nodeInfo[i], true, true);
16781 var node = this.getNode(nodeInfo);
16782 if(!node || this.isSelected(node)){
16783 return; // already selected.
16786 this.clearSelections(true);
16789 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16790 Roo.fly(node).addClass(this.selectedClass);
16791 this.selections.push(node);
16792 if(!suppressEvent){
16793 this.fireEvent("selectionchange", this, this.selections);
16801 * @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
16802 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16803 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16805 unselect : function(nodeInfo, keepExisting, suppressEvent)
16807 if(nodeInfo instanceof Array){
16808 Roo.each(this.selections, function(s) {
16809 this.unselect(s, nodeInfo);
16813 var node = this.getNode(nodeInfo);
16814 if(!node || !this.isSelected(node)){
16815 //Roo.log("not selected");
16816 return; // not selected.
16820 Roo.each(this.selections, function(s) {
16822 Roo.fly(node).removeClass(this.selectedClass);
16829 this.selections= ns;
16830 this.fireEvent("selectionchange", this, this.selections);
16834 * Gets a template node.
16835 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16836 * @return {HTMLElement} The node or null if it wasn't found
16838 getNode : function(nodeInfo){
16839 if(typeof nodeInfo == "string"){
16840 return document.getElementById(nodeInfo);
16841 }else if(typeof nodeInfo == "number"){
16842 return this.nodes[nodeInfo];
16848 * Gets a range template nodes.
16849 * @param {Number} startIndex
16850 * @param {Number} endIndex
16851 * @return {Array} An array of nodes
16853 getNodes : function(start, end){
16854 var ns = this.nodes;
16855 start = start || 0;
16856 end = typeof end == "undefined" ? ns.length - 1 : end;
16859 for(var i = start; i <= end; i++){
16863 for(var i = start; i >= end; i--){
16871 * Finds the index of the passed node
16872 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16873 * @return {Number} The index of the node or -1
16875 indexOf : function(node){
16876 node = this.getNode(node);
16877 if(typeof node.nodeIndex == "number"){
16878 return node.nodeIndex;
16880 var ns = this.nodes;
16881 for(var i = 0, len = ns.length; i < len; i++){
16892 * based on jquery fullcalendar
16896 Roo.bootstrap = Roo.bootstrap || {};
16898 * @class Roo.bootstrap.Calendar
16899 * @extends Roo.bootstrap.Component
16900 * Bootstrap Calendar class
16901 * @cfg {Boolean} loadMask (true|false) default false
16902 * @cfg {Object} header generate the user specific header of the calendar, default false
16905 * Create a new Container
16906 * @param {Object} config The config object
16911 Roo.bootstrap.Calendar = function(config){
16912 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16916 * Fires when a date is selected
16917 * @param {DatePicker} this
16918 * @param {Date} date The selected date
16922 * @event monthchange
16923 * Fires when the displayed month changes
16924 * @param {DatePicker} this
16925 * @param {Date} date The selected month
16927 'monthchange': true,
16929 * @event evententer
16930 * Fires when mouse over an event
16931 * @param {Calendar} this
16932 * @param {event} Event
16934 'evententer': true,
16936 * @event eventleave
16937 * Fires when the mouse leaves an
16938 * @param {Calendar} this
16941 'eventleave': true,
16943 * @event eventclick
16944 * Fires when the mouse click an
16945 * @param {Calendar} this
16954 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16957 * @cfg {Number} startDay
16958 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16966 getAutoCreate : function(){
16969 var fc_button = function(name, corner, style, content ) {
16970 return Roo.apply({},{
16972 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16974 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16977 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16988 style : 'width:100%',
16995 cls : 'fc-header-left',
16997 fc_button('prev', 'left', 'arrow', '‹' ),
16998 fc_button('next', 'right', 'arrow', '›' ),
16999 { tag: 'span', cls: 'fc-header-space' },
17000 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17008 cls : 'fc-header-center',
17012 cls: 'fc-header-title',
17015 html : 'month / year'
17023 cls : 'fc-header-right',
17025 /* fc_button('month', 'left', '', 'month' ),
17026 fc_button('week', '', '', 'week' ),
17027 fc_button('day', 'right', '', 'day' )
17039 header = this.header;
17042 var cal_heads = function() {
17044 // fixme - handle this.
17046 for (var i =0; i < Date.dayNames.length; i++) {
17047 var d = Date.dayNames[i];
17050 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17051 html : d.substring(0,3)
17055 ret[0].cls += ' fc-first';
17056 ret[6].cls += ' fc-last';
17059 var cal_cell = function(n) {
17062 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17067 cls: 'fc-day-number',
17071 cls: 'fc-day-content',
17075 style: 'position: relative;' // height: 17px;
17087 var cal_rows = function() {
17090 for (var r = 0; r < 6; r++) {
17097 for (var i =0; i < Date.dayNames.length; i++) {
17098 var d = Date.dayNames[i];
17099 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17102 row.cn[0].cls+=' fc-first';
17103 row.cn[0].cn[0].style = 'min-height:90px';
17104 row.cn[6].cls+=' fc-last';
17108 ret[0].cls += ' fc-first';
17109 ret[4].cls += ' fc-prev-last';
17110 ret[5].cls += ' fc-last';
17117 cls: 'fc-border-separate',
17118 style : 'width:100%',
17126 cls : 'fc-first fc-last',
17144 cls : 'fc-content',
17145 style : "position: relative;",
17148 cls : 'fc-view fc-view-month fc-grid',
17149 style : 'position: relative',
17150 unselectable : 'on',
17153 cls : 'fc-event-container',
17154 style : 'position:absolute;z-index:8;top:0;left:0;'
17172 initEvents : function()
17175 throw "can not find store for calendar";
17181 style: "text-align:center",
17185 style: "background-color:white;width:50%;margin:250 auto",
17189 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17200 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17202 var size = this.el.select('.fc-content', true).first().getSize();
17203 this.maskEl.setSize(size.width, size.height);
17204 this.maskEl.enableDisplayMode("block");
17205 if(!this.loadMask){
17206 this.maskEl.hide();
17209 this.store = Roo.factory(this.store, Roo.data);
17210 this.store.on('load', this.onLoad, this);
17211 this.store.on('beforeload', this.onBeforeLoad, this);
17215 this.cells = this.el.select('.fc-day',true);
17216 //Roo.log(this.cells);
17217 this.textNodes = this.el.query('.fc-day-number');
17218 this.cells.addClassOnOver('fc-state-hover');
17220 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17221 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17222 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17223 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17225 this.on('monthchange', this.onMonthChange, this);
17227 this.update(new Date().clearTime());
17230 resize : function() {
17231 var sz = this.el.getSize();
17233 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17234 this.el.select('.fc-day-content div',true).setHeight(34);
17239 showPrevMonth : function(e){
17240 this.update(this.activeDate.add("mo", -1));
17242 showToday : function(e){
17243 this.update(new Date().clearTime());
17246 showNextMonth : function(e){
17247 this.update(this.activeDate.add("mo", 1));
17251 showPrevYear : function(){
17252 this.update(this.activeDate.add("y", -1));
17256 showNextYear : function(){
17257 this.update(this.activeDate.add("y", 1));
17262 update : function(date)
17264 var vd = this.activeDate;
17265 this.activeDate = date;
17266 // if(vd && this.el){
17267 // var t = date.getTime();
17268 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17269 // Roo.log('using add remove');
17271 // this.fireEvent('monthchange', this, date);
17273 // this.cells.removeClass("fc-state-highlight");
17274 // this.cells.each(function(c){
17275 // if(c.dateValue == t){
17276 // c.addClass("fc-state-highlight");
17277 // setTimeout(function(){
17278 // try{c.dom.firstChild.focus();}catch(e){}
17288 var days = date.getDaysInMonth();
17290 var firstOfMonth = date.getFirstDateOfMonth();
17291 var startingPos = firstOfMonth.getDay()-this.startDay;
17293 if(startingPos < this.startDay){
17297 var pm = date.add(Date.MONTH, -1);
17298 var prevStart = pm.getDaysInMonth()-startingPos;
17300 this.cells = this.el.select('.fc-day',true);
17301 this.textNodes = this.el.query('.fc-day-number');
17302 this.cells.addClassOnOver('fc-state-hover');
17304 var cells = this.cells.elements;
17305 var textEls = this.textNodes;
17307 Roo.each(cells, function(cell){
17308 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17311 days += startingPos;
17313 // convert everything to numbers so it's fast
17314 var day = 86400000;
17315 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17318 //Roo.log(prevStart);
17320 var today = new Date().clearTime().getTime();
17321 var sel = date.clearTime().getTime();
17322 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17323 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17324 var ddMatch = this.disabledDatesRE;
17325 var ddText = this.disabledDatesText;
17326 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17327 var ddaysText = this.disabledDaysText;
17328 var format = this.format;
17330 var setCellClass = function(cal, cell){
17334 //Roo.log('set Cell Class');
17336 var t = d.getTime();
17340 cell.dateValue = t;
17342 cell.className += " fc-today";
17343 cell.className += " fc-state-highlight";
17344 cell.title = cal.todayText;
17347 // disable highlight in other month..
17348 //cell.className += " fc-state-highlight";
17353 cell.className = " fc-state-disabled";
17354 cell.title = cal.minText;
17358 cell.className = " fc-state-disabled";
17359 cell.title = cal.maxText;
17363 if(ddays.indexOf(d.getDay()) != -1){
17364 cell.title = ddaysText;
17365 cell.className = " fc-state-disabled";
17368 if(ddMatch && format){
17369 var fvalue = d.dateFormat(format);
17370 if(ddMatch.test(fvalue)){
17371 cell.title = ddText.replace("%0", fvalue);
17372 cell.className = " fc-state-disabled";
17376 if (!cell.initialClassName) {
17377 cell.initialClassName = cell.dom.className;
17380 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17385 for(; i < startingPos; i++) {
17386 textEls[i].innerHTML = (++prevStart);
17387 d.setDate(d.getDate()+1);
17389 cells[i].className = "fc-past fc-other-month";
17390 setCellClass(this, cells[i]);
17395 for(; i < days; i++){
17396 intDay = i - startingPos + 1;
17397 textEls[i].innerHTML = (intDay);
17398 d.setDate(d.getDate()+1);
17400 cells[i].className = ''; // "x-date-active";
17401 setCellClass(this, cells[i]);
17405 for(; i < 42; i++) {
17406 textEls[i].innerHTML = (++extraDays);
17407 d.setDate(d.getDate()+1);
17409 cells[i].className = "fc-future fc-other-month";
17410 setCellClass(this, cells[i]);
17413 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17415 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17417 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17418 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17420 if(totalRows != 6){
17421 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17422 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17425 this.fireEvent('monthchange', this, date);
17429 if(!this.internalRender){
17430 var main = this.el.dom.firstChild;
17431 var w = main.offsetWidth;
17432 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17433 Roo.fly(main).setWidth(w);
17434 this.internalRender = true;
17435 // opera does not respect the auto grow header center column
17436 // then, after it gets a width opera refuses to recalculate
17437 // without a second pass
17438 if(Roo.isOpera && !this.secondPass){
17439 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17440 this.secondPass = true;
17441 this.update.defer(10, this, [date]);
17448 findCell : function(dt) {
17449 dt = dt.clearTime().getTime();
17451 this.cells.each(function(c){
17452 //Roo.log("check " +c.dateValue + '?=' + dt);
17453 if(c.dateValue == dt){
17463 findCells : function(ev) {
17464 var s = ev.start.clone().clearTime().getTime();
17466 var e= ev.end.clone().clearTime().getTime();
17469 this.cells.each(function(c){
17470 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17472 if(c.dateValue > e){
17475 if(c.dateValue < s){
17484 // findBestRow: function(cells)
17488 // for (var i =0 ; i < cells.length;i++) {
17489 // ret = Math.max(cells[i].rows || 0,ret);
17496 addItem : function(ev)
17498 // look for vertical location slot in
17499 var cells = this.findCells(ev);
17501 // ev.row = this.findBestRow(cells);
17503 // work out the location.
17507 for(var i =0; i < cells.length; i++) {
17509 cells[i].row = cells[0].row;
17512 cells[i].row = cells[i].row + 1;
17522 if (crow.start.getY() == cells[i].getY()) {
17524 crow.end = cells[i];
17541 cells[0].events.push(ev);
17543 this.calevents.push(ev);
17546 clearEvents: function() {
17548 if(!this.calevents){
17552 Roo.each(this.cells.elements, function(c){
17558 Roo.each(this.calevents, function(e) {
17559 Roo.each(e.els, function(el) {
17560 el.un('mouseenter' ,this.onEventEnter, this);
17561 el.un('mouseleave' ,this.onEventLeave, this);
17566 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17572 renderEvents: function()
17576 this.cells.each(function(c) {
17585 if(c.row != c.events.length){
17586 r = 4 - (4 - (c.row - c.events.length));
17589 c.events = ev.slice(0, r);
17590 c.more = ev.slice(r);
17592 if(c.more.length && c.more.length == 1){
17593 c.events.push(c.more.pop());
17596 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17600 this.cells.each(function(c) {
17602 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17605 for (var e = 0; e < c.events.length; e++){
17606 var ev = c.events[e];
17607 var rows = ev.rows;
17609 for(var i = 0; i < rows.length; i++) {
17611 // how many rows should it span..
17614 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17615 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17617 unselectable : "on",
17620 cls: 'fc-event-inner',
17624 // cls: 'fc-event-time',
17625 // html : cells.length > 1 ? '' : ev.time
17629 cls: 'fc-event-title',
17630 html : String.format('{0}', ev.title)
17637 cls: 'ui-resizable-handle ui-resizable-e',
17638 html : '  '
17645 cfg.cls += ' fc-event-start';
17647 if ((i+1) == rows.length) {
17648 cfg.cls += ' fc-event-end';
17651 var ctr = _this.el.select('.fc-event-container',true).first();
17652 var cg = ctr.createChild(cfg);
17654 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17655 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17657 var r = (c.more.length) ? 1 : 0;
17658 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17659 cg.setWidth(ebox.right - sbox.x -2);
17661 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17662 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17663 cg.on('click', _this.onEventClick, _this, ev);
17674 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17675 style : 'position: absolute',
17676 unselectable : "on",
17679 cls: 'fc-event-inner',
17683 cls: 'fc-event-title',
17691 cls: 'ui-resizable-handle ui-resizable-e',
17692 html : '  '
17698 var ctr = _this.el.select('.fc-event-container',true).first();
17699 var cg = ctr.createChild(cfg);
17701 var sbox = c.select('.fc-day-content',true).first().getBox();
17702 var ebox = c.select('.fc-day-content',true).first().getBox();
17704 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17705 cg.setWidth(ebox.right - sbox.x -2);
17707 cg.on('click', _this.onMoreEventClick, _this, c.more);
17717 onEventEnter: function (e, el,event,d) {
17718 this.fireEvent('evententer', this, el, event);
17721 onEventLeave: function (e, el,event,d) {
17722 this.fireEvent('eventleave', this, el, event);
17725 onEventClick: function (e, el,event,d) {
17726 this.fireEvent('eventclick', this, el, event);
17729 onMonthChange: function () {
17733 onMoreEventClick: function(e, el, more)
17737 this.calpopover.placement = 'right';
17738 this.calpopover.setTitle('More');
17740 this.calpopover.setContent('');
17742 var ctr = this.calpopover.el.select('.popover-content', true).first();
17744 Roo.each(more, function(m){
17746 cls : 'fc-event-hori fc-event-draggable',
17749 var cg = ctr.createChild(cfg);
17751 cg.on('click', _this.onEventClick, _this, m);
17754 this.calpopover.show(el);
17759 onLoad: function ()
17761 this.calevents = [];
17764 if(this.store.getCount() > 0){
17765 this.store.data.each(function(d){
17768 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17769 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17770 time : d.data.start_time,
17771 title : d.data.title,
17772 description : d.data.description,
17773 venue : d.data.venue
17778 this.renderEvents();
17780 if(this.calevents.length && this.loadMask){
17781 this.maskEl.hide();
17785 onBeforeLoad: function()
17787 this.clearEvents();
17789 this.maskEl.show();
17803 * @class Roo.bootstrap.Popover
17804 * @extends Roo.bootstrap.Component
17805 * Bootstrap Popover class
17806 * @cfg {String} html contents of the popover (or false to use children..)
17807 * @cfg {String} title of popover (or false to hide)
17808 * @cfg {String} placement how it is placed
17809 * @cfg {String} trigger click || hover (or false to trigger manually)
17810 * @cfg {String} over what (parent or false to trigger manually.)
17811 * @cfg {Number} delay - delay before showing
17814 * Create a new Popover
17815 * @param {Object} config The config object
17818 Roo.bootstrap.Popover = function(config){
17819 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17825 * After the popover show
17827 * @param {Roo.bootstrap.Popover} this
17832 * After the popover hide
17834 * @param {Roo.bootstrap.Popover} this
17840 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17842 title: 'Fill in a title',
17845 placement : 'right',
17846 trigger : 'hover', // hover
17852 can_build_overlaid : false,
17854 getChildContainer : function()
17856 return this.el.select('.popover-content',true).first();
17859 getAutoCreate : function(){
17862 cls : 'popover roo-dynamic',
17863 style: 'display:block',
17869 cls : 'popover-inner',
17873 cls: 'popover-title popover-header',
17877 cls : 'popover-content popover-body',
17888 setTitle: function(str)
17891 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17893 setContent: function(str)
17896 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17898 // as it get's added to the bottom of the page.
17899 onRender : function(ct, position)
17901 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17903 var cfg = Roo.apply({}, this.getAutoCreate());
17907 cfg.cls += ' ' + this.cls;
17910 cfg.style = this.style;
17912 //Roo.log("adding to ");
17913 this.el = Roo.get(document.body).createChild(cfg, position);
17914 // Roo.log(this.el);
17919 initEvents : function()
17921 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17922 this.el.enableDisplayMode('block');
17924 if (this.over === false) {
17927 if (this.triggers === false) {
17930 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17931 var triggers = this.trigger ? this.trigger.split(' ') : [];
17932 Roo.each(triggers, function(trigger) {
17934 if (trigger == 'click') {
17935 on_el.on('click', this.toggle, this);
17936 } else if (trigger != 'manual') {
17937 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17938 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17940 on_el.on(eventIn ,this.enter, this);
17941 on_el.on(eventOut, this.leave, this);
17952 toggle : function () {
17953 this.hoverState == 'in' ? this.leave() : this.enter();
17956 enter : function () {
17958 clearTimeout(this.timeout);
17960 this.hoverState = 'in';
17962 if (!this.delay || !this.delay.show) {
17967 this.timeout = setTimeout(function () {
17968 if (_t.hoverState == 'in') {
17971 }, this.delay.show)
17974 leave : function() {
17975 clearTimeout(this.timeout);
17977 this.hoverState = 'out';
17979 if (!this.delay || !this.delay.hide) {
17984 this.timeout = setTimeout(function () {
17985 if (_t.hoverState == 'out') {
17988 }, this.delay.hide)
17991 show : function (on_el)
17994 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17998 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17999 if (this.html !== false) {
18000 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18002 this.el.removeClass([
18003 'fade','top','bottom', 'left', 'right','in',
18004 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18006 if (!this.title.length) {
18007 this.el.select('.popover-title',true).hide();
18010 var placement = typeof this.placement == 'function' ?
18011 this.placement.call(this, this.el, on_el) :
18014 var autoToken = /\s?auto?\s?/i;
18015 var autoPlace = autoToken.test(placement);
18017 placement = placement.replace(autoToken, '') || 'top';
18021 //this.el.setXY([0,0]);
18023 this.el.dom.style.display='block';
18024 this.el.addClass(placement);
18026 //this.el.appendTo(on_el);
18028 var p = this.getPosition();
18029 var box = this.el.getBox();
18034 var align = Roo.bootstrap.Popover.alignment[placement];
18037 this.el.alignTo(on_el, align[0],align[1]);
18038 //var arrow = this.el.select('.arrow',true).first();
18039 //arrow.set(align[2],
18041 this.el.addClass('in');
18044 if (this.el.hasClass('fade')) {
18048 this.hoverState = 'in';
18050 this.fireEvent('show', this);
18055 this.el.setXY([0,0]);
18056 this.el.removeClass('in');
18058 this.hoverState = null;
18060 this.fireEvent('hide', this);
18065 Roo.bootstrap.Popover.alignment = {
18066 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18067 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18068 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18069 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18080 * @class Roo.bootstrap.Progress
18081 * @extends Roo.bootstrap.Component
18082 * Bootstrap Progress class
18083 * @cfg {Boolean} striped striped of the progress bar
18084 * @cfg {Boolean} active animated of the progress bar
18088 * Create a new Progress
18089 * @param {Object} config The config object
18092 Roo.bootstrap.Progress = function(config){
18093 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18096 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18101 getAutoCreate : function(){
18109 cfg.cls += ' progress-striped';
18113 cfg.cls += ' active';
18132 * @class Roo.bootstrap.ProgressBar
18133 * @extends Roo.bootstrap.Component
18134 * Bootstrap ProgressBar class
18135 * @cfg {Number} aria_valuenow aria-value now
18136 * @cfg {Number} aria_valuemin aria-value min
18137 * @cfg {Number} aria_valuemax aria-value max
18138 * @cfg {String} label label for the progress bar
18139 * @cfg {String} panel (success | info | warning | danger )
18140 * @cfg {String} role role of the progress bar
18141 * @cfg {String} sr_only text
18145 * Create a new ProgressBar
18146 * @param {Object} config The config object
18149 Roo.bootstrap.ProgressBar = function(config){
18150 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18153 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18157 aria_valuemax : 100,
18163 getAutoCreate : function()
18168 cls: 'progress-bar',
18169 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18181 cfg.role = this.role;
18184 if(this.aria_valuenow){
18185 cfg['aria-valuenow'] = this.aria_valuenow;
18188 if(this.aria_valuemin){
18189 cfg['aria-valuemin'] = this.aria_valuemin;
18192 if(this.aria_valuemax){
18193 cfg['aria-valuemax'] = this.aria_valuemax;
18196 if(this.label && !this.sr_only){
18197 cfg.html = this.label;
18201 cfg.cls += ' progress-bar-' + this.panel;
18207 update : function(aria_valuenow)
18209 this.aria_valuenow = aria_valuenow;
18211 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18226 * @class Roo.bootstrap.TabGroup
18227 * @extends Roo.bootstrap.Column
18228 * Bootstrap Column class
18229 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18230 * @cfg {Boolean} carousel true to make the group behave like a carousel
18231 * @cfg {Boolean} bullets show bullets for the panels
18232 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18233 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18234 * @cfg {Boolean} showarrow (true|false) show arrow default true
18237 * Create a new TabGroup
18238 * @param {Object} config The config object
18241 Roo.bootstrap.TabGroup = function(config){
18242 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18244 this.navId = Roo.id();
18247 Roo.bootstrap.TabGroup.register(this);
18251 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18254 transition : false,
18259 slideOnTouch : false,
18262 getAutoCreate : function()
18264 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18266 cfg.cls += ' tab-content';
18268 if (this.carousel) {
18269 cfg.cls += ' carousel slide';
18272 cls : 'carousel-inner',
18276 if(this.bullets && !Roo.isTouch){
18279 cls : 'carousel-bullets',
18283 if(this.bullets_cls){
18284 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18291 cfg.cn[0].cn.push(bullets);
18294 if(this.showarrow){
18295 cfg.cn[0].cn.push({
18297 class : 'carousel-arrow',
18301 class : 'carousel-prev',
18305 class : 'fa fa-chevron-left'
18311 class : 'carousel-next',
18315 class : 'fa fa-chevron-right'
18328 initEvents: function()
18330 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18331 // this.el.on("touchstart", this.onTouchStart, this);
18334 if(this.autoslide){
18337 this.slideFn = window.setInterval(function() {
18338 _this.showPanelNext();
18342 if(this.showarrow){
18343 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18344 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18350 // onTouchStart : function(e, el, o)
18352 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18356 // this.showPanelNext();
18360 getChildContainer : function()
18362 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18366 * register a Navigation item
18367 * @param {Roo.bootstrap.NavItem} the navitem to add
18369 register : function(item)
18371 this.tabs.push( item);
18372 item.navId = this.navId; // not really needed..
18377 getActivePanel : function()
18380 Roo.each(this.tabs, function(t) {
18390 getPanelByName : function(n)
18393 Roo.each(this.tabs, function(t) {
18394 if (t.tabId == n) {
18402 indexOfPanel : function(p)
18405 Roo.each(this.tabs, function(t,i) {
18406 if (t.tabId == p.tabId) {
18415 * show a specific panel
18416 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18417 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18419 showPanel : function (pan)
18421 if(this.transition || typeof(pan) == 'undefined'){
18422 Roo.log("waiting for the transitionend");
18426 if (typeof(pan) == 'number') {
18427 pan = this.tabs[pan];
18430 if (typeof(pan) == 'string') {
18431 pan = this.getPanelByName(pan);
18434 var cur = this.getActivePanel();
18437 Roo.log('pan or acitve pan is undefined');
18441 if (pan.tabId == this.getActivePanel().tabId) {
18445 if (false === cur.fireEvent('beforedeactivate')) {
18449 if(this.bullets > 0 && !Roo.isTouch){
18450 this.setActiveBullet(this.indexOfPanel(pan));
18453 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18455 //class="carousel-item carousel-item-next carousel-item-left"
18457 this.transition = true;
18458 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18459 var lr = dir == 'next' ? 'left' : 'right';
18460 pan.el.addClass(dir); // or prev
18461 pan.el.addClass('carousel-item-' + dir); // or prev
18462 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18463 cur.el.addClass(lr); // or right
18464 pan.el.addClass(lr);
18465 cur.el.addClass('carousel-item-' +lr); // or right
18466 pan.el.addClass('carousel-item-' +lr);
18470 cur.el.on('transitionend', function() {
18471 Roo.log("trans end?");
18473 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18474 pan.setActive(true);
18476 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18477 cur.setActive(false);
18479 _this.transition = false;
18481 }, this, { single: true } );
18486 cur.setActive(false);
18487 pan.setActive(true);
18492 showPanelNext : function()
18494 var i = this.indexOfPanel(this.getActivePanel());
18496 if (i >= this.tabs.length - 1 && !this.autoslide) {
18500 if (i >= this.tabs.length - 1 && this.autoslide) {
18504 this.showPanel(this.tabs[i+1]);
18507 showPanelPrev : function()
18509 var i = this.indexOfPanel(this.getActivePanel());
18511 if (i < 1 && !this.autoslide) {
18515 if (i < 1 && this.autoslide) {
18516 i = this.tabs.length;
18519 this.showPanel(this.tabs[i-1]);
18523 addBullet: function()
18525 if(!this.bullets || Roo.isTouch){
18528 var ctr = this.el.select('.carousel-bullets',true).first();
18529 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18530 var bullet = ctr.createChild({
18531 cls : 'bullet bullet-' + i
18532 },ctr.dom.lastChild);
18537 bullet.on('click', (function(e, el, o, ii, t){
18539 e.preventDefault();
18541 this.showPanel(ii);
18543 if(this.autoslide && this.slideFn){
18544 clearInterval(this.slideFn);
18545 this.slideFn = window.setInterval(function() {
18546 _this.showPanelNext();
18550 }).createDelegate(this, [i, bullet], true));
18555 setActiveBullet : function(i)
18561 Roo.each(this.el.select('.bullet', true).elements, function(el){
18562 el.removeClass('selected');
18565 var bullet = this.el.select('.bullet-' + i, true).first();
18571 bullet.addClass('selected');
18582 Roo.apply(Roo.bootstrap.TabGroup, {
18586 * register a Navigation Group
18587 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18589 register : function(navgrp)
18591 this.groups[navgrp.navId] = navgrp;
18595 * fetch a Navigation Group based on the navigation ID
18596 * if one does not exist , it will get created.
18597 * @param {string} the navgroup to add
18598 * @returns {Roo.bootstrap.NavGroup} the navgroup
18600 get: function(navId) {
18601 if (typeof(this.groups[navId]) == 'undefined') {
18602 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18604 return this.groups[navId] ;
18619 * @class Roo.bootstrap.TabPanel
18620 * @extends Roo.bootstrap.Component
18621 * Bootstrap TabPanel class
18622 * @cfg {Boolean} active panel active
18623 * @cfg {String} html panel content
18624 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18625 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18626 * @cfg {String} href click to link..
18630 * Create a new TabPanel
18631 * @param {Object} config The config object
18634 Roo.bootstrap.TabPanel = function(config){
18635 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18639 * Fires when the active status changes
18640 * @param {Roo.bootstrap.TabPanel} this
18641 * @param {Boolean} state the new state
18646 * @event beforedeactivate
18647 * Fires before a tab is de-activated - can be used to do validation on a form.
18648 * @param {Roo.bootstrap.TabPanel} this
18649 * @return {Boolean} false if there is an error
18652 'beforedeactivate': true
18655 this.tabId = this.tabId || Roo.id();
18659 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18667 getAutoCreate : function(){
18672 // item is needed for carousel - not sure if it has any effect otherwise
18673 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18674 html: this.html || ''
18678 cfg.cls += ' active';
18682 cfg.tabId = this.tabId;
18690 initEvents: function()
18692 var p = this.parent();
18694 this.navId = this.navId || p.navId;
18696 if (typeof(this.navId) != 'undefined') {
18697 // not really needed.. but just in case.. parent should be a NavGroup.
18698 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18702 var i = tg.tabs.length - 1;
18704 if(this.active && tg.bullets > 0 && i < tg.bullets){
18705 tg.setActiveBullet(i);
18709 this.el.on('click', this.onClick, this);
18712 this.el.on("touchstart", this.onTouchStart, this);
18713 this.el.on("touchmove", this.onTouchMove, this);
18714 this.el.on("touchend", this.onTouchEnd, this);
18719 onRender : function(ct, position)
18721 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18724 setActive : function(state)
18726 Roo.log("panel - set active " + this.tabId + "=" + state);
18728 this.active = state;
18730 this.el.removeClass('active');
18732 } else if (!this.el.hasClass('active')) {
18733 this.el.addClass('active');
18736 this.fireEvent('changed', this, state);
18739 onClick : function(e)
18741 e.preventDefault();
18743 if(!this.href.length){
18747 window.location.href = this.href;
18756 onTouchStart : function(e)
18758 this.swiping = false;
18760 this.startX = e.browserEvent.touches[0].clientX;
18761 this.startY = e.browserEvent.touches[0].clientY;
18764 onTouchMove : function(e)
18766 this.swiping = true;
18768 this.endX = e.browserEvent.touches[0].clientX;
18769 this.endY = e.browserEvent.touches[0].clientY;
18772 onTouchEnd : function(e)
18779 var tabGroup = this.parent();
18781 if(this.endX > this.startX){ // swiping right
18782 tabGroup.showPanelPrev();
18786 if(this.startX > this.endX){ // swiping left
18787 tabGroup.showPanelNext();
18806 * @class Roo.bootstrap.DateField
18807 * @extends Roo.bootstrap.Input
18808 * Bootstrap DateField class
18809 * @cfg {Number} weekStart default 0
18810 * @cfg {String} viewMode default empty, (months|years)
18811 * @cfg {String} minViewMode default empty, (months|years)
18812 * @cfg {Number} startDate default -Infinity
18813 * @cfg {Number} endDate default Infinity
18814 * @cfg {Boolean} todayHighlight default false
18815 * @cfg {Boolean} todayBtn default false
18816 * @cfg {Boolean} calendarWeeks default false
18817 * @cfg {Object} daysOfWeekDisabled default empty
18818 * @cfg {Boolean} singleMode default false (true | false)
18820 * @cfg {Boolean} keyboardNavigation default true
18821 * @cfg {String} language default en
18824 * Create a new DateField
18825 * @param {Object} config The config object
18828 Roo.bootstrap.DateField = function(config){
18829 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18833 * Fires when this field show.
18834 * @param {Roo.bootstrap.DateField} this
18835 * @param {Mixed} date The date value
18840 * Fires when this field hide.
18841 * @param {Roo.bootstrap.DateField} this
18842 * @param {Mixed} date The date value
18847 * Fires when select a date.
18848 * @param {Roo.bootstrap.DateField} this
18849 * @param {Mixed} date The date value
18853 * @event beforeselect
18854 * Fires when before select a date.
18855 * @param {Roo.bootstrap.DateField} this
18856 * @param {Mixed} date The date value
18858 beforeselect : true
18862 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18865 * @cfg {String} format
18866 * The default date format string which can be overriden for localization support. The format must be
18867 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18871 * @cfg {String} altFormats
18872 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18873 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18875 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18883 todayHighlight : false,
18889 keyboardNavigation: true,
18891 calendarWeeks: false,
18893 startDate: -Infinity,
18897 daysOfWeekDisabled: [],
18901 singleMode : false,
18903 UTCDate: function()
18905 return new Date(Date.UTC.apply(Date, arguments));
18908 UTCToday: function()
18910 var today = new Date();
18911 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18914 getDate: function() {
18915 var d = this.getUTCDate();
18916 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18919 getUTCDate: function() {
18923 setDate: function(d) {
18924 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18927 setUTCDate: function(d) {
18929 this.setValue(this.formatDate(this.date));
18932 onRender: function(ct, position)
18935 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18937 this.language = this.language || 'en';
18938 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18939 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18941 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18942 this.format = this.format || 'm/d/y';
18943 this.isInline = false;
18944 this.isInput = true;
18945 this.component = this.el.select('.add-on', true).first() || false;
18946 this.component = (this.component && this.component.length === 0) ? false : this.component;
18947 this.hasInput = this.component && this.inputEl().length;
18949 if (typeof(this.minViewMode === 'string')) {
18950 switch (this.minViewMode) {
18952 this.minViewMode = 1;
18955 this.minViewMode = 2;
18958 this.minViewMode = 0;
18963 if (typeof(this.viewMode === 'string')) {
18964 switch (this.viewMode) {
18977 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18979 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18981 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18983 this.picker().on('mousedown', this.onMousedown, this);
18984 this.picker().on('click', this.onClick, this);
18986 this.picker().addClass('datepicker-dropdown');
18988 this.startViewMode = this.viewMode;
18990 if(this.singleMode){
18991 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18992 v.setVisibilityMode(Roo.Element.DISPLAY);
18996 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18997 v.setStyle('width', '189px');
19001 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19002 if(!this.calendarWeeks){
19007 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19008 v.attr('colspan', function(i, val){
19009 return parseInt(val) + 1;
19014 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19016 this.setStartDate(this.startDate);
19017 this.setEndDate(this.endDate);
19019 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19026 if(this.isInline) {
19031 picker : function()
19033 return this.pickerEl;
19034 // return this.el.select('.datepicker', true).first();
19037 fillDow: function()
19039 var dowCnt = this.weekStart;
19048 if(this.calendarWeeks){
19056 while (dowCnt < this.weekStart + 7) {
19060 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19064 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19067 fillMonths: function()
19070 var months = this.picker().select('>.datepicker-months td', true).first();
19072 months.dom.innerHTML = '';
19078 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19081 months.createChild(month);
19088 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;
19090 if (this.date < this.startDate) {
19091 this.viewDate = new Date(this.startDate);
19092 } else if (this.date > this.endDate) {
19093 this.viewDate = new Date(this.endDate);
19095 this.viewDate = new Date(this.date);
19103 var d = new Date(this.viewDate),
19104 year = d.getUTCFullYear(),
19105 month = d.getUTCMonth(),
19106 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19107 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19108 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19109 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19110 currentDate = this.date && this.date.valueOf(),
19111 today = this.UTCToday();
19113 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19115 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19117 // this.picker.select('>tfoot th.today').
19118 // .text(dates[this.language].today)
19119 // .toggle(this.todayBtn !== false);
19121 this.updateNavArrows();
19124 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19126 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19128 prevMonth.setUTCDate(day);
19130 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19132 var nextMonth = new Date(prevMonth);
19134 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19136 nextMonth = nextMonth.valueOf();
19138 var fillMonths = false;
19140 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19142 while(prevMonth.valueOf() <= nextMonth) {
19145 if (prevMonth.getUTCDay() === this.weekStart) {
19147 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19155 if(this.calendarWeeks){
19156 // ISO 8601: First week contains first thursday.
19157 // ISO also states week starts on Monday, but we can be more abstract here.
19159 // Start of current week: based on weekstart/current date
19160 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19161 // Thursday of this week
19162 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19163 // First Thursday of year, year from thursday
19164 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19165 // Calendar week: ms between thursdays, div ms per day, div 7 days
19166 calWeek = (th - yth) / 864e5 / 7 + 1;
19168 fillMonths.cn.push({
19176 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19178 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19181 if (this.todayHighlight &&
19182 prevMonth.getUTCFullYear() == today.getFullYear() &&
19183 prevMonth.getUTCMonth() == today.getMonth() &&
19184 prevMonth.getUTCDate() == today.getDate()) {
19185 clsName += ' today';
19188 if (currentDate && prevMonth.valueOf() === currentDate) {
19189 clsName += ' active';
19192 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19193 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19194 clsName += ' disabled';
19197 fillMonths.cn.push({
19199 cls: 'day ' + clsName,
19200 html: prevMonth.getDate()
19203 prevMonth.setDate(prevMonth.getDate()+1);
19206 var currentYear = this.date && this.date.getUTCFullYear();
19207 var currentMonth = this.date && this.date.getUTCMonth();
19209 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19211 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19212 v.removeClass('active');
19214 if(currentYear === year && k === currentMonth){
19215 v.addClass('active');
19218 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19219 v.addClass('disabled');
19225 year = parseInt(year/10, 10) * 10;
19227 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19229 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19232 for (var i = -1; i < 11; i++) {
19233 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19235 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19243 showMode: function(dir)
19246 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19249 Roo.each(this.picker().select('>div',true).elements, function(v){
19250 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19253 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19258 if(this.isInline) {
19262 this.picker().removeClass(['bottom', 'top']);
19264 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19266 * place to the top of element!
19270 this.picker().addClass('top');
19271 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19276 this.picker().addClass('bottom');
19278 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19281 parseDate : function(value)
19283 if(!value || value instanceof Date){
19286 var v = Date.parseDate(value, this.format);
19287 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19288 v = Date.parseDate(value, 'Y-m-d');
19290 if(!v && this.altFormats){
19291 if(!this.altFormatsArray){
19292 this.altFormatsArray = this.altFormats.split("|");
19294 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19295 v = Date.parseDate(value, this.altFormatsArray[i]);
19301 formatDate : function(date, fmt)
19303 return (!date || !(date instanceof Date)) ?
19304 date : date.dateFormat(fmt || this.format);
19307 onFocus : function()
19309 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19313 onBlur : function()
19315 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19317 var d = this.inputEl().getValue();
19324 showPopup : function()
19326 this.picker().show();
19330 this.fireEvent('showpopup', this, this.date);
19333 hidePopup : function()
19335 if(this.isInline) {
19338 this.picker().hide();
19339 this.viewMode = this.startViewMode;
19342 this.fireEvent('hidepopup', this, this.date);
19346 onMousedown: function(e)
19348 e.stopPropagation();
19349 e.preventDefault();
19354 Roo.bootstrap.DateField.superclass.keyup.call(this);
19358 setValue: function(v)
19360 if(this.fireEvent('beforeselect', this, v) !== false){
19361 var d = new Date(this.parseDate(v) ).clearTime();
19363 if(isNaN(d.getTime())){
19364 this.date = this.viewDate = '';
19365 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19369 v = this.formatDate(d);
19371 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19373 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19377 this.fireEvent('select', this, this.date);
19381 getValue: function()
19383 return this.formatDate(this.date);
19386 fireKey: function(e)
19388 if (!this.picker().isVisible()){
19389 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19395 var dateChanged = false,
19397 newDate, newViewDate;
19402 e.preventDefault();
19406 if (!this.keyboardNavigation) {
19409 dir = e.keyCode == 37 ? -1 : 1;
19412 newDate = this.moveYear(this.date, dir);
19413 newViewDate = this.moveYear(this.viewDate, dir);
19414 } else if (e.shiftKey){
19415 newDate = this.moveMonth(this.date, dir);
19416 newViewDate = this.moveMonth(this.viewDate, dir);
19418 newDate = new Date(this.date);
19419 newDate.setUTCDate(this.date.getUTCDate() + dir);
19420 newViewDate = new Date(this.viewDate);
19421 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19423 if (this.dateWithinRange(newDate)){
19424 this.date = newDate;
19425 this.viewDate = newViewDate;
19426 this.setValue(this.formatDate(this.date));
19428 e.preventDefault();
19429 dateChanged = true;
19434 if (!this.keyboardNavigation) {
19437 dir = e.keyCode == 38 ? -1 : 1;
19439 newDate = this.moveYear(this.date, dir);
19440 newViewDate = this.moveYear(this.viewDate, dir);
19441 } else if (e.shiftKey){
19442 newDate = this.moveMonth(this.date, dir);
19443 newViewDate = this.moveMonth(this.viewDate, dir);
19445 newDate = new Date(this.date);
19446 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19447 newViewDate = new Date(this.viewDate);
19448 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19450 if (this.dateWithinRange(newDate)){
19451 this.date = newDate;
19452 this.viewDate = newViewDate;
19453 this.setValue(this.formatDate(this.date));
19455 e.preventDefault();
19456 dateChanged = true;
19460 this.setValue(this.formatDate(this.date));
19462 e.preventDefault();
19465 this.setValue(this.formatDate(this.date));
19479 onClick: function(e)
19481 e.stopPropagation();
19482 e.preventDefault();
19484 var target = e.getTarget();
19486 if(target.nodeName.toLowerCase() === 'i'){
19487 target = Roo.get(target).dom.parentNode;
19490 var nodeName = target.nodeName;
19491 var className = target.className;
19492 var html = target.innerHTML;
19493 //Roo.log(nodeName);
19495 switch(nodeName.toLowerCase()) {
19497 switch(className) {
19503 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19504 switch(this.viewMode){
19506 this.viewDate = this.moveMonth(this.viewDate, dir);
19510 this.viewDate = this.moveYear(this.viewDate, dir);
19516 var date = new Date();
19517 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19519 this.setValue(this.formatDate(this.date));
19526 if (className.indexOf('disabled') < 0) {
19527 this.viewDate.setUTCDate(1);
19528 if (className.indexOf('month') > -1) {
19529 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19531 var year = parseInt(html, 10) || 0;
19532 this.viewDate.setUTCFullYear(year);
19536 if(this.singleMode){
19537 this.setValue(this.formatDate(this.viewDate));
19548 //Roo.log(className);
19549 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19550 var day = parseInt(html, 10) || 1;
19551 var year = this.viewDate.getUTCFullYear(),
19552 month = this.viewDate.getUTCMonth();
19554 if (className.indexOf('old') > -1) {
19561 } else if (className.indexOf('new') > -1) {
19569 //Roo.log([year,month,day]);
19570 this.date = this.UTCDate(year, month, day,0,0,0,0);
19571 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19573 //Roo.log(this.formatDate(this.date));
19574 this.setValue(this.formatDate(this.date));
19581 setStartDate: function(startDate)
19583 this.startDate = startDate || -Infinity;
19584 if (this.startDate !== -Infinity) {
19585 this.startDate = this.parseDate(this.startDate);
19588 this.updateNavArrows();
19591 setEndDate: function(endDate)
19593 this.endDate = endDate || Infinity;
19594 if (this.endDate !== Infinity) {
19595 this.endDate = this.parseDate(this.endDate);
19598 this.updateNavArrows();
19601 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19603 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19604 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19605 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19607 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19608 return parseInt(d, 10);
19611 this.updateNavArrows();
19614 updateNavArrows: function()
19616 if(this.singleMode){
19620 var d = new Date(this.viewDate),
19621 year = d.getUTCFullYear(),
19622 month = d.getUTCMonth();
19624 Roo.each(this.picker().select('.prev', true).elements, function(v){
19626 switch (this.viewMode) {
19629 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19635 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19642 Roo.each(this.picker().select('.next', true).elements, function(v){
19644 switch (this.viewMode) {
19647 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19653 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19661 moveMonth: function(date, dir)
19666 var new_date = new Date(date.valueOf()),
19667 day = new_date.getUTCDate(),
19668 month = new_date.getUTCMonth(),
19669 mag = Math.abs(dir),
19671 dir = dir > 0 ? 1 : -1;
19674 // If going back one month, make sure month is not current month
19675 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19677 return new_date.getUTCMonth() == month;
19679 // If going forward one month, make sure month is as expected
19680 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19682 return new_date.getUTCMonth() != new_month;
19684 new_month = month + dir;
19685 new_date.setUTCMonth(new_month);
19686 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19687 if (new_month < 0 || new_month > 11) {
19688 new_month = (new_month + 12) % 12;
19691 // For magnitudes >1, move one month at a time...
19692 for (var i=0; i<mag; i++) {
19693 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19694 new_date = this.moveMonth(new_date, dir);
19696 // ...then reset the day, keeping it in the new month
19697 new_month = new_date.getUTCMonth();
19698 new_date.setUTCDate(day);
19700 return new_month != new_date.getUTCMonth();
19703 // Common date-resetting loop -- if date is beyond end of month, make it
19706 new_date.setUTCDate(--day);
19707 new_date.setUTCMonth(new_month);
19712 moveYear: function(date, dir)
19714 return this.moveMonth(date, dir*12);
19717 dateWithinRange: function(date)
19719 return date >= this.startDate && date <= this.endDate;
19725 this.picker().remove();
19728 validateValue : function(value)
19730 if(this.getVisibilityEl().hasClass('hidden')){
19734 if(value.length < 1) {
19735 if(this.allowBlank){
19741 if(value.length < this.minLength){
19744 if(value.length > this.maxLength){
19748 var vt = Roo.form.VTypes;
19749 if(!vt[this.vtype](value, this)){
19753 if(typeof this.validator == "function"){
19754 var msg = this.validator(value);
19760 if(this.regex && !this.regex.test(value)){
19764 if(typeof(this.parseDate(value)) == 'undefined'){
19768 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19772 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19782 this.date = this.viewDate = '';
19784 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19789 Roo.apply(Roo.bootstrap.DateField, {
19800 html: '<i class="fa fa-arrow-left"/>'
19810 html: '<i class="fa fa-arrow-right"/>'
19852 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19853 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19854 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19855 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19856 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19869 navFnc: 'FullYear',
19874 navFnc: 'FullYear',
19879 Roo.apply(Roo.bootstrap.DateField, {
19883 cls: 'datepicker dropdown-menu roo-dynamic',
19887 cls: 'datepicker-days',
19891 cls: 'table-condensed',
19893 Roo.bootstrap.DateField.head,
19897 Roo.bootstrap.DateField.footer
19904 cls: 'datepicker-months',
19908 cls: 'table-condensed',
19910 Roo.bootstrap.DateField.head,
19911 Roo.bootstrap.DateField.content,
19912 Roo.bootstrap.DateField.footer
19919 cls: 'datepicker-years',
19923 cls: 'table-condensed',
19925 Roo.bootstrap.DateField.head,
19926 Roo.bootstrap.DateField.content,
19927 Roo.bootstrap.DateField.footer
19946 * @class Roo.bootstrap.TimeField
19947 * @extends Roo.bootstrap.Input
19948 * Bootstrap DateField class
19952 * Create a new TimeField
19953 * @param {Object} config The config object
19956 Roo.bootstrap.TimeField = function(config){
19957 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19961 * Fires when this field show.
19962 * @param {Roo.bootstrap.DateField} thisthis
19963 * @param {Mixed} date The date value
19968 * Fires when this field hide.
19969 * @param {Roo.bootstrap.DateField} this
19970 * @param {Mixed} date The date value
19975 * Fires when select a date.
19976 * @param {Roo.bootstrap.DateField} this
19977 * @param {Mixed} date The date value
19983 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19986 * @cfg {String} format
19987 * The default time format string which can be overriden for localization support. The format must be
19988 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19992 onRender: function(ct, position)
19995 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19997 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19999 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20001 this.pop = this.picker().select('>.datepicker-time',true).first();
20002 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20004 this.picker().on('mousedown', this.onMousedown, this);
20005 this.picker().on('click', this.onClick, this);
20007 this.picker().addClass('datepicker-dropdown');
20012 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20013 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20014 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20015 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20016 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20017 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20021 fireKey: function(e){
20022 if (!this.picker().isVisible()){
20023 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20029 e.preventDefault();
20037 this.onTogglePeriod();
20040 this.onIncrementMinutes();
20043 this.onDecrementMinutes();
20052 onClick: function(e) {
20053 e.stopPropagation();
20054 e.preventDefault();
20057 picker : function()
20059 return this.el.select('.datepicker', true).first();
20062 fillTime: function()
20064 var time = this.pop.select('tbody', true).first();
20066 time.dom.innerHTML = '';
20081 cls: 'hours-up glyphicon glyphicon-chevron-up'
20101 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20122 cls: 'timepicker-hour',
20137 cls: 'timepicker-minute',
20152 cls: 'btn btn-primary period',
20174 cls: 'hours-down glyphicon glyphicon-chevron-down'
20194 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20212 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20219 var hours = this.time.getHours();
20220 var minutes = this.time.getMinutes();
20233 hours = hours - 12;
20237 hours = '0' + hours;
20241 minutes = '0' + minutes;
20244 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20245 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20246 this.pop.select('button', true).first().dom.innerHTML = period;
20252 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20254 var cls = ['bottom'];
20256 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20263 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20268 this.picker().addClass(cls.join('-'));
20272 Roo.each(cls, function(c){
20274 _this.picker().setTop(_this.inputEl().getHeight());
20278 _this.picker().setTop(0 - _this.picker().getHeight());
20283 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20287 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20294 onFocus : function()
20296 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20300 onBlur : function()
20302 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20308 this.picker().show();
20313 this.fireEvent('show', this, this.date);
20318 this.picker().hide();
20321 this.fireEvent('hide', this, this.date);
20324 setTime : function()
20327 this.setValue(this.time.format(this.format));
20329 this.fireEvent('select', this, this.date);
20334 onMousedown: function(e){
20335 e.stopPropagation();
20336 e.preventDefault();
20339 onIncrementHours: function()
20341 Roo.log('onIncrementHours');
20342 this.time = this.time.add(Date.HOUR, 1);
20347 onDecrementHours: function()
20349 Roo.log('onDecrementHours');
20350 this.time = this.time.add(Date.HOUR, -1);
20354 onIncrementMinutes: function()
20356 Roo.log('onIncrementMinutes');
20357 this.time = this.time.add(Date.MINUTE, 1);
20361 onDecrementMinutes: function()
20363 Roo.log('onDecrementMinutes');
20364 this.time = this.time.add(Date.MINUTE, -1);
20368 onTogglePeriod: function()
20370 Roo.log('onTogglePeriod');
20371 this.time = this.time.add(Date.HOUR, 12);
20378 Roo.apply(Roo.bootstrap.TimeField, {
20408 cls: 'btn btn-info ok',
20420 Roo.apply(Roo.bootstrap.TimeField, {
20424 cls: 'datepicker dropdown-menu',
20428 cls: 'datepicker-time',
20432 cls: 'table-condensed',
20434 Roo.bootstrap.TimeField.content,
20435 Roo.bootstrap.TimeField.footer
20454 * @class Roo.bootstrap.MonthField
20455 * @extends Roo.bootstrap.Input
20456 * Bootstrap MonthField class
20458 * @cfg {String} language default en
20461 * Create a new MonthField
20462 * @param {Object} config The config object
20465 Roo.bootstrap.MonthField = function(config){
20466 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20471 * Fires when this field show.
20472 * @param {Roo.bootstrap.MonthField} this
20473 * @param {Mixed} date The date value
20478 * Fires when this field hide.
20479 * @param {Roo.bootstrap.MonthField} this
20480 * @param {Mixed} date The date value
20485 * Fires when select a date.
20486 * @param {Roo.bootstrap.MonthField} this
20487 * @param {String} oldvalue The old value
20488 * @param {String} newvalue The new value
20494 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20496 onRender: function(ct, position)
20499 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20501 this.language = this.language || 'en';
20502 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20503 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20505 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20506 this.isInline = false;
20507 this.isInput = true;
20508 this.component = this.el.select('.add-on', true).first() || false;
20509 this.component = (this.component && this.component.length === 0) ? false : this.component;
20510 this.hasInput = this.component && this.inputEL().length;
20512 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20514 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20516 this.picker().on('mousedown', this.onMousedown, this);
20517 this.picker().on('click', this.onClick, this);
20519 this.picker().addClass('datepicker-dropdown');
20521 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20522 v.setStyle('width', '189px');
20529 if(this.isInline) {
20535 setValue: function(v, suppressEvent)
20537 var o = this.getValue();
20539 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20543 if(suppressEvent !== true){
20544 this.fireEvent('select', this, o, v);
20549 getValue: function()
20554 onClick: function(e)
20556 e.stopPropagation();
20557 e.preventDefault();
20559 var target = e.getTarget();
20561 if(target.nodeName.toLowerCase() === 'i'){
20562 target = Roo.get(target).dom.parentNode;
20565 var nodeName = target.nodeName;
20566 var className = target.className;
20567 var html = target.innerHTML;
20569 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20573 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20575 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20581 picker : function()
20583 return this.pickerEl;
20586 fillMonths: function()
20589 var months = this.picker().select('>.datepicker-months td', true).first();
20591 months.dom.innerHTML = '';
20597 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20600 months.createChild(month);
20609 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20610 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20613 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20614 e.removeClass('active');
20616 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20617 e.addClass('active');
20624 if(this.isInline) {
20628 this.picker().removeClass(['bottom', 'top']);
20630 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20632 * place to the top of element!
20636 this.picker().addClass('top');
20637 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20642 this.picker().addClass('bottom');
20644 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20647 onFocus : function()
20649 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20653 onBlur : function()
20655 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20657 var d = this.inputEl().getValue();
20666 this.picker().show();
20667 this.picker().select('>.datepicker-months', true).first().show();
20671 this.fireEvent('show', this, this.date);
20676 if(this.isInline) {
20679 this.picker().hide();
20680 this.fireEvent('hide', this, this.date);
20684 onMousedown: function(e)
20686 e.stopPropagation();
20687 e.preventDefault();
20692 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20696 fireKey: function(e)
20698 if (!this.picker().isVisible()){
20699 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20710 e.preventDefault();
20714 dir = e.keyCode == 37 ? -1 : 1;
20716 this.vIndex = this.vIndex + dir;
20718 if(this.vIndex < 0){
20722 if(this.vIndex > 11){
20726 if(isNaN(this.vIndex)){
20730 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20736 dir = e.keyCode == 38 ? -1 : 1;
20738 this.vIndex = this.vIndex + dir * 4;
20740 if(this.vIndex < 0){
20744 if(this.vIndex > 11){
20748 if(isNaN(this.vIndex)){
20752 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20757 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20758 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20762 e.preventDefault();
20765 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20766 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20782 this.picker().remove();
20787 Roo.apply(Roo.bootstrap.MonthField, {
20806 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20807 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20812 Roo.apply(Roo.bootstrap.MonthField, {
20816 cls: 'datepicker dropdown-menu roo-dynamic',
20820 cls: 'datepicker-months',
20824 cls: 'table-condensed',
20826 Roo.bootstrap.DateField.content
20846 * @class Roo.bootstrap.CheckBox
20847 * @extends Roo.bootstrap.Input
20848 * Bootstrap CheckBox class
20850 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20851 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20852 * @cfg {String} boxLabel The text that appears beside the checkbox
20853 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20854 * @cfg {Boolean} checked initnal the element
20855 * @cfg {Boolean} inline inline the element (default false)
20856 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20857 * @cfg {String} tooltip label tooltip
20860 * Create a new CheckBox
20861 * @param {Object} config The config object
20864 Roo.bootstrap.CheckBox = function(config){
20865 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20870 * Fires when the element is checked or unchecked.
20871 * @param {Roo.bootstrap.CheckBox} this This input
20872 * @param {Boolean} checked The new checked value
20877 * Fires when the element is click.
20878 * @param {Roo.bootstrap.CheckBox} this This input
20885 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20887 inputType: 'checkbox',
20896 getAutoCreate : function()
20898 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20904 cfg.cls = 'form-group ' + this.inputType; //input-group
20907 cfg.cls += ' ' + this.inputType + '-inline';
20913 type : this.inputType,
20914 value : this.inputValue,
20915 cls : 'roo-' + this.inputType, //'form-box',
20916 placeholder : this.placeholder || ''
20920 if(this.inputType != 'radio'){
20924 cls : 'roo-hidden-value',
20925 value : this.checked ? this.inputValue : this.valueOff
20930 if (this.weight) { // Validity check?
20931 cfg.cls += " " + this.inputType + "-" + this.weight;
20934 if (this.disabled) {
20935 input.disabled=true;
20939 input.checked = this.checked;
20944 input.name = this.name;
20946 if(this.inputType != 'radio'){
20947 hidden.name = this.name;
20948 input.name = '_hidden_' + this.name;
20953 input.cls += ' input-' + this.size;
20958 ['xs','sm','md','lg'].map(function(size){
20959 if (settings[size]) {
20960 cfg.cls += ' col-' + size + '-' + settings[size];
20964 var inputblock = input;
20966 if (this.before || this.after) {
20969 cls : 'input-group',
20974 inputblock.cn.push({
20976 cls : 'input-group-addon',
20981 inputblock.cn.push(input);
20983 if(this.inputType != 'radio'){
20984 inputblock.cn.push(hidden);
20988 inputblock.cn.push({
20990 cls : 'input-group-addon',
20997 if (align ==='left' && this.fieldLabel.length) {
20998 // Roo.log("left and has label");
21003 cls : 'control-label',
21004 html : this.fieldLabel
21014 if(this.labelWidth > 12){
21015 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21018 if(this.labelWidth < 13 && this.labelmd == 0){
21019 this.labelmd = this.labelWidth;
21022 if(this.labellg > 0){
21023 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21024 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21027 if(this.labelmd > 0){
21028 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21029 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21032 if(this.labelsm > 0){
21033 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21034 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21037 if(this.labelxs > 0){
21038 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21039 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21042 } else if ( this.fieldLabel.length) {
21043 // Roo.log(" label");
21047 tag: this.boxLabel ? 'span' : 'label',
21049 cls: 'control-label box-input-label',
21050 //cls : 'input-group-addon',
21051 html : this.fieldLabel
21060 // Roo.log(" no label && no align");
21061 cfg.cn = [ inputblock ] ;
21067 var boxLabelCfg = {
21069 //'for': id, // box label is handled by onclick - so no for...
21071 html: this.boxLabel
21075 boxLabelCfg.tooltip = this.tooltip;
21078 cfg.cn.push(boxLabelCfg);
21081 if(this.inputType != 'radio'){
21082 cfg.cn.push(hidden);
21090 * return the real input element.
21092 inputEl: function ()
21094 return this.el.select('input.roo-' + this.inputType,true).first();
21096 hiddenEl: function ()
21098 return this.el.select('input.roo-hidden-value',true).first();
21101 labelEl: function()
21103 return this.el.select('label.control-label',true).first();
21105 /* depricated... */
21109 return this.labelEl();
21112 boxLabelEl: function()
21114 return this.el.select('label.box-label',true).first();
21117 initEvents : function()
21119 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21121 this.inputEl().on('click', this.onClick, this);
21123 if (this.boxLabel) {
21124 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21127 this.startValue = this.getValue();
21130 Roo.bootstrap.CheckBox.register(this);
21134 onClick : function(e)
21136 if(this.fireEvent('click', this, e) !== false){
21137 this.setChecked(!this.checked);
21142 setChecked : function(state,suppressEvent)
21144 this.startValue = this.getValue();
21146 if(this.inputType == 'radio'){
21148 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149 e.dom.checked = false;
21152 this.inputEl().dom.checked = true;
21154 this.inputEl().dom.value = this.inputValue;
21156 if(suppressEvent !== true){
21157 this.fireEvent('check', this, true);
21165 this.checked = state;
21167 this.inputEl().dom.checked = state;
21170 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21172 if(suppressEvent !== true){
21173 this.fireEvent('check', this, state);
21179 getValue : function()
21181 if(this.inputType == 'radio'){
21182 return this.getGroupValue();
21185 return this.hiddenEl().dom.value;
21189 getGroupValue : function()
21191 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21195 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21198 setValue : function(v,suppressEvent)
21200 if(this.inputType == 'radio'){
21201 this.setGroupValue(v, suppressEvent);
21205 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21210 setGroupValue : function(v, suppressEvent)
21212 this.startValue = this.getValue();
21214 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21215 e.dom.checked = false;
21217 if(e.dom.value == v){
21218 e.dom.checked = true;
21222 if(suppressEvent !== true){
21223 this.fireEvent('check', this, true);
21231 validate : function()
21233 if(this.getVisibilityEl().hasClass('hidden')){
21239 (this.inputType == 'radio' && this.validateRadio()) ||
21240 (this.inputType == 'checkbox' && this.validateCheckbox())
21246 this.markInvalid();
21250 validateRadio : function()
21252 if(this.getVisibilityEl().hasClass('hidden')){
21256 if(this.allowBlank){
21262 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21263 if(!e.dom.checked){
21275 validateCheckbox : function()
21278 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21279 //return (this.getValue() == this.inputValue) ? true : false;
21282 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21290 for(var i in group){
21291 if(group[i].el.isVisible(true)){
21299 for(var i in group){
21304 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21311 * Mark this field as valid
21313 markValid : function()
21317 this.fireEvent('valid', this);
21319 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21322 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21329 if(this.inputType == 'radio'){
21330 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21331 var fg = e.findParent('.form-group', false, true);
21332 if (Roo.bootstrap.version == 3) {
21333 fg.removeClass([_this.invalidClass, _this.validClass]);
21334 fg.addClass(_this.validClass);
21336 fg.removeClass(['is-valid', 'is-invalid']);
21337 fg.addClass('is-valid');
21345 var fg = this.el.findParent('.form-group', false, true);
21346 if (Roo.bootstrap.version == 3) {
21347 fg.removeClass([this.invalidClass, this.validClass]);
21348 fg.addClass(this.validClass);
21350 fg.removeClass(['is-valid', 'is-invalid']);
21351 fg.addClass('is-valid');
21356 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21362 for(var i in group){
21363 var fg = group[i].el.findParent('.form-group', false, true);
21364 if (Roo.bootstrap.version == 3) {
21365 fg.removeClass([this.invalidClass, this.validClass]);
21366 fg.addClass(this.validClass);
21368 fg.removeClass(['is-valid', 'is-invalid']);
21369 fg.addClass('is-valid');
21375 * Mark this field as invalid
21376 * @param {String} msg The validation message
21378 markInvalid : function(msg)
21380 if(this.allowBlank){
21386 this.fireEvent('invalid', this, msg);
21388 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21391 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21395 label.markInvalid();
21398 if(this.inputType == 'radio'){
21400 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21401 var fg = e.findParent('.form-group', false, true);
21402 if (Roo.bootstrap.version == 3) {
21403 fg.removeClass([_this.invalidClass, _this.validClass]);
21404 fg.addClass(_this.invalidClass);
21406 fg.removeClass(['is-invalid', 'is-valid']);
21407 fg.addClass('is-invalid');
21415 var fg = this.el.findParent('.form-group', false, true);
21416 if (Roo.bootstrap.version == 3) {
21417 fg.removeClass([_this.invalidClass, _this.validClass]);
21418 fg.addClass(_this.invalidClass);
21420 fg.removeClass(['is-invalid', 'is-valid']);
21421 fg.addClass('is-invalid');
21426 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21432 for(var i in group){
21433 var fg = group[i].el.findParent('.form-group', false, true);
21434 if (Roo.bootstrap.version == 3) {
21435 fg.removeClass([_this.invalidClass, _this.validClass]);
21436 fg.addClass(_this.invalidClass);
21438 fg.removeClass(['is-invalid', 'is-valid']);
21439 fg.addClass('is-invalid');
21445 clearInvalid : function()
21447 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21449 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21451 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21453 if (label && label.iconEl) {
21454 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21455 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21459 disable : function()
21461 if(this.inputType != 'radio'){
21462 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21469 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21470 _this.getActionEl().addClass(this.disabledClass);
21471 e.dom.disabled = true;
21475 this.disabled = true;
21476 this.fireEvent("disable", this);
21480 enable : function()
21482 if(this.inputType != 'radio'){
21483 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21490 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21491 _this.getActionEl().removeClass(this.disabledClass);
21492 e.dom.disabled = false;
21496 this.disabled = false;
21497 this.fireEvent("enable", this);
21501 setBoxLabel : function(v)
21506 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21512 Roo.apply(Roo.bootstrap.CheckBox, {
21517 * register a CheckBox Group
21518 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21520 register : function(checkbox)
21522 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21523 this.groups[checkbox.groupId] = {};
21526 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21530 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21534 * fetch a CheckBox Group based on the group ID
21535 * @param {string} the group ID
21536 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21538 get: function(groupId) {
21539 if (typeof(this.groups[groupId]) == 'undefined') {
21543 return this.groups[groupId] ;
21556 * @class Roo.bootstrap.Radio
21557 * @extends Roo.bootstrap.Component
21558 * Bootstrap Radio class
21559 * @cfg {String} boxLabel - the label associated
21560 * @cfg {String} value - the value of radio
21563 * Create a new Radio
21564 * @param {Object} config The config object
21566 Roo.bootstrap.Radio = function(config){
21567 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21571 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21577 getAutoCreate : function()
21581 cls : 'form-group radio',
21586 html : this.boxLabel
21594 initEvents : function()
21596 this.parent().register(this);
21598 this.el.on('click', this.onClick, this);
21602 onClick : function(e)
21604 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21605 this.setChecked(true);
21609 setChecked : function(state, suppressEvent)
21611 this.parent().setValue(this.value, suppressEvent);
21615 setBoxLabel : function(v)
21620 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21635 * @class Roo.bootstrap.SecurePass
21636 * @extends Roo.bootstrap.Input
21637 * Bootstrap SecurePass class
21641 * Create a new SecurePass
21642 * @param {Object} config The config object
21645 Roo.bootstrap.SecurePass = function (config) {
21646 // these go here, so the translation tool can replace them..
21648 PwdEmpty: "Please type a password, and then retype it to confirm.",
21649 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21650 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21651 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21652 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21653 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21654 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21655 TooWeak: "Your password is Too Weak."
21657 this.meterLabel = "Password strength:";
21658 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21659 this.meterClass = [
21660 "roo-password-meter-tooweak",
21661 "roo-password-meter-weak",
21662 "roo-password-meter-medium",
21663 "roo-password-meter-strong",
21664 "roo-password-meter-grey"
21669 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21672 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21674 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21676 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21677 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21678 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21679 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21680 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21681 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21682 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21692 * @cfg {String/Object} Label for the strength meter (defaults to
21693 * 'Password strength:')
21698 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21699 * ['Weak', 'Medium', 'Strong'])
21702 pwdStrengths: false,
21715 initEvents: function ()
21717 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21719 if (this.el.is('input[type=password]') && Roo.isSafari) {
21720 this.el.on('keydown', this.SafariOnKeyDown, this);
21723 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21726 onRender: function (ct, position)
21728 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21729 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21730 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21732 this.trigger.createChild({
21737 cls: 'roo-password-meter-grey col-xs-12',
21740 //width: this.meterWidth + 'px'
21744 cls: 'roo-password-meter-text'
21750 if (this.hideTrigger) {
21751 this.trigger.setDisplayed(false);
21753 this.setSize(this.width || '', this.height || '');
21756 onDestroy: function ()
21758 if (this.trigger) {
21759 this.trigger.removeAllListeners();
21760 this.trigger.remove();
21763 this.wrap.remove();
21765 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21768 checkStrength: function ()
21770 var pwd = this.inputEl().getValue();
21771 if (pwd == this._lastPwd) {
21776 if (this.ClientSideStrongPassword(pwd)) {
21778 } else if (this.ClientSideMediumPassword(pwd)) {
21780 } else if (this.ClientSideWeakPassword(pwd)) {
21786 Roo.log('strength1: ' + strength);
21788 //var pm = this.trigger.child('div/div/div').dom;
21789 var pm = this.trigger.child('div/div');
21790 pm.removeClass(this.meterClass);
21791 pm.addClass(this.meterClass[strength]);
21794 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21796 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21798 this._lastPwd = pwd;
21802 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21804 this._lastPwd = '';
21806 var pm = this.trigger.child('div/div');
21807 pm.removeClass(this.meterClass);
21808 pm.addClass('roo-password-meter-grey');
21811 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21814 this.inputEl().dom.type='password';
21817 validateValue: function (value)
21820 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21823 if (value.length == 0) {
21824 if (this.allowBlank) {
21825 this.clearInvalid();
21829 this.markInvalid(this.errors.PwdEmpty);
21830 this.errorMsg = this.errors.PwdEmpty;
21838 if ('[\x21-\x7e]*'.match(value)) {
21839 this.markInvalid(this.errors.PwdBadChar);
21840 this.errorMsg = this.errors.PwdBadChar;
21843 if (value.length < 6) {
21844 this.markInvalid(this.errors.PwdShort);
21845 this.errorMsg = this.errors.PwdShort;
21848 if (value.length > 16) {
21849 this.markInvalid(this.errors.PwdLong);
21850 this.errorMsg = this.errors.PwdLong;
21854 if (this.ClientSideStrongPassword(value)) {
21856 } else if (this.ClientSideMediumPassword(value)) {
21858 } else if (this.ClientSideWeakPassword(value)) {
21865 if (strength < 2) {
21866 //this.markInvalid(this.errors.TooWeak);
21867 this.errorMsg = this.errors.TooWeak;
21872 console.log('strength2: ' + strength);
21874 //var pm = this.trigger.child('div/div/div').dom;
21876 var pm = this.trigger.child('div/div');
21877 pm.removeClass(this.meterClass);
21878 pm.addClass(this.meterClass[strength]);
21880 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21882 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21884 this.errorMsg = '';
21888 CharacterSetChecks: function (type)
21891 this.fResult = false;
21894 isctype: function (character, type)
21897 case this.kCapitalLetter:
21898 if (character >= 'A' && character <= 'Z') {
21903 case this.kSmallLetter:
21904 if (character >= 'a' && character <= 'z') {
21910 if (character >= '0' && character <= '9') {
21915 case this.kPunctuation:
21916 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21927 IsLongEnough: function (pwd, size)
21929 return !(pwd == null || isNaN(size) || pwd.length < size);
21932 SpansEnoughCharacterSets: function (word, nb)
21934 if (!this.IsLongEnough(word, nb))
21939 var characterSetChecks = new Array(
21940 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21941 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21944 for (var index = 0; index < word.length; ++index) {
21945 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21946 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21947 characterSetChecks[nCharSet].fResult = true;
21954 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21955 if (characterSetChecks[nCharSet].fResult) {
21960 if (nCharSets < nb) {
21966 ClientSideStrongPassword: function (pwd)
21968 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21971 ClientSideMediumPassword: function (pwd)
21973 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21976 ClientSideWeakPassword: function (pwd)
21978 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21981 })//<script type="text/javascript">
21984 * Based Ext JS Library 1.1.1
21985 * Copyright(c) 2006-2007, Ext JS, LLC.
21991 * @class Roo.HtmlEditorCore
21992 * @extends Roo.Component
21993 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21995 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21998 Roo.HtmlEditorCore = function(config){
22001 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22006 * @event initialize
22007 * Fires when the editor is fully initialized (including the iframe)
22008 * @param {Roo.HtmlEditorCore} this
22013 * Fires when the editor is first receives the focus. Any insertion must wait
22014 * until after this event.
22015 * @param {Roo.HtmlEditorCore} this
22019 * @event beforesync
22020 * Fires before the textarea is updated with content from the editor iframe. Return false
22021 * to cancel the sync.
22022 * @param {Roo.HtmlEditorCore} this
22023 * @param {String} html
22027 * @event beforepush
22028 * Fires before the iframe editor is updated with content from the textarea. Return false
22029 * to cancel the push.
22030 * @param {Roo.HtmlEditorCore} this
22031 * @param {String} html
22036 * Fires when the textarea is updated with content from the editor iframe.
22037 * @param {Roo.HtmlEditorCore} this
22038 * @param {String} html
22043 * Fires when the iframe editor is updated with content from the textarea.
22044 * @param {Roo.HtmlEditorCore} this
22045 * @param {String} html
22050 * @event editorevent
22051 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22052 * @param {Roo.HtmlEditorCore} this
22058 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22060 // defaults : white / black...
22061 this.applyBlacklists();
22068 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22072 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22078 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22083 * @cfg {Number} height (in pixels)
22087 * @cfg {Number} width (in pixels)
22092 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22095 stylesheets: false,
22100 // private properties
22101 validationEvent : false,
22103 initialized : false,
22105 sourceEditMode : false,
22106 onFocus : Roo.emptyFn,
22108 hideMode:'offsets',
22112 // blacklist + whitelisted elements..
22119 * Protected method that will not generally be called directly. It
22120 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22121 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22123 getDocMarkup : function(){
22127 // inherit styels from page...??
22128 if (this.stylesheets === false) {
22130 Roo.get(document.head).select('style').each(function(node) {
22131 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22134 Roo.get(document.head).select('link').each(function(node) {
22135 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22138 } else if (!this.stylesheets.length) {
22140 st = '<style type="text/css">' +
22141 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22144 st = '<style type="text/css">' +
22149 st += '<style type="text/css">' +
22150 'IMG { cursor: pointer } ' +
22153 var cls = 'roo-htmleditor-body';
22155 if(this.bodyCls.length){
22156 cls += ' ' + this.bodyCls;
22159 return '<html><head>' + st +
22160 //<style type="text/css">' +
22161 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22163 ' </head><body class="' + cls + '"></body></html>';
22167 onRender : function(ct, position)
22170 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22171 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22174 this.el.dom.style.border = '0 none';
22175 this.el.dom.setAttribute('tabIndex', -1);
22176 this.el.addClass('x-hidden hide');
22180 if(Roo.isIE){ // fix IE 1px bogus margin
22181 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22185 this.frameId = Roo.id();
22189 var iframe = this.owner.wrap.createChild({
22191 cls: 'form-control', // bootstrap..
22193 name: this.frameId,
22194 frameBorder : 'no',
22195 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22200 this.iframe = iframe.dom;
22202 this.assignDocWin();
22204 this.doc.designMode = 'on';
22207 this.doc.write(this.getDocMarkup());
22211 var task = { // must defer to wait for browser to be ready
22213 //console.log("run task?" + this.doc.readyState);
22214 this.assignDocWin();
22215 if(this.doc.body || this.doc.readyState == 'complete'){
22217 this.doc.designMode="on";
22221 Roo.TaskMgr.stop(task);
22222 this.initEditor.defer(10, this);
22229 Roo.TaskMgr.start(task);
22234 onResize : function(w, h)
22236 Roo.log('resize: ' +w + ',' + h );
22237 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22241 if(typeof w == 'number'){
22243 this.iframe.style.width = w + 'px';
22245 if(typeof h == 'number'){
22247 this.iframe.style.height = h + 'px';
22249 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22256 * Toggles the editor between standard and source edit mode.
22257 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22259 toggleSourceEdit : function(sourceEditMode){
22261 this.sourceEditMode = sourceEditMode === true;
22263 if(this.sourceEditMode){
22265 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22268 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22269 //this.iframe.className = '';
22272 //this.setSize(this.owner.wrap.getSize());
22273 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22280 * Protected method that will not generally be called directly. If you need/want
22281 * custom HTML cleanup, this is the method you should override.
22282 * @param {String} html The HTML to be cleaned
22283 * return {String} The cleaned HTML
22285 cleanHtml : function(html){
22286 html = String(html);
22287 if(html.length > 5){
22288 if(Roo.isSafari){ // strip safari nonsense
22289 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22292 if(html == ' '){
22299 * HTML Editor -> Textarea
22300 * Protected method that will not generally be called directly. Syncs the contents
22301 * of the editor iframe with the textarea.
22303 syncValue : function(){
22304 if(this.initialized){
22305 var bd = (this.doc.body || this.doc.documentElement);
22306 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22307 var html = bd.innerHTML;
22309 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22310 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22312 html = '<div style="'+m[0]+'">' + html + '</div>';
22315 html = this.cleanHtml(html);
22316 // fix up the special chars.. normaly like back quotes in word...
22317 // however we do not want to do this with chinese..
22318 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22319 var cc = b.charCodeAt();
22321 (cc >= 0x4E00 && cc < 0xA000 ) ||
22322 (cc >= 0x3400 && cc < 0x4E00 ) ||
22323 (cc >= 0xf900 && cc < 0xfb00 )
22329 if(this.owner.fireEvent('beforesync', this, html) !== false){
22330 this.el.dom.value = html;
22331 this.owner.fireEvent('sync', this, html);
22337 * Protected method that will not generally be called directly. Pushes the value of the textarea
22338 * into the iframe editor.
22340 pushValue : function(){
22341 if(this.initialized){
22342 var v = this.el.dom.value.trim();
22344 // if(v.length < 1){
22348 if(this.owner.fireEvent('beforepush', this, v) !== false){
22349 var d = (this.doc.body || this.doc.documentElement);
22351 this.cleanUpPaste();
22352 this.el.dom.value = d.innerHTML;
22353 this.owner.fireEvent('push', this, v);
22359 deferFocus : function(){
22360 this.focus.defer(10, this);
22364 focus : function(){
22365 if(this.win && !this.sourceEditMode){
22372 assignDocWin: function()
22374 var iframe = this.iframe;
22377 this.doc = iframe.contentWindow.document;
22378 this.win = iframe.contentWindow;
22380 // if (!Roo.get(this.frameId)) {
22383 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22384 // this.win = Roo.get(this.frameId).dom.contentWindow;
22386 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22390 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22391 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22396 initEditor : function(){
22397 //console.log("INIT EDITOR");
22398 this.assignDocWin();
22402 this.doc.designMode="on";
22404 this.doc.write(this.getDocMarkup());
22407 var dbody = (this.doc.body || this.doc.documentElement);
22408 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22409 // this copies styles from the containing element into thsi one..
22410 // not sure why we need all of this..
22411 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22413 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22414 //ss['background-attachment'] = 'fixed'; // w3c
22415 dbody.bgProperties = 'fixed'; // ie
22416 //Roo.DomHelper.applyStyles(dbody, ss);
22417 Roo.EventManager.on(this.doc, {
22418 //'mousedown': this.onEditorEvent,
22419 'mouseup': this.onEditorEvent,
22420 'dblclick': this.onEditorEvent,
22421 'click': this.onEditorEvent,
22422 'keyup': this.onEditorEvent,
22427 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22429 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22430 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22432 this.initialized = true;
22434 this.owner.fireEvent('initialize', this);
22439 onDestroy : function(){
22445 //for (var i =0; i < this.toolbars.length;i++) {
22446 // // fixme - ask toolbars for heights?
22447 // this.toolbars[i].onDestroy();
22450 //this.wrap.dom.innerHTML = '';
22451 //this.wrap.remove();
22456 onFirstFocus : function(){
22458 this.assignDocWin();
22461 this.activated = true;
22464 if(Roo.isGecko){ // prevent silly gecko errors
22466 var s = this.win.getSelection();
22467 if(!s.focusNode || s.focusNode.nodeType != 3){
22468 var r = s.getRangeAt(0);
22469 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22474 this.execCmd('useCSS', true);
22475 this.execCmd('styleWithCSS', false);
22478 this.owner.fireEvent('activate', this);
22482 adjustFont: function(btn){
22483 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22484 //if(Roo.isSafari){ // safari
22487 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22488 if(Roo.isSafari){ // safari
22489 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22490 v = (v < 10) ? 10 : v;
22491 v = (v > 48) ? 48 : v;
22492 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22497 v = Math.max(1, v+adjust);
22499 this.execCmd('FontSize', v );
22502 onEditorEvent : function(e)
22504 this.owner.fireEvent('editorevent', this, e);
22505 // this.updateToolbar();
22506 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22509 insertTag : function(tg)
22511 // could be a bit smarter... -> wrap the current selected tRoo..
22512 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22514 range = this.createRange(this.getSelection());
22515 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22516 wrappingNode.appendChild(range.extractContents());
22517 range.insertNode(wrappingNode);
22524 this.execCmd("formatblock", tg);
22528 insertText : function(txt)
22532 var range = this.createRange();
22533 range.deleteContents();
22534 //alert(Sender.getAttribute('label'));
22536 range.insertNode(this.doc.createTextNode(txt));
22542 * Executes a Midas editor command on the editor document and performs necessary focus and
22543 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22544 * @param {String} cmd The Midas command
22545 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22547 relayCmd : function(cmd, value){
22549 this.execCmd(cmd, value);
22550 this.owner.fireEvent('editorevent', this);
22551 //this.updateToolbar();
22552 this.owner.deferFocus();
22556 * Executes a Midas editor command directly on the editor document.
22557 * For visual commands, you should use {@link #relayCmd} instead.
22558 * <b>This should only be called after the editor is initialized.</b>
22559 * @param {String} cmd The Midas command
22560 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22562 execCmd : function(cmd, value){
22563 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22570 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22572 * @param {String} text | dom node..
22574 insertAtCursor : function(text)
22577 if(!this.activated){
22583 var r = this.doc.selection.createRange();
22594 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22598 // from jquery ui (MIT licenced)
22600 var win = this.win;
22602 if (win.getSelection && win.getSelection().getRangeAt) {
22603 range = win.getSelection().getRangeAt(0);
22604 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22605 range.insertNode(node);
22606 } else if (win.document.selection && win.document.selection.createRange) {
22607 // no firefox support
22608 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22609 win.document.selection.createRange().pasteHTML(txt);
22611 // no firefox support
22612 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22613 this.execCmd('InsertHTML', txt);
22622 mozKeyPress : function(e){
22624 var c = e.getCharCode(), cmd;
22627 c = String.fromCharCode(c).toLowerCase();
22641 this.cleanUpPaste.defer(100, this);
22649 e.preventDefault();
22657 fixKeys : function(){ // load time branching for fastest keydown performance
22659 return function(e){
22660 var k = e.getKey(), r;
22663 r = this.doc.selection.createRange();
22666 r.pasteHTML('    ');
22673 r = this.doc.selection.createRange();
22675 var target = r.parentElement();
22676 if(!target || target.tagName.toLowerCase() != 'li'){
22678 r.pasteHTML('<br />');
22684 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22685 this.cleanUpPaste.defer(100, this);
22691 }else if(Roo.isOpera){
22692 return function(e){
22693 var k = e.getKey();
22697 this.execCmd('InsertHTML','    ');
22700 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22701 this.cleanUpPaste.defer(100, this);
22706 }else if(Roo.isSafari){
22707 return function(e){
22708 var k = e.getKey();
22712 this.execCmd('InsertText','\t');
22716 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22717 this.cleanUpPaste.defer(100, this);
22725 getAllAncestors: function()
22727 var p = this.getSelectedNode();
22730 a.push(p); // push blank onto stack..
22731 p = this.getParentElement();
22735 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22739 a.push(this.doc.body);
22743 lastSelNode : false,
22746 getSelection : function()
22748 this.assignDocWin();
22749 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22752 getSelectedNode: function()
22754 // this may only work on Gecko!!!
22756 // should we cache this!!!!
22761 var range = this.createRange(this.getSelection()).cloneRange();
22764 var parent = range.parentElement();
22766 var testRange = range.duplicate();
22767 testRange.moveToElementText(parent);
22768 if (testRange.inRange(range)) {
22771 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22774 parent = parent.parentElement;
22779 // is ancestor a text element.
22780 var ac = range.commonAncestorContainer;
22781 if (ac.nodeType == 3) {
22782 ac = ac.parentNode;
22785 var ar = ac.childNodes;
22788 var other_nodes = [];
22789 var has_other_nodes = false;
22790 for (var i=0;i<ar.length;i++) {
22791 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22794 // fullly contained node.
22796 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22801 // probably selected..
22802 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22803 other_nodes.push(ar[i]);
22807 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22812 has_other_nodes = true;
22814 if (!nodes.length && other_nodes.length) {
22815 nodes= other_nodes;
22817 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22823 createRange: function(sel)
22825 // this has strange effects when using with
22826 // top toolbar - not sure if it's a great idea.
22827 //this.editor.contentWindow.focus();
22828 if (typeof sel != "undefined") {
22830 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22832 return this.doc.createRange();
22835 return this.doc.createRange();
22838 getParentElement: function()
22841 this.assignDocWin();
22842 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22844 var range = this.createRange(sel);
22847 var p = range.commonAncestorContainer;
22848 while (p.nodeType == 3) { // text node
22859 * Range intersection.. the hard stuff...
22863 * [ -- selected range --- ]
22867 * if end is before start or hits it. fail.
22868 * if start is after end or hits it fail.
22870 * if either hits (but other is outside. - then it's not
22876 // @see http://www.thismuchiknow.co.uk/?p=64.
22877 rangeIntersectsNode : function(range, node)
22879 var nodeRange = node.ownerDocument.createRange();
22881 nodeRange.selectNode(node);
22883 nodeRange.selectNodeContents(node);
22886 var rangeStartRange = range.cloneRange();
22887 rangeStartRange.collapse(true);
22889 var rangeEndRange = range.cloneRange();
22890 rangeEndRange.collapse(false);
22892 var nodeStartRange = nodeRange.cloneRange();
22893 nodeStartRange.collapse(true);
22895 var nodeEndRange = nodeRange.cloneRange();
22896 nodeEndRange.collapse(false);
22898 return rangeStartRange.compareBoundaryPoints(
22899 Range.START_TO_START, nodeEndRange) == -1 &&
22900 rangeEndRange.compareBoundaryPoints(
22901 Range.START_TO_START, nodeStartRange) == 1;
22905 rangeCompareNode : function(range, node)
22907 var nodeRange = node.ownerDocument.createRange();
22909 nodeRange.selectNode(node);
22911 nodeRange.selectNodeContents(node);
22915 range.collapse(true);
22917 nodeRange.collapse(true);
22919 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22920 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22922 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22924 var nodeIsBefore = ss == 1;
22925 var nodeIsAfter = ee == -1;
22927 if (nodeIsBefore && nodeIsAfter) {
22930 if (!nodeIsBefore && nodeIsAfter) {
22931 return 1; //right trailed.
22934 if (nodeIsBefore && !nodeIsAfter) {
22935 return 2; // left trailed.
22941 // private? - in a new class?
22942 cleanUpPaste : function()
22944 // cleans up the whole document..
22945 Roo.log('cleanuppaste');
22947 this.cleanUpChildren(this.doc.body);
22948 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22949 if (clean != this.doc.body.innerHTML) {
22950 this.doc.body.innerHTML = clean;
22955 cleanWordChars : function(input) {// change the chars to hex code
22956 var he = Roo.HtmlEditorCore;
22958 var output = input;
22959 Roo.each(he.swapCodes, function(sw) {
22960 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22962 output = output.replace(swapper, sw[1]);
22969 cleanUpChildren : function (n)
22971 if (!n.childNodes.length) {
22974 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22975 this.cleanUpChild(n.childNodes[i]);
22982 cleanUpChild : function (node)
22985 //console.log(node);
22986 if (node.nodeName == "#text") {
22987 // clean up silly Windows -- stuff?
22990 if (node.nodeName == "#comment") {
22991 node.parentNode.removeChild(node);
22992 // clean up silly Windows -- stuff?
22995 var lcname = node.tagName.toLowerCase();
22996 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22997 // whitelist of tags..
22999 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23001 node.parentNode.removeChild(node);
23006 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23008 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23009 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23011 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23012 // remove_keep_children = true;
23015 if (remove_keep_children) {
23016 this.cleanUpChildren(node);
23017 // inserts everything just before this node...
23018 while (node.childNodes.length) {
23019 var cn = node.childNodes[0];
23020 node.removeChild(cn);
23021 node.parentNode.insertBefore(cn, node);
23023 node.parentNode.removeChild(node);
23027 if (!node.attributes || !node.attributes.length) {
23028 this.cleanUpChildren(node);
23032 function cleanAttr(n,v)
23035 if (v.match(/^\./) || v.match(/^\//)) {
23038 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23041 if (v.match(/^#/)) {
23044 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23045 node.removeAttribute(n);
23049 var cwhite = this.cwhite;
23050 var cblack = this.cblack;
23052 function cleanStyle(n,v)
23054 if (v.match(/expression/)) { //XSS?? should we even bother..
23055 node.removeAttribute(n);
23059 var parts = v.split(/;/);
23062 Roo.each(parts, function(p) {
23063 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23067 var l = p.split(':').shift().replace(/\s+/g,'');
23068 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23070 if ( cwhite.length && cblack.indexOf(l) > -1) {
23071 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23072 //node.removeAttribute(n);
23076 // only allow 'c whitelisted system attributes'
23077 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23078 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23079 //node.removeAttribute(n);
23089 if (clean.length) {
23090 node.setAttribute(n, clean.join(';'));
23092 node.removeAttribute(n);
23098 for (var i = node.attributes.length-1; i > -1 ; i--) {
23099 var a = node.attributes[i];
23102 if (a.name.toLowerCase().substr(0,2)=='on') {
23103 node.removeAttribute(a.name);
23106 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23107 node.removeAttribute(a.name);
23110 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23111 cleanAttr(a.name,a.value); // fixme..
23114 if (a.name == 'style') {
23115 cleanStyle(a.name,a.value);
23118 /// clean up MS crap..
23119 // tecnically this should be a list of valid class'es..
23122 if (a.name == 'class') {
23123 if (a.value.match(/^Mso/)) {
23124 node.className = '';
23127 if (a.value.match(/^body$/)) {
23128 node.className = '';
23139 this.cleanUpChildren(node);
23145 * Clean up MS wordisms...
23147 cleanWord : function(node)
23152 this.cleanWord(this.doc.body);
23155 if (node.nodeName == "#text") {
23156 // clean up silly Windows -- stuff?
23159 if (node.nodeName == "#comment") {
23160 node.parentNode.removeChild(node);
23161 // clean up silly Windows -- stuff?
23165 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23166 node.parentNode.removeChild(node);
23170 // remove - but keep children..
23171 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23172 while (node.childNodes.length) {
23173 var cn = node.childNodes[0];
23174 node.removeChild(cn);
23175 node.parentNode.insertBefore(cn, node);
23177 node.parentNode.removeChild(node);
23178 this.iterateChildren(node, this.cleanWord);
23182 if (node.className.length) {
23184 var cn = node.className.split(/\W+/);
23186 Roo.each(cn, function(cls) {
23187 if (cls.match(/Mso[a-zA-Z]+/)) {
23192 node.className = cna.length ? cna.join(' ') : '';
23194 node.removeAttribute("class");
23198 if (node.hasAttribute("lang")) {
23199 node.removeAttribute("lang");
23202 if (node.hasAttribute("style")) {
23204 var styles = node.getAttribute("style").split(";");
23206 Roo.each(styles, function(s) {
23207 if (!s.match(/:/)) {
23210 var kv = s.split(":");
23211 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23214 // what ever is left... we allow.
23217 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23218 if (!nstyle.length) {
23219 node.removeAttribute('style');
23222 this.iterateChildren(node, this.cleanWord);
23228 * iterateChildren of a Node, calling fn each time, using this as the scole..
23229 * @param {DomNode} node node to iterate children of.
23230 * @param {Function} fn method of this class to call on each item.
23232 iterateChildren : function(node, fn)
23234 if (!node.childNodes.length) {
23237 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23238 fn.call(this, node.childNodes[i])
23244 * cleanTableWidths.
23246 * Quite often pasting from word etc.. results in tables with column and widths.
23247 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23250 cleanTableWidths : function(node)
23255 this.cleanTableWidths(this.doc.body);
23260 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23263 Roo.log(node.tagName);
23264 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23265 this.iterateChildren(node, this.cleanTableWidths);
23268 if (node.hasAttribute('width')) {
23269 node.removeAttribute('width');
23273 if (node.hasAttribute("style")) {
23276 var styles = node.getAttribute("style").split(";");
23278 Roo.each(styles, function(s) {
23279 if (!s.match(/:/)) {
23282 var kv = s.split(":");
23283 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23286 // what ever is left... we allow.
23289 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23290 if (!nstyle.length) {
23291 node.removeAttribute('style');
23295 this.iterateChildren(node, this.cleanTableWidths);
23303 domToHTML : function(currentElement, depth, nopadtext) {
23305 depth = depth || 0;
23306 nopadtext = nopadtext || false;
23308 if (!currentElement) {
23309 return this.domToHTML(this.doc.body);
23312 //Roo.log(currentElement);
23314 var allText = false;
23315 var nodeName = currentElement.nodeName;
23316 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23318 if (nodeName == '#text') {
23320 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23325 if (nodeName != 'BODY') {
23328 // Prints the node tagName, such as <A>, <IMG>, etc
23331 for(i = 0; i < currentElement.attributes.length;i++) {
23333 var aname = currentElement.attributes.item(i).name;
23334 if (!currentElement.attributes.item(i).value.length) {
23337 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23340 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23349 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23352 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23357 // Traverse the tree
23359 var currentElementChild = currentElement.childNodes.item(i);
23360 var allText = true;
23361 var innerHTML = '';
23363 while (currentElementChild) {
23364 // Formatting code (indent the tree so it looks nice on the screen)
23365 var nopad = nopadtext;
23366 if (lastnode == 'SPAN') {
23370 if (currentElementChild.nodeName == '#text') {
23371 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23372 toadd = nopadtext ? toadd : toadd.trim();
23373 if (!nopad && toadd.length > 80) {
23374 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23376 innerHTML += toadd;
23379 currentElementChild = currentElement.childNodes.item(i);
23385 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23387 // Recursively traverse the tree structure of the child node
23388 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23389 lastnode = currentElementChild.nodeName;
23391 currentElementChild=currentElement.childNodes.item(i);
23397 // The remaining code is mostly for formatting the tree
23398 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23403 ret+= "</"+tagName+">";
23409 applyBlacklists : function()
23411 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23412 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23416 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23417 if (b.indexOf(tag) > -1) {
23420 this.white.push(tag);
23424 Roo.each(w, function(tag) {
23425 if (b.indexOf(tag) > -1) {
23428 if (this.white.indexOf(tag) > -1) {
23431 this.white.push(tag);
23436 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23437 if (w.indexOf(tag) > -1) {
23440 this.black.push(tag);
23444 Roo.each(b, function(tag) {
23445 if (w.indexOf(tag) > -1) {
23448 if (this.black.indexOf(tag) > -1) {
23451 this.black.push(tag);
23456 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23457 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23461 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23462 if (b.indexOf(tag) > -1) {
23465 this.cwhite.push(tag);
23469 Roo.each(w, function(tag) {
23470 if (b.indexOf(tag) > -1) {
23473 if (this.cwhite.indexOf(tag) > -1) {
23476 this.cwhite.push(tag);
23481 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23482 if (w.indexOf(tag) > -1) {
23485 this.cblack.push(tag);
23489 Roo.each(b, function(tag) {
23490 if (w.indexOf(tag) > -1) {
23493 if (this.cblack.indexOf(tag) > -1) {
23496 this.cblack.push(tag);
23501 setStylesheets : function(stylesheets)
23503 if(typeof(stylesheets) == 'string'){
23504 Roo.get(this.iframe.contentDocument.head).createChild({
23506 rel : 'stylesheet',
23515 Roo.each(stylesheets, function(s) {
23520 Roo.get(_this.iframe.contentDocument.head).createChild({
23522 rel : 'stylesheet',
23531 removeStylesheets : function()
23535 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23540 setStyle : function(style)
23542 Roo.get(this.iframe.contentDocument.head).createChild({
23551 // hide stuff that is not compatible
23565 * @event specialkey
23569 * @cfg {String} fieldClass @hide
23572 * @cfg {String} focusClass @hide
23575 * @cfg {String} autoCreate @hide
23578 * @cfg {String} inputType @hide
23581 * @cfg {String} invalidClass @hide
23584 * @cfg {String} invalidText @hide
23587 * @cfg {String} msgFx @hide
23590 * @cfg {String} validateOnBlur @hide
23594 Roo.HtmlEditorCore.white = [
23595 'area', 'br', 'img', 'input', 'hr', 'wbr',
23597 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23598 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23599 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23600 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23601 'table', 'ul', 'xmp',
23603 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23606 'dir', 'menu', 'ol', 'ul', 'dl',
23612 Roo.HtmlEditorCore.black = [
23613 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23615 'base', 'basefont', 'bgsound', 'blink', 'body',
23616 'frame', 'frameset', 'head', 'html', 'ilayer',
23617 'iframe', 'layer', 'link', 'meta', 'object',
23618 'script', 'style' ,'title', 'xml' // clean later..
23620 Roo.HtmlEditorCore.clean = [
23621 'script', 'style', 'title', 'xml'
23623 Roo.HtmlEditorCore.remove = [
23628 Roo.HtmlEditorCore.ablack = [
23632 Roo.HtmlEditorCore.aclean = [
23633 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23637 Roo.HtmlEditorCore.pwhite= [
23638 'http', 'https', 'mailto'
23641 // white listed style attributes.
23642 Roo.HtmlEditorCore.cwhite= [
23643 // 'text-align', /// default is to allow most things..
23649 // black listed style attributes.
23650 Roo.HtmlEditorCore.cblack= [
23651 // 'font-size' -- this can be set by the project
23655 Roo.HtmlEditorCore.swapCodes =[
23674 * @class Roo.bootstrap.HtmlEditor
23675 * @extends Roo.bootstrap.TextArea
23676 * Bootstrap HtmlEditor class
23679 * Create a new HtmlEditor
23680 * @param {Object} config The config object
23683 Roo.bootstrap.HtmlEditor = function(config){
23684 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23685 if (!this.toolbars) {
23686 this.toolbars = [];
23689 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23692 * @event initialize
23693 * Fires when the editor is fully initialized (including the iframe)
23694 * @param {HtmlEditor} this
23699 * Fires when the editor is first receives the focus. Any insertion must wait
23700 * until after this event.
23701 * @param {HtmlEditor} this
23705 * @event beforesync
23706 * Fires before the textarea is updated with content from the editor iframe. Return false
23707 * to cancel the sync.
23708 * @param {HtmlEditor} this
23709 * @param {String} html
23713 * @event beforepush
23714 * Fires before the iframe editor is updated with content from the textarea. Return false
23715 * to cancel the push.
23716 * @param {HtmlEditor} this
23717 * @param {String} html
23722 * Fires when the textarea is updated with content from the editor iframe.
23723 * @param {HtmlEditor} this
23724 * @param {String} html
23729 * Fires when the iframe editor is updated with content from the textarea.
23730 * @param {HtmlEditor} this
23731 * @param {String} html
23735 * @event editmodechange
23736 * Fires when the editor switches edit modes
23737 * @param {HtmlEditor} this
23738 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23740 editmodechange: true,
23742 * @event editorevent
23743 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23744 * @param {HtmlEditor} this
23748 * @event firstfocus
23749 * Fires when on first focus - needed by toolbars..
23750 * @param {HtmlEditor} this
23755 * Auto save the htmlEditor value as a file into Events
23756 * @param {HtmlEditor} this
23760 * @event savedpreview
23761 * preview the saved version of htmlEditor
23762 * @param {HtmlEditor} this
23769 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23773 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23778 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23783 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23788 * @cfg {Number} height (in pixels)
23792 * @cfg {Number} width (in pixels)
23797 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23800 stylesheets: false,
23805 // private properties
23806 validationEvent : false,
23808 initialized : false,
23811 onFocus : Roo.emptyFn,
23813 hideMode:'offsets',
23815 tbContainer : false,
23819 toolbarContainer :function() {
23820 return this.wrap.select('.x-html-editor-tb',true).first();
23824 * Protected method that will not generally be called directly. It
23825 * is called when the editor creates its toolbar. Override this method if you need to
23826 * add custom toolbar buttons.
23827 * @param {HtmlEditor} editor
23829 createToolbar : function(){
23830 Roo.log('renewing');
23831 Roo.log("create toolbars");
23833 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23834 this.toolbars[0].render(this.toolbarContainer());
23838 // if (!editor.toolbars || !editor.toolbars.length) {
23839 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23842 // for (var i =0 ; i < editor.toolbars.length;i++) {
23843 // editor.toolbars[i] = Roo.factory(
23844 // typeof(editor.toolbars[i]) == 'string' ?
23845 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23846 // Roo.bootstrap.HtmlEditor);
23847 // editor.toolbars[i].init(editor);
23853 onRender : function(ct, position)
23855 // Roo.log("Call onRender: " + this.xtype);
23857 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23859 this.wrap = this.inputEl().wrap({
23860 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23863 this.editorcore.onRender(ct, position);
23865 if (this.resizable) {
23866 this.resizeEl = new Roo.Resizable(this.wrap, {
23870 minHeight : this.height,
23871 height: this.height,
23872 handles : this.resizable,
23875 resize : function(r, w, h) {
23876 _t.onResize(w,h); // -something
23882 this.createToolbar(this);
23885 if(!this.width && this.resizable){
23886 this.setSize(this.wrap.getSize());
23888 if (this.resizeEl) {
23889 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23890 // should trigger onReize..
23896 onResize : function(w, h)
23898 Roo.log('resize: ' +w + ',' + h );
23899 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23903 if(this.inputEl() ){
23904 if(typeof w == 'number'){
23905 var aw = w - this.wrap.getFrameWidth('lr');
23906 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23909 if(typeof h == 'number'){
23910 var tbh = -11; // fixme it needs to tool bar size!
23911 for (var i =0; i < this.toolbars.length;i++) {
23912 // fixme - ask toolbars for heights?
23913 tbh += this.toolbars[i].el.getHeight();
23914 //if (this.toolbars[i].footer) {
23915 // tbh += this.toolbars[i].footer.el.getHeight();
23923 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23924 ah -= 5; // knock a few pixes off for look..
23925 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23929 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23930 this.editorcore.onResize(ew,eh);
23935 * Toggles the editor between standard and source edit mode.
23936 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23938 toggleSourceEdit : function(sourceEditMode)
23940 this.editorcore.toggleSourceEdit(sourceEditMode);
23942 if(this.editorcore.sourceEditMode){
23943 Roo.log('editor - showing textarea');
23946 // Roo.log(this.syncValue());
23948 this.inputEl().removeClass(['hide', 'x-hidden']);
23949 this.inputEl().dom.removeAttribute('tabIndex');
23950 this.inputEl().focus();
23952 Roo.log('editor - hiding textarea');
23954 // Roo.log(this.pushValue());
23957 this.inputEl().addClass(['hide', 'x-hidden']);
23958 this.inputEl().dom.setAttribute('tabIndex', -1);
23959 //this.deferFocus();
23962 if(this.resizable){
23963 this.setSize(this.wrap.getSize());
23966 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23969 // private (for BoxComponent)
23970 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23972 // private (for BoxComponent)
23973 getResizeEl : function(){
23977 // private (for BoxComponent)
23978 getPositionEl : function(){
23983 initEvents : function(){
23984 this.originalValue = this.getValue();
23988 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23991 // markInvalid : Roo.emptyFn,
23993 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23996 // clearInvalid : Roo.emptyFn,
23998 setValue : function(v){
23999 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24000 this.editorcore.pushValue();
24005 deferFocus : function(){
24006 this.focus.defer(10, this);
24010 focus : function(){
24011 this.editorcore.focus();
24017 onDestroy : function(){
24023 for (var i =0; i < this.toolbars.length;i++) {
24024 // fixme - ask toolbars for heights?
24025 this.toolbars[i].onDestroy();
24028 this.wrap.dom.innerHTML = '';
24029 this.wrap.remove();
24034 onFirstFocus : function(){
24035 //Roo.log("onFirstFocus");
24036 this.editorcore.onFirstFocus();
24037 for (var i =0; i < this.toolbars.length;i++) {
24038 this.toolbars[i].onFirstFocus();
24044 syncValue : function()
24046 this.editorcore.syncValue();
24049 pushValue : function()
24051 this.editorcore.pushValue();
24055 // hide stuff that is not compatible
24069 * @event specialkey
24073 * @cfg {String} fieldClass @hide
24076 * @cfg {String} focusClass @hide
24079 * @cfg {String} autoCreate @hide
24082 * @cfg {String} inputType @hide
24086 * @cfg {String} invalidText @hide
24089 * @cfg {String} msgFx @hide
24092 * @cfg {String} validateOnBlur @hide
24101 Roo.namespace('Roo.bootstrap.htmleditor');
24103 * @class Roo.bootstrap.HtmlEditorToolbar1
24109 new Roo.bootstrap.HtmlEditor({
24112 new Roo.bootstrap.HtmlEditorToolbar1({
24113 disable : { fonts: 1 , format: 1, ..., ... , ...],
24119 * @cfg {Object} disable List of elements to disable..
24120 * @cfg {Array} btns List of additional buttons.
24124 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24127 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24130 Roo.apply(this, config);
24132 // default disabled, based on 'good practice'..
24133 this.disable = this.disable || {};
24134 Roo.applyIf(this.disable, {
24137 specialElements : true
24139 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24141 this.editor = config.editor;
24142 this.editorcore = config.editor.editorcore;
24144 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24146 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24147 // dont call parent... till later.
24149 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24154 editorcore : false,
24159 "h1","h2","h3","h4","h5","h6",
24161 "abbr", "acronym", "address", "cite", "samp", "var",
24165 onRender : function(ct, position)
24167 // Roo.log("Call onRender: " + this.xtype);
24169 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24171 this.el.dom.style.marginBottom = '0';
24173 var editorcore = this.editorcore;
24174 var editor= this.editor;
24177 var btn = function(id,cmd , toggle, handler, html){
24179 var event = toggle ? 'toggle' : 'click';
24184 xns: Roo.bootstrap,
24188 enableToggle:toggle !== false,
24190 pressed : toggle ? false : null,
24193 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24194 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24200 // var cb_box = function...
24205 xns: Roo.bootstrap,
24210 xns: Roo.bootstrap,
24214 Roo.each(this.formats, function(f) {
24215 style.menu.items.push({
24217 xns: Roo.bootstrap,
24218 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24223 editorcore.insertTag(this.tagname);
24230 children.push(style);
24232 btn('bold',false,true);
24233 btn('italic',false,true);
24234 btn('align-left', 'justifyleft',true);
24235 btn('align-center', 'justifycenter',true);
24236 btn('align-right' , 'justifyright',true);
24237 btn('link', false, false, function(btn) {
24238 //Roo.log("create link?");
24239 var url = prompt(this.createLinkText, this.defaultLinkValue);
24240 if(url && url != 'http:/'+'/'){
24241 this.editorcore.relayCmd('createlink', url);
24244 btn('list','insertunorderedlist',true);
24245 btn('pencil', false,true, function(btn){
24247 this.toggleSourceEdit(btn.pressed);
24250 if (this.editor.btns.length > 0) {
24251 for (var i = 0; i<this.editor.btns.length; i++) {
24252 children.push(this.editor.btns[i]);
24260 xns: Roo.bootstrap,
24265 xns: Roo.bootstrap,
24270 cog.menu.items.push({
24272 xns: Roo.bootstrap,
24273 html : Clean styles,
24278 editorcore.insertTag(this.tagname);
24287 this.xtype = 'NavSimplebar';
24289 for(var i=0;i< children.length;i++) {
24291 this.buttons.add(this.addxtypeChild(children[i]));
24295 editor.on('editorevent', this.updateToolbar, this);
24297 onBtnClick : function(id)
24299 this.editorcore.relayCmd(id);
24300 this.editorcore.focus();
24304 * Protected method that will not generally be called directly. It triggers
24305 * a toolbar update by reading the markup state of the current selection in the editor.
24307 updateToolbar: function(){
24309 if(!this.editorcore.activated){
24310 this.editor.onFirstFocus(); // is this neeed?
24314 var btns = this.buttons;
24315 var doc = this.editorcore.doc;
24316 btns.get('bold').setActive(doc.queryCommandState('bold'));
24317 btns.get('italic').setActive(doc.queryCommandState('italic'));
24318 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24320 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24321 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24322 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24324 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24325 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24328 var ans = this.editorcore.getAllAncestors();
24329 if (this.formatCombo) {
24332 var store = this.formatCombo.store;
24333 this.formatCombo.setValue("");
24334 for (var i =0; i < ans.length;i++) {
24335 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24337 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24345 // hides menus... - so this cant be on a menu...
24346 Roo.bootstrap.MenuMgr.hideAll();
24348 Roo.bootstrap.MenuMgr.hideAll();
24349 //this.editorsyncValue();
24351 onFirstFocus: function() {
24352 this.buttons.each(function(item){
24356 toggleSourceEdit : function(sourceEditMode){
24359 if(sourceEditMode){
24360 Roo.log("disabling buttons");
24361 this.buttons.each( function(item){
24362 if(item.cmd != 'pencil'){
24368 Roo.log("enabling buttons");
24369 if(this.editorcore.initialized){
24370 this.buttons.each( function(item){
24376 Roo.log("calling toggole on editor");
24377 // tell the editor that it's been pressed..
24378 this.editor.toggleSourceEdit(sourceEditMode);
24388 * @class Roo.bootstrap.Table.AbstractSelectionModel
24389 * @extends Roo.util.Observable
24390 * Abstract base class for grid SelectionModels. It provides the interface that should be
24391 * implemented by descendant classes. This class should not be directly instantiated.
24394 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24395 this.locked = false;
24396 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24400 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24401 /** @ignore Called by the grid automatically. Do not call directly. */
24402 init : function(grid){
24408 * Locks the selections.
24411 this.locked = true;
24415 * Unlocks the selections.
24417 unlock : function(){
24418 this.locked = false;
24422 * Returns true if the selections are locked.
24423 * @return {Boolean}
24425 isLocked : function(){
24426 return this.locked;
24430 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24431 * @class Roo.bootstrap.Table.RowSelectionModel
24432 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24433 * It supports multiple selections and keyboard selection/navigation.
24435 * @param {Object} config
24438 Roo.bootstrap.Table.RowSelectionModel = function(config){
24439 Roo.apply(this, config);
24440 this.selections = new Roo.util.MixedCollection(false, function(o){
24445 this.lastActive = false;
24449 * @event selectionchange
24450 * Fires when the selection changes
24451 * @param {SelectionModel} this
24453 "selectionchange" : true,
24455 * @event afterselectionchange
24456 * Fires after the selection changes (eg. by key press or clicking)
24457 * @param {SelectionModel} this
24459 "afterselectionchange" : true,
24461 * @event beforerowselect
24462 * Fires when a row is selected being selected, return false to cancel.
24463 * @param {SelectionModel} this
24464 * @param {Number} rowIndex The selected index
24465 * @param {Boolean} keepExisting False if other selections will be cleared
24467 "beforerowselect" : true,
24470 * Fires when a row is selected.
24471 * @param {SelectionModel} this
24472 * @param {Number} rowIndex The selected index
24473 * @param {Roo.data.Record} r The record
24475 "rowselect" : true,
24477 * @event rowdeselect
24478 * Fires when a row is deselected.
24479 * @param {SelectionModel} this
24480 * @param {Number} rowIndex The selected index
24482 "rowdeselect" : true
24484 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24485 this.locked = false;
24488 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24490 * @cfg {Boolean} singleSelect
24491 * True to allow selection of only one row at a time (defaults to false)
24493 singleSelect : false,
24496 initEvents : function()
24499 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24500 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24501 //}else{ // allow click to work like normal
24502 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24504 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24505 this.grid.on("rowclick", this.handleMouseDown, this);
24507 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24508 "up" : function(e){
24510 this.selectPrevious(e.shiftKey);
24511 }else if(this.last !== false && this.lastActive !== false){
24512 var last = this.last;
24513 this.selectRange(this.last, this.lastActive-1);
24514 this.grid.getView().focusRow(this.lastActive);
24515 if(last !== false){
24519 this.selectFirstRow();
24521 this.fireEvent("afterselectionchange", this);
24523 "down" : function(e){
24525 this.selectNext(e.shiftKey);
24526 }else if(this.last !== false && this.lastActive !== false){
24527 var last = this.last;
24528 this.selectRange(this.last, this.lastActive+1);
24529 this.grid.getView().focusRow(this.lastActive);
24530 if(last !== false){
24534 this.selectFirstRow();
24536 this.fireEvent("afterselectionchange", this);
24540 this.grid.store.on('load', function(){
24541 this.selections.clear();
24544 var view = this.grid.view;
24545 view.on("refresh", this.onRefresh, this);
24546 view.on("rowupdated", this.onRowUpdated, this);
24547 view.on("rowremoved", this.onRemove, this);
24552 onRefresh : function()
24554 var ds = this.grid.store, i, v = this.grid.view;
24555 var s = this.selections;
24556 s.each(function(r){
24557 if((i = ds.indexOfId(r.id)) != -1){
24566 onRemove : function(v, index, r){
24567 this.selections.remove(r);
24571 onRowUpdated : function(v, index, r){
24572 if(this.isSelected(r)){
24573 v.onRowSelect(index);
24579 * @param {Array} records The records to select
24580 * @param {Boolean} keepExisting (optional) True to keep existing selections
24582 selectRecords : function(records, keepExisting)
24585 this.clearSelections();
24587 var ds = this.grid.store;
24588 for(var i = 0, len = records.length; i < len; i++){
24589 this.selectRow(ds.indexOf(records[i]), true);
24594 * Gets the number of selected rows.
24597 getCount : function(){
24598 return this.selections.length;
24602 * Selects the first row in the grid.
24604 selectFirstRow : function(){
24609 * Select the last row.
24610 * @param {Boolean} keepExisting (optional) True to keep existing selections
24612 selectLastRow : function(keepExisting){
24613 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24614 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24618 * Selects the row immediately following the last selected row.
24619 * @param {Boolean} keepExisting (optional) True to keep existing selections
24621 selectNext : function(keepExisting)
24623 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24624 this.selectRow(this.last+1, keepExisting);
24625 this.grid.getView().focusRow(this.last);
24630 * Selects the row that precedes the last selected row.
24631 * @param {Boolean} keepExisting (optional) True to keep existing selections
24633 selectPrevious : function(keepExisting){
24635 this.selectRow(this.last-1, keepExisting);
24636 this.grid.getView().focusRow(this.last);
24641 * Returns the selected records
24642 * @return {Array} Array of selected records
24644 getSelections : function(){
24645 return [].concat(this.selections.items);
24649 * Returns the first selected record.
24652 getSelected : function(){
24653 return this.selections.itemAt(0);
24658 * Clears all selections.
24660 clearSelections : function(fast)
24666 var ds = this.grid.store;
24667 var s = this.selections;
24668 s.each(function(r){
24669 this.deselectRow(ds.indexOfId(r.id));
24673 this.selections.clear();
24680 * Selects all rows.
24682 selectAll : function(){
24686 this.selections.clear();
24687 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24688 this.selectRow(i, true);
24693 * Returns True if there is a selection.
24694 * @return {Boolean}
24696 hasSelection : function(){
24697 return this.selections.length > 0;
24701 * Returns True if the specified row is selected.
24702 * @param {Number/Record} record The record or index of the record to check
24703 * @return {Boolean}
24705 isSelected : function(index){
24706 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24707 return (r && this.selections.key(r.id) ? true : false);
24711 * Returns True if the specified record id is selected.
24712 * @param {String} id The id of record to check
24713 * @return {Boolean}
24715 isIdSelected : function(id){
24716 return (this.selections.key(id) ? true : false);
24721 handleMouseDBClick : function(e, t){
24725 handleMouseDown : function(e, t)
24727 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24728 if(this.isLocked() || rowIndex < 0 ){
24731 if(e.shiftKey && this.last !== false){
24732 var last = this.last;
24733 this.selectRange(last, rowIndex, e.ctrlKey);
24734 this.last = last; // reset the last
24738 var isSelected = this.isSelected(rowIndex);
24739 //Roo.log("select row:" + rowIndex);
24741 this.deselectRow(rowIndex);
24743 this.selectRow(rowIndex, true);
24747 if(e.button !== 0 && isSelected){
24748 alert('rowIndex 2: ' + rowIndex);
24749 view.focusRow(rowIndex);
24750 }else if(e.ctrlKey && isSelected){
24751 this.deselectRow(rowIndex);
24752 }else if(!isSelected){
24753 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24754 view.focusRow(rowIndex);
24758 this.fireEvent("afterselectionchange", this);
24761 handleDragableRowClick : function(grid, rowIndex, e)
24763 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24764 this.selectRow(rowIndex, false);
24765 grid.view.focusRow(rowIndex);
24766 this.fireEvent("afterselectionchange", this);
24771 * Selects multiple rows.
24772 * @param {Array} rows Array of the indexes of the row to select
24773 * @param {Boolean} keepExisting (optional) True to keep existing selections
24775 selectRows : function(rows, keepExisting){
24777 this.clearSelections();
24779 for(var i = 0, len = rows.length; i < len; i++){
24780 this.selectRow(rows[i], true);
24785 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24786 * @param {Number} startRow The index of the first row in the range
24787 * @param {Number} endRow The index of the last row in the range
24788 * @param {Boolean} keepExisting (optional) True to retain existing selections
24790 selectRange : function(startRow, endRow, keepExisting){
24795 this.clearSelections();
24797 if(startRow <= endRow){
24798 for(var i = startRow; i <= endRow; i++){
24799 this.selectRow(i, true);
24802 for(var i = startRow; i >= endRow; i--){
24803 this.selectRow(i, true);
24809 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24810 * @param {Number} startRow The index of the first row in the range
24811 * @param {Number} endRow The index of the last row in the range
24813 deselectRange : function(startRow, endRow, preventViewNotify){
24817 for(var i = startRow; i <= endRow; i++){
24818 this.deselectRow(i, preventViewNotify);
24824 * @param {Number} row The index of the row to select
24825 * @param {Boolean} keepExisting (optional) True to keep existing selections
24827 selectRow : function(index, keepExisting, preventViewNotify)
24829 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24832 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24833 if(!keepExisting || this.singleSelect){
24834 this.clearSelections();
24837 var r = this.grid.store.getAt(index);
24838 //console.log('selectRow - record id :' + r.id);
24840 this.selections.add(r);
24841 this.last = this.lastActive = index;
24842 if(!preventViewNotify){
24843 var proxy = new Roo.Element(
24844 this.grid.getRowDom(index)
24846 proxy.addClass('bg-info info');
24848 this.fireEvent("rowselect", this, index, r);
24849 this.fireEvent("selectionchange", this);
24855 * @param {Number} row The index of the row to deselect
24857 deselectRow : function(index, preventViewNotify)
24862 if(this.last == index){
24865 if(this.lastActive == index){
24866 this.lastActive = false;
24869 var r = this.grid.store.getAt(index);
24874 this.selections.remove(r);
24875 //.console.log('deselectRow - record id :' + r.id);
24876 if(!preventViewNotify){
24878 var proxy = new Roo.Element(
24879 this.grid.getRowDom(index)
24881 proxy.removeClass('bg-info info');
24883 this.fireEvent("rowdeselect", this, index);
24884 this.fireEvent("selectionchange", this);
24888 restoreLast : function(){
24890 this.last = this._last;
24895 acceptsNav : function(row, col, cm){
24896 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24900 onEditorKey : function(field, e){
24901 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24906 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24908 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24910 }else if(k == e.ENTER && !e.ctrlKey){
24914 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24916 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24918 }else if(k == e.ESC){
24922 g.startEditing(newCell[0], newCell[1]);
24928 * Ext JS Library 1.1.1
24929 * Copyright(c) 2006-2007, Ext JS, LLC.
24931 * Originally Released Under LGPL - original licence link has changed is not relivant.
24934 * <script type="text/javascript">
24938 * @class Roo.bootstrap.PagingToolbar
24939 * @extends Roo.bootstrap.NavSimplebar
24940 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24942 * Create a new PagingToolbar
24943 * @param {Object} config The config object
24944 * @param {Roo.data.Store} store
24946 Roo.bootstrap.PagingToolbar = function(config)
24948 // old args format still supported... - xtype is prefered..
24949 // created from xtype...
24951 this.ds = config.dataSource;
24953 if (config.store && !this.ds) {
24954 this.store= Roo.factory(config.store, Roo.data);
24955 this.ds = this.store;
24956 this.ds.xmodule = this.xmodule || false;
24959 this.toolbarItems = [];
24960 if (config.items) {
24961 this.toolbarItems = config.items;
24964 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24969 this.bind(this.ds);
24972 if (Roo.bootstrap.version == 4) {
24973 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24975 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24980 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24982 * @cfg {Roo.data.Store} dataSource
24983 * The underlying data store providing the paged data
24986 * @cfg {String/HTMLElement/Element} container
24987 * container The id or element that will contain the toolbar
24990 * @cfg {Boolean} displayInfo
24991 * True to display the displayMsg (defaults to false)
24994 * @cfg {Number} pageSize
24995 * The number of records to display per page (defaults to 20)
24999 * @cfg {String} displayMsg
25000 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25002 displayMsg : 'Displaying {0} - {1} of {2}',
25004 * @cfg {String} emptyMsg
25005 * The message to display when no records are found (defaults to "No data to display")
25007 emptyMsg : 'No data to display',
25009 * Customizable piece of the default paging text (defaults to "Page")
25012 beforePageText : "Page",
25014 * Customizable piece of the default paging text (defaults to "of %0")
25017 afterPageText : "of {0}",
25019 * Customizable piece of the default paging text (defaults to "First Page")
25022 firstText : "First Page",
25024 * Customizable piece of the default paging text (defaults to "Previous Page")
25027 prevText : "Previous Page",
25029 * Customizable piece of the default paging text (defaults to "Next Page")
25032 nextText : "Next Page",
25034 * Customizable piece of the default paging text (defaults to "Last Page")
25037 lastText : "Last Page",
25039 * Customizable piece of the default paging text (defaults to "Refresh")
25042 refreshText : "Refresh",
25046 onRender : function(ct, position)
25048 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25049 this.navgroup.parentId = this.id;
25050 this.navgroup.onRender(this.el, null);
25051 // add the buttons to the navgroup
25053 if(this.displayInfo){
25054 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25055 this.displayEl = this.el.select('.x-paging-info', true).first();
25056 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25057 // this.displayEl = navel.el.select('span',true).first();
25063 Roo.each(_this.buttons, function(e){ // this might need to use render????
25064 Roo.factory(e).render(_this.el);
25068 Roo.each(_this.toolbarItems, function(e) {
25069 _this.navgroup.addItem(e);
25073 this.first = this.navgroup.addItem({
25074 tooltip: this.firstText,
25075 cls: "prev btn-outline-secondary",
25076 html : ' <i class="fa fa-step-backward"></i>',
25078 preventDefault: true,
25079 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25082 this.prev = this.navgroup.addItem({
25083 tooltip: this.prevText,
25084 cls: "prev btn-outline-secondary",
25085 html : ' <i class="fa fa-backward"></i>',
25087 preventDefault: true,
25088 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25090 //this.addSeparator();
25093 var field = this.navgroup.addItem( {
25095 cls : 'x-paging-position btn-outline-secondary',
25097 html : this.beforePageText +
25098 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25099 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25102 this.field = field.el.select('input', true).first();
25103 this.field.on("keydown", this.onPagingKeydown, this);
25104 this.field.on("focus", function(){this.dom.select();});
25107 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25108 //this.field.setHeight(18);
25109 //this.addSeparator();
25110 this.next = this.navgroup.addItem({
25111 tooltip: this.nextText,
25112 cls: "next btn-outline-secondary",
25113 html : ' <i class="fa fa-forward"></i>',
25115 preventDefault: true,
25116 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25118 this.last = this.navgroup.addItem({
25119 tooltip: this.lastText,
25120 html : ' <i class="fa fa-step-forward"></i>',
25121 cls: "next btn-outline-secondary",
25123 preventDefault: true,
25124 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25126 //this.addSeparator();
25127 this.loading = this.navgroup.addItem({
25128 tooltip: this.refreshText,
25129 cls: "btn-outline-secondary",
25130 html : ' <i class="fa fa-refresh"></i>',
25131 preventDefault: true,
25132 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25138 updateInfo : function(){
25139 if(this.displayEl){
25140 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25141 var msg = count == 0 ?
25145 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25147 this.displayEl.update(msg);
25152 onLoad : function(ds, r, o)
25154 this.cursor = o.params.start ? o.params.start : 0;
25156 var d = this.getPageData(),
25161 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25162 this.field.dom.value = ap;
25163 this.first.setDisabled(ap == 1);
25164 this.prev.setDisabled(ap == 1);
25165 this.next.setDisabled(ap == ps);
25166 this.last.setDisabled(ap == ps);
25167 this.loading.enable();
25172 getPageData : function(){
25173 var total = this.ds.getTotalCount();
25176 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25177 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25182 onLoadError : function(){
25183 this.loading.enable();
25187 onPagingKeydown : function(e){
25188 var k = e.getKey();
25189 var d = this.getPageData();
25191 var v = this.field.dom.value, pageNum;
25192 if(!v || isNaN(pageNum = parseInt(v, 10))){
25193 this.field.dom.value = d.activePage;
25196 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25197 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25200 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))
25202 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25203 this.field.dom.value = pageNum;
25204 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25207 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25209 var v = this.field.dom.value, pageNum;
25210 var increment = (e.shiftKey) ? 10 : 1;
25211 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25214 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25215 this.field.dom.value = d.activePage;
25218 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25220 this.field.dom.value = parseInt(v, 10) + increment;
25221 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25222 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25229 beforeLoad : function(){
25231 this.loading.disable();
25236 onClick : function(which){
25245 ds.load({params:{start: 0, limit: this.pageSize}});
25248 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25251 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25254 var total = ds.getTotalCount();
25255 var extra = total % this.pageSize;
25256 var lastStart = extra ? (total - extra) : total-this.pageSize;
25257 ds.load({params:{start: lastStart, limit: this.pageSize}});
25260 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25266 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25267 * @param {Roo.data.Store} store The data store to unbind
25269 unbind : function(ds){
25270 ds.un("beforeload", this.beforeLoad, this);
25271 ds.un("load", this.onLoad, this);
25272 ds.un("loadexception", this.onLoadError, this);
25273 ds.un("remove", this.updateInfo, this);
25274 ds.un("add", this.updateInfo, this);
25275 this.ds = undefined;
25279 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25280 * @param {Roo.data.Store} store The data store to bind
25282 bind : function(ds){
25283 ds.on("beforeload", this.beforeLoad, this);
25284 ds.on("load", this.onLoad, this);
25285 ds.on("loadexception", this.onLoadError, this);
25286 ds.on("remove", this.updateInfo, this);
25287 ds.on("add", this.updateInfo, this);
25298 * @class Roo.bootstrap.MessageBar
25299 * @extends Roo.bootstrap.Component
25300 * Bootstrap MessageBar class
25301 * @cfg {String} html contents of the MessageBar
25302 * @cfg {String} weight (info | success | warning | danger) default info
25303 * @cfg {String} beforeClass insert the bar before the given class
25304 * @cfg {Boolean} closable (true | false) default false
25305 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25308 * Create a new Element
25309 * @param {Object} config The config object
25312 Roo.bootstrap.MessageBar = function(config){
25313 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25316 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25322 beforeClass: 'bootstrap-sticky-wrap',
25324 getAutoCreate : function(){
25328 cls: 'alert alert-dismissable alert-' + this.weight,
25333 html: this.html || ''
25339 cfg.cls += ' alert-messages-fixed';
25353 onRender : function(ct, position)
25355 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25358 var cfg = Roo.apply({}, this.getAutoCreate());
25362 cfg.cls += ' ' + this.cls;
25365 cfg.style = this.style;
25367 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25369 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25372 this.el.select('>button.close').on('click', this.hide, this);
25378 if (!this.rendered) {
25384 this.fireEvent('show', this);
25390 if (!this.rendered) {
25396 this.fireEvent('hide', this);
25399 update : function()
25401 // var e = this.el.dom.firstChild;
25403 // if(this.closable){
25404 // e = e.nextSibling;
25407 // e.data = this.html || '';
25409 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25425 * @class Roo.bootstrap.Graph
25426 * @extends Roo.bootstrap.Component
25427 * Bootstrap Graph class
25431 @cfg {String} graphtype bar | vbar | pie
25432 @cfg {number} g_x coodinator | centre x (pie)
25433 @cfg {number} g_y coodinator | centre y (pie)
25434 @cfg {number} g_r radius (pie)
25435 @cfg {number} g_height height of the chart (respected by all elements in the set)
25436 @cfg {number} g_width width of the chart (respected by all elements in the set)
25437 @cfg {Object} title The title of the chart
25440 -opts (object) options for the chart
25442 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25443 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25445 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.
25446 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25448 o stretch (boolean)
25450 -opts (object) options for the pie
25453 o startAngle (number)
25454 o endAngle (number)
25458 * Create a new Input
25459 * @param {Object} config The config object
25462 Roo.bootstrap.Graph = function(config){
25463 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25469 * The img click event for the img.
25470 * @param {Roo.EventObject} e
25476 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25487 //g_colors: this.colors,
25494 getAutoCreate : function(){
25505 onRender : function(ct,position){
25508 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25510 if (typeof(Raphael) == 'undefined') {
25511 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25515 this.raphael = Raphael(this.el.dom);
25517 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25518 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25519 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25520 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25522 r.text(160, 10, "Single Series Chart").attr(txtattr);
25523 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25524 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25525 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25527 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25528 r.barchart(330, 10, 300, 220, data1);
25529 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25530 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25533 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25534 // r.barchart(30, 30, 560, 250, xdata, {
25535 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25536 // axis : "0 0 1 1",
25537 // axisxlabels : xdata
25538 // //yvalues : cols,
25541 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25543 // this.load(null,xdata,{
25544 // axis : "0 0 1 1",
25545 // axisxlabels : xdata
25550 load : function(graphtype,xdata,opts)
25552 this.raphael.clear();
25554 graphtype = this.graphtype;
25559 var r = this.raphael,
25560 fin = function () {
25561 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25563 fout = function () {
25564 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25566 pfin = function() {
25567 this.sector.stop();
25568 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25571 this.label[0].stop();
25572 this.label[0].attr({ r: 7.5 });
25573 this.label[1].attr({ "font-weight": 800 });
25576 pfout = function() {
25577 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25580 this.label[0].animate({ r: 5 }, 500, "bounce");
25581 this.label[1].attr({ "font-weight": 400 });
25587 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25590 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25593 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25594 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25596 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25603 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25608 setTitle: function(o)
25613 initEvents: function() {
25616 this.el.on('click', this.onClick, this);
25620 onClick : function(e)
25622 Roo.log('img onclick');
25623 this.fireEvent('click', this, e);
25635 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25638 * @class Roo.bootstrap.dash.NumberBox
25639 * @extends Roo.bootstrap.Component
25640 * Bootstrap NumberBox class
25641 * @cfg {String} headline Box headline
25642 * @cfg {String} content Box content
25643 * @cfg {String} icon Box icon
25644 * @cfg {String} footer Footer text
25645 * @cfg {String} fhref Footer href
25648 * Create a new NumberBox
25649 * @param {Object} config The config object
25653 Roo.bootstrap.dash.NumberBox = function(config){
25654 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25658 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25667 getAutoCreate : function(){
25671 cls : 'small-box ',
25679 cls : 'roo-headline',
25680 html : this.headline
25684 cls : 'roo-content',
25685 html : this.content
25699 cls : 'ion ' + this.icon
25708 cls : 'small-box-footer',
25709 href : this.fhref || '#',
25713 cfg.cn.push(footer);
25720 onRender : function(ct,position){
25721 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25728 setHeadline: function (value)
25730 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25733 setFooter: function (value, href)
25735 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25738 this.el.select('a.small-box-footer',true).first().attr('href', href);
25743 setContent: function (value)
25745 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25748 initEvents: function()
25762 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25765 * @class Roo.bootstrap.dash.TabBox
25766 * @extends Roo.bootstrap.Component
25767 * Bootstrap TabBox class
25768 * @cfg {String} title Title of the TabBox
25769 * @cfg {String} icon Icon of the TabBox
25770 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25771 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25774 * Create a new TabBox
25775 * @param {Object} config The config object
25779 Roo.bootstrap.dash.TabBox = function(config){
25780 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25785 * When a pane is added
25786 * @param {Roo.bootstrap.dash.TabPane} pane
25790 * @event activatepane
25791 * When a pane is activated
25792 * @param {Roo.bootstrap.dash.TabPane} pane
25794 "activatepane" : true
25802 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25807 tabScrollable : false,
25809 getChildContainer : function()
25811 return this.el.select('.tab-content', true).first();
25814 getAutoCreate : function(){
25818 cls: 'pull-left header',
25826 cls: 'fa ' + this.icon
25832 cls: 'nav nav-tabs pull-right',
25838 if(this.tabScrollable){
25845 cls: 'nav nav-tabs pull-right',
25856 cls: 'nav-tabs-custom',
25861 cls: 'tab-content no-padding',
25869 initEvents : function()
25871 //Roo.log('add add pane handler');
25872 this.on('addpane', this.onAddPane, this);
25875 * Updates the box title
25876 * @param {String} html to set the title to.
25878 setTitle : function(value)
25880 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25882 onAddPane : function(pane)
25884 this.panes.push(pane);
25885 //Roo.log('addpane');
25887 // tabs are rendere left to right..
25888 if(!this.showtabs){
25892 var ctr = this.el.select('.nav-tabs', true).first();
25895 var existing = ctr.select('.nav-tab',true);
25896 var qty = existing.getCount();;
25899 var tab = ctr.createChild({
25901 cls : 'nav-tab' + (qty ? '' : ' active'),
25909 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25912 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25914 pane.el.addClass('active');
25919 onTabClick : function(ev,un,ob,pane)
25921 //Roo.log('tab - prev default');
25922 ev.preventDefault();
25925 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25926 pane.tab.addClass('active');
25927 //Roo.log(pane.title);
25928 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25929 // technically we should have a deactivate event.. but maybe add later.
25930 // and it should not de-activate the selected tab...
25931 this.fireEvent('activatepane', pane);
25932 pane.el.addClass('active');
25933 pane.fireEvent('activate');
25938 getActivePane : function()
25941 Roo.each(this.panes, function(p) {
25942 if(p.el.hasClass('active')){
25963 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25965 * @class Roo.bootstrap.TabPane
25966 * @extends Roo.bootstrap.Component
25967 * Bootstrap TabPane class
25968 * @cfg {Boolean} active (false | true) Default false
25969 * @cfg {String} title title of panel
25973 * Create a new TabPane
25974 * @param {Object} config The config object
25977 Roo.bootstrap.dash.TabPane = function(config){
25978 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25984 * When a pane is activated
25985 * @param {Roo.bootstrap.dash.TabPane} pane
25992 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25997 // the tabBox that this is attached to.
26000 getAutoCreate : function()
26008 cfg.cls += ' active';
26013 initEvents : function()
26015 //Roo.log('trigger add pane handler');
26016 this.parent().fireEvent('addpane', this)
26020 * Updates the tab title
26021 * @param {String} html to set the title to.
26023 setTitle: function(str)
26029 this.tab.select('a', true).first().dom.innerHTML = str;
26046 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26049 * @class Roo.bootstrap.menu.Menu
26050 * @extends Roo.bootstrap.Component
26051 * Bootstrap Menu class - container for Menu
26052 * @cfg {String} html Text of the menu
26053 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26054 * @cfg {String} icon Font awesome icon
26055 * @cfg {String} pos Menu align to (top | bottom) default bottom
26059 * Create a new Menu
26060 * @param {Object} config The config object
26064 Roo.bootstrap.menu.Menu = function(config){
26065 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26069 * @event beforeshow
26070 * Fires before this menu is displayed
26071 * @param {Roo.bootstrap.menu.Menu} this
26075 * @event beforehide
26076 * Fires before this menu is hidden
26077 * @param {Roo.bootstrap.menu.Menu} this
26082 * Fires after this menu is displayed
26083 * @param {Roo.bootstrap.menu.Menu} this
26088 * Fires after this menu is hidden
26089 * @param {Roo.bootstrap.menu.Menu} this
26094 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26095 * @param {Roo.bootstrap.menu.Menu} this
26096 * @param {Roo.EventObject} e
26103 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26107 weight : 'default',
26112 getChildContainer : function() {
26113 if(this.isSubMenu){
26117 return this.el.select('ul.dropdown-menu', true).first();
26120 getAutoCreate : function()
26125 cls : 'roo-menu-text',
26133 cls : 'fa ' + this.icon
26144 cls : 'dropdown-button btn btn-' + this.weight,
26149 cls : 'dropdown-toggle btn btn-' + this.weight,
26159 cls : 'dropdown-menu'
26165 if(this.pos == 'top'){
26166 cfg.cls += ' dropup';
26169 if(this.isSubMenu){
26172 cls : 'dropdown-menu'
26179 onRender : function(ct, position)
26181 this.isSubMenu = ct.hasClass('dropdown-submenu');
26183 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26186 initEvents : function()
26188 if(this.isSubMenu){
26192 this.hidden = true;
26194 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26195 this.triggerEl.on('click', this.onTriggerPress, this);
26197 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26198 this.buttonEl.on('click', this.onClick, this);
26204 if(this.isSubMenu){
26208 return this.el.select('ul.dropdown-menu', true).first();
26211 onClick : function(e)
26213 this.fireEvent("click", this, e);
26216 onTriggerPress : function(e)
26218 if (this.isVisible()) {
26225 isVisible : function(){
26226 return !this.hidden;
26231 this.fireEvent("beforeshow", this);
26233 this.hidden = false;
26234 this.el.addClass('open');
26236 Roo.get(document).on("mouseup", this.onMouseUp, this);
26238 this.fireEvent("show", this);
26245 this.fireEvent("beforehide", this);
26247 this.hidden = true;
26248 this.el.removeClass('open');
26250 Roo.get(document).un("mouseup", this.onMouseUp);
26252 this.fireEvent("hide", this);
26255 onMouseUp : function()
26269 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26272 * @class Roo.bootstrap.menu.Item
26273 * @extends Roo.bootstrap.Component
26274 * Bootstrap MenuItem class
26275 * @cfg {Boolean} submenu (true | false) default false
26276 * @cfg {String} html text of the item
26277 * @cfg {String} href the link
26278 * @cfg {Boolean} disable (true | false) default false
26279 * @cfg {Boolean} preventDefault (true | false) default true
26280 * @cfg {String} icon Font awesome icon
26281 * @cfg {String} pos Submenu align to (left | right) default right
26285 * Create a new Item
26286 * @param {Object} config The config object
26290 Roo.bootstrap.menu.Item = function(config){
26291 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26295 * Fires when the mouse is hovering over this menu
26296 * @param {Roo.bootstrap.menu.Item} this
26297 * @param {Roo.EventObject} e
26302 * Fires when the mouse exits this menu
26303 * @param {Roo.bootstrap.menu.Item} this
26304 * @param {Roo.EventObject} e
26310 * The raw click event for the entire grid.
26311 * @param {Roo.EventObject} e
26317 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26322 preventDefault: true,
26327 getAutoCreate : function()
26332 cls : 'roo-menu-item-text',
26340 cls : 'fa ' + this.icon
26349 href : this.href || '#',
26356 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26360 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26362 if(this.pos == 'left'){
26363 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26370 initEvents : function()
26372 this.el.on('mouseover', this.onMouseOver, this);
26373 this.el.on('mouseout', this.onMouseOut, this);
26375 this.el.select('a', true).first().on('click', this.onClick, this);
26379 onClick : function(e)
26381 if(this.preventDefault){
26382 e.preventDefault();
26385 this.fireEvent("click", this, e);
26388 onMouseOver : function(e)
26390 if(this.submenu && this.pos == 'left'){
26391 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26394 this.fireEvent("mouseover", this, e);
26397 onMouseOut : function(e)
26399 this.fireEvent("mouseout", this, e);
26411 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26414 * @class Roo.bootstrap.menu.Separator
26415 * @extends Roo.bootstrap.Component
26416 * Bootstrap Separator class
26419 * Create a new Separator
26420 * @param {Object} config The config object
26424 Roo.bootstrap.menu.Separator = function(config){
26425 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26428 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26430 getAutoCreate : function(){
26451 * @class Roo.bootstrap.Tooltip
26452 * Bootstrap Tooltip class
26453 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26454 * to determine which dom element triggers the tooltip.
26456 * It needs to add support for additional attributes like tooltip-position
26459 * Create a new Toolti
26460 * @param {Object} config The config object
26463 Roo.bootstrap.Tooltip = function(config){
26464 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26466 this.alignment = Roo.bootstrap.Tooltip.alignment;
26468 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26469 this.alignment = config.alignment;
26474 Roo.apply(Roo.bootstrap.Tooltip, {
26476 * @function init initialize tooltip monitoring.
26480 currentTip : false,
26481 currentRegion : false,
26487 Roo.get(document).on('mouseover', this.enter ,this);
26488 Roo.get(document).on('mouseout', this.leave, this);
26491 this.currentTip = new Roo.bootstrap.Tooltip();
26494 enter : function(ev)
26496 var dom = ev.getTarget();
26498 //Roo.log(['enter',dom]);
26499 var el = Roo.fly(dom);
26500 if (this.currentEl) {
26502 //Roo.log(this.currentEl);
26503 //Roo.log(this.currentEl.contains(dom));
26504 if (this.currentEl == el) {
26507 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26513 if (this.currentTip.el) {
26514 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26518 if(!el || el.dom == document){
26524 // you can not look for children, as if el is the body.. then everythign is the child..
26525 if (!el.attr('tooltip')) { //
26526 if (!el.select("[tooltip]").elements.length) {
26529 // is the mouse over this child...?
26530 bindEl = el.select("[tooltip]").first();
26531 var xy = ev.getXY();
26532 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26533 //Roo.log("not in region.");
26536 //Roo.log("child element over..");
26539 this.currentEl = bindEl;
26540 this.currentTip.bind(bindEl);
26541 this.currentRegion = Roo.lib.Region.getRegion(dom);
26542 this.currentTip.enter();
26545 leave : function(ev)
26547 var dom = ev.getTarget();
26548 //Roo.log(['leave',dom]);
26549 if (!this.currentEl) {
26554 if (dom != this.currentEl.dom) {
26557 var xy = ev.getXY();
26558 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26561 // only activate leave if mouse cursor is outside... bounding box..
26566 if (this.currentTip) {
26567 this.currentTip.leave();
26569 //Roo.log('clear currentEl');
26570 this.currentEl = false;
26575 'left' : ['r-l', [-2,0], 'right'],
26576 'right' : ['l-r', [2,0], 'left'],
26577 'bottom' : ['t-b', [0,2], 'top'],
26578 'top' : [ 'b-t', [0,-2], 'bottom']
26584 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26589 delay : null, // can be { show : 300 , hide: 500}
26593 hoverState : null, //???
26595 placement : 'bottom',
26599 getAutoCreate : function(){
26606 cls : 'tooltip-arrow'
26609 cls : 'tooltip-inner'
26616 bind : function(el)
26622 enter : function () {
26624 if (this.timeout != null) {
26625 clearTimeout(this.timeout);
26628 this.hoverState = 'in';
26629 //Roo.log("enter - show");
26630 if (!this.delay || !this.delay.show) {
26635 this.timeout = setTimeout(function () {
26636 if (_t.hoverState == 'in') {
26639 }, this.delay.show);
26643 clearTimeout(this.timeout);
26645 this.hoverState = 'out';
26646 if (!this.delay || !this.delay.hide) {
26652 this.timeout = setTimeout(function () {
26653 //Roo.log("leave - timeout");
26655 if (_t.hoverState == 'out') {
26657 Roo.bootstrap.Tooltip.currentEl = false;
26662 show : function (msg)
26665 this.render(document.body);
26668 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26670 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26672 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26674 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26676 var placement = typeof this.placement == 'function' ?
26677 this.placement.call(this, this.el, on_el) :
26680 var autoToken = /\s?auto?\s?/i;
26681 var autoPlace = autoToken.test(placement);
26683 placement = placement.replace(autoToken, '') || 'top';
26687 //this.el.setXY([0,0]);
26689 //this.el.dom.style.display='block';
26691 //this.el.appendTo(on_el);
26693 var p = this.getPosition();
26694 var box = this.el.getBox();
26700 var align = this.alignment[placement];
26702 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26704 if(placement == 'top' || placement == 'bottom'){
26706 placement = 'right';
26709 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26710 placement = 'left';
26713 var scroll = Roo.select('body', true).first().getScroll();
26715 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26719 align = this.alignment[placement];
26722 this.el.alignTo(this.bindEl, align[0],align[1]);
26723 //var arrow = this.el.select('.arrow',true).first();
26724 //arrow.set(align[2],
26726 this.el.addClass(placement);
26728 this.el.addClass('in fade');
26730 this.hoverState = null;
26732 if (this.el.hasClass('fade')) {
26743 //this.el.setXY([0,0]);
26744 this.el.removeClass('in');
26760 * @class Roo.bootstrap.LocationPicker
26761 * @extends Roo.bootstrap.Component
26762 * Bootstrap LocationPicker class
26763 * @cfg {Number} latitude Position when init default 0
26764 * @cfg {Number} longitude Position when init default 0
26765 * @cfg {Number} zoom default 15
26766 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26767 * @cfg {Boolean} mapTypeControl default false
26768 * @cfg {Boolean} disableDoubleClickZoom default false
26769 * @cfg {Boolean} scrollwheel default true
26770 * @cfg {Boolean} streetViewControl default false
26771 * @cfg {Number} radius default 0
26772 * @cfg {String} locationName
26773 * @cfg {Boolean} draggable default true
26774 * @cfg {Boolean} enableAutocomplete default false
26775 * @cfg {Boolean} enableReverseGeocode default true
26776 * @cfg {String} markerTitle
26779 * Create a new LocationPicker
26780 * @param {Object} config The config object
26784 Roo.bootstrap.LocationPicker = function(config){
26786 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26791 * Fires when the picker initialized.
26792 * @param {Roo.bootstrap.LocationPicker} this
26793 * @param {Google Location} location
26797 * @event positionchanged
26798 * Fires when the picker position changed.
26799 * @param {Roo.bootstrap.LocationPicker} this
26800 * @param {Google Location} location
26802 positionchanged : true,
26805 * Fires when the map resize.
26806 * @param {Roo.bootstrap.LocationPicker} this
26811 * Fires when the map show.
26812 * @param {Roo.bootstrap.LocationPicker} this
26817 * Fires when the map hide.
26818 * @param {Roo.bootstrap.LocationPicker} this
26823 * Fires when click the map.
26824 * @param {Roo.bootstrap.LocationPicker} this
26825 * @param {Map event} e
26829 * @event mapRightClick
26830 * Fires when right click the map.
26831 * @param {Roo.bootstrap.LocationPicker} this
26832 * @param {Map event} e
26834 mapRightClick : true,
26836 * @event markerClick
26837 * Fires when click the marker.
26838 * @param {Roo.bootstrap.LocationPicker} this
26839 * @param {Map event} e
26841 markerClick : true,
26843 * @event markerRightClick
26844 * Fires when right click the marker.
26845 * @param {Roo.bootstrap.LocationPicker} this
26846 * @param {Map event} e
26848 markerRightClick : true,
26850 * @event OverlayViewDraw
26851 * Fires when OverlayView Draw
26852 * @param {Roo.bootstrap.LocationPicker} this
26854 OverlayViewDraw : true,
26856 * @event OverlayViewOnAdd
26857 * Fires when OverlayView Draw
26858 * @param {Roo.bootstrap.LocationPicker} this
26860 OverlayViewOnAdd : true,
26862 * @event OverlayViewOnRemove
26863 * Fires when OverlayView Draw
26864 * @param {Roo.bootstrap.LocationPicker} this
26866 OverlayViewOnRemove : true,
26868 * @event OverlayViewShow
26869 * Fires when OverlayView Draw
26870 * @param {Roo.bootstrap.LocationPicker} this
26871 * @param {Pixel} cpx
26873 OverlayViewShow : true,
26875 * @event OverlayViewHide
26876 * Fires when OverlayView Draw
26877 * @param {Roo.bootstrap.LocationPicker} this
26879 OverlayViewHide : true,
26881 * @event loadexception
26882 * Fires when load google lib failed.
26883 * @param {Roo.bootstrap.LocationPicker} this
26885 loadexception : true
26890 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26892 gMapContext: false,
26898 mapTypeControl: false,
26899 disableDoubleClickZoom: false,
26901 streetViewControl: false,
26905 enableAutocomplete: false,
26906 enableReverseGeocode: true,
26909 getAutoCreate: function()
26914 cls: 'roo-location-picker'
26920 initEvents: function(ct, position)
26922 if(!this.el.getWidth() || this.isApplied()){
26926 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26931 initial: function()
26933 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26934 this.fireEvent('loadexception', this);
26938 if(!this.mapTypeId){
26939 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26942 this.gMapContext = this.GMapContext();
26944 this.initOverlayView();
26946 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26950 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26951 _this.setPosition(_this.gMapContext.marker.position);
26954 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26955 _this.fireEvent('mapClick', this, event);
26959 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26960 _this.fireEvent('mapRightClick', this, event);
26964 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26965 _this.fireEvent('markerClick', this, event);
26969 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26970 _this.fireEvent('markerRightClick', this, event);
26974 this.setPosition(this.gMapContext.location);
26976 this.fireEvent('initial', this, this.gMapContext.location);
26979 initOverlayView: function()
26983 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26987 _this.fireEvent('OverlayViewDraw', _this);
26992 _this.fireEvent('OverlayViewOnAdd', _this);
26995 onRemove: function()
26997 _this.fireEvent('OverlayViewOnRemove', _this);
27000 show: function(cpx)
27002 _this.fireEvent('OverlayViewShow', _this, cpx);
27007 _this.fireEvent('OverlayViewHide', _this);
27013 fromLatLngToContainerPixel: function(event)
27015 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27018 isApplied: function()
27020 return this.getGmapContext() == false ? false : true;
27023 getGmapContext: function()
27025 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27028 GMapContext: function()
27030 var position = new google.maps.LatLng(this.latitude, this.longitude);
27032 var _map = new google.maps.Map(this.el.dom, {
27035 mapTypeId: this.mapTypeId,
27036 mapTypeControl: this.mapTypeControl,
27037 disableDoubleClickZoom: this.disableDoubleClickZoom,
27038 scrollwheel: this.scrollwheel,
27039 streetViewControl: this.streetViewControl,
27040 locationName: this.locationName,
27041 draggable: this.draggable,
27042 enableAutocomplete: this.enableAutocomplete,
27043 enableReverseGeocode: this.enableReverseGeocode
27046 var _marker = new google.maps.Marker({
27047 position: position,
27049 title: this.markerTitle,
27050 draggable: this.draggable
27057 location: position,
27058 radius: this.radius,
27059 locationName: this.locationName,
27060 addressComponents: {
27061 formatted_address: null,
27062 addressLine1: null,
27063 addressLine2: null,
27065 streetNumber: null,
27069 stateOrProvince: null
27072 domContainer: this.el.dom,
27073 geodecoder: new google.maps.Geocoder()
27077 drawCircle: function(center, radius, options)
27079 if (this.gMapContext.circle != null) {
27080 this.gMapContext.circle.setMap(null);
27084 options = Roo.apply({}, options, {
27085 strokeColor: "#0000FF",
27086 strokeOpacity: .35,
27088 fillColor: "#0000FF",
27092 options.map = this.gMapContext.map;
27093 options.radius = radius;
27094 options.center = center;
27095 this.gMapContext.circle = new google.maps.Circle(options);
27096 return this.gMapContext.circle;
27102 setPosition: function(location)
27104 this.gMapContext.location = location;
27105 this.gMapContext.marker.setPosition(location);
27106 this.gMapContext.map.panTo(location);
27107 this.drawCircle(location, this.gMapContext.radius, {});
27111 if (this.gMapContext.settings.enableReverseGeocode) {
27112 this.gMapContext.geodecoder.geocode({
27113 latLng: this.gMapContext.location
27114 }, function(results, status) {
27116 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27117 _this.gMapContext.locationName = results[0].formatted_address;
27118 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27120 _this.fireEvent('positionchanged', this, location);
27127 this.fireEvent('positionchanged', this, location);
27132 google.maps.event.trigger(this.gMapContext.map, "resize");
27134 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27136 this.fireEvent('resize', this);
27139 setPositionByLatLng: function(latitude, longitude)
27141 this.setPosition(new google.maps.LatLng(latitude, longitude));
27144 getCurrentPosition: function()
27147 latitude: this.gMapContext.location.lat(),
27148 longitude: this.gMapContext.location.lng()
27152 getAddressName: function()
27154 return this.gMapContext.locationName;
27157 getAddressComponents: function()
27159 return this.gMapContext.addressComponents;
27162 address_component_from_google_geocode: function(address_components)
27166 for (var i = 0; i < address_components.length; i++) {
27167 var component = address_components[i];
27168 if (component.types.indexOf("postal_code") >= 0) {
27169 result.postalCode = component.short_name;
27170 } else if (component.types.indexOf("street_number") >= 0) {
27171 result.streetNumber = component.short_name;
27172 } else if (component.types.indexOf("route") >= 0) {
27173 result.streetName = component.short_name;
27174 } else if (component.types.indexOf("neighborhood") >= 0) {
27175 result.city = component.short_name;
27176 } else if (component.types.indexOf("locality") >= 0) {
27177 result.city = component.short_name;
27178 } else if (component.types.indexOf("sublocality") >= 0) {
27179 result.district = component.short_name;
27180 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27181 result.stateOrProvince = component.short_name;
27182 } else if (component.types.indexOf("country") >= 0) {
27183 result.country = component.short_name;
27187 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27188 result.addressLine2 = "";
27192 setZoomLevel: function(zoom)
27194 this.gMapContext.map.setZoom(zoom);
27207 this.fireEvent('show', this);
27218 this.fireEvent('hide', this);
27223 Roo.apply(Roo.bootstrap.LocationPicker, {
27225 OverlayView : function(map, options)
27227 options = options || {};
27234 * @class Roo.bootstrap.Alert
27235 * @extends Roo.bootstrap.Component
27236 * Bootstrap Alert class - shows an alert area box
27238 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27239 Enter a valid email address
27242 * @cfg {String} title The title of alert
27243 * @cfg {String} html The content of alert
27244 * @cfg {String} weight ( success | info | warning | danger )
27245 * @cfg {String} faicon font-awesomeicon
27248 * Create a new alert
27249 * @param {Object} config The config object
27253 Roo.bootstrap.Alert = function(config){
27254 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27258 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27265 getAutoCreate : function()
27274 cls : 'roo-alert-icon'
27279 cls : 'roo-alert-title',
27284 cls : 'roo-alert-text',
27291 cfg.cn[0].cls += ' fa ' + this.faicon;
27295 cfg.cls += ' alert-' + this.weight;
27301 initEvents: function()
27303 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27306 setTitle : function(str)
27308 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27311 setText : function(str)
27313 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27316 setWeight : function(weight)
27319 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27322 this.weight = weight;
27324 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27327 setIcon : function(icon)
27330 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27333 this.faicon = icon;
27335 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27356 * @class Roo.bootstrap.UploadCropbox
27357 * @extends Roo.bootstrap.Component
27358 * Bootstrap UploadCropbox class
27359 * @cfg {String} emptyText show when image has been loaded
27360 * @cfg {String} rotateNotify show when image too small to rotate
27361 * @cfg {Number} errorTimeout default 3000
27362 * @cfg {Number} minWidth default 300
27363 * @cfg {Number} minHeight default 300
27364 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27365 * @cfg {Boolean} isDocument (true|false) default false
27366 * @cfg {String} url action url
27367 * @cfg {String} paramName default 'imageUpload'
27368 * @cfg {String} method default POST
27369 * @cfg {Boolean} loadMask (true|false) default true
27370 * @cfg {Boolean} loadingText default 'Loading...'
27373 * Create a new UploadCropbox
27374 * @param {Object} config The config object
27377 Roo.bootstrap.UploadCropbox = function(config){
27378 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27382 * @event beforeselectfile
27383 * Fire before select file
27384 * @param {Roo.bootstrap.UploadCropbox} this
27386 "beforeselectfile" : true,
27389 * Fire after initEvent
27390 * @param {Roo.bootstrap.UploadCropbox} this
27395 * Fire after initEvent
27396 * @param {Roo.bootstrap.UploadCropbox} this
27397 * @param {String} data
27402 * Fire when preparing the file data
27403 * @param {Roo.bootstrap.UploadCropbox} this
27404 * @param {Object} file
27409 * Fire when get exception
27410 * @param {Roo.bootstrap.UploadCropbox} this
27411 * @param {XMLHttpRequest} xhr
27413 "exception" : true,
27415 * @event beforeloadcanvas
27416 * Fire before load the canvas
27417 * @param {Roo.bootstrap.UploadCropbox} this
27418 * @param {String} src
27420 "beforeloadcanvas" : true,
27423 * Fire when trash image
27424 * @param {Roo.bootstrap.UploadCropbox} this
27429 * Fire when download the image
27430 * @param {Roo.bootstrap.UploadCropbox} this
27434 * @event footerbuttonclick
27435 * Fire when footerbuttonclick
27436 * @param {Roo.bootstrap.UploadCropbox} this
27437 * @param {String} type
27439 "footerbuttonclick" : true,
27443 * @param {Roo.bootstrap.UploadCropbox} this
27448 * Fire when rotate the image
27449 * @param {Roo.bootstrap.UploadCropbox} this
27450 * @param {String} pos
27455 * Fire when inspect the file
27456 * @param {Roo.bootstrap.UploadCropbox} this
27457 * @param {Object} file
27462 * Fire when xhr upload the file
27463 * @param {Roo.bootstrap.UploadCropbox} this
27464 * @param {Object} data
27469 * Fire when arrange the file data
27470 * @param {Roo.bootstrap.UploadCropbox} this
27471 * @param {Object} formData
27476 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27479 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27481 emptyText : 'Click to upload image',
27482 rotateNotify : 'Image is too small to rotate',
27483 errorTimeout : 3000,
27497 cropType : 'image/jpeg',
27499 canvasLoaded : false,
27500 isDocument : false,
27502 paramName : 'imageUpload',
27504 loadingText : 'Loading...',
27507 getAutoCreate : function()
27511 cls : 'roo-upload-cropbox',
27515 cls : 'roo-upload-cropbox-selector',
27520 cls : 'roo-upload-cropbox-body',
27521 style : 'cursor:pointer',
27525 cls : 'roo-upload-cropbox-preview'
27529 cls : 'roo-upload-cropbox-thumb'
27533 cls : 'roo-upload-cropbox-empty-notify',
27534 html : this.emptyText
27538 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27539 html : this.rotateNotify
27545 cls : 'roo-upload-cropbox-footer',
27548 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27558 onRender : function(ct, position)
27560 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27562 if (this.buttons.length) {
27564 Roo.each(this.buttons, function(bb) {
27566 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27568 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27574 this.maskEl = this.el;
27578 initEvents : function()
27580 this.urlAPI = (window.createObjectURL && window) ||
27581 (window.URL && URL.revokeObjectURL && URL) ||
27582 (window.webkitURL && webkitURL);
27584 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27585 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27587 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27588 this.selectorEl.hide();
27590 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27591 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27593 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27594 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27595 this.thumbEl.hide();
27597 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27598 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27600 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27601 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27602 this.errorEl.hide();
27604 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27605 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27606 this.footerEl.hide();
27608 this.setThumbBoxSize();
27614 this.fireEvent('initial', this);
27621 window.addEventListener("resize", function() { _this.resize(); } );
27623 this.bodyEl.on('click', this.beforeSelectFile, this);
27626 this.bodyEl.on('touchstart', this.onTouchStart, this);
27627 this.bodyEl.on('touchmove', this.onTouchMove, this);
27628 this.bodyEl.on('touchend', this.onTouchEnd, this);
27632 this.bodyEl.on('mousedown', this.onMouseDown, this);
27633 this.bodyEl.on('mousemove', this.onMouseMove, this);
27634 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27635 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27636 Roo.get(document).on('mouseup', this.onMouseUp, this);
27639 this.selectorEl.on('change', this.onFileSelected, this);
27645 this.baseScale = 1;
27647 this.baseRotate = 1;
27648 this.dragable = false;
27649 this.pinching = false;
27652 this.cropData = false;
27653 this.notifyEl.dom.innerHTML = this.emptyText;
27655 this.selectorEl.dom.value = '';
27659 resize : function()
27661 if(this.fireEvent('resize', this) != false){
27662 this.setThumbBoxPosition();
27663 this.setCanvasPosition();
27667 onFooterButtonClick : function(e, el, o, type)
27670 case 'rotate-left' :
27671 this.onRotateLeft(e);
27673 case 'rotate-right' :
27674 this.onRotateRight(e);
27677 this.beforeSelectFile(e);
27692 this.fireEvent('footerbuttonclick', this, type);
27695 beforeSelectFile : function(e)
27697 e.preventDefault();
27699 if(this.fireEvent('beforeselectfile', this) != false){
27700 this.selectorEl.dom.click();
27704 onFileSelected : function(e)
27706 e.preventDefault();
27708 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27712 var file = this.selectorEl.dom.files[0];
27714 if(this.fireEvent('inspect', this, file) != false){
27715 this.prepare(file);
27720 trash : function(e)
27722 this.fireEvent('trash', this);
27725 download : function(e)
27727 this.fireEvent('download', this);
27730 loadCanvas : function(src)
27732 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27736 this.imageEl = document.createElement('img');
27740 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27742 this.imageEl.src = src;
27746 onLoadCanvas : function()
27748 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27749 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27751 this.bodyEl.un('click', this.beforeSelectFile, this);
27753 this.notifyEl.hide();
27754 this.thumbEl.show();
27755 this.footerEl.show();
27757 this.baseRotateLevel();
27759 if(this.isDocument){
27760 this.setThumbBoxSize();
27763 this.setThumbBoxPosition();
27765 this.baseScaleLevel();
27771 this.canvasLoaded = true;
27774 this.maskEl.unmask();
27779 setCanvasPosition : function()
27781 if(!this.canvasEl){
27785 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27786 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27788 this.previewEl.setLeft(pw);
27789 this.previewEl.setTop(ph);
27793 onMouseDown : function(e)
27797 this.dragable = true;
27798 this.pinching = false;
27800 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27801 this.dragable = false;
27805 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27806 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27810 onMouseMove : function(e)
27814 if(!this.canvasLoaded){
27818 if (!this.dragable){
27822 var minX = Math.ceil(this.thumbEl.getLeft(true));
27823 var minY = Math.ceil(this.thumbEl.getTop(true));
27825 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27826 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27828 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27829 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27831 x = x - this.mouseX;
27832 y = y - this.mouseY;
27834 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27835 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27837 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27838 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27840 this.previewEl.setLeft(bgX);
27841 this.previewEl.setTop(bgY);
27843 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27844 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27847 onMouseUp : function(e)
27851 this.dragable = false;
27854 onMouseWheel : function(e)
27858 this.startScale = this.scale;
27860 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27862 if(!this.zoomable()){
27863 this.scale = this.startScale;
27872 zoomable : function()
27874 var minScale = this.thumbEl.getWidth() / this.minWidth;
27876 if(this.minWidth < this.minHeight){
27877 minScale = this.thumbEl.getHeight() / this.minHeight;
27880 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27881 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27885 (this.rotate == 0 || this.rotate == 180) &&
27887 width > this.imageEl.OriginWidth ||
27888 height > this.imageEl.OriginHeight ||
27889 (width < this.minWidth && height < this.minHeight)
27897 (this.rotate == 90 || this.rotate == 270) &&
27899 width > this.imageEl.OriginWidth ||
27900 height > this.imageEl.OriginHeight ||
27901 (width < this.minHeight && height < this.minWidth)
27908 !this.isDocument &&
27909 (this.rotate == 0 || this.rotate == 180) &&
27911 width < this.minWidth ||
27912 width > this.imageEl.OriginWidth ||
27913 height < this.minHeight ||
27914 height > this.imageEl.OriginHeight
27921 !this.isDocument &&
27922 (this.rotate == 90 || this.rotate == 270) &&
27924 width < this.minHeight ||
27925 width > this.imageEl.OriginWidth ||
27926 height < this.minWidth ||
27927 height > this.imageEl.OriginHeight
27937 onRotateLeft : function(e)
27939 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27941 var minScale = this.thumbEl.getWidth() / this.minWidth;
27943 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27944 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27946 this.startScale = this.scale;
27948 while (this.getScaleLevel() < minScale){
27950 this.scale = this.scale + 1;
27952 if(!this.zoomable()){
27957 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27958 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27963 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27970 this.scale = this.startScale;
27972 this.onRotateFail();
27977 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27979 if(this.isDocument){
27980 this.setThumbBoxSize();
27981 this.setThumbBoxPosition();
27982 this.setCanvasPosition();
27987 this.fireEvent('rotate', this, 'left');
27991 onRotateRight : function(e)
27993 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27995 var minScale = this.thumbEl.getWidth() / this.minWidth;
27997 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27998 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28000 this.startScale = this.scale;
28002 while (this.getScaleLevel() < minScale){
28004 this.scale = this.scale + 1;
28006 if(!this.zoomable()){
28011 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28012 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28017 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28024 this.scale = this.startScale;
28026 this.onRotateFail();
28031 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28033 if(this.isDocument){
28034 this.setThumbBoxSize();
28035 this.setThumbBoxPosition();
28036 this.setCanvasPosition();
28041 this.fireEvent('rotate', this, 'right');
28044 onRotateFail : function()
28046 this.errorEl.show(true);
28050 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28055 this.previewEl.dom.innerHTML = '';
28057 var canvasEl = document.createElement("canvas");
28059 var contextEl = canvasEl.getContext("2d");
28061 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28062 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28063 var center = this.imageEl.OriginWidth / 2;
28065 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28066 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28067 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28068 center = this.imageEl.OriginHeight / 2;
28071 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28073 contextEl.translate(center, center);
28074 contextEl.rotate(this.rotate * Math.PI / 180);
28076 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28078 this.canvasEl = document.createElement("canvas");
28080 this.contextEl = this.canvasEl.getContext("2d");
28082 switch (this.rotate) {
28085 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28086 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28088 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28093 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28094 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28096 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28097 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);
28101 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28106 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28107 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28109 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28110 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);
28114 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);
28119 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28120 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28122 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28123 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28127 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);
28134 this.previewEl.appendChild(this.canvasEl);
28136 this.setCanvasPosition();
28141 if(!this.canvasLoaded){
28145 var imageCanvas = document.createElement("canvas");
28147 var imageContext = imageCanvas.getContext("2d");
28149 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28150 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28152 var center = imageCanvas.width / 2;
28154 imageContext.translate(center, center);
28156 imageContext.rotate(this.rotate * Math.PI / 180);
28158 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28160 var canvas = document.createElement("canvas");
28162 var context = canvas.getContext("2d");
28164 canvas.width = this.minWidth;
28165 canvas.height = this.minHeight;
28167 switch (this.rotate) {
28170 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28171 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28173 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28174 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28176 var targetWidth = this.minWidth - 2 * x;
28177 var targetHeight = this.minHeight - 2 * y;
28181 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28182 scale = targetWidth / width;
28185 if(x > 0 && y == 0){
28186 scale = targetHeight / height;
28189 if(x > 0 && y > 0){
28190 scale = targetWidth / width;
28192 if(width < height){
28193 scale = targetHeight / height;
28197 context.scale(scale, scale);
28199 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28200 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28202 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28203 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28205 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28210 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28211 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28213 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28214 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28216 var targetWidth = this.minWidth - 2 * x;
28217 var targetHeight = this.minHeight - 2 * y;
28221 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28222 scale = targetWidth / width;
28225 if(x > 0 && y == 0){
28226 scale = targetHeight / height;
28229 if(x > 0 && y > 0){
28230 scale = targetWidth / width;
28232 if(width < height){
28233 scale = targetHeight / height;
28237 context.scale(scale, scale);
28239 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28240 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28242 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28243 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28245 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28247 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28252 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28253 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28255 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28256 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28258 var targetWidth = this.minWidth - 2 * x;
28259 var targetHeight = this.minHeight - 2 * y;
28263 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28264 scale = targetWidth / width;
28267 if(x > 0 && y == 0){
28268 scale = targetHeight / height;
28271 if(x > 0 && y > 0){
28272 scale = targetWidth / width;
28274 if(width < height){
28275 scale = targetHeight / height;
28279 context.scale(scale, scale);
28281 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28282 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28284 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28285 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28287 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28288 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28290 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28295 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28296 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28298 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28299 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28301 var targetWidth = this.minWidth - 2 * x;
28302 var targetHeight = this.minHeight - 2 * y;
28306 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28307 scale = targetWidth / width;
28310 if(x > 0 && y == 0){
28311 scale = targetHeight / height;
28314 if(x > 0 && y > 0){
28315 scale = targetWidth / width;
28317 if(width < height){
28318 scale = targetHeight / height;
28322 context.scale(scale, scale);
28324 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28325 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28327 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28328 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28330 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28332 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28339 this.cropData = canvas.toDataURL(this.cropType);
28341 if(this.fireEvent('crop', this, this.cropData) !== false){
28342 this.process(this.file, this.cropData);
28349 setThumbBoxSize : function()
28353 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28354 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28355 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28357 this.minWidth = width;
28358 this.minHeight = height;
28360 if(this.rotate == 90 || this.rotate == 270){
28361 this.minWidth = height;
28362 this.minHeight = width;
28367 width = Math.ceil(this.minWidth * height / this.minHeight);
28369 if(this.minWidth > this.minHeight){
28371 height = Math.ceil(this.minHeight * width / this.minWidth);
28374 this.thumbEl.setStyle({
28375 width : width + 'px',
28376 height : height + 'px'
28383 setThumbBoxPosition : function()
28385 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28386 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28388 this.thumbEl.setLeft(x);
28389 this.thumbEl.setTop(y);
28393 baseRotateLevel : function()
28395 this.baseRotate = 1;
28398 typeof(this.exif) != 'undefined' &&
28399 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28400 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28402 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28405 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28409 baseScaleLevel : function()
28413 if(this.isDocument){
28415 if(this.baseRotate == 6 || this.baseRotate == 8){
28417 height = this.thumbEl.getHeight();
28418 this.baseScale = height / this.imageEl.OriginWidth;
28420 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28421 width = this.thumbEl.getWidth();
28422 this.baseScale = width / this.imageEl.OriginHeight;
28428 height = this.thumbEl.getHeight();
28429 this.baseScale = height / this.imageEl.OriginHeight;
28431 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28432 width = this.thumbEl.getWidth();
28433 this.baseScale = width / this.imageEl.OriginWidth;
28439 if(this.baseRotate == 6 || this.baseRotate == 8){
28441 width = this.thumbEl.getHeight();
28442 this.baseScale = width / this.imageEl.OriginHeight;
28444 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28445 height = this.thumbEl.getWidth();
28446 this.baseScale = height / this.imageEl.OriginHeight;
28449 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28450 height = this.thumbEl.getWidth();
28451 this.baseScale = height / this.imageEl.OriginHeight;
28453 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28454 width = this.thumbEl.getHeight();
28455 this.baseScale = width / this.imageEl.OriginWidth;
28462 width = this.thumbEl.getWidth();
28463 this.baseScale = width / this.imageEl.OriginWidth;
28465 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28466 height = this.thumbEl.getHeight();
28467 this.baseScale = height / this.imageEl.OriginHeight;
28470 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28472 height = this.thumbEl.getHeight();
28473 this.baseScale = height / this.imageEl.OriginHeight;
28475 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28476 width = this.thumbEl.getWidth();
28477 this.baseScale = width / this.imageEl.OriginWidth;
28485 getScaleLevel : function()
28487 return this.baseScale * Math.pow(1.1, this.scale);
28490 onTouchStart : function(e)
28492 if(!this.canvasLoaded){
28493 this.beforeSelectFile(e);
28497 var touches = e.browserEvent.touches;
28503 if(touches.length == 1){
28504 this.onMouseDown(e);
28508 if(touches.length != 2){
28514 for(var i = 0, finger; finger = touches[i]; i++){
28515 coords.push(finger.pageX, finger.pageY);
28518 var x = Math.pow(coords[0] - coords[2], 2);
28519 var y = Math.pow(coords[1] - coords[3], 2);
28521 this.startDistance = Math.sqrt(x + y);
28523 this.startScale = this.scale;
28525 this.pinching = true;
28526 this.dragable = false;
28530 onTouchMove : function(e)
28532 if(!this.pinching && !this.dragable){
28536 var touches = e.browserEvent.touches;
28543 this.onMouseMove(e);
28549 for(var i = 0, finger; finger = touches[i]; i++){
28550 coords.push(finger.pageX, finger.pageY);
28553 var x = Math.pow(coords[0] - coords[2], 2);
28554 var y = Math.pow(coords[1] - coords[3], 2);
28556 this.endDistance = Math.sqrt(x + y);
28558 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28560 if(!this.zoomable()){
28561 this.scale = this.startScale;
28569 onTouchEnd : function(e)
28571 this.pinching = false;
28572 this.dragable = false;
28576 process : function(file, crop)
28579 this.maskEl.mask(this.loadingText);
28582 this.xhr = new XMLHttpRequest();
28584 file.xhr = this.xhr;
28586 this.xhr.open(this.method, this.url, true);
28589 "Accept": "application/json",
28590 "Cache-Control": "no-cache",
28591 "X-Requested-With": "XMLHttpRequest"
28594 for (var headerName in headers) {
28595 var headerValue = headers[headerName];
28597 this.xhr.setRequestHeader(headerName, headerValue);
28603 this.xhr.onload = function()
28605 _this.xhrOnLoad(_this.xhr);
28608 this.xhr.onerror = function()
28610 _this.xhrOnError(_this.xhr);
28613 var formData = new FormData();
28615 formData.append('returnHTML', 'NO');
28618 formData.append('crop', crop);
28621 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28622 formData.append(this.paramName, file, file.name);
28625 if(typeof(file.filename) != 'undefined'){
28626 formData.append('filename', file.filename);
28629 if(typeof(file.mimetype) != 'undefined'){
28630 formData.append('mimetype', file.mimetype);
28633 if(this.fireEvent('arrange', this, formData) != false){
28634 this.xhr.send(formData);
28638 xhrOnLoad : function(xhr)
28641 this.maskEl.unmask();
28644 if (xhr.readyState !== 4) {
28645 this.fireEvent('exception', this, xhr);
28649 var response = Roo.decode(xhr.responseText);
28651 if(!response.success){
28652 this.fireEvent('exception', this, xhr);
28656 var response = Roo.decode(xhr.responseText);
28658 this.fireEvent('upload', this, response);
28662 xhrOnError : function()
28665 this.maskEl.unmask();
28668 Roo.log('xhr on error');
28670 var response = Roo.decode(xhr.responseText);
28676 prepare : function(file)
28679 this.maskEl.mask(this.loadingText);
28685 if(typeof(file) === 'string'){
28686 this.loadCanvas(file);
28690 if(!file || !this.urlAPI){
28695 this.cropType = file.type;
28699 if(this.fireEvent('prepare', this, this.file) != false){
28701 var reader = new FileReader();
28703 reader.onload = function (e) {
28704 if (e.target.error) {
28705 Roo.log(e.target.error);
28709 var buffer = e.target.result,
28710 dataView = new DataView(buffer),
28712 maxOffset = dataView.byteLength - 4,
28716 if (dataView.getUint16(0) === 0xffd8) {
28717 while (offset < maxOffset) {
28718 markerBytes = dataView.getUint16(offset);
28720 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28721 markerLength = dataView.getUint16(offset + 2) + 2;
28722 if (offset + markerLength > dataView.byteLength) {
28723 Roo.log('Invalid meta data: Invalid segment size.');
28727 if(markerBytes == 0xffe1){
28728 _this.parseExifData(
28735 offset += markerLength;
28745 var url = _this.urlAPI.createObjectURL(_this.file);
28747 _this.loadCanvas(url);
28752 reader.readAsArrayBuffer(this.file);
28758 parseExifData : function(dataView, offset, length)
28760 var tiffOffset = offset + 10,
28764 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28765 // No Exif data, might be XMP data instead
28769 // Check for the ASCII code for "Exif" (0x45786966):
28770 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28771 // No Exif data, might be XMP data instead
28774 if (tiffOffset + 8 > dataView.byteLength) {
28775 Roo.log('Invalid Exif data: Invalid segment size.');
28778 // Check for the two null bytes:
28779 if (dataView.getUint16(offset + 8) !== 0x0000) {
28780 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28783 // Check the byte alignment:
28784 switch (dataView.getUint16(tiffOffset)) {
28786 littleEndian = true;
28789 littleEndian = false;
28792 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28795 // Check for the TIFF tag marker (0x002A):
28796 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28797 Roo.log('Invalid Exif data: Missing TIFF marker.');
28800 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28801 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28803 this.parseExifTags(
28806 tiffOffset + dirOffset,
28811 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28816 if (dirOffset + 6 > dataView.byteLength) {
28817 Roo.log('Invalid Exif data: Invalid directory offset.');
28820 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28821 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28822 if (dirEndOffset + 4 > dataView.byteLength) {
28823 Roo.log('Invalid Exif data: Invalid directory size.');
28826 for (i = 0; i < tagsNumber; i += 1) {
28830 dirOffset + 2 + 12 * i, // tag offset
28834 // Return the offset to the next directory:
28835 return dataView.getUint32(dirEndOffset, littleEndian);
28838 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28840 var tag = dataView.getUint16(offset, littleEndian);
28842 this.exif[tag] = this.getExifValue(
28846 dataView.getUint16(offset + 2, littleEndian), // tag type
28847 dataView.getUint32(offset + 4, littleEndian), // tag length
28852 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28854 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28863 Roo.log('Invalid Exif data: Invalid tag type.');
28867 tagSize = tagType.size * length;
28868 // Determine if the value is contained in the dataOffset bytes,
28869 // or if the value at the dataOffset is a pointer to the actual data:
28870 dataOffset = tagSize > 4 ?
28871 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28872 if (dataOffset + tagSize > dataView.byteLength) {
28873 Roo.log('Invalid Exif data: Invalid data offset.');
28876 if (length === 1) {
28877 return tagType.getValue(dataView, dataOffset, littleEndian);
28880 for (i = 0; i < length; i += 1) {
28881 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28884 if (tagType.ascii) {
28886 // Concatenate the chars:
28887 for (i = 0; i < values.length; i += 1) {
28889 // Ignore the terminating NULL byte(s):
28890 if (c === '\u0000') {
28902 Roo.apply(Roo.bootstrap.UploadCropbox, {
28904 'Orientation': 0x0112
28908 1: 0, //'top-left',
28910 3: 180, //'bottom-right',
28911 // 4: 'bottom-left',
28913 6: 90, //'right-top',
28914 // 7: 'right-bottom',
28915 8: 270 //'left-bottom'
28919 // byte, 8-bit unsigned int:
28921 getValue: function (dataView, dataOffset) {
28922 return dataView.getUint8(dataOffset);
28926 // ascii, 8-bit byte:
28928 getValue: function (dataView, dataOffset) {
28929 return String.fromCharCode(dataView.getUint8(dataOffset));
28934 // short, 16 bit int:
28936 getValue: function (dataView, dataOffset, littleEndian) {
28937 return dataView.getUint16(dataOffset, littleEndian);
28941 // long, 32 bit int:
28943 getValue: function (dataView, dataOffset, littleEndian) {
28944 return dataView.getUint32(dataOffset, littleEndian);
28948 // rational = two long values, first is numerator, second is denominator:
28950 getValue: function (dataView, dataOffset, littleEndian) {
28951 return dataView.getUint32(dataOffset, littleEndian) /
28952 dataView.getUint32(dataOffset + 4, littleEndian);
28956 // slong, 32 bit signed int:
28958 getValue: function (dataView, dataOffset, littleEndian) {
28959 return dataView.getInt32(dataOffset, littleEndian);
28963 // srational, two slongs, first is numerator, second is denominator:
28965 getValue: function (dataView, dataOffset, littleEndian) {
28966 return dataView.getInt32(dataOffset, littleEndian) /
28967 dataView.getInt32(dataOffset + 4, littleEndian);
28977 cls : 'btn-group roo-upload-cropbox-rotate-left',
28978 action : 'rotate-left',
28982 cls : 'btn btn-default',
28983 html : '<i class="fa fa-undo"></i>'
28989 cls : 'btn-group roo-upload-cropbox-picture',
28990 action : 'picture',
28994 cls : 'btn btn-default',
28995 html : '<i class="fa fa-picture-o"></i>'
29001 cls : 'btn-group roo-upload-cropbox-rotate-right',
29002 action : 'rotate-right',
29006 cls : 'btn btn-default',
29007 html : '<i class="fa fa-repeat"></i>'
29015 cls : 'btn-group roo-upload-cropbox-rotate-left',
29016 action : 'rotate-left',
29020 cls : 'btn btn-default',
29021 html : '<i class="fa fa-undo"></i>'
29027 cls : 'btn-group roo-upload-cropbox-download',
29028 action : 'download',
29032 cls : 'btn btn-default',
29033 html : '<i class="fa fa-download"></i>'
29039 cls : 'btn-group roo-upload-cropbox-crop',
29044 cls : 'btn btn-default',
29045 html : '<i class="fa fa-crop"></i>'
29051 cls : 'btn-group roo-upload-cropbox-trash',
29056 cls : 'btn btn-default',
29057 html : '<i class="fa fa-trash"></i>'
29063 cls : 'btn-group roo-upload-cropbox-rotate-right',
29064 action : 'rotate-right',
29068 cls : 'btn btn-default',
29069 html : '<i class="fa fa-repeat"></i>'
29077 cls : 'btn-group roo-upload-cropbox-rotate-left',
29078 action : 'rotate-left',
29082 cls : 'btn btn-default',
29083 html : '<i class="fa fa-undo"></i>'
29089 cls : 'btn-group roo-upload-cropbox-rotate-right',
29090 action : 'rotate-right',
29094 cls : 'btn btn-default',
29095 html : '<i class="fa fa-repeat"></i>'
29108 * @class Roo.bootstrap.DocumentManager
29109 * @extends Roo.bootstrap.Component
29110 * Bootstrap DocumentManager class
29111 * @cfg {String} paramName default 'imageUpload'
29112 * @cfg {String} toolTipName default 'filename'
29113 * @cfg {String} method default POST
29114 * @cfg {String} url action url
29115 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29116 * @cfg {Boolean} multiple multiple upload default true
29117 * @cfg {Number} thumbSize default 300
29118 * @cfg {String} fieldLabel
29119 * @cfg {Number} labelWidth default 4
29120 * @cfg {String} labelAlign (left|top) default left
29121 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29122 * @cfg {Number} labellg set the width of label (1-12)
29123 * @cfg {Number} labelmd set the width of label (1-12)
29124 * @cfg {Number} labelsm set the width of label (1-12)
29125 * @cfg {Number} labelxs set the width of label (1-12)
29128 * Create a new DocumentManager
29129 * @param {Object} config The config object
29132 Roo.bootstrap.DocumentManager = function(config){
29133 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29136 this.delegates = [];
29141 * Fire when initial the DocumentManager
29142 * @param {Roo.bootstrap.DocumentManager} this
29147 * inspect selected file
29148 * @param {Roo.bootstrap.DocumentManager} this
29149 * @param {File} file
29154 * Fire when xhr load exception
29155 * @param {Roo.bootstrap.DocumentManager} this
29156 * @param {XMLHttpRequest} xhr
29158 "exception" : true,
29160 * @event afterupload
29161 * Fire when xhr load exception
29162 * @param {Roo.bootstrap.DocumentManager} this
29163 * @param {XMLHttpRequest} xhr
29165 "afterupload" : true,
29168 * prepare the form data
29169 * @param {Roo.bootstrap.DocumentManager} this
29170 * @param {Object} formData
29175 * Fire when remove the file
29176 * @param {Roo.bootstrap.DocumentManager} this
29177 * @param {Object} file
29182 * Fire after refresh the file
29183 * @param {Roo.bootstrap.DocumentManager} this
29188 * Fire after click the image
29189 * @param {Roo.bootstrap.DocumentManager} this
29190 * @param {Object} file
29195 * Fire when upload a image and editable set to true
29196 * @param {Roo.bootstrap.DocumentManager} this
29197 * @param {Object} file
29201 * @event beforeselectfile
29202 * Fire before select file
29203 * @param {Roo.bootstrap.DocumentManager} this
29205 "beforeselectfile" : true,
29208 * Fire before process file
29209 * @param {Roo.bootstrap.DocumentManager} this
29210 * @param {Object} file
29214 * @event previewrendered
29215 * Fire when preview rendered
29216 * @param {Roo.bootstrap.DocumentManager} this
29217 * @param {Object} file
29219 "previewrendered" : true,
29222 "previewResize" : true
29227 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29236 paramName : 'imageUpload',
29237 toolTipName : 'filename',
29240 labelAlign : 'left',
29250 getAutoCreate : function()
29252 var managerWidget = {
29254 cls : 'roo-document-manager',
29258 cls : 'roo-document-manager-selector',
29263 cls : 'roo-document-manager-uploader',
29267 cls : 'roo-document-manager-upload-btn',
29268 html : '<i class="fa fa-plus"></i>'
29279 cls : 'column col-md-12',
29284 if(this.fieldLabel.length){
29289 cls : 'column col-md-12',
29290 html : this.fieldLabel
29294 cls : 'column col-md-12',
29299 if(this.labelAlign == 'left'){
29304 html : this.fieldLabel
29313 if(this.labelWidth > 12){
29314 content[0].style = "width: " + this.labelWidth + 'px';
29317 if(this.labelWidth < 13 && this.labelmd == 0){
29318 this.labelmd = this.labelWidth;
29321 if(this.labellg > 0){
29322 content[0].cls += ' col-lg-' + this.labellg;
29323 content[1].cls += ' col-lg-' + (12 - this.labellg);
29326 if(this.labelmd > 0){
29327 content[0].cls += ' col-md-' + this.labelmd;
29328 content[1].cls += ' col-md-' + (12 - this.labelmd);
29331 if(this.labelsm > 0){
29332 content[0].cls += ' col-sm-' + this.labelsm;
29333 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29336 if(this.labelxs > 0){
29337 content[0].cls += ' col-xs-' + this.labelxs;
29338 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29346 cls : 'row clearfix',
29354 initEvents : function()
29356 this.managerEl = this.el.select('.roo-document-manager', true).first();
29357 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29359 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29360 this.selectorEl.hide();
29363 this.selectorEl.attr('multiple', 'multiple');
29366 this.selectorEl.on('change', this.onFileSelected, this);
29368 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29369 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29371 this.uploader.on('click', this.onUploaderClick, this);
29373 this.renderProgressDialog();
29377 window.addEventListener("resize", function() { _this.refresh(); } );
29379 this.fireEvent('initial', this);
29382 renderProgressDialog : function()
29386 this.progressDialog = new Roo.bootstrap.Modal({
29387 cls : 'roo-document-manager-progress-dialog',
29388 allow_close : false,
29399 btnclick : function() {
29400 _this.uploadCancel();
29406 this.progressDialog.render(Roo.get(document.body));
29408 this.progress = new Roo.bootstrap.Progress({
29409 cls : 'roo-document-manager-progress',
29414 this.progress.render(this.progressDialog.getChildContainer());
29416 this.progressBar = new Roo.bootstrap.ProgressBar({
29417 cls : 'roo-document-manager-progress-bar',
29420 aria_valuemax : 12,
29424 this.progressBar.render(this.progress.getChildContainer());
29427 onUploaderClick : function(e)
29429 e.preventDefault();
29431 if(this.fireEvent('beforeselectfile', this) != false){
29432 this.selectorEl.dom.click();
29437 onFileSelected : function(e)
29439 e.preventDefault();
29441 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29445 Roo.each(this.selectorEl.dom.files, function(file){
29446 if(this.fireEvent('inspect', this, file) != false){
29447 this.files.push(file);
29457 this.selectorEl.dom.value = '';
29459 if(!this.files || !this.files.length){
29463 if(this.boxes > 0 && this.files.length > this.boxes){
29464 this.files = this.files.slice(0, this.boxes);
29467 this.uploader.show();
29469 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29470 this.uploader.hide();
29479 Roo.each(this.files, function(file){
29481 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29482 var f = this.renderPreview(file);
29487 if(file.type.indexOf('image') != -1){
29488 this.delegates.push(
29490 _this.process(file);
29491 }).createDelegate(this)
29499 _this.process(file);
29500 }).createDelegate(this)
29505 this.files = files;
29507 this.delegates = this.delegates.concat(docs);
29509 if(!this.delegates.length){
29514 this.progressBar.aria_valuemax = this.delegates.length;
29521 arrange : function()
29523 if(!this.delegates.length){
29524 this.progressDialog.hide();
29529 var delegate = this.delegates.shift();
29531 this.progressDialog.show();
29533 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29535 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29540 refresh : function()
29542 this.uploader.show();
29544 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29545 this.uploader.hide();
29548 Roo.isTouch ? this.closable(false) : this.closable(true);
29550 this.fireEvent('refresh', this);
29553 onRemove : function(e, el, o)
29555 e.preventDefault();
29557 this.fireEvent('remove', this, o);
29561 remove : function(o)
29565 Roo.each(this.files, function(file){
29566 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29575 this.files = files;
29582 Roo.each(this.files, function(file){
29587 file.target.remove();
29596 onClick : function(e, el, o)
29598 e.preventDefault();
29600 this.fireEvent('click', this, o);
29604 closable : function(closable)
29606 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29608 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29620 xhrOnLoad : function(xhr)
29622 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29626 if (xhr.readyState !== 4) {
29628 this.fireEvent('exception', this, xhr);
29632 var response = Roo.decode(xhr.responseText);
29634 if(!response.success){
29636 this.fireEvent('exception', this, xhr);
29640 var file = this.renderPreview(response.data);
29642 this.files.push(file);
29646 this.fireEvent('afterupload', this, xhr);
29650 xhrOnError : function(xhr)
29652 Roo.log('xhr on error');
29654 var response = Roo.decode(xhr.responseText);
29661 process : function(file)
29663 if(this.fireEvent('process', this, file) !== false){
29664 if(this.editable && file.type.indexOf('image') != -1){
29665 this.fireEvent('edit', this, file);
29669 this.uploadStart(file, false);
29676 uploadStart : function(file, crop)
29678 this.xhr = new XMLHttpRequest();
29680 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29685 file.xhr = this.xhr;
29687 this.managerEl.createChild({
29689 cls : 'roo-document-manager-loading',
29693 tooltip : file.name,
29694 cls : 'roo-document-manager-thumb',
29695 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29701 this.xhr.open(this.method, this.url, true);
29704 "Accept": "application/json",
29705 "Cache-Control": "no-cache",
29706 "X-Requested-With": "XMLHttpRequest"
29709 for (var headerName in headers) {
29710 var headerValue = headers[headerName];
29712 this.xhr.setRequestHeader(headerName, headerValue);
29718 this.xhr.onload = function()
29720 _this.xhrOnLoad(_this.xhr);
29723 this.xhr.onerror = function()
29725 _this.xhrOnError(_this.xhr);
29728 var formData = new FormData();
29730 formData.append('returnHTML', 'NO');
29733 formData.append('crop', crop);
29736 formData.append(this.paramName, file, file.name);
29743 if(this.fireEvent('prepare', this, formData, options) != false){
29745 if(options.manually){
29749 this.xhr.send(formData);
29753 this.uploadCancel();
29756 uploadCancel : function()
29762 this.delegates = [];
29764 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29771 renderPreview : function(file)
29773 if(typeof(file.target) != 'undefined' && file.target){
29777 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29779 var previewEl = this.managerEl.createChild({
29781 cls : 'roo-document-manager-preview',
29785 tooltip : file[this.toolTipName],
29786 cls : 'roo-document-manager-thumb',
29787 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29792 html : '<i class="fa fa-times-circle"></i>'
29797 var close = previewEl.select('button.close', true).first();
29799 close.on('click', this.onRemove, this, file);
29801 file.target = previewEl;
29803 var image = previewEl.select('img', true).first();
29807 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29809 image.on('click', this.onClick, this, file);
29811 this.fireEvent('previewrendered', this, file);
29817 onPreviewLoad : function(file, image)
29819 if(typeof(file.target) == 'undefined' || !file.target){
29823 var width = image.dom.naturalWidth || image.dom.width;
29824 var height = image.dom.naturalHeight || image.dom.height;
29826 if(!this.previewResize) {
29830 if(width > height){
29831 file.target.addClass('wide');
29835 file.target.addClass('tall');
29840 uploadFromSource : function(file, crop)
29842 this.xhr = new XMLHttpRequest();
29844 this.managerEl.createChild({
29846 cls : 'roo-document-manager-loading',
29850 tooltip : file.name,
29851 cls : 'roo-document-manager-thumb',
29852 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29858 this.xhr.open(this.method, this.url, true);
29861 "Accept": "application/json",
29862 "Cache-Control": "no-cache",
29863 "X-Requested-With": "XMLHttpRequest"
29866 for (var headerName in headers) {
29867 var headerValue = headers[headerName];
29869 this.xhr.setRequestHeader(headerName, headerValue);
29875 this.xhr.onload = function()
29877 _this.xhrOnLoad(_this.xhr);
29880 this.xhr.onerror = function()
29882 _this.xhrOnError(_this.xhr);
29885 var formData = new FormData();
29887 formData.append('returnHTML', 'NO');
29889 formData.append('crop', crop);
29891 if(typeof(file.filename) != 'undefined'){
29892 formData.append('filename', file.filename);
29895 if(typeof(file.mimetype) != 'undefined'){
29896 formData.append('mimetype', file.mimetype);
29901 if(this.fireEvent('prepare', this, formData) != false){
29902 this.xhr.send(formData);
29912 * @class Roo.bootstrap.DocumentViewer
29913 * @extends Roo.bootstrap.Component
29914 * Bootstrap DocumentViewer class
29915 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29916 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29919 * Create a new DocumentViewer
29920 * @param {Object} config The config object
29923 Roo.bootstrap.DocumentViewer = function(config){
29924 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29929 * Fire after initEvent
29930 * @param {Roo.bootstrap.DocumentViewer} this
29936 * @param {Roo.bootstrap.DocumentViewer} this
29941 * Fire after download button
29942 * @param {Roo.bootstrap.DocumentViewer} this
29947 * Fire after trash button
29948 * @param {Roo.bootstrap.DocumentViewer} this
29955 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29957 showDownload : true,
29961 getAutoCreate : function()
29965 cls : 'roo-document-viewer',
29969 cls : 'roo-document-viewer-body',
29973 cls : 'roo-document-viewer-thumb',
29977 cls : 'roo-document-viewer-image'
29985 cls : 'roo-document-viewer-footer',
29988 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29992 cls : 'btn-group roo-document-viewer-download',
29996 cls : 'btn btn-default',
29997 html : '<i class="fa fa-download"></i>'
30003 cls : 'btn-group roo-document-viewer-trash',
30007 cls : 'btn btn-default',
30008 html : '<i class="fa fa-trash"></i>'
30021 initEvents : function()
30023 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30024 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30026 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30027 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30029 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30030 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30032 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30033 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30035 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30036 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30038 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30039 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30041 this.bodyEl.on('click', this.onClick, this);
30042 this.downloadBtn.on('click', this.onDownload, this);
30043 this.trashBtn.on('click', this.onTrash, this);
30045 this.downloadBtn.hide();
30046 this.trashBtn.hide();
30048 if(this.showDownload){
30049 this.downloadBtn.show();
30052 if(this.showTrash){
30053 this.trashBtn.show();
30056 if(!this.showDownload && !this.showTrash) {
30057 this.footerEl.hide();
30062 initial : function()
30064 this.fireEvent('initial', this);
30068 onClick : function(e)
30070 e.preventDefault();
30072 this.fireEvent('click', this);
30075 onDownload : function(e)
30077 e.preventDefault();
30079 this.fireEvent('download', this);
30082 onTrash : function(e)
30084 e.preventDefault();
30086 this.fireEvent('trash', this);
30098 * @class Roo.bootstrap.NavProgressBar
30099 * @extends Roo.bootstrap.Component
30100 * Bootstrap NavProgressBar class
30103 * Create a new nav progress bar
30104 * @param {Object} config The config object
30107 Roo.bootstrap.NavProgressBar = function(config){
30108 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30110 this.bullets = this.bullets || [];
30112 // Roo.bootstrap.NavProgressBar.register(this);
30116 * Fires when the active item changes
30117 * @param {Roo.bootstrap.NavProgressBar} this
30118 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30119 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30126 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30131 getAutoCreate : function()
30133 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30137 cls : 'roo-navigation-bar-group',
30141 cls : 'roo-navigation-top-bar'
30145 cls : 'roo-navigation-bullets-bar',
30149 cls : 'roo-navigation-bar'
30156 cls : 'roo-navigation-bottom-bar'
30166 initEvents: function()
30171 onRender : function(ct, position)
30173 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30175 if(this.bullets.length){
30176 Roo.each(this.bullets, function(b){
30185 addItem : function(cfg)
30187 var item = new Roo.bootstrap.NavProgressItem(cfg);
30189 item.parentId = this.id;
30190 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30193 var top = new Roo.bootstrap.Element({
30195 cls : 'roo-navigation-bar-text'
30198 var bottom = new Roo.bootstrap.Element({
30200 cls : 'roo-navigation-bar-text'
30203 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30204 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30206 var topText = new Roo.bootstrap.Element({
30208 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30211 var bottomText = new Roo.bootstrap.Element({
30213 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30216 topText.onRender(top.el, null);
30217 bottomText.onRender(bottom.el, null);
30220 item.bottomEl = bottom;
30223 this.barItems.push(item);
30228 getActive : function()
30230 var active = false;
30232 Roo.each(this.barItems, function(v){
30234 if (!v.isActive()) {
30246 setActiveItem : function(item)
30250 Roo.each(this.barItems, function(v){
30251 if (v.rid == item.rid) {
30255 if (v.isActive()) {
30256 v.setActive(false);
30261 item.setActive(true);
30263 this.fireEvent('changed', this, item, prev);
30266 getBarItem: function(rid)
30270 Roo.each(this.barItems, function(e) {
30271 if (e.rid != rid) {
30282 indexOfItem : function(item)
30286 Roo.each(this.barItems, function(v, i){
30288 if (v.rid != item.rid) {
30299 setActiveNext : function()
30301 var i = this.indexOfItem(this.getActive());
30303 if (i > this.barItems.length) {
30307 this.setActiveItem(this.barItems[i+1]);
30310 setActivePrev : function()
30312 var i = this.indexOfItem(this.getActive());
30318 this.setActiveItem(this.barItems[i-1]);
30321 format : function()
30323 if(!this.barItems.length){
30327 var width = 100 / this.barItems.length;
30329 Roo.each(this.barItems, function(i){
30330 i.el.setStyle('width', width + '%');
30331 i.topEl.el.setStyle('width', width + '%');
30332 i.bottomEl.el.setStyle('width', width + '%');
30341 * Nav Progress Item
30346 * @class Roo.bootstrap.NavProgressItem
30347 * @extends Roo.bootstrap.Component
30348 * Bootstrap NavProgressItem class
30349 * @cfg {String} rid the reference id
30350 * @cfg {Boolean} active (true|false) Is item active default false
30351 * @cfg {Boolean} disabled (true|false) Is item active default false
30352 * @cfg {String} html
30353 * @cfg {String} position (top|bottom) text position default bottom
30354 * @cfg {String} icon show icon instead of number
30357 * Create a new NavProgressItem
30358 * @param {Object} config The config object
30360 Roo.bootstrap.NavProgressItem = function(config){
30361 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30366 * The raw click event for the entire grid.
30367 * @param {Roo.bootstrap.NavProgressItem} this
30368 * @param {Roo.EventObject} e
30375 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30381 position : 'bottom',
30384 getAutoCreate : function()
30386 var iconCls = 'roo-navigation-bar-item-icon';
30388 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30392 cls: 'roo-navigation-bar-item',
30402 cfg.cls += ' active';
30405 cfg.cls += ' disabled';
30411 disable : function()
30413 this.setDisabled(true);
30416 enable : function()
30418 this.setDisabled(false);
30421 initEvents: function()
30423 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30425 this.iconEl.on('click', this.onClick, this);
30428 onClick : function(e)
30430 e.preventDefault();
30436 if(this.fireEvent('click', this, e) === false){
30440 this.parent().setActiveItem(this);
30443 isActive: function ()
30445 return this.active;
30448 setActive : function(state)
30450 if(this.active == state){
30454 this.active = state;
30457 this.el.addClass('active');
30461 this.el.removeClass('active');
30466 setDisabled : function(state)
30468 if(this.disabled == state){
30472 this.disabled = state;
30475 this.el.addClass('disabled');
30479 this.el.removeClass('disabled');
30482 tooltipEl : function()
30484 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30497 * @class Roo.bootstrap.FieldLabel
30498 * @extends Roo.bootstrap.Component
30499 * Bootstrap FieldLabel class
30500 * @cfg {String} html contents of the element
30501 * @cfg {String} tag tag of the element default label
30502 * @cfg {String} cls class of the element
30503 * @cfg {String} target label target
30504 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30505 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30506 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30507 * @cfg {String} iconTooltip default "This field is required"
30508 * @cfg {String} indicatorpos (left|right) default left
30511 * Create a new FieldLabel
30512 * @param {Object} config The config object
30515 Roo.bootstrap.FieldLabel = function(config){
30516 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30521 * Fires after the field has been marked as invalid.
30522 * @param {Roo.form.FieldLabel} this
30523 * @param {String} msg The validation message
30528 * Fires after the field has been validated with no errors.
30529 * @param {Roo.form.FieldLabel} this
30535 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30542 invalidClass : 'has-warning',
30543 validClass : 'has-success',
30544 iconTooltip : 'This field is required',
30545 indicatorpos : 'left',
30547 getAutoCreate : function(){
30550 if (!this.allowBlank) {
30556 cls : 'roo-bootstrap-field-label ' + this.cls,
30561 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30562 tooltip : this.iconTooltip
30571 if(this.indicatorpos == 'right'){
30574 cls : 'roo-bootstrap-field-label ' + this.cls,
30583 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30584 tooltip : this.iconTooltip
30593 initEvents: function()
30595 Roo.bootstrap.Element.superclass.initEvents.call(this);
30597 this.indicator = this.indicatorEl();
30599 if(this.indicator){
30600 this.indicator.removeClass('visible');
30601 this.indicator.addClass('invisible');
30604 Roo.bootstrap.FieldLabel.register(this);
30607 indicatorEl : function()
30609 var indicator = this.el.select('i.roo-required-indicator',true).first();
30620 * Mark this field as valid
30622 markValid : function()
30624 if(this.indicator){
30625 this.indicator.removeClass('visible');
30626 this.indicator.addClass('invisible');
30628 if (Roo.bootstrap.version == 3) {
30629 this.el.removeClass(this.invalidClass);
30630 this.el.addClass(this.validClass);
30632 this.el.removeClass('is-invalid');
30633 this.el.addClass('is-valid');
30637 this.fireEvent('valid', this);
30641 * Mark this field as invalid
30642 * @param {String} msg The validation message
30644 markInvalid : function(msg)
30646 if(this.indicator){
30647 this.indicator.removeClass('invisible');
30648 this.indicator.addClass('visible');
30650 if (Roo.bootstrap.version == 3) {
30651 this.el.removeClass(this.validClass);
30652 this.el.addClass(this.invalidClass);
30654 this.el.removeClass('is-valid');
30655 this.el.addClass('is-invalid');
30659 this.fireEvent('invalid', this, msg);
30665 Roo.apply(Roo.bootstrap.FieldLabel, {
30670 * register a FieldLabel Group
30671 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30673 register : function(label)
30675 if(this.groups.hasOwnProperty(label.target)){
30679 this.groups[label.target] = label;
30683 * fetch a FieldLabel Group based on the target
30684 * @param {string} target
30685 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30687 get: function(target) {
30688 if (typeof(this.groups[target]) == 'undefined') {
30692 return this.groups[target] ;
30701 * page DateSplitField.
30707 * @class Roo.bootstrap.DateSplitField
30708 * @extends Roo.bootstrap.Component
30709 * Bootstrap DateSplitField class
30710 * @cfg {string} fieldLabel - the label associated
30711 * @cfg {Number} labelWidth set the width of label (0-12)
30712 * @cfg {String} labelAlign (top|left)
30713 * @cfg {Boolean} dayAllowBlank (true|false) default false
30714 * @cfg {Boolean} monthAllowBlank (true|false) default false
30715 * @cfg {Boolean} yearAllowBlank (true|false) default false
30716 * @cfg {string} dayPlaceholder
30717 * @cfg {string} monthPlaceholder
30718 * @cfg {string} yearPlaceholder
30719 * @cfg {string} dayFormat default 'd'
30720 * @cfg {string} monthFormat default 'm'
30721 * @cfg {string} yearFormat default 'Y'
30722 * @cfg {Number} labellg set the width of label (1-12)
30723 * @cfg {Number} labelmd set the width of label (1-12)
30724 * @cfg {Number} labelsm set the width of label (1-12)
30725 * @cfg {Number} labelxs set the width of label (1-12)
30729 * Create a new DateSplitField
30730 * @param {Object} config The config object
30733 Roo.bootstrap.DateSplitField = function(config){
30734 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30740 * getting the data of years
30741 * @param {Roo.bootstrap.DateSplitField} this
30742 * @param {Object} years
30747 * getting the data of days
30748 * @param {Roo.bootstrap.DateSplitField} this
30749 * @param {Object} days
30754 * Fires after the field has been marked as invalid.
30755 * @param {Roo.form.Field} this
30756 * @param {String} msg The validation message
30761 * Fires after the field has been validated with no errors.
30762 * @param {Roo.form.Field} this
30768 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30771 labelAlign : 'top',
30773 dayAllowBlank : false,
30774 monthAllowBlank : false,
30775 yearAllowBlank : false,
30776 dayPlaceholder : '',
30777 monthPlaceholder : '',
30778 yearPlaceholder : '',
30782 isFormField : true,
30788 getAutoCreate : function()
30792 cls : 'row roo-date-split-field-group',
30797 cls : 'form-hidden-field roo-date-split-field-group-value',
30803 var labelCls = 'col-md-12';
30804 var contentCls = 'col-md-4';
30806 if(this.fieldLabel){
30810 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30814 html : this.fieldLabel
30819 if(this.labelAlign == 'left'){
30821 if(this.labelWidth > 12){
30822 label.style = "width: " + this.labelWidth + 'px';
30825 if(this.labelWidth < 13 && this.labelmd == 0){
30826 this.labelmd = this.labelWidth;
30829 if(this.labellg > 0){
30830 labelCls = ' col-lg-' + this.labellg;
30831 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30834 if(this.labelmd > 0){
30835 labelCls = ' col-md-' + this.labelmd;
30836 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30839 if(this.labelsm > 0){
30840 labelCls = ' col-sm-' + this.labelsm;
30841 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30844 if(this.labelxs > 0){
30845 labelCls = ' col-xs-' + this.labelxs;
30846 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30850 label.cls += ' ' + labelCls;
30852 cfg.cn.push(label);
30855 Roo.each(['day', 'month', 'year'], function(t){
30858 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30865 inputEl: function ()
30867 return this.el.select('.roo-date-split-field-group-value', true).first();
30870 onRender : function(ct, position)
30874 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30876 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30878 this.dayField = new Roo.bootstrap.ComboBox({
30879 allowBlank : this.dayAllowBlank,
30880 alwaysQuery : true,
30881 displayField : 'value',
30884 forceSelection : true,
30886 placeholder : this.dayPlaceholder,
30887 selectOnFocus : true,
30888 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30889 triggerAction : 'all',
30891 valueField : 'value',
30892 store : new Roo.data.SimpleStore({
30893 data : (function() {
30895 _this.fireEvent('days', _this, days);
30898 fields : [ 'value' ]
30901 select : function (_self, record, index)
30903 _this.setValue(_this.getValue());
30908 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30910 this.monthField = new Roo.bootstrap.MonthField({
30911 after : '<i class=\"fa fa-calendar\"></i>',
30912 allowBlank : this.monthAllowBlank,
30913 placeholder : this.monthPlaceholder,
30916 render : function (_self)
30918 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30919 e.preventDefault();
30923 select : function (_self, oldvalue, newvalue)
30925 _this.setValue(_this.getValue());
30930 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30932 this.yearField = new Roo.bootstrap.ComboBox({
30933 allowBlank : this.yearAllowBlank,
30934 alwaysQuery : true,
30935 displayField : 'value',
30938 forceSelection : true,
30940 placeholder : this.yearPlaceholder,
30941 selectOnFocus : true,
30942 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30943 triggerAction : 'all',
30945 valueField : 'value',
30946 store : new Roo.data.SimpleStore({
30947 data : (function() {
30949 _this.fireEvent('years', _this, years);
30952 fields : [ 'value' ]
30955 select : function (_self, record, index)
30957 _this.setValue(_this.getValue());
30962 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30965 setValue : function(v, format)
30967 this.inputEl.dom.value = v;
30969 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30971 var d = Date.parseDate(v, f);
30978 this.setDay(d.format(this.dayFormat));
30979 this.setMonth(d.format(this.monthFormat));
30980 this.setYear(d.format(this.yearFormat));
30987 setDay : function(v)
30989 this.dayField.setValue(v);
30990 this.inputEl.dom.value = this.getValue();
30995 setMonth : function(v)
30997 this.monthField.setValue(v, true);
30998 this.inputEl.dom.value = this.getValue();
31003 setYear : function(v)
31005 this.yearField.setValue(v);
31006 this.inputEl.dom.value = this.getValue();
31011 getDay : function()
31013 return this.dayField.getValue();
31016 getMonth : function()
31018 return this.monthField.getValue();
31021 getYear : function()
31023 return this.yearField.getValue();
31026 getValue : function()
31028 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31030 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31040 this.inputEl.dom.value = '';
31045 validate : function()
31047 var d = this.dayField.validate();
31048 var m = this.monthField.validate();
31049 var y = this.yearField.validate();
31054 (!this.dayAllowBlank && !d) ||
31055 (!this.monthAllowBlank && !m) ||
31056 (!this.yearAllowBlank && !y)
31061 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31070 this.markInvalid();
31075 markValid : function()
31078 var label = this.el.select('label', true).first();
31079 var icon = this.el.select('i.fa-star', true).first();
31085 this.fireEvent('valid', this);
31089 * Mark this field as invalid
31090 * @param {String} msg The validation message
31092 markInvalid : function(msg)
31095 var label = this.el.select('label', true).first();
31096 var icon = this.el.select('i.fa-star', true).first();
31098 if(label && !icon){
31099 this.el.select('.roo-date-split-field-label', true).createChild({
31101 cls : 'text-danger fa fa-lg fa-star',
31102 tooltip : 'This field is required',
31103 style : 'margin-right:5px;'
31107 this.fireEvent('invalid', this, msg);
31110 clearInvalid : function()
31112 var label = this.el.select('label', true).first();
31113 var icon = this.el.select('i.fa-star', true).first();
31119 this.fireEvent('valid', this);
31122 getName: function()
31132 * http://masonry.desandro.com
31134 * The idea is to render all the bricks based on vertical width...
31136 * The original code extends 'outlayer' - we might need to use that....
31142 * @class Roo.bootstrap.LayoutMasonry
31143 * @extends Roo.bootstrap.Component
31144 * Bootstrap Layout Masonry class
31147 * Create a new Element
31148 * @param {Object} config The config object
31151 Roo.bootstrap.LayoutMasonry = function(config){
31153 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31157 Roo.bootstrap.LayoutMasonry.register(this);
31163 * Fire after layout the items
31164 * @param {Roo.bootstrap.LayoutMasonry} this
31165 * @param {Roo.EventObject} e
31172 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31175 * @cfg {Boolean} isLayoutInstant = no animation?
31177 isLayoutInstant : false, // needed?
31180 * @cfg {Number} boxWidth width of the columns
31185 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31190 * @cfg {Number} padWidth padding below box..
31195 * @cfg {Number} gutter gutter width..
31200 * @cfg {Number} maxCols maximum number of columns
31206 * @cfg {Boolean} isAutoInitial defalut true
31208 isAutoInitial : true,
31213 * @cfg {Boolean} isHorizontal defalut false
31215 isHorizontal : false,
31217 currentSize : null,
31223 bricks: null, //CompositeElement
31227 _isLayoutInited : false,
31229 // isAlternative : false, // only use for vertical layout...
31232 * @cfg {Number} alternativePadWidth padding below box..
31234 alternativePadWidth : 50,
31236 selectedBrick : [],
31238 getAutoCreate : function(){
31240 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31244 cls: 'blog-masonary-wrapper ' + this.cls,
31246 cls : 'mas-boxes masonary'
31253 getChildContainer: function( )
31255 if (this.boxesEl) {
31256 return this.boxesEl;
31259 this.boxesEl = this.el.select('.mas-boxes').first();
31261 return this.boxesEl;
31265 initEvents : function()
31269 if(this.isAutoInitial){
31270 Roo.log('hook children rendered');
31271 this.on('childrenrendered', function() {
31272 Roo.log('children rendered');
31278 initial : function()
31280 this.selectedBrick = [];
31282 this.currentSize = this.el.getBox(true);
31284 Roo.EventManager.onWindowResize(this.resize, this);
31286 if(!this.isAutoInitial){
31294 //this.layout.defer(500,this);
31298 resize : function()
31300 var cs = this.el.getBox(true);
31303 this.currentSize.width == cs.width &&
31304 this.currentSize.x == cs.x &&
31305 this.currentSize.height == cs.height &&
31306 this.currentSize.y == cs.y
31308 Roo.log("no change in with or X or Y");
31312 this.currentSize = cs;
31318 layout : function()
31320 this._resetLayout();
31322 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31324 this.layoutItems( isInstant );
31326 this._isLayoutInited = true;
31328 this.fireEvent('layout', this);
31332 _resetLayout : function()
31334 if(this.isHorizontal){
31335 this.horizontalMeasureColumns();
31339 this.verticalMeasureColumns();
31343 verticalMeasureColumns : function()
31345 this.getContainerWidth();
31347 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31348 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31352 var boxWidth = this.boxWidth + this.padWidth;
31354 if(this.containerWidth < this.boxWidth){
31355 boxWidth = this.containerWidth
31358 var containerWidth = this.containerWidth;
31360 var cols = Math.floor(containerWidth / boxWidth);
31362 this.cols = Math.max( cols, 1 );
31364 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31366 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31368 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31370 this.colWidth = boxWidth + avail - this.padWidth;
31372 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31373 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31376 horizontalMeasureColumns : function()
31378 this.getContainerWidth();
31380 var boxWidth = this.boxWidth;
31382 if(this.containerWidth < boxWidth){
31383 boxWidth = this.containerWidth;
31386 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31388 this.el.setHeight(boxWidth);
31392 getContainerWidth : function()
31394 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31397 layoutItems : function( isInstant )
31399 Roo.log(this.bricks);
31401 var items = Roo.apply([], this.bricks);
31403 if(this.isHorizontal){
31404 this._horizontalLayoutItems( items , isInstant );
31408 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31409 // this._verticalAlternativeLayoutItems( items , isInstant );
31413 this._verticalLayoutItems( items , isInstant );
31417 _verticalLayoutItems : function ( items , isInstant)
31419 if ( !items || !items.length ) {
31424 ['xs', 'xs', 'xs', 'tall'],
31425 ['xs', 'xs', 'tall'],
31426 ['xs', 'xs', 'sm'],
31427 ['xs', 'xs', 'xs'],
31433 ['sm', 'xs', 'xs'],
31437 ['tall', 'xs', 'xs', 'xs'],
31438 ['tall', 'xs', 'xs'],
31450 Roo.each(items, function(item, k){
31452 switch (item.size) {
31453 // these layouts take up a full box,
31464 boxes.push([item]);
31487 var filterPattern = function(box, length)
31495 var pattern = box.slice(0, length);
31499 Roo.each(pattern, function(i){
31500 format.push(i.size);
31503 Roo.each(standard, function(s){
31505 if(String(s) != String(format)){
31514 if(!match && length == 1){
31519 filterPattern(box, length - 1);
31523 queue.push(pattern);
31525 box = box.slice(length, box.length);
31527 filterPattern(box, 4);
31533 Roo.each(boxes, function(box, k){
31539 if(box.length == 1){
31544 filterPattern(box, 4);
31548 this._processVerticalLayoutQueue( queue, isInstant );
31552 // _verticalAlternativeLayoutItems : function( items , isInstant )
31554 // if ( !items || !items.length ) {
31558 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31562 _horizontalLayoutItems : function ( items , isInstant)
31564 if ( !items || !items.length || items.length < 3) {
31570 var eItems = items.slice(0, 3);
31572 items = items.slice(3, items.length);
31575 ['xs', 'xs', 'xs', 'wide'],
31576 ['xs', 'xs', 'wide'],
31577 ['xs', 'xs', 'sm'],
31578 ['xs', 'xs', 'xs'],
31584 ['sm', 'xs', 'xs'],
31588 ['wide', 'xs', 'xs', 'xs'],
31589 ['wide', 'xs', 'xs'],
31602 Roo.each(items, function(item, k){
31604 switch (item.size) {
31615 boxes.push([item]);
31639 var filterPattern = function(box, length)
31647 var pattern = box.slice(0, length);
31651 Roo.each(pattern, function(i){
31652 format.push(i.size);
31655 Roo.each(standard, function(s){
31657 if(String(s) != String(format)){
31666 if(!match && length == 1){
31671 filterPattern(box, length - 1);
31675 queue.push(pattern);
31677 box = box.slice(length, box.length);
31679 filterPattern(box, 4);
31685 Roo.each(boxes, function(box, k){
31691 if(box.length == 1){
31696 filterPattern(box, 4);
31703 var pos = this.el.getBox(true);
31707 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31709 var hit_end = false;
31711 Roo.each(queue, function(box){
31715 Roo.each(box, function(b){
31717 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31727 Roo.each(box, function(b){
31729 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31732 mx = Math.max(mx, b.x);
31736 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31740 Roo.each(box, function(b){
31742 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31756 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31759 /** Sets position of item in DOM
31760 * @param {Element} item
31761 * @param {Number} x - horizontal position
31762 * @param {Number} y - vertical position
31763 * @param {Boolean} isInstant - disables transitions
31765 _processVerticalLayoutQueue : function( queue, isInstant )
31767 var pos = this.el.getBox(true);
31772 for (var i = 0; i < this.cols; i++){
31776 Roo.each(queue, function(box, k){
31778 var col = k % this.cols;
31780 Roo.each(box, function(b,kk){
31782 b.el.position('absolute');
31784 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31785 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31787 if(b.size == 'md-left' || b.size == 'md-right'){
31788 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31789 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31792 b.el.setWidth(width);
31793 b.el.setHeight(height);
31795 b.el.select('iframe',true).setSize(width,height);
31799 for (var i = 0; i < this.cols; i++){
31801 if(maxY[i] < maxY[col]){
31806 col = Math.min(col, i);
31810 x = pos.x + col * (this.colWidth + this.padWidth);
31814 var positions = [];
31816 switch (box.length){
31818 positions = this.getVerticalOneBoxColPositions(x, y, box);
31821 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31824 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31827 positions = this.getVerticalFourBoxColPositions(x, y, box);
31833 Roo.each(box, function(b,kk){
31835 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31837 var sz = b.el.getSize();
31839 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31847 for (var i = 0; i < this.cols; i++){
31848 mY = Math.max(mY, maxY[i]);
31851 this.el.setHeight(mY - pos.y);
31855 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31857 // var pos = this.el.getBox(true);
31860 // var maxX = pos.right;
31862 // var maxHeight = 0;
31864 // Roo.each(items, function(item, k){
31868 // item.el.position('absolute');
31870 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31872 // item.el.setWidth(width);
31874 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31876 // item.el.setHeight(height);
31879 // item.el.setXY([x, y], isInstant ? false : true);
31881 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31884 // y = y + height + this.alternativePadWidth;
31886 // maxHeight = maxHeight + height + this.alternativePadWidth;
31890 // this.el.setHeight(maxHeight);
31894 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31896 var pos = this.el.getBox(true);
31901 var maxX = pos.right;
31903 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31905 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31907 Roo.each(queue, function(box, k){
31909 Roo.each(box, function(b, kk){
31911 b.el.position('absolute');
31913 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31914 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31916 if(b.size == 'md-left' || b.size == 'md-right'){
31917 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31918 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31921 b.el.setWidth(width);
31922 b.el.setHeight(height);
31930 var positions = [];
31932 switch (box.length){
31934 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31937 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31940 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31943 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31949 Roo.each(box, function(b,kk){
31951 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31953 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31961 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31963 Roo.each(eItems, function(b,k){
31965 b.size = (k == 0) ? 'sm' : 'xs';
31966 b.x = (k == 0) ? 2 : 1;
31967 b.y = (k == 0) ? 2 : 1;
31969 b.el.position('absolute');
31971 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31973 b.el.setWidth(width);
31975 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31977 b.el.setHeight(height);
31981 var positions = [];
31984 x : maxX - this.unitWidth * 2 - this.gutter,
31989 x : maxX - this.unitWidth,
31990 y : minY + (this.unitWidth + this.gutter) * 2
31994 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31998 Roo.each(eItems, function(b,k){
32000 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32006 getVerticalOneBoxColPositions : function(x, y, box)
32010 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32012 if(box[0].size == 'md-left'){
32016 if(box[0].size == 'md-right'){
32021 x : x + (this.unitWidth + this.gutter) * rand,
32028 getVerticalTwoBoxColPositions : function(x, y, box)
32032 if(box[0].size == 'xs'){
32036 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32040 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32054 x : x + (this.unitWidth + this.gutter) * 2,
32055 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32062 getVerticalThreeBoxColPositions : function(x, y, box)
32066 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32074 x : x + (this.unitWidth + this.gutter) * 1,
32079 x : x + (this.unitWidth + this.gutter) * 2,
32087 if(box[0].size == 'xs' && box[1].size == 'xs'){
32096 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32100 x : x + (this.unitWidth + this.gutter) * 1,
32114 x : x + (this.unitWidth + this.gutter) * 2,
32119 x : x + (this.unitWidth + this.gutter) * 2,
32120 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32127 getVerticalFourBoxColPositions : function(x, y, box)
32131 if(box[0].size == 'xs'){
32140 y : y + (this.unitHeight + this.gutter) * 1
32145 y : y + (this.unitHeight + this.gutter) * 2
32149 x : x + (this.unitWidth + this.gutter) * 1,
32163 x : x + (this.unitWidth + this.gutter) * 2,
32168 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32169 y : y + (this.unitHeight + this.gutter) * 1
32173 x : x + (this.unitWidth + this.gutter) * 2,
32174 y : y + (this.unitWidth + this.gutter) * 2
32181 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32185 if(box[0].size == 'md-left'){
32187 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32194 if(box[0].size == 'md-right'){
32196 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32197 y : minY + (this.unitWidth + this.gutter) * 1
32203 var rand = Math.floor(Math.random() * (4 - box[0].y));
32206 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32207 y : minY + (this.unitWidth + this.gutter) * rand
32214 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32218 if(box[0].size == 'xs'){
32221 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32226 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32227 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32235 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32240 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32241 y : minY + (this.unitWidth + this.gutter) * 2
32248 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32252 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32255 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32260 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32261 y : minY + (this.unitWidth + this.gutter) * 1
32265 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32266 y : minY + (this.unitWidth + this.gutter) * 2
32273 if(box[0].size == 'xs' && box[1].size == 'xs'){
32276 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32281 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32286 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32287 y : minY + (this.unitWidth + this.gutter) * 1
32295 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32300 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32301 y : minY + (this.unitWidth + this.gutter) * 2
32305 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32306 y : minY + (this.unitWidth + this.gutter) * 2
32313 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32317 if(box[0].size == 'xs'){
32320 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32325 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32330 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),
32335 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32336 y : minY + (this.unitWidth + this.gutter) * 1
32344 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32349 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32350 y : minY + (this.unitWidth + this.gutter) * 2
32354 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32355 y : minY + (this.unitWidth + this.gutter) * 2
32359 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),
32360 y : minY + (this.unitWidth + this.gutter) * 2
32368 * remove a Masonry Brick
32369 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32371 removeBrick : function(brick_id)
32377 for (var i = 0; i<this.bricks.length; i++) {
32378 if (this.bricks[i].id == brick_id) {
32379 this.bricks.splice(i,1);
32380 this.el.dom.removeChild(Roo.get(brick_id).dom);
32387 * adds a Masonry Brick
32388 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32390 addBrick : function(cfg)
32392 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32393 //this.register(cn);
32394 cn.parentId = this.id;
32395 cn.render(this.el);
32400 * register a Masonry Brick
32401 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32404 register : function(brick)
32406 this.bricks.push(brick);
32407 brick.masonryId = this.id;
32411 * clear all the Masonry Brick
32413 clearAll : function()
32416 //this.getChildContainer().dom.innerHTML = "";
32417 this.el.dom.innerHTML = '';
32420 getSelected : function()
32422 if (!this.selectedBrick) {
32426 return this.selectedBrick;
32430 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32434 * register a Masonry Layout
32435 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32438 register : function(layout)
32440 this.groups[layout.id] = layout;
32443 * fetch a Masonry Layout based on the masonry layout ID
32444 * @param {string} the masonry layout to add
32445 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32448 get: function(layout_id) {
32449 if (typeof(this.groups[layout_id]) == 'undefined') {
32452 return this.groups[layout_id] ;
32464 * http://masonry.desandro.com
32466 * The idea is to render all the bricks based on vertical width...
32468 * The original code extends 'outlayer' - we might need to use that....
32474 * @class Roo.bootstrap.LayoutMasonryAuto
32475 * @extends Roo.bootstrap.Component
32476 * Bootstrap Layout Masonry class
32479 * Create a new Element
32480 * @param {Object} config The config object
32483 Roo.bootstrap.LayoutMasonryAuto = function(config){
32484 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32487 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32490 * @cfg {Boolean} isFitWidth - resize the width..
32492 isFitWidth : false, // options..
32494 * @cfg {Boolean} isOriginLeft = left align?
32496 isOriginLeft : true,
32498 * @cfg {Boolean} isOriginTop = top align?
32500 isOriginTop : false,
32502 * @cfg {Boolean} isLayoutInstant = no animation?
32504 isLayoutInstant : false, // needed?
32506 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32508 isResizingContainer : true,
32510 * @cfg {Number} columnWidth width of the columns
32516 * @cfg {Number} maxCols maximum number of columns
32521 * @cfg {Number} padHeight padding below box..
32527 * @cfg {Boolean} isAutoInitial defalut true
32530 isAutoInitial : true,
32536 initialColumnWidth : 0,
32537 currentSize : null,
32539 colYs : null, // array.
32546 bricks: null, //CompositeElement
32547 cols : 0, // array?
32548 // element : null, // wrapped now this.el
32549 _isLayoutInited : null,
32552 getAutoCreate : function(){
32556 cls: 'blog-masonary-wrapper ' + this.cls,
32558 cls : 'mas-boxes masonary'
32565 getChildContainer: function( )
32567 if (this.boxesEl) {
32568 return this.boxesEl;
32571 this.boxesEl = this.el.select('.mas-boxes').first();
32573 return this.boxesEl;
32577 initEvents : function()
32581 if(this.isAutoInitial){
32582 Roo.log('hook children rendered');
32583 this.on('childrenrendered', function() {
32584 Roo.log('children rendered');
32591 initial : function()
32593 this.reloadItems();
32595 this.currentSize = this.el.getBox(true);
32597 /// was window resize... - let's see if this works..
32598 Roo.EventManager.onWindowResize(this.resize, this);
32600 if(!this.isAutoInitial){
32605 this.layout.defer(500,this);
32608 reloadItems: function()
32610 this.bricks = this.el.select('.masonry-brick', true);
32612 this.bricks.each(function(b) {
32613 //Roo.log(b.getSize());
32614 if (!b.attr('originalwidth')) {
32615 b.attr('originalwidth', b.getSize().width);
32620 Roo.log(this.bricks.elements.length);
32623 resize : function()
32626 var cs = this.el.getBox(true);
32628 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32629 Roo.log("no change in with or X");
32632 this.currentSize = cs;
32636 layout : function()
32639 this._resetLayout();
32640 //this._manageStamps();
32642 // don't animate first layout
32643 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32644 this.layoutItems( isInstant );
32646 // flag for initalized
32647 this._isLayoutInited = true;
32650 layoutItems : function( isInstant )
32652 //var items = this._getItemsForLayout( this.items );
32653 // original code supports filtering layout items.. we just ignore it..
32655 this._layoutItems( this.bricks , isInstant );
32657 this._postLayout();
32659 _layoutItems : function ( items , isInstant)
32661 //this.fireEvent( 'layout', this, items );
32664 if ( !items || !items.elements.length ) {
32665 // no items, emit event with empty array
32670 items.each(function(item) {
32671 Roo.log("layout item");
32673 // get x/y object from method
32674 var position = this._getItemLayoutPosition( item );
32676 position.item = item;
32677 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32678 queue.push( position );
32681 this._processLayoutQueue( queue );
32683 /** Sets position of item in DOM
32684 * @param {Element} item
32685 * @param {Number} x - horizontal position
32686 * @param {Number} y - vertical position
32687 * @param {Boolean} isInstant - disables transitions
32689 _processLayoutQueue : function( queue )
32691 for ( var i=0, len = queue.length; i < len; i++ ) {
32692 var obj = queue[i];
32693 obj.item.position('absolute');
32694 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32700 * Any logic you want to do after each layout,
32701 * i.e. size the container
32703 _postLayout : function()
32705 this.resizeContainer();
32708 resizeContainer : function()
32710 if ( !this.isResizingContainer ) {
32713 var size = this._getContainerSize();
32715 this.el.setSize(size.width,size.height);
32716 this.boxesEl.setSize(size.width,size.height);
32722 _resetLayout : function()
32724 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32725 this.colWidth = this.el.getWidth();
32726 //this.gutter = this.el.getWidth();
32728 this.measureColumns();
32734 this.colYs.push( 0 );
32740 measureColumns : function()
32742 this.getContainerWidth();
32743 // if columnWidth is 0, default to outerWidth of first item
32744 if ( !this.columnWidth ) {
32745 var firstItem = this.bricks.first();
32746 Roo.log(firstItem);
32747 this.columnWidth = this.containerWidth;
32748 if (firstItem && firstItem.attr('originalwidth') ) {
32749 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32751 // columnWidth fall back to item of first element
32752 Roo.log("set column width?");
32753 this.initialColumnWidth = this.columnWidth ;
32755 // if first elem has no width, default to size of container
32760 if (this.initialColumnWidth) {
32761 this.columnWidth = this.initialColumnWidth;
32766 // column width is fixed at the top - however if container width get's smaller we should
32769 // this bit calcs how man columns..
32771 var columnWidth = this.columnWidth += this.gutter;
32773 // calculate columns
32774 var containerWidth = this.containerWidth + this.gutter;
32776 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32777 // fix rounding errors, typically with gutters
32778 var excess = columnWidth - containerWidth % columnWidth;
32781 // if overshoot is less than a pixel, round up, otherwise floor it
32782 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32783 cols = Math[ mathMethod ]( cols );
32784 this.cols = Math.max( cols, 1 );
32785 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32787 // padding positioning..
32788 var totalColWidth = this.cols * this.columnWidth;
32789 var padavail = this.containerWidth - totalColWidth;
32790 // so for 2 columns - we need 3 'pads'
32792 var padNeeded = (1+this.cols) * this.padWidth;
32794 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32796 this.columnWidth += padExtra
32797 //this.padWidth = Math.floor(padavail / ( this.cols));
32799 // adjust colum width so that padding is fixed??
32801 // we have 3 columns ... total = width * 3
32802 // we have X left over... that should be used by
32804 //if (this.expandC) {
32812 getContainerWidth : function()
32814 /* // container is parent if fit width
32815 var container = this.isFitWidth ? this.element.parentNode : this.element;
32816 // check that this.size and size are there
32817 // IE8 triggers resize on body size change, so they might not be
32819 var size = getSize( container ); //FIXME
32820 this.containerWidth = size && size.innerWidth; //FIXME
32823 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32827 _getItemLayoutPosition : function( item ) // what is item?
32829 // we resize the item to our columnWidth..
32831 item.setWidth(this.columnWidth);
32832 item.autoBoxAdjust = false;
32834 var sz = item.getSize();
32836 // how many columns does this brick span
32837 var remainder = this.containerWidth % this.columnWidth;
32839 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32840 // round if off by 1 pixel, otherwise use ceil
32841 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32842 colSpan = Math.min( colSpan, this.cols );
32844 // normally this should be '1' as we dont' currently allow multi width columns..
32846 var colGroup = this._getColGroup( colSpan );
32847 // get the minimum Y value from the columns
32848 var minimumY = Math.min.apply( Math, colGroup );
32849 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32851 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32853 // position the brick
32855 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32856 y: this.currentSize.y + minimumY + this.padHeight
32860 // apply setHeight to necessary columns
32861 var setHeight = minimumY + sz.height + this.padHeight;
32862 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32864 var setSpan = this.cols + 1 - colGroup.length;
32865 for ( var i = 0; i < setSpan; i++ ) {
32866 this.colYs[ shortColIndex + i ] = setHeight ;
32873 * @param {Number} colSpan - number of columns the element spans
32874 * @returns {Array} colGroup
32876 _getColGroup : function( colSpan )
32878 if ( colSpan < 2 ) {
32879 // if brick spans only one column, use all the column Ys
32884 // how many different places could this brick fit horizontally
32885 var groupCount = this.cols + 1 - colSpan;
32886 // for each group potential horizontal position
32887 for ( var i = 0; i < groupCount; i++ ) {
32888 // make an array of colY values for that one group
32889 var groupColYs = this.colYs.slice( i, i + colSpan );
32890 // and get the max value of the array
32891 colGroup[i] = Math.max.apply( Math, groupColYs );
32896 _manageStamp : function( stamp )
32898 var stampSize = stamp.getSize();
32899 var offset = stamp.getBox();
32900 // get the columns that this stamp affects
32901 var firstX = this.isOriginLeft ? offset.x : offset.right;
32902 var lastX = firstX + stampSize.width;
32903 var firstCol = Math.floor( firstX / this.columnWidth );
32904 firstCol = Math.max( 0, firstCol );
32906 var lastCol = Math.floor( lastX / this.columnWidth );
32907 // lastCol should not go over if multiple of columnWidth #425
32908 lastCol -= lastX % this.columnWidth ? 0 : 1;
32909 lastCol = Math.min( this.cols - 1, lastCol );
32911 // set colYs to bottom of the stamp
32912 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32915 for ( var i = firstCol; i <= lastCol; i++ ) {
32916 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32921 _getContainerSize : function()
32923 this.maxY = Math.max.apply( Math, this.colYs );
32928 if ( this.isFitWidth ) {
32929 size.width = this._getContainerFitWidth();
32935 _getContainerFitWidth : function()
32937 var unusedCols = 0;
32938 // count unused columns
32941 if ( this.colYs[i] !== 0 ) {
32946 // fit container to columns that have been used
32947 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32950 needsResizeLayout : function()
32952 var previousWidth = this.containerWidth;
32953 this.getContainerWidth();
32954 return previousWidth !== this.containerWidth;
32969 * @class Roo.bootstrap.MasonryBrick
32970 * @extends Roo.bootstrap.Component
32971 * Bootstrap MasonryBrick class
32974 * Create a new MasonryBrick
32975 * @param {Object} config The config object
32978 Roo.bootstrap.MasonryBrick = function(config){
32980 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32982 Roo.bootstrap.MasonryBrick.register(this);
32988 * When a MasonryBrick is clcik
32989 * @param {Roo.bootstrap.MasonryBrick} this
32990 * @param {Roo.EventObject} e
32996 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32999 * @cfg {String} title
33003 * @cfg {String} html
33007 * @cfg {String} bgimage
33011 * @cfg {String} videourl
33015 * @cfg {String} cls
33019 * @cfg {String} href
33023 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33028 * @cfg {String} placetitle (center|bottom)
33033 * @cfg {Boolean} isFitContainer defalut true
33035 isFitContainer : true,
33038 * @cfg {Boolean} preventDefault defalut false
33040 preventDefault : false,
33043 * @cfg {Boolean} inverse defalut false
33045 maskInverse : false,
33047 getAutoCreate : function()
33049 if(!this.isFitContainer){
33050 return this.getSplitAutoCreate();
33053 var cls = 'masonry-brick masonry-brick-full';
33055 if(this.href.length){
33056 cls += ' masonry-brick-link';
33059 if(this.bgimage.length){
33060 cls += ' masonry-brick-image';
33063 if(this.maskInverse){
33064 cls += ' mask-inverse';
33067 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33068 cls += ' enable-mask';
33072 cls += ' masonry-' + this.size + '-brick';
33075 if(this.placetitle.length){
33077 switch (this.placetitle) {
33079 cls += ' masonry-center-title';
33082 cls += ' masonry-bottom-title';
33089 if(!this.html.length && !this.bgimage.length){
33090 cls += ' masonry-center-title';
33093 if(!this.html.length && this.bgimage.length){
33094 cls += ' masonry-bottom-title';
33099 cls += ' ' + this.cls;
33103 tag: (this.href.length) ? 'a' : 'div',
33108 cls: 'masonry-brick-mask'
33112 cls: 'masonry-brick-paragraph',
33118 if(this.href.length){
33119 cfg.href = this.href;
33122 var cn = cfg.cn[1].cn;
33124 if(this.title.length){
33127 cls: 'masonry-brick-title',
33132 if(this.html.length){
33135 cls: 'masonry-brick-text',
33140 if (!this.title.length && !this.html.length) {
33141 cfg.cn[1].cls += ' hide';
33144 if(this.bgimage.length){
33147 cls: 'masonry-brick-image-view',
33152 if(this.videourl.length){
33153 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33154 // youtube support only?
33157 cls: 'masonry-brick-image-view',
33160 allowfullscreen : true
33168 getSplitAutoCreate : function()
33170 var cls = 'masonry-brick masonry-brick-split';
33172 if(this.href.length){
33173 cls += ' masonry-brick-link';
33176 if(this.bgimage.length){
33177 cls += ' masonry-brick-image';
33181 cls += ' masonry-' + this.size + '-brick';
33184 switch (this.placetitle) {
33186 cls += ' masonry-center-title';
33189 cls += ' masonry-bottom-title';
33192 if(!this.bgimage.length){
33193 cls += ' masonry-center-title';
33196 if(this.bgimage.length){
33197 cls += ' masonry-bottom-title';
33203 cls += ' ' + this.cls;
33207 tag: (this.href.length) ? 'a' : 'div',
33212 cls: 'masonry-brick-split-head',
33216 cls: 'masonry-brick-paragraph',
33223 cls: 'masonry-brick-split-body',
33229 if(this.href.length){
33230 cfg.href = this.href;
33233 if(this.title.length){
33234 cfg.cn[0].cn[0].cn.push({
33236 cls: 'masonry-brick-title',
33241 if(this.html.length){
33242 cfg.cn[1].cn.push({
33244 cls: 'masonry-brick-text',
33249 if(this.bgimage.length){
33250 cfg.cn[0].cn.push({
33252 cls: 'masonry-brick-image-view',
33257 if(this.videourl.length){
33258 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33259 // youtube support only?
33260 cfg.cn[0].cn.cn.push({
33262 cls: 'masonry-brick-image-view',
33265 allowfullscreen : true
33272 initEvents: function()
33274 switch (this.size) {
33307 this.el.on('touchstart', this.onTouchStart, this);
33308 this.el.on('touchmove', this.onTouchMove, this);
33309 this.el.on('touchend', this.onTouchEnd, this);
33310 this.el.on('contextmenu', this.onContextMenu, this);
33312 this.el.on('mouseenter' ,this.enter, this);
33313 this.el.on('mouseleave', this.leave, this);
33314 this.el.on('click', this.onClick, this);
33317 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33318 this.parent().bricks.push(this);
33323 onClick: function(e, el)
33325 var time = this.endTimer - this.startTimer;
33326 // Roo.log(e.preventDefault());
33329 e.preventDefault();
33334 if(!this.preventDefault){
33338 e.preventDefault();
33340 if (this.activeClass != '') {
33341 this.selectBrick();
33344 this.fireEvent('click', this, e);
33347 enter: function(e, el)
33349 e.preventDefault();
33351 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33355 if(this.bgimage.length && this.html.length){
33356 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33360 leave: function(e, el)
33362 e.preventDefault();
33364 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33368 if(this.bgimage.length && this.html.length){
33369 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33373 onTouchStart: function(e, el)
33375 // e.preventDefault();
33377 this.touchmoved = false;
33379 if(!this.isFitContainer){
33383 if(!this.bgimage.length || !this.html.length){
33387 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33389 this.timer = new Date().getTime();
33393 onTouchMove: function(e, el)
33395 this.touchmoved = true;
33398 onContextMenu : function(e,el)
33400 e.preventDefault();
33401 e.stopPropagation();
33405 onTouchEnd: function(e, el)
33407 // e.preventDefault();
33409 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33416 if(!this.bgimage.length || !this.html.length){
33418 if(this.href.length){
33419 window.location.href = this.href;
33425 if(!this.isFitContainer){
33429 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33431 window.location.href = this.href;
33434 //selection on single brick only
33435 selectBrick : function() {
33437 if (!this.parentId) {
33441 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33442 var index = m.selectedBrick.indexOf(this.id);
33445 m.selectedBrick.splice(index,1);
33446 this.el.removeClass(this.activeClass);
33450 for(var i = 0; i < m.selectedBrick.length; i++) {
33451 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33452 b.el.removeClass(b.activeClass);
33455 m.selectedBrick = [];
33457 m.selectedBrick.push(this.id);
33458 this.el.addClass(this.activeClass);
33462 isSelected : function(){
33463 return this.el.hasClass(this.activeClass);
33468 Roo.apply(Roo.bootstrap.MasonryBrick, {
33471 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33473 * register a Masonry Brick
33474 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33477 register : function(brick)
33479 //this.groups[brick.id] = brick;
33480 this.groups.add(brick.id, brick);
33483 * fetch a masonry brick based on the masonry brick ID
33484 * @param {string} the masonry brick to add
33485 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33488 get: function(brick_id)
33490 // if (typeof(this.groups[brick_id]) == 'undefined') {
33493 // return this.groups[brick_id] ;
33495 if(this.groups.key(brick_id)) {
33496 return this.groups.key(brick_id);
33514 * @class Roo.bootstrap.Brick
33515 * @extends Roo.bootstrap.Component
33516 * Bootstrap Brick class
33519 * Create a new Brick
33520 * @param {Object} config The config object
33523 Roo.bootstrap.Brick = function(config){
33524 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33530 * When a Brick is click
33531 * @param {Roo.bootstrap.Brick} this
33532 * @param {Roo.EventObject} e
33538 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33541 * @cfg {String} title
33545 * @cfg {String} html
33549 * @cfg {String} bgimage
33553 * @cfg {String} cls
33557 * @cfg {String} href
33561 * @cfg {String} video
33565 * @cfg {Boolean} square
33569 getAutoCreate : function()
33571 var cls = 'roo-brick';
33573 if(this.href.length){
33574 cls += ' roo-brick-link';
33577 if(this.bgimage.length){
33578 cls += ' roo-brick-image';
33581 if(!this.html.length && !this.bgimage.length){
33582 cls += ' roo-brick-center-title';
33585 if(!this.html.length && this.bgimage.length){
33586 cls += ' roo-brick-bottom-title';
33590 cls += ' ' + this.cls;
33594 tag: (this.href.length) ? 'a' : 'div',
33599 cls: 'roo-brick-paragraph',
33605 if(this.href.length){
33606 cfg.href = this.href;
33609 var cn = cfg.cn[0].cn;
33611 if(this.title.length){
33614 cls: 'roo-brick-title',
33619 if(this.html.length){
33622 cls: 'roo-brick-text',
33629 if(this.bgimage.length){
33632 cls: 'roo-brick-image-view',
33640 initEvents: function()
33642 if(this.title.length || this.html.length){
33643 this.el.on('mouseenter' ,this.enter, this);
33644 this.el.on('mouseleave', this.leave, this);
33647 Roo.EventManager.onWindowResize(this.resize, this);
33649 if(this.bgimage.length){
33650 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33651 this.imageEl.on('load', this.onImageLoad, this);
33658 onImageLoad : function()
33663 resize : function()
33665 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33667 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33669 if(this.bgimage.length){
33670 var image = this.el.select('.roo-brick-image-view', true).first();
33672 image.setWidth(paragraph.getWidth());
33675 image.setHeight(paragraph.getWidth());
33678 this.el.setHeight(image.getHeight());
33679 paragraph.setHeight(image.getHeight());
33685 enter: function(e, el)
33687 e.preventDefault();
33689 if(this.bgimage.length){
33690 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33691 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33695 leave: function(e, el)
33697 e.preventDefault();
33699 if(this.bgimage.length){
33700 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33701 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33716 * @class Roo.bootstrap.NumberField
33717 * @extends Roo.bootstrap.Input
33718 * Bootstrap NumberField class
33724 * Create a new NumberField
33725 * @param {Object} config The config object
33728 Roo.bootstrap.NumberField = function(config){
33729 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33732 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33735 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33737 allowDecimals : true,
33739 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33741 decimalSeparator : ".",
33743 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33745 decimalPrecision : 2,
33747 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33749 allowNegative : true,
33752 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33756 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33758 minValue : Number.NEGATIVE_INFINITY,
33760 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33762 maxValue : Number.MAX_VALUE,
33764 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33766 minText : "The minimum value for this field is {0}",
33768 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33770 maxText : "The maximum value for this field is {0}",
33772 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33773 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33775 nanText : "{0} is not a valid number",
33777 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33779 thousandsDelimiter : false,
33781 * @cfg {String} valueAlign alignment of value
33783 valueAlign : "left",
33785 getAutoCreate : function()
33787 var hiddenInput = {
33791 cls: 'hidden-number-input'
33795 hiddenInput.name = this.name;
33800 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33802 this.name = hiddenInput.name;
33804 if(cfg.cn.length > 0) {
33805 cfg.cn.push(hiddenInput);
33812 initEvents : function()
33814 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33816 var allowed = "0123456789";
33818 if(this.allowDecimals){
33819 allowed += this.decimalSeparator;
33822 if(this.allowNegative){
33826 if(this.thousandsDelimiter) {
33830 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33832 var keyPress = function(e){
33834 var k = e.getKey();
33836 var c = e.getCharCode();
33839 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33840 allowed.indexOf(String.fromCharCode(c)) === -1
33846 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33850 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33855 this.el.on("keypress", keyPress, this);
33858 validateValue : function(value)
33861 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33865 var num = this.parseValue(value);
33868 this.markInvalid(String.format(this.nanText, value));
33872 if(num < this.minValue){
33873 this.markInvalid(String.format(this.minText, this.minValue));
33877 if(num > this.maxValue){
33878 this.markInvalid(String.format(this.maxText, this.maxValue));
33885 getValue : function()
33887 var v = this.hiddenEl().getValue();
33889 return this.fixPrecision(this.parseValue(v));
33892 parseValue : function(value)
33894 if(this.thousandsDelimiter) {
33896 r = new RegExp(",", "g");
33897 value = value.replace(r, "");
33900 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33901 return isNaN(value) ? '' : value;
33904 fixPrecision : function(value)
33906 if(this.thousandsDelimiter) {
33908 r = new RegExp(",", "g");
33909 value = value.replace(r, "");
33912 var nan = isNaN(value);
33914 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33915 return nan ? '' : value;
33917 return parseFloat(value).toFixed(this.decimalPrecision);
33920 setValue : function(v)
33922 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33928 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33930 this.inputEl().dom.value = (v == '') ? '' :
33931 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33933 if(!this.allowZero && v === '0') {
33934 this.hiddenEl().dom.value = '';
33935 this.inputEl().dom.value = '';
33942 decimalPrecisionFcn : function(v)
33944 return Math.floor(v);
33947 beforeBlur : function()
33949 var v = this.parseValue(this.getRawValue());
33951 if(v || v === 0 || v === ''){
33956 hiddenEl : function()
33958 return this.el.select('input.hidden-number-input',true).first();
33970 * @class Roo.bootstrap.DocumentSlider
33971 * @extends Roo.bootstrap.Component
33972 * Bootstrap DocumentSlider class
33975 * Create a new DocumentViewer
33976 * @param {Object} config The config object
33979 Roo.bootstrap.DocumentSlider = function(config){
33980 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33987 * Fire after initEvent
33988 * @param {Roo.bootstrap.DocumentSlider} this
33993 * Fire after update
33994 * @param {Roo.bootstrap.DocumentSlider} this
34000 * @param {Roo.bootstrap.DocumentSlider} this
34006 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34012 getAutoCreate : function()
34016 cls : 'roo-document-slider',
34020 cls : 'roo-document-slider-header',
34024 cls : 'roo-document-slider-header-title'
34030 cls : 'roo-document-slider-body',
34034 cls : 'roo-document-slider-prev',
34038 cls : 'fa fa-chevron-left'
34044 cls : 'roo-document-slider-thumb',
34048 cls : 'roo-document-slider-image'
34054 cls : 'roo-document-slider-next',
34058 cls : 'fa fa-chevron-right'
34070 initEvents : function()
34072 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34073 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34075 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34076 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34078 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34079 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34081 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34082 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34084 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34085 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34087 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34088 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34090 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34091 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34093 this.thumbEl.on('click', this.onClick, this);
34095 this.prevIndicator.on('click', this.prev, this);
34097 this.nextIndicator.on('click', this.next, this);
34101 initial : function()
34103 if(this.files.length){
34104 this.indicator = 1;
34108 this.fireEvent('initial', this);
34111 update : function()
34113 this.imageEl.attr('src', this.files[this.indicator - 1]);
34115 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34117 this.prevIndicator.show();
34119 if(this.indicator == 1){
34120 this.prevIndicator.hide();
34123 this.nextIndicator.show();
34125 if(this.indicator == this.files.length){
34126 this.nextIndicator.hide();
34129 this.thumbEl.scrollTo('top');
34131 this.fireEvent('update', this);
34134 onClick : function(e)
34136 e.preventDefault();
34138 this.fireEvent('click', this);
34143 e.preventDefault();
34145 this.indicator = Math.max(1, this.indicator - 1);
34152 e.preventDefault();
34154 this.indicator = Math.min(this.files.length, this.indicator + 1);
34168 * @class Roo.bootstrap.RadioSet
34169 * @extends Roo.bootstrap.Input
34170 * Bootstrap RadioSet class
34171 * @cfg {String} indicatorpos (left|right) default left
34172 * @cfg {Boolean} inline (true|false) inline the element (default true)
34173 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34175 * Create a new RadioSet
34176 * @param {Object} config The config object
34179 Roo.bootstrap.RadioSet = function(config){
34181 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34185 Roo.bootstrap.RadioSet.register(this);
34190 * Fires when the element is checked or unchecked.
34191 * @param {Roo.bootstrap.RadioSet} this This radio
34192 * @param {Roo.bootstrap.Radio} item The checked item
34197 * Fires when the element is click.
34198 * @param {Roo.bootstrap.RadioSet} this This radio set
34199 * @param {Roo.bootstrap.Radio} item The checked item
34200 * @param {Roo.EventObject} e The event object
34207 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34215 indicatorpos : 'left',
34217 getAutoCreate : function()
34221 cls : 'roo-radio-set-label',
34225 html : this.fieldLabel
34229 if (Roo.bootstrap.version == 3) {
34232 if(this.indicatorpos == 'left'){
34235 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34236 tooltip : 'This field is required'
34241 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34242 tooltip : 'This field is required'
34248 cls : 'roo-radio-set-items'
34251 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34253 if (align === 'left' && this.fieldLabel.length) {
34256 cls : "roo-radio-set-right",
34262 if(this.labelWidth > 12){
34263 label.style = "width: " + this.labelWidth + 'px';
34266 if(this.labelWidth < 13 && this.labelmd == 0){
34267 this.labelmd = this.labelWidth;
34270 if(this.labellg > 0){
34271 label.cls += ' col-lg-' + this.labellg;
34272 items.cls += ' col-lg-' + (12 - this.labellg);
34275 if(this.labelmd > 0){
34276 label.cls += ' col-md-' + this.labelmd;
34277 items.cls += ' col-md-' + (12 - this.labelmd);
34280 if(this.labelsm > 0){
34281 label.cls += ' col-sm-' + this.labelsm;
34282 items.cls += ' col-sm-' + (12 - this.labelsm);
34285 if(this.labelxs > 0){
34286 label.cls += ' col-xs-' + this.labelxs;
34287 items.cls += ' col-xs-' + (12 - this.labelxs);
34293 cls : 'roo-radio-set',
34297 cls : 'roo-radio-set-input',
34300 value : this.value ? this.value : ''
34307 if(this.weight.length){
34308 cfg.cls += ' roo-radio-' + this.weight;
34312 cfg.cls += ' roo-radio-set-inline';
34316 ['xs','sm','md','lg'].map(function(size){
34317 if (settings[size]) {
34318 cfg.cls += ' col-' + size + '-' + settings[size];
34326 initEvents : function()
34328 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34329 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34331 if(!this.fieldLabel.length){
34332 this.labelEl.hide();
34335 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34336 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34338 this.indicator = this.indicatorEl();
34340 if(this.indicator){
34341 this.indicator.addClass('invisible');
34344 this.originalValue = this.getValue();
34348 inputEl: function ()
34350 return this.el.select('.roo-radio-set-input', true).first();
34353 getChildContainer : function()
34355 return this.itemsEl;
34358 register : function(item)
34360 this.radioes.push(item);
34364 validate : function()
34366 if(this.getVisibilityEl().hasClass('hidden')){
34372 Roo.each(this.radioes, function(i){
34381 if(this.allowBlank) {
34385 if(this.disabled || valid){
34390 this.markInvalid();
34395 markValid : function()
34397 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34398 this.indicatorEl().removeClass('visible');
34399 this.indicatorEl().addClass('invisible');
34403 if (Roo.bootstrap.version == 3) {
34404 this.el.removeClass([this.invalidClass, this.validClass]);
34405 this.el.addClass(this.validClass);
34407 this.el.removeClass(['is-invalid','is-valid']);
34408 this.el.addClass(['is-valid']);
34410 this.fireEvent('valid', this);
34413 markInvalid : function(msg)
34415 if(this.allowBlank || this.disabled){
34419 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34420 this.indicatorEl().removeClass('invisible');
34421 this.indicatorEl().addClass('visible');
34423 if (Roo.bootstrap.version == 3) {
34424 this.el.removeClass([this.invalidClass, this.validClass]);
34425 this.el.addClass(this.invalidClass);
34427 this.el.removeClass(['is-invalid','is-valid']);
34428 this.el.addClass(['is-invalid']);
34431 this.fireEvent('invalid', this, msg);
34435 setValue : function(v, suppressEvent)
34437 if(this.value === v){
34444 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34447 Roo.each(this.radioes, function(i){
34449 i.el.removeClass('checked');
34452 Roo.each(this.radioes, function(i){
34454 if(i.value === v || i.value.toString() === v.toString()){
34456 i.el.addClass('checked');
34458 if(suppressEvent !== true){
34459 this.fireEvent('check', this, i);
34470 clearInvalid : function(){
34472 if(!this.el || this.preventMark){
34476 this.el.removeClass([this.invalidClass]);
34478 this.fireEvent('valid', this);
34483 Roo.apply(Roo.bootstrap.RadioSet, {
34487 register : function(set)
34489 this.groups[set.name] = set;
34492 get: function(name)
34494 if (typeof(this.groups[name]) == 'undefined') {
34498 return this.groups[name] ;
34504 * Ext JS Library 1.1.1
34505 * Copyright(c) 2006-2007, Ext JS, LLC.
34507 * Originally Released Under LGPL - original licence link has changed is not relivant.
34510 * <script type="text/javascript">
34515 * @class Roo.bootstrap.SplitBar
34516 * @extends Roo.util.Observable
34517 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34521 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34522 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34523 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34524 split.minSize = 100;
34525 split.maxSize = 600;
34526 split.animate = true;
34527 split.on('moved', splitterMoved);
34530 * Create a new SplitBar
34531 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34532 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34533 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34534 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34535 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34536 position of the SplitBar).
34538 Roo.bootstrap.SplitBar = function(cfg){
34543 // dragElement : elm
34544 // resizingElement: el,
34546 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34547 // placement : Roo.bootstrap.SplitBar.LEFT ,
34548 // existingProxy ???
34551 this.el = Roo.get(cfg.dragElement, true);
34552 this.el.dom.unselectable = "on";
34554 this.resizingEl = Roo.get(cfg.resizingElement, true);
34558 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34559 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34562 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34565 * The minimum size of the resizing element. (Defaults to 0)
34571 * The maximum size of the resizing element. (Defaults to 2000)
34574 this.maxSize = 2000;
34577 * Whether to animate the transition to the new size
34580 this.animate = false;
34583 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34586 this.useShim = false;
34591 if(!cfg.existingProxy){
34593 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34595 this.proxy = Roo.get(cfg.existingProxy).dom;
34598 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34601 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34604 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34607 this.dragSpecs = {};
34610 * @private The adapter to use to positon and resize elements
34612 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34613 this.adapter.init(this);
34615 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34617 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34618 this.el.addClass("roo-splitbar-h");
34621 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34622 this.el.addClass("roo-splitbar-v");
34628 * Fires when the splitter is moved (alias for {@link #event-moved})
34629 * @param {Roo.bootstrap.SplitBar} this
34630 * @param {Number} newSize the new width or height
34635 * Fires when the splitter is moved
34636 * @param {Roo.bootstrap.SplitBar} this
34637 * @param {Number} newSize the new width or height
34641 * @event beforeresize
34642 * Fires before the splitter is dragged
34643 * @param {Roo.bootstrap.SplitBar} this
34645 "beforeresize" : true,
34647 "beforeapply" : true
34650 Roo.util.Observable.call(this);
34653 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34654 onStartProxyDrag : function(x, y){
34655 this.fireEvent("beforeresize", this);
34657 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34659 o.enableDisplayMode("block");
34660 // all splitbars share the same overlay
34661 Roo.bootstrap.SplitBar.prototype.overlay = o;
34663 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34664 this.overlay.show();
34665 Roo.get(this.proxy).setDisplayed("block");
34666 var size = this.adapter.getElementSize(this);
34667 this.activeMinSize = this.getMinimumSize();;
34668 this.activeMaxSize = this.getMaximumSize();;
34669 var c1 = size - this.activeMinSize;
34670 var c2 = Math.max(this.activeMaxSize - size, 0);
34671 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34672 this.dd.resetConstraints();
34673 this.dd.setXConstraint(
34674 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34675 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34677 this.dd.setYConstraint(0, 0);
34679 this.dd.resetConstraints();
34680 this.dd.setXConstraint(0, 0);
34681 this.dd.setYConstraint(
34682 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34683 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34686 this.dragSpecs.startSize = size;
34687 this.dragSpecs.startPoint = [x, y];
34688 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34692 * @private Called after the drag operation by the DDProxy
34694 onEndProxyDrag : function(e){
34695 Roo.get(this.proxy).setDisplayed(false);
34696 var endPoint = Roo.lib.Event.getXY(e);
34698 this.overlay.hide();
34701 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34702 newSize = this.dragSpecs.startSize +
34703 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34704 endPoint[0] - this.dragSpecs.startPoint[0] :
34705 this.dragSpecs.startPoint[0] - endPoint[0]
34708 newSize = this.dragSpecs.startSize +
34709 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34710 endPoint[1] - this.dragSpecs.startPoint[1] :
34711 this.dragSpecs.startPoint[1] - endPoint[1]
34714 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34715 if(newSize != this.dragSpecs.startSize){
34716 if(this.fireEvent('beforeapply', this, newSize) !== false){
34717 this.adapter.setElementSize(this, newSize);
34718 this.fireEvent("moved", this, newSize);
34719 this.fireEvent("resize", this, newSize);
34725 * Get the adapter this SplitBar uses
34726 * @return The adapter object
34728 getAdapter : function(){
34729 return this.adapter;
34733 * Set the adapter this SplitBar uses
34734 * @param {Object} adapter A SplitBar adapter object
34736 setAdapter : function(adapter){
34737 this.adapter = adapter;
34738 this.adapter.init(this);
34742 * Gets the minimum size for the resizing element
34743 * @return {Number} The minimum size
34745 getMinimumSize : function(){
34746 return this.minSize;
34750 * Sets the minimum size for the resizing element
34751 * @param {Number} minSize The minimum size
34753 setMinimumSize : function(minSize){
34754 this.minSize = minSize;
34758 * Gets the maximum size for the resizing element
34759 * @return {Number} The maximum size
34761 getMaximumSize : function(){
34762 return this.maxSize;
34766 * Sets the maximum size for the resizing element
34767 * @param {Number} maxSize The maximum size
34769 setMaximumSize : function(maxSize){
34770 this.maxSize = maxSize;
34774 * Sets the initialize size for the resizing element
34775 * @param {Number} size The initial size
34777 setCurrentSize : function(size){
34778 var oldAnimate = this.animate;
34779 this.animate = false;
34780 this.adapter.setElementSize(this, size);
34781 this.animate = oldAnimate;
34785 * Destroy this splitbar.
34786 * @param {Boolean} removeEl True to remove the element
34788 destroy : function(removeEl){
34790 this.shim.remove();
34793 this.proxy.parentNode.removeChild(this.proxy);
34801 * @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.
34803 Roo.bootstrap.SplitBar.createProxy = function(dir){
34804 var proxy = new Roo.Element(document.createElement("div"));
34805 proxy.unselectable();
34806 var cls = 'roo-splitbar-proxy';
34807 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34808 document.body.appendChild(proxy.dom);
34813 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34814 * Default Adapter. It assumes the splitter and resizing element are not positioned
34815 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34817 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34820 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34821 // do nothing for now
34822 init : function(s){
34826 * Called before drag operations to get the current size of the resizing element.
34827 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34829 getElementSize : function(s){
34830 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34831 return s.resizingEl.getWidth();
34833 return s.resizingEl.getHeight();
34838 * Called after drag operations to set the size of the resizing element.
34839 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34840 * @param {Number} newSize The new size to set
34841 * @param {Function} onComplete A function to be invoked when resizing is complete
34843 setElementSize : function(s, newSize, onComplete){
34844 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34846 s.resizingEl.setWidth(newSize);
34848 onComplete(s, newSize);
34851 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34856 s.resizingEl.setHeight(newSize);
34858 onComplete(s, newSize);
34861 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34868 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34869 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34870 * Adapter that moves the splitter element to align with the resized sizing element.
34871 * Used with an absolute positioned SplitBar.
34872 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34873 * document.body, make sure you assign an id to the body element.
34875 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34876 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34877 this.container = Roo.get(container);
34880 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34881 init : function(s){
34882 this.basic.init(s);
34885 getElementSize : function(s){
34886 return this.basic.getElementSize(s);
34889 setElementSize : function(s, newSize, onComplete){
34890 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34893 moveSplitter : function(s){
34894 var yes = Roo.bootstrap.SplitBar;
34895 switch(s.placement){
34897 s.el.setX(s.resizingEl.getRight());
34900 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34903 s.el.setY(s.resizingEl.getBottom());
34906 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34913 * Orientation constant - Create a vertical SplitBar
34917 Roo.bootstrap.SplitBar.VERTICAL = 1;
34920 * Orientation constant - Create a horizontal SplitBar
34924 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34927 * Placement constant - The resizing element is to the left of the splitter element
34931 Roo.bootstrap.SplitBar.LEFT = 1;
34934 * Placement constant - The resizing element is to the right of the splitter element
34938 Roo.bootstrap.SplitBar.RIGHT = 2;
34941 * Placement constant - The resizing element is positioned above the splitter element
34945 Roo.bootstrap.SplitBar.TOP = 3;
34948 * Placement constant - The resizing element is positioned under splitter element
34952 Roo.bootstrap.SplitBar.BOTTOM = 4;
34953 Roo.namespace("Roo.bootstrap.layout");/*
34955 * Ext JS Library 1.1.1
34956 * Copyright(c) 2006-2007, Ext JS, LLC.
34958 * Originally Released Under LGPL - original licence link has changed is not relivant.
34961 * <script type="text/javascript">
34965 * @class Roo.bootstrap.layout.Manager
34966 * @extends Roo.bootstrap.Component
34967 * Base class for layout managers.
34969 Roo.bootstrap.layout.Manager = function(config)
34971 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34977 /** false to disable window resize monitoring @type Boolean */
34978 this.monitorWindowResize = true;
34983 * Fires when a layout is performed.
34984 * @param {Roo.LayoutManager} this
34988 * @event regionresized
34989 * Fires when the user resizes a region.
34990 * @param {Roo.LayoutRegion} region The resized region
34991 * @param {Number} newSize The new size (width for east/west, height for north/south)
34993 "regionresized" : true,
34995 * @event regioncollapsed
34996 * Fires when a region is collapsed.
34997 * @param {Roo.LayoutRegion} region The collapsed region
34999 "regioncollapsed" : true,
35001 * @event regionexpanded
35002 * Fires when a region is expanded.
35003 * @param {Roo.LayoutRegion} region The expanded region
35005 "regionexpanded" : true
35007 this.updating = false;
35010 this.el = Roo.get(config.el);
35016 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35021 monitorWindowResize : true,
35027 onRender : function(ct, position)
35030 this.el = Roo.get(ct);
35033 //this.fireEvent('render',this);
35037 initEvents: function()
35041 // ie scrollbar fix
35042 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35043 document.body.scroll = "no";
35044 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35045 this.el.position('relative');
35047 this.id = this.el.id;
35048 this.el.addClass("roo-layout-container");
35049 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35050 if(this.el.dom != document.body ) {
35051 this.el.on('resize', this.layout,this);
35052 this.el.on('show', this.layout,this);
35058 * Returns true if this layout is currently being updated
35059 * @return {Boolean}
35061 isUpdating : function(){
35062 return this.updating;
35066 * Suspend the LayoutManager from doing auto-layouts while
35067 * making multiple add or remove calls
35069 beginUpdate : function(){
35070 this.updating = true;
35074 * Restore auto-layouts and optionally disable the manager from performing a layout
35075 * @param {Boolean} noLayout true to disable a layout update
35077 endUpdate : function(noLayout){
35078 this.updating = false;
35084 layout: function(){
35088 onRegionResized : function(region, newSize){
35089 this.fireEvent("regionresized", region, newSize);
35093 onRegionCollapsed : function(region){
35094 this.fireEvent("regioncollapsed", region);
35097 onRegionExpanded : function(region){
35098 this.fireEvent("regionexpanded", region);
35102 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35103 * performs box-model adjustments.
35104 * @return {Object} The size as an object {width: (the width), height: (the height)}
35106 getViewSize : function()
35109 if(this.el.dom != document.body){
35110 size = this.el.getSize();
35112 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35114 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35115 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35120 * Returns the Element this layout is bound to.
35121 * @return {Roo.Element}
35123 getEl : function(){
35128 * Returns the specified region.
35129 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35130 * @return {Roo.LayoutRegion}
35132 getRegion : function(target){
35133 return this.regions[target.toLowerCase()];
35136 onWindowResize : function(){
35137 if(this.monitorWindowResize){
35144 * Ext JS Library 1.1.1
35145 * Copyright(c) 2006-2007, Ext JS, LLC.
35147 * Originally Released Under LGPL - original licence link has changed is not relivant.
35150 * <script type="text/javascript">
35153 * @class Roo.bootstrap.layout.Border
35154 * @extends Roo.bootstrap.layout.Manager
35155 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35156 * please see: examples/bootstrap/nested.html<br><br>
35158 <b>The container the layout is rendered into can be either the body element or any other element.
35159 If it is not the body element, the container needs to either be an absolute positioned element,
35160 or you will need to add "position:relative" to the css of the container. You will also need to specify
35161 the container size if it is not the body element.</b>
35164 * Create a new Border
35165 * @param {Object} config Configuration options
35167 Roo.bootstrap.layout.Border = function(config){
35168 config = config || {};
35169 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35173 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35174 if(config[region]){
35175 config[region].region = region;
35176 this.addRegion(config[region]);
35182 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35184 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35186 parent : false, // this might point to a 'nest' or a ???
35189 * Creates and adds a new region if it doesn't already exist.
35190 * @param {String} target The target region key (north, south, east, west or center).
35191 * @param {Object} config The regions config object
35192 * @return {BorderLayoutRegion} The new region
35194 addRegion : function(config)
35196 if(!this.regions[config.region]){
35197 var r = this.factory(config);
35198 this.bindRegion(r);
35200 return this.regions[config.region];
35204 bindRegion : function(r){
35205 this.regions[r.config.region] = r;
35207 r.on("visibilitychange", this.layout, this);
35208 r.on("paneladded", this.layout, this);
35209 r.on("panelremoved", this.layout, this);
35210 r.on("invalidated", this.layout, this);
35211 r.on("resized", this.onRegionResized, this);
35212 r.on("collapsed", this.onRegionCollapsed, this);
35213 r.on("expanded", this.onRegionExpanded, this);
35217 * Performs a layout update.
35219 layout : function()
35221 if(this.updating) {
35225 // render all the rebions if they have not been done alreayd?
35226 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35227 if(this.regions[region] && !this.regions[region].bodyEl){
35228 this.regions[region].onRender(this.el)
35232 var size = this.getViewSize();
35233 var w = size.width;
35234 var h = size.height;
35239 //var x = 0, y = 0;
35241 var rs = this.regions;
35242 var north = rs["north"];
35243 var south = rs["south"];
35244 var west = rs["west"];
35245 var east = rs["east"];
35246 var center = rs["center"];
35247 //if(this.hideOnLayout){ // not supported anymore
35248 //c.el.setStyle("display", "none");
35250 if(north && north.isVisible()){
35251 var b = north.getBox();
35252 var m = north.getMargins();
35253 b.width = w - (m.left+m.right);
35256 centerY = b.height + b.y + m.bottom;
35257 centerH -= centerY;
35258 north.updateBox(this.safeBox(b));
35260 if(south && south.isVisible()){
35261 var b = south.getBox();
35262 var m = south.getMargins();
35263 b.width = w - (m.left+m.right);
35265 var totalHeight = (b.height + m.top + m.bottom);
35266 b.y = h - totalHeight + m.top;
35267 centerH -= totalHeight;
35268 south.updateBox(this.safeBox(b));
35270 if(west && west.isVisible()){
35271 var b = west.getBox();
35272 var m = west.getMargins();
35273 b.height = centerH - (m.top+m.bottom);
35275 b.y = centerY + m.top;
35276 var totalWidth = (b.width + m.left + m.right);
35277 centerX += totalWidth;
35278 centerW -= totalWidth;
35279 west.updateBox(this.safeBox(b));
35281 if(east && east.isVisible()){
35282 var b = east.getBox();
35283 var m = east.getMargins();
35284 b.height = centerH - (m.top+m.bottom);
35285 var totalWidth = (b.width + m.left + m.right);
35286 b.x = w - totalWidth + m.left;
35287 b.y = centerY + m.top;
35288 centerW -= totalWidth;
35289 east.updateBox(this.safeBox(b));
35292 var m = center.getMargins();
35294 x: centerX + m.left,
35295 y: centerY + m.top,
35296 width: centerW - (m.left+m.right),
35297 height: centerH - (m.top+m.bottom)
35299 //if(this.hideOnLayout){
35300 //center.el.setStyle("display", "block");
35302 center.updateBox(this.safeBox(centerBox));
35305 this.fireEvent("layout", this);
35309 safeBox : function(box){
35310 box.width = Math.max(0, box.width);
35311 box.height = Math.max(0, box.height);
35316 * Adds a ContentPanel (or subclass) to this layout.
35317 * @param {String} target The target region key (north, south, east, west or center).
35318 * @param {Roo.ContentPanel} panel The panel to add
35319 * @return {Roo.ContentPanel} The added panel
35321 add : function(target, panel){
35323 target = target.toLowerCase();
35324 return this.regions[target].add(panel);
35328 * Remove a ContentPanel (or subclass) to this layout.
35329 * @param {String} target The target region key (north, south, east, west or center).
35330 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35331 * @return {Roo.ContentPanel} The removed panel
35333 remove : function(target, panel){
35334 target = target.toLowerCase();
35335 return this.regions[target].remove(panel);
35339 * Searches all regions for a panel with the specified id
35340 * @param {String} panelId
35341 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35343 findPanel : function(panelId){
35344 var rs = this.regions;
35345 for(var target in rs){
35346 if(typeof rs[target] != "function"){
35347 var p = rs[target].getPanel(panelId);
35357 * Searches all regions for a panel with the specified id and activates (shows) it.
35358 * @param {String/ContentPanel} panelId The panels id or the panel itself
35359 * @return {Roo.ContentPanel} The shown panel or null
35361 showPanel : function(panelId) {
35362 var rs = this.regions;
35363 for(var target in rs){
35364 var r = rs[target];
35365 if(typeof r != "function"){
35366 if(r.hasPanel(panelId)){
35367 return r.showPanel(panelId);
35375 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35376 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35379 restoreState : function(provider){
35381 provider = Roo.state.Manager;
35383 var sm = new Roo.LayoutStateManager();
35384 sm.init(this, provider);
35390 * Adds a xtype elements to the layout.
35394 xtype : 'ContentPanel',
35401 xtype : 'NestedLayoutPanel',
35407 items : [ ... list of content panels or nested layout panels.. ]
35411 * @param {Object} cfg Xtype definition of item to add.
35413 addxtype : function(cfg)
35415 // basically accepts a pannel...
35416 // can accept a layout region..!?!?
35417 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35420 // theory? children can only be panels??
35422 //if (!cfg.xtype.match(/Panel$/)) {
35427 if (typeof(cfg.region) == 'undefined') {
35428 Roo.log("Failed to add Panel, region was not set");
35432 var region = cfg.region;
35438 xitems = cfg.items;
35443 if ( region == 'center') {
35444 Roo.log("Center: " + cfg.title);
35450 case 'Content': // ContentPanel (el, cfg)
35451 case 'Scroll': // ContentPanel (el, cfg)
35453 cfg.autoCreate = cfg.autoCreate || true;
35454 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35456 // var el = this.el.createChild();
35457 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35460 this.add(region, ret);
35464 case 'TreePanel': // our new panel!
35465 cfg.el = this.el.createChild();
35466 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35467 this.add(region, ret);
35472 // create a new Layout (which is a Border Layout...
35474 var clayout = cfg.layout;
35475 clayout.el = this.el.createChild();
35476 clayout.items = clayout.items || [];
35480 // replace this exitems with the clayout ones..
35481 xitems = clayout.items;
35483 // force background off if it's in center...
35484 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35485 cfg.background = false;
35487 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35490 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35491 //console.log('adding nested layout panel ' + cfg.toSource());
35492 this.add(region, ret);
35493 nb = {}; /// find first...
35498 // needs grid and region
35500 //var el = this.getRegion(region).el.createChild();
35502 *var el = this.el.createChild();
35503 // create the grid first...
35504 cfg.grid.container = el;
35505 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35508 if (region == 'center' && this.active ) {
35509 cfg.background = false;
35512 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35514 this.add(region, ret);
35516 if (cfg.background) {
35517 // render grid on panel activation (if panel background)
35518 ret.on('activate', function(gp) {
35519 if (!gp.grid.rendered) {
35520 // gp.grid.render(el);
35524 // cfg.grid.render(el);
35530 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35531 // it was the old xcomponent building that caused this before.
35532 // espeically if border is the top element in the tree.
35542 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35544 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35545 this.add(region, ret);
35549 throw "Can not add '" + cfg.xtype + "' to Border";
35555 this.beginUpdate();
35559 Roo.each(xitems, function(i) {
35560 region = nb && i.region ? i.region : false;
35562 var add = ret.addxtype(i);
35565 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35566 if (!i.background) {
35567 abn[region] = nb[region] ;
35574 // make the last non-background panel active..
35575 //if (nb) { Roo.log(abn); }
35578 for(var r in abn) {
35579 region = this.getRegion(r);
35581 // tried using nb[r], but it does not work..
35583 region.showPanel(abn[r]);
35594 factory : function(cfg)
35597 var validRegions = Roo.bootstrap.layout.Border.regions;
35599 var target = cfg.region;
35602 var r = Roo.bootstrap.layout;
35606 return new r.North(cfg);
35608 return new r.South(cfg);
35610 return new r.East(cfg);
35612 return new r.West(cfg);
35614 return new r.Center(cfg);
35616 throw 'Layout region "'+target+'" not supported.';
35623 * Ext JS Library 1.1.1
35624 * Copyright(c) 2006-2007, Ext JS, LLC.
35626 * Originally Released Under LGPL - original licence link has changed is not relivant.
35629 * <script type="text/javascript">
35633 * @class Roo.bootstrap.layout.Basic
35634 * @extends Roo.util.Observable
35635 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35636 * and does not have a titlebar, tabs or any other features. All it does is size and position
35637 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35638 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35639 * @cfg {string} region the region that it inhabits..
35640 * @cfg {bool} skipConfig skip config?
35644 Roo.bootstrap.layout.Basic = function(config){
35646 this.mgr = config.mgr;
35648 this.position = config.region;
35650 var skipConfig = config.skipConfig;
35654 * @scope Roo.BasicLayoutRegion
35658 * @event beforeremove
35659 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35660 * @param {Roo.LayoutRegion} this
35661 * @param {Roo.ContentPanel} panel The panel
35662 * @param {Object} e The cancel event object
35664 "beforeremove" : true,
35666 * @event invalidated
35667 * Fires when the layout for this region is changed.
35668 * @param {Roo.LayoutRegion} this
35670 "invalidated" : true,
35672 * @event visibilitychange
35673 * Fires when this region is shown or hidden
35674 * @param {Roo.LayoutRegion} this
35675 * @param {Boolean} visibility true or false
35677 "visibilitychange" : true,
35679 * @event paneladded
35680 * Fires when a panel is added.
35681 * @param {Roo.LayoutRegion} this
35682 * @param {Roo.ContentPanel} panel The panel
35684 "paneladded" : true,
35686 * @event panelremoved
35687 * Fires when a panel is removed.
35688 * @param {Roo.LayoutRegion} this
35689 * @param {Roo.ContentPanel} panel The panel
35691 "panelremoved" : true,
35693 * @event beforecollapse
35694 * Fires when this region before collapse.
35695 * @param {Roo.LayoutRegion} this
35697 "beforecollapse" : true,
35700 * Fires when this region is collapsed.
35701 * @param {Roo.LayoutRegion} this
35703 "collapsed" : true,
35706 * Fires when this region is expanded.
35707 * @param {Roo.LayoutRegion} this
35712 * Fires when this region is slid into view.
35713 * @param {Roo.LayoutRegion} this
35715 "slideshow" : true,
35718 * Fires when this region slides out of view.
35719 * @param {Roo.LayoutRegion} this
35721 "slidehide" : true,
35723 * @event panelactivated
35724 * Fires when a panel is activated.
35725 * @param {Roo.LayoutRegion} this
35726 * @param {Roo.ContentPanel} panel The activated panel
35728 "panelactivated" : true,
35731 * Fires when the user resizes this region.
35732 * @param {Roo.LayoutRegion} this
35733 * @param {Number} newSize The new size (width for east/west, height for north/south)
35737 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35738 this.panels = new Roo.util.MixedCollection();
35739 this.panels.getKey = this.getPanelId.createDelegate(this);
35741 this.activePanel = null;
35742 // ensure listeners are added...
35744 if (config.listeners || config.events) {
35745 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35746 listeners : config.listeners || {},
35747 events : config.events || {}
35751 if(skipConfig !== true){
35752 this.applyConfig(config);
35756 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35758 getPanelId : function(p){
35762 applyConfig : function(config){
35763 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35764 this.config = config;
35769 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35770 * the width, for horizontal (north, south) the height.
35771 * @param {Number} newSize The new width or height
35773 resizeTo : function(newSize){
35774 var el = this.el ? this.el :
35775 (this.activePanel ? this.activePanel.getEl() : null);
35777 switch(this.position){
35780 el.setWidth(newSize);
35781 this.fireEvent("resized", this, newSize);
35785 el.setHeight(newSize);
35786 this.fireEvent("resized", this, newSize);
35792 getBox : function(){
35793 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35796 getMargins : function(){
35797 return this.margins;
35800 updateBox : function(box){
35802 var el = this.activePanel.getEl();
35803 el.dom.style.left = box.x + "px";
35804 el.dom.style.top = box.y + "px";
35805 this.activePanel.setSize(box.width, box.height);
35809 * Returns the container element for this region.
35810 * @return {Roo.Element}
35812 getEl : function(){
35813 return this.activePanel;
35817 * Returns true if this region is currently visible.
35818 * @return {Boolean}
35820 isVisible : function(){
35821 return this.activePanel ? true : false;
35824 setActivePanel : function(panel){
35825 panel = this.getPanel(panel);
35826 if(this.activePanel && this.activePanel != panel){
35827 this.activePanel.setActiveState(false);
35828 this.activePanel.getEl().setLeftTop(-10000,-10000);
35830 this.activePanel = panel;
35831 panel.setActiveState(true);
35833 panel.setSize(this.box.width, this.box.height);
35835 this.fireEvent("panelactivated", this, panel);
35836 this.fireEvent("invalidated");
35840 * Show the specified panel.
35841 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35842 * @return {Roo.ContentPanel} The shown panel or null
35844 showPanel : function(panel){
35845 panel = this.getPanel(panel);
35847 this.setActivePanel(panel);
35853 * Get the active panel for this region.
35854 * @return {Roo.ContentPanel} The active panel or null
35856 getActivePanel : function(){
35857 return this.activePanel;
35861 * Add the passed ContentPanel(s)
35862 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35863 * @return {Roo.ContentPanel} The panel added (if only one was added)
35865 add : function(panel){
35866 if(arguments.length > 1){
35867 for(var i = 0, len = arguments.length; i < len; i++) {
35868 this.add(arguments[i]);
35872 if(this.hasPanel(panel)){
35873 this.showPanel(panel);
35876 var el = panel.getEl();
35877 if(el.dom.parentNode != this.mgr.el.dom){
35878 this.mgr.el.dom.appendChild(el.dom);
35880 if(panel.setRegion){
35881 panel.setRegion(this);
35883 this.panels.add(panel);
35884 el.setStyle("position", "absolute");
35885 if(!panel.background){
35886 this.setActivePanel(panel);
35887 if(this.config.initialSize && this.panels.getCount()==1){
35888 this.resizeTo(this.config.initialSize);
35891 this.fireEvent("paneladded", this, panel);
35896 * Returns true if the panel is in this region.
35897 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35898 * @return {Boolean}
35900 hasPanel : function(panel){
35901 if(typeof panel == "object"){ // must be panel obj
35902 panel = panel.getId();
35904 return this.getPanel(panel) ? true : false;
35908 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35909 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35910 * @param {Boolean} preservePanel Overrides the config preservePanel option
35911 * @return {Roo.ContentPanel} The panel that was removed
35913 remove : function(panel, preservePanel){
35914 panel = this.getPanel(panel);
35919 this.fireEvent("beforeremove", this, panel, e);
35920 if(e.cancel === true){
35923 var panelId = panel.getId();
35924 this.panels.removeKey(panelId);
35929 * Returns the panel specified or null if it's not in this region.
35930 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35931 * @return {Roo.ContentPanel}
35933 getPanel : function(id){
35934 if(typeof id == "object"){ // must be panel obj
35937 return this.panels.get(id);
35941 * Returns this regions position (north/south/east/west/center).
35944 getPosition: function(){
35945 return this.position;
35949 * Ext JS Library 1.1.1
35950 * Copyright(c) 2006-2007, Ext JS, LLC.
35952 * Originally Released Under LGPL - original licence link has changed is not relivant.
35955 * <script type="text/javascript">
35959 * @class Roo.bootstrap.layout.Region
35960 * @extends Roo.bootstrap.layout.Basic
35961 * This class represents a region in a layout manager.
35963 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35964 * @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})
35965 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35966 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35967 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35968 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35969 * @cfg {String} title The title for the region (overrides panel titles)
35970 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35971 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35972 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35973 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35974 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35975 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35976 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35977 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35978 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35979 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35981 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35982 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35983 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35984 * @cfg {Number} width For East/West panels
35985 * @cfg {Number} height For North/South panels
35986 * @cfg {Boolean} split To show the splitter
35987 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35989 * @cfg {string} cls Extra CSS classes to add to region
35991 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35992 * @cfg {string} region the region that it inhabits..
35995 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35996 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35998 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35999 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36000 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36002 Roo.bootstrap.layout.Region = function(config)
36004 this.applyConfig(config);
36006 var mgr = config.mgr;
36007 var pos = config.region;
36008 config.skipConfig = true;
36009 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36012 this.onRender(mgr.el);
36015 this.visible = true;
36016 this.collapsed = false;
36017 this.unrendered_panels = [];
36020 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36022 position: '', // set by wrapper (eg. north/south etc..)
36023 unrendered_panels : null, // unrendered panels.
36025 tabPosition : false,
36027 mgr: false, // points to 'Border'
36030 createBody : function(){
36031 /** This region's body element
36032 * @type Roo.Element */
36033 this.bodyEl = this.el.createChild({
36035 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36039 onRender: function(ctr, pos)
36041 var dh = Roo.DomHelper;
36042 /** This region's container element
36043 * @type Roo.Element */
36044 this.el = dh.append(ctr.dom, {
36046 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36048 /** This region's title element
36049 * @type Roo.Element */
36051 this.titleEl = dh.append(this.el.dom, {
36053 unselectable: "on",
36054 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36056 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36057 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36061 this.titleEl.enableDisplayMode();
36062 /** This region's title text element
36063 * @type HTMLElement */
36064 this.titleTextEl = this.titleEl.dom.firstChild;
36065 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36067 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36068 this.closeBtn.enableDisplayMode();
36069 this.closeBtn.on("click", this.closeClicked, this);
36070 this.closeBtn.hide();
36072 this.createBody(this.config);
36073 if(this.config.hideWhenEmpty){
36075 this.on("paneladded", this.validateVisibility, this);
36076 this.on("panelremoved", this.validateVisibility, this);
36078 if(this.autoScroll){
36079 this.bodyEl.setStyle("overflow", "auto");
36081 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36083 //if(c.titlebar !== false){
36084 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36085 this.titleEl.hide();
36087 this.titleEl.show();
36088 if(this.config.title){
36089 this.titleTextEl.innerHTML = this.config.title;
36093 if(this.config.collapsed){
36094 this.collapse(true);
36096 if(this.config.hidden){
36100 if (this.unrendered_panels && this.unrendered_panels.length) {
36101 for (var i =0;i< this.unrendered_panels.length; i++) {
36102 this.add(this.unrendered_panels[i]);
36104 this.unrendered_panels = null;
36110 applyConfig : function(c)
36113 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36114 var dh = Roo.DomHelper;
36115 if(c.titlebar !== false){
36116 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36117 this.collapseBtn.on("click", this.collapse, this);
36118 this.collapseBtn.enableDisplayMode();
36120 if(c.showPin === true || this.showPin){
36121 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36122 this.stickBtn.enableDisplayMode();
36123 this.stickBtn.on("click", this.expand, this);
36124 this.stickBtn.hide();
36129 /** This region's collapsed element
36130 * @type Roo.Element */
36133 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36134 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36137 if(c.floatable !== false){
36138 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36139 this.collapsedEl.on("click", this.collapseClick, this);
36142 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36143 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36144 id: "message", unselectable: "on", style:{"float":"left"}});
36145 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36147 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36148 this.expandBtn.on("click", this.expand, this);
36152 if(this.collapseBtn){
36153 this.collapseBtn.setVisible(c.collapsible == true);
36156 this.cmargins = c.cmargins || this.cmargins ||
36157 (this.position == "west" || this.position == "east" ?
36158 {top: 0, left: 2, right:2, bottom: 0} :
36159 {top: 2, left: 0, right:0, bottom: 2});
36161 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36164 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36166 this.autoScroll = c.autoScroll || false;
36171 this.duration = c.duration || .30;
36172 this.slideDuration = c.slideDuration || .45;
36177 * Returns true if this region is currently visible.
36178 * @return {Boolean}
36180 isVisible : function(){
36181 return this.visible;
36185 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36186 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36188 //setCollapsedTitle : function(title){
36189 // title = title || " ";
36190 // if(this.collapsedTitleTextEl){
36191 // this.collapsedTitleTextEl.innerHTML = title;
36195 getBox : function(){
36197 // if(!this.collapsed){
36198 b = this.el.getBox(false, true);
36200 // b = this.collapsedEl.getBox(false, true);
36205 getMargins : function(){
36206 return this.margins;
36207 //return this.collapsed ? this.cmargins : this.margins;
36210 highlight : function(){
36211 this.el.addClass("x-layout-panel-dragover");
36214 unhighlight : function(){
36215 this.el.removeClass("x-layout-panel-dragover");
36218 updateBox : function(box)
36220 if (!this.bodyEl) {
36221 return; // not rendered yet..
36225 if(!this.collapsed){
36226 this.el.dom.style.left = box.x + "px";
36227 this.el.dom.style.top = box.y + "px";
36228 this.updateBody(box.width, box.height);
36230 this.collapsedEl.dom.style.left = box.x + "px";
36231 this.collapsedEl.dom.style.top = box.y + "px";
36232 this.collapsedEl.setSize(box.width, box.height);
36235 this.tabs.autoSizeTabs();
36239 updateBody : function(w, h)
36242 this.el.setWidth(w);
36243 w -= this.el.getBorderWidth("rl");
36244 if(this.config.adjustments){
36245 w += this.config.adjustments[0];
36248 if(h !== null && h > 0){
36249 this.el.setHeight(h);
36250 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36251 h -= this.el.getBorderWidth("tb");
36252 if(this.config.adjustments){
36253 h += this.config.adjustments[1];
36255 this.bodyEl.setHeight(h);
36257 h = this.tabs.syncHeight(h);
36260 if(this.panelSize){
36261 w = w !== null ? w : this.panelSize.width;
36262 h = h !== null ? h : this.panelSize.height;
36264 if(this.activePanel){
36265 var el = this.activePanel.getEl();
36266 w = w !== null ? w : el.getWidth();
36267 h = h !== null ? h : el.getHeight();
36268 this.panelSize = {width: w, height: h};
36269 this.activePanel.setSize(w, h);
36271 if(Roo.isIE && this.tabs){
36272 this.tabs.el.repaint();
36277 * Returns the container element for this region.
36278 * @return {Roo.Element}
36280 getEl : function(){
36285 * Hides this region.
36288 //if(!this.collapsed){
36289 this.el.dom.style.left = "-2000px";
36292 // this.collapsedEl.dom.style.left = "-2000px";
36293 // this.collapsedEl.hide();
36295 this.visible = false;
36296 this.fireEvent("visibilitychange", this, false);
36300 * Shows this region if it was previously hidden.
36303 //if(!this.collapsed){
36306 // this.collapsedEl.show();
36308 this.visible = true;
36309 this.fireEvent("visibilitychange", this, true);
36312 closeClicked : function(){
36313 if(this.activePanel){
36314 this.remove(this.activePanel);
36318 collapseClick : function(e){
36320 e.stopPropagation();
36323 e.stopPropagation();
36329 * Collapses this region.
36330 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36333 collapse : function(skipAnim, skipCheck = false){
36334 if(this.collapsed) {
36338 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36340 this.collapsed = true;
36342 this.split.el.hide();
36344 if(this.config.animate && skipAnim !== true){
36345 this.fireEvent("invalidated", this);
36346 this.animateCollapse();
36348 this.el.setLocation(-20000,-20000);
36350 this.collapsedEl.show();
36351 this.fireEvent("collapsed", this);
36352 this.fireEvent("invalidated", this);
36358 animateCollapse : function(){
36363 * Expands this region if it was previously collapsed.
36364 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36365 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36368 expand : function(e, skipAnim){
36370 e.stopPropagation();
36372 if(!this.collapsed || this.el.hasActiveFx()) {
36376 this.afterSlideIn();
36379 this.collapsed = false;
36380 if(this.config.animate && skipAnim !== true){
36381 this.animateExpand();
36385 this.split.el.show();
36387 this.collapsedEl.setLocation(-2000,-2000);
36388 this.collapsedEl.hide();
36389 this.fireEvent("invalidated", this);
36390 this.fireEvent("expanded", this);
36394 animateExpand : function(){
36398 initTabs : function()
36400 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36402 var ts = new Roo.bootstrap.panel.Tabs({
36403 el: this.bodyEl.dom,
36405 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36406 disableTooltips: this.config.disableTabTips,
36407 toolbar : this.config.toolbar
36410 if(this.config.hideTabs){
36411 ts.stripWrap.setDisplayed(false);
36414 ts.resizeTabs = this.config.resizeTabs === true;
36415 ts.minTabWidth = this.config.minTabWidth || 40;
36416 ts.maxTabWidth = this.config.maxTabWidth || 250;
36417 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36418 ts.monitorResize = false;
36419 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36420 ts.bodyEl.addClass('roo-layout-tabs-body');
36421 this.panels.each(this.initPanelAsTab, this);
36424 initPanelAsTab : function(panel){
36425 var ti = this.tabs.addTab(
36429 this.config.closeOnTab && panel.isClosable(),
36432 if(panel.tabTip !== undefined){
36433 ti.setTooltip(panel.tabTip);
36435 ti.on("activate", function(){
36436 this.setActivePanel(panel);
36439 if(this.config.closeOnTab){
36440 ti.on("beforeclose", function(t, e){
36442 this.remove(panel);
36446 panel.tabItem = ti;
36451 updatePanelTitle : function(panel, title)
36453 if(this.activePanel == panel){
36454 this.updateTitle(title);
36457 var ti = this.tabs.getTab(panel.getEl().id);
36459 if(panel.tabTip !== undefined){
36460 ti.setTooltip(panel.tabTip);
36465 updateTitle : function(title){
36466 if(this.titleTextEl && !this.config.title){
36467 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36471 setActivePanel : function(panel)
36473 panel = this.getPanel(panel);
36474 if(this.activePanel && this.activePanel != panel){
36475 if(this.activePanel.setActiveState(false) === false){
36479 this.activePanel = panel;
36480 panel.setActiveState(true);
36481 if(this.panelSize){
36482 panel.setSize(this.panelSize.width, this.panelSize.height);
36485 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36487 this.updateTitle(panel.getTitle());
36489 this.fireEvent("invalidated", this);
36491 this.fireEvent("panelactivated", this, panel);
36495 * Shows the specified panel.
36496 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36497 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36499 showPanel : function(panel)
36501 panel = this.getPanel(panel);
36504 var tab = this.tabs.getTab(panel.getEl().id);
36505 if(tab.isHidden()){
36506 this.tabs.unhideTab(tab.id);
36510 this.setActivePanel(panel);
36517 * Get the active panel for this region.
36518 * @return {Roo.ContentPanel} The active panel or null
36520 getActivePanel : function(){
36521 return this.activePanel;
36524 validateVisibility : function(){
36525 if(this.panels.getCount() < 1){
36526 this.updateTitle(" ");
36527 this.closeBtn.hide();
36530 if(!this.isVisible()){
36537 * Adds the passed ContentPanel(s) to this region.
36538 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36539 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36541 add : function(panel)
36543 if(arguments.length > 1){
36544 for(var i = 0, len = arguments.length; i < len; i++) {
36545 this.add(arguments[i]);
36550 // if we have not been rendered yet, then we can not really do much of this..
36551 if (!this.bodyEl) {
36552 this.unrendered_panels.push(panel);
36559 if(this.hasPanel(panel)){
36560 this.showPanel(panel);
36563 panel.setRegion(this);
36564 this.panels.add(panel);
36565 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36566 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36567 // and hide them... ???
36568 this.bodyEl.dom.appendChild(panel.getEl().dom);
36569 if(panel.background !== true){
36570 this.setActivePanel(panel);
36572 this.fireEvent("paneladded", this, panel);
36579 this.initPanelAsTab(panel);
36583 if(panel.background !== true){
36584 this.tabs.activate(panel.getEl().id);
36586 this.fireEvent("paneladded", this, panel);
36591 * Hides the tab for the specified panel.
36592 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36594 hidePanel : function(panel){
36595 if(this.tabs && (panel = this.getPanel(panel))){
36596 this.tabs.hideTab(panel.getEl().id);
36601 * Unhides the tab for a previously hidden panel.
36602 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36604 unhidePanel : function(panel){
36605 if(this.tabs && (panel = this.getPanel(panel))){
36606 this.tabs.unhideTab(panel.getEl().id);
36610 clearPanels : function(){
36611 while(this.panels.getCount() > 0){
36612 this.remove(this.panels.first());
36617 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36618 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36619 * @param {Boolean} preservePanel Overrides the config preservePanel option
36620 * @return {Roo.ContentPanel} The panel that was removed
36622 remove : function(panel, preservePanel)
36624 panel = this.getPanel(panel);
36629 this.fireEvent("beforeremove", this, panel, e);
36630 if(e.cancel === true){
36633 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36634 var panelId = panel.getId();
36635 this.panels.removeKey(panelId);
36637 document.body.appendChild(panel.getEl().dom);
36640 this.tabs.removeTab(panel.getEl().id);
36641 }else if (!preservePanel){
36642 this.bodyEl.dom.removeChild(panel.getEl().dom);
36644 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36645 var p = this.panels.first();
36646 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36647 tempEl.appendChild(p.getEl().dom);
36648 this.bodyEl.update("");
36649 this.bodyEl.dom.appendChild(p.getEl().dom);
36651 this.updateTitle(p.getTitle());
36653 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36654 this.setActivePanel(p);
36656 panel.setRegion(null);
36657 if(this.activePanel == panel){
36658 this.activePanel = null;
36660 if(this.config.autoDestroy !== false && preservePanel !== true){
36661 try{panel.destroy();}catch(e){}
36663 this.fireEvent("panelremoved", this, panel);
36668 * Returns the TabPanel component used by this region
36669 * @return {Roo.TabPanel}
36671 getTabs : function(){
36675 createTool : function(parentEl, className){
36676 var btn = Roo.DomHelper.append(parentEl, {
36678 cls: "x-layout-tools-button",
36681 cls: "roo-layout-tools-button-inner " + className,
36685 btn.addClassOnOver("roo-layout-tools-button-over");
36690 * Ext JS Library 1.1.1
36691 * Copyright(c) 2006-2007, Ext JS, LLC.
36693 * Originally Released Under LGPL - original licence link has changed is not relivant.
36696 * <script type="text/javascript">
36702 * @class Roo.SplitLayoutRegion
36703 * @extends Roo.LayoutRegion
36704 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36706 Roo.bootstrap.layout.Split = function(config){
36707 this.cursor = config.cursor;
36708 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36711 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36713 splitTip : "Drag to resize.",
36714 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36715 useSplitTips : false,
36717 applyConfig : function(config){
36718 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36721 onRender : function(ctr,pos) {
36723 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36724 if(!this.config.split){
36729 var splitEl = Roo.DomHelper.append(ctr.dom, {
36731 id: this.el.id + "-split",
36732 cls: "roo-layout-split roo-layout-split-"+this.position,
36735 /** The SplitBar for this region
36736 * @type Roo.SplitBar */
36737 // does not exist yet...
36738 Roo.log([this.position, this.orientation]);
36740 this.split = new Roo.bootstrap.SplitBar({
36741 dragElement : splitEl,
36742 resizingElement: this.el,
36743 orientation : this.orientation
36746 this.split.on("moved", this.onSplitMove, this);
36747 this.split.useShim = this.config.useShim === true;
36748 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36749 if(this.useSplitTips){
36750 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36752 //if(config.collapsible){
36753 // this.split.el.on("dblclick", this.collapse, this);
36756 if(typeof this.config.minSize != "undefined"){
36757 this.split.minSize = this.config.minSize;
36759 if(typeof this.config.maxSize != "undefined"){
36760 this.split.maxSize = this.config.maxSize;
36762 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36763 this.hideSplitter();
36768 getHMaxSize : function(){
36769 var cmax = this.config.maxSize || 10000;
36770 var center = this.mgr.getRegion("center");
36771 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36774 getVMaxSize : function(){
36775 var cmax = this.config.maxSize || 10000;
36776 var center = this.mgr.getRegion("center");
36777 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36780 onSplitMove : function(split, newSize){
36781 this.fireEvent("resized", this, newSize);
36785 * Returns the {@link Roo.SplitBar} for this region.
36786 * @return {Roo.SplitBar}
36788 getSplitBar : function(){
36793 this.hideSplitter();
36794 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36797 hideSplitter : function(){
36799 this.split.el.setLocation(-2000,-2000);
36800 this.split.el.hide();
36806 this.split.el.show();
36808 Roo.bootstrap.layout.Split.superclass.show.call(this);
36811 beforeSlide: function(){
36812 if(Roo.isGecko){// firefox overflow auto bug workaround
36813 this.bodyEl.clip();
36815 this.tabs.bodyEl.clip();
36817 if(this.activePanel){
36818 this.activePanel.getEl().clip();
36820 if(this.activePanel.beforeSlide){
36821 this.activePanel.beforeSlide();
36827 afterSlide : function(){
36828 if(Roo.isGecko){// firefox overflow auto bug workaround
36829 this.bodyEl.unclip();
36831 this.tabs.bodyEl.unclip();
36833 if(this.activePanel){
36834 this.activePanel.getEl().unclip();
36835 if(this.activePanel.afterSlide){
36836 this.activePanel.afterSlide();
36842 initAutoHide : function(){
36843 if(this.autoHide !== false){
36844 if(!this.autoHideHd){
36845 var st = new Roo.util.DelayedTask(this.slideIn, this);
36846 this.autoHideHd = {
36847 "mouseout": function(e){
36848 if(!e.within(this.el, true)){
36852 "mouseover" : function(e){
36858 this.el.on(this.autoHideHd);
36862 clearAutoHide : function(){
36863 if(this.autoHide !== false){
36864 this.el.un("mouseout", this.autoHideHd.mouseout);
36865 this.el.un("mouseover", this.autoHideHd.mouseover);
36869 clearMonitor : function(){
36870 Roo.get(document).un("click", this.slideInIf, this);
36873 // these names are backwards but not changed for compat
36874 slideOut : function(){
36875 if(this.isSlid || this.el.hasActiveFx()){
36878 this.isSlid = true;
36879 if(this.collapseBtn){
36880 this.collapseBtn.hide();
36882 this.closeBtnState = this.closeBtn.getStyle('display');
36883 this.closeBtn.hide();
36885 this.stickBtn.show();
36888 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36889 this.beforeSlide();
36890 this.el.setStyle("z-index", 10001);
36891 this.el.slideIn(this.getSlideAnchor(), {
36892 callback: function(){
36894 this.initAutoHide();
36895 Roo.get(document).on("click", this.slideInIf, this);
36896 this.fireEvent("slideshow", this);
36903 afterSlideIn : function(){
36904 this.clearAutoHide();
36905 this.isSlid = false;
36906 this.clearMonitor();
36907 this.el.setStyle("z-index", "");
36908 if(this.collapseBtn){
36909 this.collapseBtn.show();
36911 this.closeBtn.setStyle('display', this.closeBtnState);
36913 this.stickBtn.hide();
36915 this.fireEvent("slidehide", this);
36918 slideIn : function(cb){
36919 if(!this.isSlid || this.el.hasActiveFx()){
36923 this.isSlid = false;
36924 this.beforeSlide();
36925 this.el.slideOut(this.getSlideAnchor(), {
36926 callback: function(){
36927 this.el.setLeftTop(-10000, -10000);
36929 this.afterSlideIn();
36937 slideInIf : function(e){
36938 if(!e.within(this.el)){
36943 animateCollapse : function(){
36944 this.beforeSlide();
36945 this.el.setStyle("z-index", 20000);
36946 var anchor = this.getSlideAnchor();
36947 this.el.slideOut(anchor, {
36948 callback : function(){
36949 this.el.setStyle("z-index", "");
36950 this.collapsedEl.slideIn(anchor, {duration:.3});
36952 this.el.setLocation(-10000,-10000);
36954 this.fireEvent("collapsed", this);
36961 animateExpand : function(){
36962 this.beforeSlide();
36963 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36964 this.el.setStyle("z-index", 20000);
36965 this.collapsedEl.hide({
36968 this.el.slideIn(this.getSlideAnchor(), {
36969 callback : function(){
36970 this.el.setStyle("z-index", "");
36973 this.split.el.show();
36975 this.fireEvent("invalidated", this);
36976 this.fireEvent("expanded", this);
37004 getAnchor : function(){
37005 return this.anchors[this.position];
37008 getCollapseAnchor : function(){
37009 return this.canchors[this.position];
37012 getSlideAnchor : function(){
37013 return this.sanchors[this.position];
37016 getAlignAdj : function(){
37017 var cm = this.cmargins;
37018 switch(this.position){
37034 getExpandAdj : function(){
37035 var c = this.collapsedEl, cm = this.cmargins;
37036 switch(this.position){
37038 return [-(cm.right+c.getWidth()+cm.left), 0];
37041 return [cm.right+c.getWidth()+cm.left, 0];
37044 return [0, -(cm.top+cm.bottom+c.getHeight())];
37047 return [0, cm.top+cm.bottom+c.getHeight()];
37053 * Ext JS Library 1.1.1
37054 * Copyright(c) 2006-2007, Ext JS, LLC.
37056 * Originally Released Under LGPL - original licence link has changed is not relivant.
37059 * <script type="text/javascript">
37062 * These classes are private internal classes
37064 Roo.bootstrap.layout.Center = function(config){
37065 config.region = "center";
37066 Roo.bootstrap.layout.Region.call(this, config);
37067 this.visible = true;
37068 this.minWidth = config.minWidth || 20;
37069 this.minHeight = config.minHeight || 20;
37072 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37074 // center panel can't be hidden
37078 // center panel can't be hidden
37081 getMinWidth: function(){
37082 return this.minWidth;
37085 getMinHeight: function(){
37086 return this.minHeight;
37100 Roo.bootstrap.layout.North = function(config)
37102 config.region = 'north';
37103 config.cursor = 'n-resize';
37105 Roo.bootstrap.layout.Split.call(this, config);
37109 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37110 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37111 this.split.el.addClass("roo-layout-split-v");
37113 var size = config.initialSize || config.height;
37114 if(typeof size != "undefined"){
37115 this.el.setHeight(size);
37118 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37120 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37124 getBox : function(){
37125 if(this.collapsed){
37126 return this.collapsedEl.getBox();
37128 var box = this.el.getBox();
37130 box.height += this.split.el.getHeight();
37135 updateBox : function(box){
37136 if(this.split && !this.collapsed){
37137 box.height -= this.split.el.getHeight();
37138 this.split.el.setLeft(box.x);
37139 this.split.el.setTop(box.y+box.height);
37140 this.split.el.setWidth(box.width);
37142 if(this.collapsed){
37143 this.updateBody(box.width, null);
37145 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37153 Roo.bootstrap.layout.South = function(config){
37154 config.region = 'south';
37155 config.cursor = 's-resize';
37156 Roo.bootstrap.layout.Split.call(this, config);
37158 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37159 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37160 this.split.el.addClass("roo-layout-split-v");
37162 var size = config.initialSize || config.height;
37163 if(typeof size != "undefined"){
37164 this.el.setHeight(size);
37168 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37169 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37170 getBox : function(){
37171 if(this.collapsed){
37172 return this.collapsedEl.getBox();
37174 var box = this.el.getBox();
37176 var sh = this.split.el.getHeight();
37183 updateBox : function(box){
37184 if(this.split && !this.collapsed){
37185 var sh = this.split.el.getHeight();
37188 this.split.el.setLeft(box.x);
37189 this.split.el.setTop(box.y-sh);
37190 this.split.el.setWidth(box.width);
37192 if(this.collapsed){
37193 this.updateBody(box.width, null);
37195 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37199 Roo.bootstrap.layout.East = function(config){
37200 config.region = "east";
37201 config.cursor = "e-resize";
37202 Roo.bootstrap.layout.Split.call(this, config);
37204 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37205 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37206 this.split.el.addClass("roo-layout-split-h");
37208 var size = config.initialSize || config.width;
37209 if(typeof size != "undefined"){
37210 this.el.setWidth(size);
37213 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37214 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37215 getBox : function(){
37216 if(this.collapsed){
37217 return this.collapsedEl.getBox();
37219 var box = this.el.getBox();
37221 var sw = this.split.el.getWidth();
37228 updateBox : function(box){
37229 if(this.split && !this.collapsed){
37230 var sw = this.split.el.getWidth();
37232 this.split.el.setLeft(box.x);
37233 this.split.el.setTop(box.y);
37234 this.split.el.setHeight(box.height);
37237 if(this.collapsed){
37238 this.updateBody(null, box.height);
37240 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37244 Roo.bootstrap.layout.West = function(config){
37245 config.region = "west";
37246 config.cursor = "w-resize";
37248 Roo.bootstrap.layout.Split.call(this, config);
37250 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37251 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37252 this.split.el.addClass("roo-layout-split-h");
37256 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37257 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37259 onRender: function(ctr, pos)
37261 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37262 var size = this.config.initialSize || this.config.width;
37263 if(typeof size != "undefined"){
37264 this.el.setWidth(size);
37268 getBox : function(){
37269 if(this.collapsed){
37270 return this.collapsedEl.getBox();
37272 var box = this.el.getBox();
37274 box.width += this.split.el.getWidth();
37279 updateBox : function(box){
37280 if(this.split && !this.collapsed){
37281 var sw = this.split.el.getWidth();
37283 this.split.el.setLeft(box.x+box.width);
37284 this.split.el.setTop(box.y);
37285 this.split.el.setHeight(box.height);
37287 if(this.collapsed){
37288 this.updateBody(null, box.height);
37290 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37292 });Roo.namespace("Roo.bootstrap.panel");/*
37294 * Ext JS Library 1.1.1
37295 * Copyright(c) 2006-2007, Ext JS, LLC.
37297 * Originally Released Under LGPL - original licence link has changed is not relivant.
37300 * <script type="text/javascript">
37303 * @class Roo.ContentPanel
37304 * @extends Roo.util.Observable
37305 * A basic ContentPanel element.
37306 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37307 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37308 * @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
37309 * @cfg {Boolean} closable True if the panel can be closed/removed
37310 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37311 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37312 * @cfg {Toolbar} toolbar A toolbar for this panel
37313 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37314 * @cfg {String} title The title for this panel
37315 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37316 * @cfg {String} url Calls {@link #setUrl} with this value
37317 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37318 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37319 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37320 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37321 * @cfg {Boolean} badges render the badges
37324 * Create a new ContentPanel.
37325 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37326 * @param {String/Object} config A string to set only the title or a config object
37327 * @param {String} content (optional) Set the HTML content for this panel
37328 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37330 Roo.bootstrap.panel.Content = function( config){
37332 this.tpl = config.tpl || false;
37334 var el = config.el;
37335 var content = config.content;
37337 if(config.autoCreate){ // xtype is available if this is called from factory
37340 this.el = Roo.get(el);
37341 if(!this.el && config && config.autoCreate){
37342 if(typeof config.autoCreate == "object"){
37343 if(!config.autoCreate.id){
37344 config.autoCreate.id = config.id||el;
37346 this.el = Roo.DomHelper.append(document.body,
37347 config.autoCreate, true);
37349 var elcfg = { tag: "div",
37350 cls: "roo-layout-inactive-content",
37354 elcfg.html = config.html;
37358 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37361 this.closable = false;
37362 this.loaded = false;
37363 this.active = false;
37366 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37368 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37370 this.wrapEl = this.el; //this.el.wrap();
37372 if (config.toolbar.items) {
37373 ti = config.toolbar.items ;
37374 delete config.toolbar.items ;
37378 this.toolbar.render(this.wrapEl, 'before');
37379 for(var i =0;i < ti.length;i++) {
37380 // Roo.log(['add child', items[i]]);
37381 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37383 this.toolbar.items = nitems;
37384 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37385 delete config.toolbar;
37389 // xtype created footer. - not sure if will work as we normally have to render first..
37390 if (this.footer && !this.footer.el && this.footer.xtype) {
37391 if (!this.wrapEl) {
37392 this.wrapEl = this.el.wrap();
37395 this.footer.container = this.wrapEl.createChild();
37397 this.footer = Roo.factory(this.footer, Roo);
37402 if(typeof config == "string"){
37403 this.title = config;
37405 Roo.apply(this, config);
37409 this.resizeEl = Roo.get(this.resizeEl, true);
37411 this.resizeEl = this.el;
37413 // handle view.xtype
37421 * Fires when this panel is activated.
37422 * @param {Roo.ContentPanel} this
37426 * @event deactivate
37427 * Fires when this panel is activated.
37428 * @param {Roo.ContentPanel} this
37430 "deactivate" : true,
37434 * Fires when this panel is resized if fitToFrame is true.
37435 * @param {Roo.ContentPanel} this
37436 * @param {Number} width The width after any component adjustments
37437 * @param {Number} height The height after any component adjustments
37443 * Fires when this tab is created
37444 * @param {Roo.ContentPanel} this
37455 if(this.autoScroll){
37456 this.resizeEl.setStyle("overflow", "auto");
37458 // fix randome scrolling
37459 //this.el.on('scroll', function() {
37460 // Roo.log('fix random scolling');
37461 // this.scrollTo('top',0);
37464 content = content || this.content;
37466 this.setContent(content);
37468 if(config && config.url){
37469 this.setUrl(this.url, this.params, this.loadOnce);
37474 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37476 if (this.view && typeof(this.view.xtype) != 'undefined') {
37477 this.view.el = this.el.appendChild(document.createElement("div"));
37478 this.view = Roo.factory(this.view);
37479 this.view.render && this.view.render(false, '');
37483 this.fireEvent('render', this);
37486 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37490 setRegion : function(region){
37491 this.region = region;
37492 this.setActiveClass(region && !this.background);
37496 setActiveClass: function(state)
37499 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37500 this.el.setStyle('position','relative');
37502 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37503 this.el.setStyle('position', 'absolute');
37508 * Returns the toolbar for this Panel if one was configured.
37509 * @return {Roo.Toolbar}
37511 getToolbar : function(){
37512 return this.toolbar;
37515 setActiveState : function(active)
37517 this.active = active;
37518 this.setActiveClass(active);
37520 if(this.fireEvent("deactivate", this) === false){
37525 this.fireEvent("activate", this);
37529 * Updates this panel's element
37530 * @param {String} content The new content
37531 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37533 setContent : function(content, loadScripts){
37534 this.el.update(content, loadScripts);
37537 ignoreResize : function(w, h){
37538 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37541 this.lastSize = {width: w, height: h};
37546 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37547 * @return {Roo.UpdateManager} The UpdateManager
37549 getUpdateManager : function(){
37550 return this.el.getUpdateManager();
37553 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37554 * @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:
37557 url: "your-url.php",
37558 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37559 callback: yourFunction,
37560 scope: yourObject, //(optional scope)
37563 text: "Loading...",
37568 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37569 * 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.
37570 * @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}
37571 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37572 * @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.
37573 * @return {Roo.ContentPanel} this
37576 var um = this.el.getUpdateManager();
37577 um.update.apply(um, arguments);
37583 * 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.
37584 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37585 * @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)
37586 * @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)
37587 * @return {Roo.UpdateManager} The UpdateManager
37589 setUrl : function(url, params, loadOnce){
37590 if(this.refreshDelegate){
37591 this.removeListener("activate", this.refreshDelegate);
37593 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37594 this.on("activate", this.refreshDelegate);
37595 return this.el.getUpdateManager();
37598 _handleRefresh : function(url, params, loadOnce){
37599 if(!loadOnce || !this.loaded){
37600 var updater = this.el.getUpdateManager();
37601 updater.update(url, params, this._setLoaded.createDelegate(this));
37605 _setLoaded : function(){
37606 this.loaded = true;
37610 * Returns this panel's id
37613 getId : function(){
37618 * Returns this panel's element - used by regiosn to add.
37619 * @return {Roo.Element}
37621 getEl : function(){
37622 return this.wrapEl || this.el;
37627 adjustForComponents : function(width, height)
37629 //Roo.log('adjustForComponents ');
37630 if(this.resizeEl != this.el){
37631 width -= this.el.getFrameWidth('lr');
37632 height -= this.el.getFrameWidth('tb');
37635 var te = this.toolbar.getEl();
37636 te.setWidth(width);
37637 height -= te.getHeight();
37640 var te = this.footer.getEl();
37641 te.setWidth(width);
37642 height -= te.getHeight();
37646 if(this.adjustments){
37647 width += this.adjustments[0];
37648 height += this.adjustments[1];
37650 return {"width": width, "height": height};
37653 setSize : function(width, height){
37654 if(this.fitToFrame && !this.ignoreResize(width, height)){
37655 if(this.fitContainer && this.resizeEl != this.el){
37656 this.el.setSize(width, height);
37658 var size = this.adjustForComponents(width, height);
37659 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37660 this.fireEvent('resize', this, size.width, size.height);
37665 * Returns this panel's title
37668 getTitle : function(){
37670 if (typeof(this.title) != 'object') {
37675 for (var k in this.title) {
37676 if (!this.title.hasOwnProperty(k)) {
37680 if (k.indexOf('-') >= 0) {
37681 var s = k.split('-');
37682 for (var i = 0; i<s.length; i++) {
37683 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37686 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37693 * Set this panel's title
37694 * @param {String} title
37696 setTitle : function(title){
37697 this.title = title;
37699 this.region.updatePanelTitle(this, title);
37704 * Returns true is this panel was configured to be closable
37705 * @return {Boolean}
37707 isClosable : function(){
37708 return this.closable;
37711 beforeSlide : function(){
37713 this.resizeEl.clip();
37716 afterSlide : function(){
37718 this.resizeEl.unclip();
37722 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37723 * Will fail silently if the {@link #setUrl} method has not been called.
37724 * This does not activate the panel, just updates its content.
37726 refresh : function(){
37727 if(this.refreshDelegate){
37728 this.loaded = false;
37729 this.refreshDelegate();
37734 * Destroys this panel
37736 destroy : function(){
37737 this.el.removeAllListeners();
37738 var tempEl = document.createElement("span");
37739 tempEl.appendChild(this.el.dom);
37740 tempEl.innerHTML = "";
37746 * form - if the content panel contains a form - this is a reference to it.
37747 * @type {Roo.form.Form}
37751 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37752 * This contains a reference to it.
37758 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37768 * @param {Object} cfg Xtype definition of item to add.
37772 getChildContainer: function () {
37773 return this.getEl();
37778 var ret = new Roo.factory(cfg);
37783 if (cfg.xtype.match(/^Form$/)) {
37786 //if (this.footer) {
37787 // el = this.footer.container.insertSibling(false, 'before');
37789 el = this.el.createChild();
37792 this.form = new Roo.form.Form(cfg);
37795 if ( this.form.allItems.length) {
37796 this.form.render(el.dom);
37800 // should only have one of theses..
37801 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37802 // views.. should not be just added - used named prop 'view''
37804 cfg.el = this.el.appendChild(document.createElement("div"));
37807 var ret = new Roo.factory(cfg);
37809 ret.render && ret.render(false, ''); // render blank..
37819 * @class Roo.bootstrap.panel.Grid
37820 * @extends Roo.bootstrap.panel.Content
37822 * Create a new GridPanel.
37823 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37824 * @param {Object} config A the config object
37830 Roo.bootstrap.panel.Grid = function(config)
37834 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37835 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37837 config.el = this.wrapper;
37838 //this.el = this.wrapper;
37840 if (config.container) {
37841 // ctor'ed from a Border/panel.grid
37844 this.wrapper.setStyle("overflow", "hidden");
37845 this.wrapper.addClass('roo-grid-container');
37850 if(config.toolbar){
37851 var tool_el = this.wrapper.createChild();
37852 this.toolbar = Roo.factory(config.toolbar);
37854 if (config.toolbar.items) {
37855 ti = config.toolbar.items ;
37856 delete config.toolbar.items ;
37860 this.toolbar.render(tool_el);
37861 for(var i =0;i < ti.length;i++) {
37862 // Roo.log(['add child', items[i]]);
37863 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37865 this.toolbar.items = nitems;
37867 delete config.toolbar;
37870 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37871 config.grid.scrollBody = true;;
37872 config.grid.monitorWindowResize = false; // turn off autosizing
37873 config.grid.autoHeight = false;
37874 config.grid.autoWidth = false;
37876 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37878 if (config.background) {
37879 // render grid on panel activation (if panel background)
37880 this.on('activate', function(gp) {
37881 if (!gp.grid.rendered) {
37882 gp.grid.render(this.wrapper);
37883 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37888 this.grid.render(this.wrapper);
37889 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37892 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37893 // ??? needed ??? config.el = this.wrapper;
37898 // xtype created footer. - not sure if will work as we normally have to render first..
37899 if (this.footer && !this.footer.el && this.footer.xtype) {
37901 var ctr = this.grid.getView().getFooterPanel(true);
37902 this.footer.dataSource = this.grid.dataSource;
37903 this.footer = Roo.factory(this.footer, Roo);
37904 this.footer.render(ctr);
37914 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37915 getId : function(){
37916 return this.grid.id;
37920 * Returns the grid for this panel
37921 * @return {Roo.bootstrap.Table}
37923 getGrid : function(){
37927 setSize : function(width, height){
37928 if(!this.ignoreResize(width, height)){
37929 var grid = this.grid;
37930 var size = this.adjustForComponents(width, height);
37931 var gridel = grid.getGridEl();
37932 gridel.setSize(size.width, size.height);
37934 var thd = grid.getGridEl().select('thead',true).first();
37935 var tbd = grid.getGridEl().select('tbody', true).first();
37937 tbd.setSize(width, height - thd.getHeight());
37946 beforeSlide : function(){
37947 this.grid.getView().scroller.clip();
37950 afterSlide : function(){
37951 this.grid.getView().scroller.unclip();
37954 destroy : function(){
37955 this.grid.destroy();
37957 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37962 * @class Roo.bootstrap.panel.Nest
37963 * @extends Roo.bootstrap.panel.Content
37965 * Create a new Panel, that can contain a layout.Border.
37968 * @param {Roo.BorderLayout} layout The layout for this panel
37969 * @param {String/Object} config A string to set only the title or a config object
37971 Roo.bootstrap.panel.Nest = function(config)
37973 // construct with only one argument..
37974 /* FIXME - implement nicer consturctors
37975 if (layout.layout) {
37977 layout = config.layout;
37978 delete config.layout;
37980 if (layout.xtype && !layout.getEl) {
37981 // then layout needs constructing..
37982 layout = Roo.factory(layout, Roo);
37986 config.el = config.layout.getEl();
37988 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37990 config.layout.monitorWindowResize = false; // turn off autosizing
37991 this.layout = config.layout;
37992 this.layout.getEl().addClass("roo-layout-nested-layout");
37993 this.layout.parent = this;
38000 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38002 setSize : function(width, height){
38003 if(!this.ignoreResize(width, height)){
38004 var size = this.adjustForComponents(width, height);
38005 var el = this.layout.getEl();
38006 if (size.height < 1) {
38007 el.setWidth(size.width);
38009 el.setSize(size.width, size.height);
38011 var touch = el.dom.offsetWidth;
38012 this.layout.layout();
38013 // ie requires a double layout on the first pass
38014 if(Roo.isIE && !this.initialized){
38015 this.initialized = true;
38016 this.layout.layout();
38021 // activate all subpanels if not currently active..
38023 setActiveState : function(active){
38024 this.active = active;
38025 this.setActiveClass(active);
38028 this.fireEvent("deactivate", this);
38032 this.fireEvent("activate", this);
38033 // not sure if this should happen before or after..
38034 if (!this.layout) {
38035 return; // should not happen..
38038 for (var r in this.layout.regions) {
38039 reg = this.layout.getRegion(r);
38040 if (reg.getActivePanel()) {
38041 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38042 reg.setActivePanel(reg.getActivePanel());
38045 if (!reg.panels.length) {
38048 reg.showPanel(reg.getPanel(0));
38057 * Returns the nested BorderLayout for this panel
38058 * @return {Roo.BorderLayout}
38060 getLayout : function(){
38061 return this.layout;
38065 * Adds a xtype elements to the layout of the nested panel
38069 xtype : 'ContentPanel',
38076 xtype : 'NestedLayoutPanel',
38082 items : [ ... list of content panels or nested layout panels.. ]
38086 * @param {Object} cfg Xtype definition of item to add.
38088 addxtype : function(cfg) {
38089 return this.layout.addxtype(cfg);
38094 * Ext JS Library 1.1.1
38095 * Copyright(c) 2006-2007, Ext JS, LLC.
38097 * Originally Released Under LGPL - original licence link has changed is not relivant.
38100 * <script type="text/javascript">
38103 * @class Roo.TabPanel
38104 * @extends Roo.util.Observable
38105 * A lightweight tab container.
38109 // basic tabs 1, built from existing content
38110 var tabs = new Roo.TabPanel("tabs1");
38111 tabs.addTab("script", "View Script");
38112 tabs.addTab("markup", "View Markup");
38113 tabs.activate("script");
38115 // more advanced tabs, built from javascript
38116 var jtabs = new Roo.TabPanel("jtabs");
38117 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38119 // set up the UpdateManager
38120 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38121 var updater = tab2.getUpdateManager();
38122 updater.setDefaultUrl("ajax1.htm");
38123 tab2.on('activate', updater.refresh, updater, true);
38125 // Use setUrl for Ajax loading
38126 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38127 tab3.setUrl("ajax2.htm", null, true);
38130 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38133 jtabs.activate("jtabs-1");
38136 * Create a new TabPanel.
38137 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38138 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38140 Roo.bootstrap.panel.Tabs = function(config){
38142 * The container element for this TabPanel.
38143 * @type Roo.Element
38145 this.el = Roo.get(config.el);
38148 if(typeof config == "boolean"){
38149 this.tabPosition = config ? "bottom" : "top";
38151 Roo.apply(this, config);
38155 if(this.tabPosition == "bottom"){
38156 // if tabs are at the bottom = create the body first.
38157 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38158 this.el.addClass("roo-tabs-bottom");
38160 // next create the tabs holders
38162 if (this.tabPosition == "west"){
38164 var reg = this.region; // fake it..
38166 if (!reg.mgr.parent) {
38169 reg = reg.mgr.parent.region;
38171 Roo.log("got nest?");
38173 if (reg.mgr.getRegion('west')) {
38174 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38175 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38176 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38177 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38178 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38186 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38187 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38188 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38189 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38194 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38197 // finally - if tabs are at the top, then create the body last..
38198 if(this.tabPosition != "bottom"){
38199 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38200 * @type Roo.Element
38202 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38203 this.el.addClass("roo-tabs-top");
38207 this.bodyEl.setStyle("position", "relative");
38209 this.active = null;
38210 this.activateDelegate = this.activate.createDelegate(this);
38215 * Fires when the active tab changes
38216 * @param {Roo.TabPanel} this
38217 * @param {Roo.TabPanelItem} activePanel The new active tab
38221 * @event beforetabchange
38222 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38223 * @param {Roo.TabPanel} this
38224 * @param {Object} e Set cancel to true on this object to cancel the tab change
38225 * @param {Roo.TabPanelItem} tab The tab being changed to
38227 "beforetabchange" : true
38230 Roo.EventManager.onWindowResize(this.onResize, this);
38231 this.cpad = this.el.getPadding("lr");
38232 this.hiddenCount = 0;
38235 // toolbar on the tabbar support...
38236 if (this.toolbar) {
38237 alert("no toolbar support yet");
38238 this.toolbar = false;
38240 var tcfg = this.toolbar;
38241 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38242 this.toolbar = new Roo.Toolbar(tcfg);
38243 if (Roo.isSafari) {
38244 var tbl = tcfg.container.child('table', true);
38245 tbl.setAttribute('width', '100%');
38253 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38256 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38258 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38260 tabPosition : "top",
38262 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38264 currentTabWidth : 0,
38266 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38270 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38274 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38276 preferredTabWidth : 175,
38278 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38280 resizeTabs : false,
38282 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38284 monitorResize : true,
38286 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38288 toolbar : false, // set by caller..
38290 region : false, /// set by caller
38292 disableTooltips : true, // not used yet...
38295 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38296 * @param {String} id The id of the div to use <b>or create</b>
38297 * @param {String} text The text for the tab
38298 * @param {String} content (optional) Content to put in the TabPanelItem body
38299 * @param {Boolean} closable (optional) True to create a close icon on the tab
38300 * @return {Roo.TabPanelItem} The created TabPanelItem
38302 addTab : function(id, text, content, closable, tpl)
38304 var item = new Roo.bootstrap.panel.TabItem({
38308 closable : closable,
38311 this.addTabItem(item);
38313 item.setContent(content);
38319 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38320 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38321 * @return {Roo.TabPanelItem}
38323 getTab : function(id){
38324 return this.items[id];
38328 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38329 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38331 hideTab : function(id){
38332 var t = this.items[id];
38335 this.hiddenCount++;
38336 this.autoSizeTabs();
38341 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38342 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38344 unhideTab : function(id){
38345 var t = this.items[id];
38347 t.setHidden(false);
38348 this.hiddenCount--;
38349 this.autoSizeTabs();
38354 * Adds an existing {@link Roo.TabPanelItem}.
38355 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38357 addTabItem : function(item)
38359 this.items[item.id] = item;
38360 this.items.push(item);
38361 this.autoSizeTabs();
38362 // if(this.resizeTabs){
38363 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38364 // this.autoSizeTabs();
38366 // item.autoSize();
38371 * Removes a {@link Roo.TabPanelItem}.
38372 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38374 removeTab : function(id){
38375 var items = this.items;
38376 var tab = items[id];
38377 if(!tab) { return; }
38378 var index = items.indexOf(tab);
38379 if(this.active == tab && items.length > 1){
38380 var newTab = this.getNextAvailable(index);
38385 this.stripEl.dom.removeChild(tab.pnode.dom);
38386 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38387 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38389 items.splice(index, 1);
38390 delete this.items[tab.id];
38391 tab.fireEvent("close", tab);
38392 tab.purgeListeners();
38393 this.autoSizeTabs();
38396 getNextAvailable : function(start){
38397 var items = this.items;
38399 // look for a next tab that will slide over to
38400 // replace the one being removed
38401 while(index < items.length){
38402 var item = items[++index];
38403 if(item && !item.isHidden()){
38407 // if one isn't found select the previous tab (on the left)
38410 var item = items[--index];
38411 if(item && !item.isHidden()){
38419 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38420 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38422 disableTab : function(id){
38423 var tab = this.items[id];
38424 if(tab && this.active != tab){
38430 * Enables a {@link Roo.TabPanelItem} that is disabled.
38431 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38433 enableTab : function(id){
38434 var tab = this.items[id];
38439 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38440 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38441 * @return {Roo.TabPanelItem} The TabPanelItem.
38443 activate : function(id)
38445 //Roo.log('activite:' + id);
38447 var tab = this.items[id];
38451 if(tab == this.active || tab.disabled){
38455 this.fireEvent("beforetabchange", this, e, tab);
38456 if(e.cancel !== true && !tab.disabled){
38458 this.active.hide();
38460 this.active = this.items[id];
38461 this.active.show();
38462 this.fireEvent("tabchange", this, this.active);
38468 * Gets the active {@link Roo.TabPanelItem}.
38469 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38471 getActiveTab : function(){
38472 return this.active;
38476 * Updates the tab body element to fit the height of the container element
38477 * for overflow scrolling
38478 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38480 syncHeight : function(targetHeight){
38481 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38482 var bm = this.bodyEl.getMargins();
38483 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38484 this.bodyEl.setHeight(newHeight);
38488 onResize : function(){
38489 if(this.monitorResize){
38490 this.autoSizeTabs();
38495 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38497 beginUpdate : function(){
38498 this.updating = true;
38502 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38504 endUpdate : function(){
38505 this.updating = false;
38506 this.autoSizeTabs();
38510 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38512 autoSizeTabs : function()
38514 var count = this.items.length;
38515 var vcount = count - this.hiddenCount;
38518 this.stripEl.hide();
38520 this.stripEl.show();
38523 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38528 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38529 var availWidth = Math.floor(w / vcount);
38530 var b = this.stripBody;
38531 if(b.getWidth() > w){
38532 var tabs = this.items;
38533 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38534 if(availWidth < this.minTabWidth){
38535 /*if(!this.sleft){ // incomplete scrolling code
38536 this.createScrollButtons();
38539 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38542 if(this.currentTabWidth < this.preferredTabWidth){
38543 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38549 * Returns the number of tabs in this TabPanel.
38552 getCount : function(){
38553 return this.items.length;
38557 * Resizes all the tabs to the passed width
38558 * @param {Number} The new width
38560 setTabWidth : function(width){
38561 this.currentTabWidth = width;
38562 for(var i = 0, len = this.items.length; i < len; i++) {
38563 if(!this.items[i].isHidden()) {
38564 this.items[i].setWidth(width);
38570 * Destroys this TabPanel
38571 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38573 destroy : function(removeEl){
38574 Roo.EventManager.removeResizeListener(this.onResize, this);
38575 for(var i = 0, len = this.items.length; i < len; i++){
38576 this.items[i].purgeListeners();
38578 if(removeEl === true){
38579 this.el.update("");
38584 createStrip : function(container)
38586 var strip = document.createElement("nav");
38587 strip.className = Roo.bootstrap.version == 4 ?
38588 "navbar-light bg-light" :
38589 "navbar navbar-default"; //"x-tabs-wrap";
38590 container.appendChild(strip);
38594 createStripList : function(strip)
38596 // div wrapper for retard IE
38597 // returns the "tr" element.
38598 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38599 //'<div class="x-tabs-strip-wrap">'+
38600 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38601 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38602 return strip.firstChild; //.firstChild.firstChild.firstChild;
38604 createBody : function(container)
38606 var body = document.createElement("div");
38607 Roo.id(body, "tab-body");
38608 //Roo.fly(body).addClass("x-tabs-body");
38609 Roo.fly(body).addClass("tab-content");
38610 container.appendChild(body);
38613 createItemBody :function(bodyEl, id){
38614 var body = Roo.getDom(id);
38616 body = document.createElement("div");
38619 //Roo.fly(body).addClass("x-tabs-item-body");
38620 Roo.fly(body).addClass("tab-pane");
38621 bodyEl.insertBefore(body, bodyEl.firstChild);
38625 createStripElements : function(stripEl, text, closable, tpl)
38627 var td = document.createElement("li"); // was td..
38628 td.className = 'nav-item';
38630 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38633 stripEl.appendChild(td);
38635 td.className = "x-tabs-closable";
38636 if(!this.closeTpl){
38637 this.closeTpl = new Roo.Template(
38638 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38639 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38640 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38643 var el = this.closeTpl.overwrite(td, {"text": text});
38644 var close = el.getElementsByTagName("div")[0];
38645 var inner = el.getElementsByTagName("em")[0];
38646 return {"el": el, "close": close, "inner": inner};
38649 // not sure what this is..
38650 // if(!this.tabTpl){
38651 //this.tabTpl = new Roo.Template(
38652 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38653 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38655 // this.tabTpl = new Roo.Template(
38656 // '<a href="#">' +
38657 // '<span unselectable="on"' +
38658 // (this.disableTooltips ? '' : ' title="{text}"') +
38659 // ' >{text}</span></a>'
38665 var template = tpl || this.tabTpl || false;
38668 template = new Roo.Template(
38669 Roo.bootstrap.version == 4 ?
38671 '<a class="nav-link" href="#" unselectable="on"' +
38672 (this.disableTooltips ? '' : ' title="{text}"') +
38675 '<a class="nav-link" href="#">' +
38676 '<span unselectable="on"' +
38677 (this.disableTooltips ? '' : ' title="{text}"') +
38678 ' >{text}</span></a>'
38683 switch (typeof(template)) {
38687 template = new Roo.Template(template);
38693 var el = template.overwrite(td, {"text": text});
38695 var inner = el.getElementsByTagName("span")[0];
38697 return {"el": el, "inner": inner};
38705 * @class Roo.TabPanelItem
38706 * @extends Roo.util.Observable
38707 * Represents an individual item (tab plus body) in a TabPanel.
38708 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38709 * @param {String} id The id of this TabPanelItem
38710 * @param {String} text The text for the tab of this TabPanelItem
38711 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38713 Roo.bootstrap.panel.TabItem = function(config){
38715 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38716 * @type Roo.TabPanel
38718 this.tabPanel = config.panel;
38720 * The id for this TabPanelItem
38723 this.id = config.id;
38725 this.disabled = false;
38727 this.text = config.text;
38729 this.loaded = false;
38730 this.closable = config.closable;
38733 * The body element for this TabPanelItem.
38734 * @type Roo.Element
38736 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38737 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38738 this.bodyEl.setStyle("display", "block");
38739 this.bodyEl.setStyle("zoom", "1");
38740 //this.hideAction();
38742 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38744 this.el = Roo.get(els.el);
38745 this.inner = Roo.get(els.inner, true);
38746 this.textEl = Roo.bootstrap.version == 4 ?
38747 this.el : Roo.get(this.el.dom.firstChild, true);
38749 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38750 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38753 // this.el.on("mousedown", this.onTabMouseDown, this);
38754 this.el.on("click", this.onTabClick, this);
38756 if(config.closable){
38757 var c = Roo.get(els.close, true);
38758 c.dom.title = this.closeText;
38759 c.addClassOnOver("close-over");
38760 c.on("click", this.closeClick, this);
38766 * Fires when this tab becomes the active tab.
38767 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38768 * @param {Roo.TabPanelItem} this
38772 * @event beforeclose
38773 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38774 * @param {Roo.TabPanelItem} this
38775 * @param {Object} e Set cancel to true on this object to cancel the close.
38777 "beforeclose": true,
38780 * Fires when this tab is closed.
38781 * @param {Roo.TabPanelItem} this
38785 * @event deactivate
38786 * Fires when this tab is no longer the active tab.
38787 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38788 * @param {Roo.TabPanelItem} this
38790 "deactivate" : true
38792 this.hidden = false;
38794 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38797 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38799 purgeListeners : function(){
38800 Roo.util.Observable.prototype.purgeListeners.call(this);
38801 this.el.removeAllListeners();
38804 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38807 this.status_node.addClass("active");
38810 this.tabPanel.stripWrap.repaint();
38812 this.fireEvent("activate", this.tabPanel, this);
38816 * Returns true if this tab is the active tab.
38817 * @return {Boolean}
38819 isActive : function(){
38820 return this.tabPanel.getActiveTab() == this;
38824 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38827 this.status_node.removeClass("active");
38829 this.fireEvent("deactivate", this.tabPanel, this);
38832 hideAction : function(){
38833 this.bodyEl.hide();
38834 this.bodyEl.setStyle("position", "absolute");
38835 this.bodyEl.setLeft("-20000px");
38836 this.bodyEl.setTop("-20000px");
38839 showAction : function(){
38840 this.bodyEl.setStyle("position", "relative");
38841 this.bodyEl.setTop("");
38842 this.bodyEl.setLeft("");
38843 this.bodyEl.show();
38847 * Set the tooltip for the tab.
38848 * @param {String} tooltip The tab's tooltip
38850 setTooltip : function(text){
38851 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38852 this.textEl.dom.qtip = text;
38853 this.textEl.dom.removeAttribute('title');
38855 this.textEl.dom.title = text;
38859 onTabClick : function(e){
38860 e.preventDefault();
38861 this.tabPanel.activate(this.id);
38864 onTabMouseDown : function(e){
38865 e.preventDefault();
38866 this.tabPanel.activate(this.id);
38869 getWidth : function(){
38870 return this.inner.getWidth();
38873 setWidth : function(width){
38874 var iwidth = width - this.linode.getPadding("lr");
38875 this.inner.setWidth(iwidth);
38876 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38877 this.linode.setWidth(width);
38881 * Show or hide the tab
38882 * @param {Boolean} hidden True to hide or false to show.
38884 setHidden : function(hidden){
38885 this.hidden = hidden;
38886 this.linode.setStyle("display", hidden ? "none" : "");
38890 * Returns true if this tab is "hidden"
38891 * @return {Boolean}
38893 isHidden : function(){
38894 return this.hidden;
38898 * Returns the text for this tab
38901 getText : function(){
38905 autoSize : function(){
38906 //this.el.beginMeasure();
38907 this.textEl.setWidth(1);
38909 * #2804 [new] Tabs in Roojs
38910 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38912 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38913 //this.el.endMeasure();
38917 * Sets the text for the tab (Note: this also sets the tooltip text)
38918 * @param {String} text The tab's text and tooltip
38920 setText : function(text){
38922 this.textEl.update(text);
38923 this.setTooltip(text);
38924 //if(!this.tabPanel.resizeTabs){
38925 // this.autoSize();
38929 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38931 activate : function(){
38932 this.tabPanel.activate(this.id);
38936 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38938 disable : function(){
38939 if(this.tabPanel.active != this){
38940 this.disabled = true;
38941 this.status_node.addClass("disabled");
38946 * Enables this TabPanelItem if it was previously disabled.
38948 enable : function(){
38949 this.disabled = false;
38950 this.status_node.removeClass("disabled");
38954 * Sets the content for this TabPanelItem.
38955 * @param {String} content The content
38956 * @param {Boolean} loadScripts true to look for and load scripts
38958 setContent : function(content, loadScripts){
38959 this.bodyEl.update(content, loadScripts);
38963 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38964 * @return {Roo.UpdateManager} The UpdateManager
38966 getUpdateManager : function(){
38967 return this.bodyEl.getUpdateManager();
38971 * Set a URL to be used to load the content for this TabPanelItem.
38972 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38973 * @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)
38974 * @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)
38975 * @return {Roo.UpdateManager} The UpdateManager
38977 setUrl : function(url, params, loadOnce){
38978 if(this.refreshDelegate){
38979 this.un('activate', this.refreshDelegate);
38981 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38982 this.on("activate", this.refreshDelegate);
38983 return this.bodyEl.getUpdateManager();
38987 _handleRefresh : function(url, params, loadOnce){
38988 if(!loadOnce || !this.loaded){
38989 var updater = this.bodyEl.getUpdateManager();
38990 updater.update(url, params, this._setLoaded.createDelegate(this));
38995 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38996 * Will fail silently if the setUrl method has not been called.
38997 * This does not activate the panel, just updates its content.
38999 refresh : function(){
39000 if(this.refreshDelegate){
39001 this.loaded = false;
39002 this.refreshDelegate();
39007 _setLoaded : function(){
39008 this.loaded = true;
39012 closeClick : function(e){
39015 this.fireEvent("beforeclose", this, o);
39016 if(o.cancel !== true){
39017 this.tabPanel.removeTab(this.id);
39021 * The text displayed in the tooltip for the close icon.
39024 closeText : "Close this tab"
39027 * This script refer to:
39028 * Title: International Telephone Input
39029 * Author: Jack O'Connor
39030 * Code version: v12.1.12
39031 * Availability: https://github.com/jackocnr/intl-tel-input.git
39034 Roo.bootstrap.PhoneInputData = function() {
39037 "Afghanistan (افغانستان)",
39042 "Albania (Shqipëri)",
39047 "Algeria (الجزائر)",
39072 "Antigua and Barbuda",
39082 "Armenia (Հայաստան)",
39098 "Austria (Österreich)",
39103 "Azerbaijan (Azərbaycan)",
39113 "Bahrain (البحرين)",
39118 "Bangladesh (বাংলাদেশ)",
39128 "Belarus (Беларусь)",
39133 "Belgium (België)",
39163 "Bosnia and Herzegovina (Босна и Херцеговина)",
39178 "British Indian Ocean Territory",
39183 "British Virgin Islands",
39193 "Bulgaria (България)",
39203 "Burundi (Uburundi)",
39208 "Cambodia (កម្ពុជា)",
39213 "Cameroon (Cameroun)",
39222 ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
39225 "Cape Verde (Kabu Verdi)",
39230 "Caribbean Netherlands",
39241 "Central African Republic (République centrafricaine)",
39261 "Christmas Island",
39267 "Cocos (Keeling) Islands",
39278 "Comoros (جزر القمر)",
39283 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39288 "Congo (Republic) (Congo-Brazzaville)",
39308 "Croatia (Hrvatska)",
39329 "Czech Republic (Česká republika)",
39334 "Denmark (Danmark)",
39349 "Dominican Republic (República Dominicana)",
39353 ["809", "829", "849"]
39371 "Equatorial Guinea (Guinea Ecuatorial)",
39391 "Falkland Islands (Islas Malvinas)",
39396 "Faroe Islands (Føroyar)",
39417 "French Guiana (Guyane française)",
39422 "French Polynesia (Polynésie française)",
39437 "Georgia (საქართველო)",
39442 "Germany (Deutschland)",
39462 "Greenland (Kalaallit Nunaat)",
39499 "Guinea-Bissau (Guiné Bissau)",
39524 "Hungary (Magyarország)",
39529 "Iceland (Ísland)",
39549 "Iraq (العراق)",
39565 "Israel (ישראל)",
39592 "Jordan (الأردن)",
39597 "Kazakhstan (Казахстан)",
39618 "Kuwait (الكويت)",
39623 "Kyrgyzstan (Кыргызстан)",
39633 "Latvia (Latvija)",
39638 "Lebanon (لبنان)",
39653 "Libya (ليبيا)",
39663 "Lithuania (Lietuva)",
39678 "Macedonia (FYROM) (Македонија)",
39683 "Madagascar (Madagasikara)",
39713 "Marshall Islands",
39723 "Mauritania (موريتانيا)",
39728 "Mauritius (Moris)",
39749 "Moldova (Republica Moldova)",
39759 "Mongolia (Монгол)",
39764 "Montenegro (Crna Gora)",
39774 "Morocco (المغرب)",
39780 "Mozambique (Moçambique)",
39785 "Myanmar (Burma) (မြန်မာ)",
39790 "Namibia (Namibië)",
39805 "Netherlands (Nederland)",
39810 "New Caledonia (Nouvelle-Calédonie)",
39845 "North Korea (조선 민주주의 인민 공화국)",
39850 "Northern Mariana Islands",
39866 "Pakistan (پاکستان)",
39876 "Palestine (فلسطين)",
39886 "Papua New Guinea",
39928 "Réunion (La Réunion)",
39934 "Romania (România)",
39950 "Saint Barthélemy",
39961 "Saint Kitts and Nevis",
39971 "Saint Martin (Saint-Martin (partie française))",
39977 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39982 "Saint Vincent and the Grenadines",
39997 "São Tomé and Príncipe (São Tomé e Príncipe)",
40002 "Saudi Arabia (المملكة العربية السعودية)",
40007 "Senegal (Sénégal)",
40037 "Slovakia (Slovensko)",
40042 "Slovenia (Slovenija)",
40052 "Somalia (Soomaaliya)",
40062 "South Korea (대한민국)",
40067 "South Sudan (جنوب السودان)",
40077 "Sri Lanka (ශ්රී ලංකාව)",
40082 "Sudan (السودان)",
40092 "Svalbard and Jan Mayen",
40103 "Sweden (Sverige)",
40108 "Switzerland (Schweiz)",
40113 "Syria (سوريا)",
40158 "Trinidad and Tobago",
40163 "Tunisia (تونس)",
40168 "Turkey (Türkiye)",
40178 "Turks and Caicos Islands",
40188 "U.S. Virgin Islands",
40198 "Ukraine (Україна)",
40203 "United Arab Emirates (الإمارات العربية المتحدة)",
40225 "Uzbekistan (Oʻzbekiston)",
40235 "Vatican City (Città del Vaticano)",
40246 "Vietnam (Việt Nam)",
40251 "Wallis and Futuna (Wallis-et-Futuna)",
40256 "Western Sahara (الصحراء الغربية)",
40262 "Yemen (اليمن)",
40286 * This script refer to:
40287 * Title: International Telephone Input
40288 * Author: Jack O'Connor
40289 * Code version: v12.1.12
40290 * Availability: https://github.com/jackocnr/intl-tel-input.git
40294 * @class Roo.bootstrap.PhoneInput
40295 * @extends Roo.bootstrap.TriggerField
40296 * An input with International dial-code selection
40298 * @cfg {String} defaultDialCode default '+852'
40299 * @cfg {Array} preferedCountries default []
40302 * Create a new PhoneInput.
40303 * @param {Object} config Configuration options
40306 Roo.bootstrap.PhoneInput = function(config) {
40307 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40310 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40312 listWidth: undefined,
40314 selectedClass: 'active',
40316 invalidClass : "has-warning",
40318 validClass: 'has-success',
40320 allowed: '0123456789',
40325 * @cfg {String} defaultDialCode The default dial code when initializing the input
40327 defaultDialCode: '+852',
40330 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
40332 preferedCountries: false,
40334 getAutoCreate : function()
40336 var data = Roo.bootstrap.PhoneInputData();
40337 var align = this.labelAlign || this.parentLabelAlign();
40340 this.allCountries = [];
40341 this.dialCodeMapping = [];
40343 for (var i = 0; i < data.length; i++) {
40345 this.allCountries[i] = {
40349 priority: c[3] || 0,
40350 areaCodes: c[4] || null
40352 this.dialCodeMapping[c[2]] = {
40355 priority: c[3] || 0,
40356 areaCodes: c[4] || null
40368 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40369 maxlength: this.max_length,
40370 cls : 'form-control tel-input',
40371 autocomplete: 'new-password'
40374 var hiddenInput = {
40377 cls: 'hidden-tel-input'
40381 hiddenInput.name = this.name;
40384 if (this.disabled) {
40385 input.disabled = true;
40388 var flag_container = {
40405 cls: this.hasFeedback ? 'has-feedback' : '',
40411 cls: 'dial-code-holder',
40418 cls: 'roo-select2-container input-group',
40425 if (this.fieldLabel.length) {
40428 tooltip: 'This field is required'
40434 cls: 'control-label',
40440 html: this.fieldLabel
40443 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40449 if(this.indicatorpos == 'right') {
40450 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40457 if(align == 'left') {
40465 if(this.labelWidth > 12){
40466 label.style = "width: " + this.labelWidth + 'px';
40468 if(this.labelWidth < 13 && this.labelmd == 0){
40469 this.labelmd = this.labelWidth;
40471 if(this.labellg > 0){
40472 label.cls += ' col-lg-' + this.labellg;
40473 input.cls += ' col-lg-' + (12 - this.labellg);
40475 if(this.labelmd > 0){
40476 label.cls += ' col-md-' + this.labelmd;
40477 container.cls += ' col-md-' + (12 - this.labelmd);
40479 if(this.labelsm > 0){
40480 label.cls += ' col-sm-' + this.labelsm;
40481 container.cls += ' col-sm-' + (12 - this.labelsm);
40483 if(this.labelxs > 0){
40484 label.cls += ' col-xs-' + this.labelxs;
40485 container.cls += ' col-xs-' + (12 - this.labelxs);
40495 var settings = this;
40497 ['xs','sm','md','lg'].map(function(size){
40498 if (settings[size]) {
40499 cfg.cls += ' col-' + size + '-' + settings[size];
40503 this.store = new Roo.data.Store({
40504 proxy : new Roo.data.MemoryProxy({}),
40505 reader : new Roo.data.JsonReader({
40516 'name' : 'dialCode',
40520 'name' : 'priority',
40524 'name' : 'areaCodes',
40531 if(!this.preferedCountries) {
40532 this.preferedCountries = [
40539 var p = this.preferedCountries.reverse();
40542 for (var i = 0; i < p.length; i++) {
40543 for (var j = 0; j < this.allCountries.length; j++) {
40544 if(this.allCountries[j].iso2 == p[i]) {
40545 var t = this.allCountries[j];
40546 this.allCountries.splice(j,1);
40547 this.allCountries.unshift(t);
40553 this.store.proxy.data = {
40555 data: this.allCountries
40561 initEvents : function()
40564 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40566 this.indicator = this.indicatorEl();
40567 this.flag = this.flagEl();
40568 this.dialCodeHolder = this.dialCodeHolderEl();
40570 this.trigger = this.el.select('div.flag-box',true).first();
40571 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40576 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40577 _this.list.setWidth(lw);
40580 this.list.on('mouseover', this.onViewOver, this);
40581 this.list.on('mousemove', this.onViewMove, this);
40582 this.inputEl().on("keyup", this.onKeyUp, this);
40583 this.inputEl().on("keypress", this.onKeyPress, this);
40585 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40587 this.view = new Roo.View(this.list, this.tpl, {
40588 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40591 this.view.on('click', this.onViewClick, this);
40592 this.setValue(this.defaultDialCode);
40595 onTriggerClick : function(e)
40597 Roo.log('trigger click');
40602 if(this.isExpanded()){
40604 this.hasFocus = false;
40606 this.store.load({});
40607 this.hasFocus = true;
40612 isExpanded : function()
40614 return this.list.isVisible();
40617 collapse : function()
40619 if(!this.isExpanded()){
40623 Roo.get(document).un('mousedown', this.collapseIf, this);
40624 Roo.get(document).un('mousewheel', this.collapseIf, this);
40625 this.fireEvent('collapse', this);
40629 expand : function()
40633 if(this.isExpanded() || !this.hasFocus){
40637 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40638 this.list.setWidth(lw);
40641 this.restrictHeight();
40643 Roo.get(document).on('mousedown', this.collapseIf, this);
40644 Roo.get(document).on('mousewheel', this.collapseIf, this);
40646 this.fireEvent('expand', this);
40649 restrictHeight : function()
40651 this.list.alignTo(this.inputEl(), this.listAlign);
40652 this.list.alignTo(this.inputEl(), this.listAlign);
40655 onViewOver : function(e, t)
40657 if(this.inKeyMode){
40660 var item = this.view.findItemFromChild(t);
40663 var index = this.view.indexOf(item);
40664 this.select(index, false);
40669 onViewClick : function(view, doFocus, el, e)
40671 var index = this.view.getSelectedIndexes()[0];
40673 var r = this.store.getAt(index);
40676 this.onSelect(r, index);
40678 if(doFocus !== false && !this.blockFocus){
40679 this.inputEl().focus();
40683 onViewMove : function(e, t)
40685 this.inKeyMode = false;
40688 select : function(index, scrollIntoView)
40690 this.selectedIndex = index;
40691 this.view.select(index);
40692 if(scrollIntoView !== false){
40693 var el = this.view.getNode(index);
40695 this.list.scrollChildIntoView(el, false);
40700 createList : function()
40702 this.list = Roo.get(document.body).createChild({
40704 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40705 style: 'display:none'
40708 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40711 collapseIf : function(e)
40713 var in_combo = e.within(this.el);
40714 var in_list = e.within(this.list);
40715 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40717 if (in_combo || in_list || is_list) {
40723 onSelect : function(record, index)
40725 if(this.fireEvent('beforeselect', this, record, index) !== false){
40727 this.setFlagClass(record.data.iso2);
40728 this.setDialCode(record.data.dialCode);
40729 this.hasFocus = false;
40731 this.fireEvent('select', this, record, index);
40735 flagEl : function()
40737 var flag = this.el.select('div.flag',true).first();
40744 dialCodeHolderEl : function()
40746 var d = this.el.select('input.dial-code-holder',true).first();
40753 setDialCode : function(v)
40755 this.dialCodeHolder.dom.value = '+'+v;
40758 setFlagClass : function(n)
40760 this.flag.dom.className = 'flag '+n;
40763 getValue : function()
40765 var v = this.inputEl().getValue();
40766 if(this.dialCodeHolder) {
40767 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40772 setValue : function(v)
40774 var d = this.getDialCode(v);
40776 //invalid dial code
40777 if(v.length == 0 || !d || d.length == 0) {
40779 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40780 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40786 this.setFlagClass(this.dialCodeMapping[d].iso2);
40787 this.setDialCode(d);
40788 this.inputEl().dom.value = v.replace('+'+d,'');
40789 this.hiddenEl().dom.value = this.getValue();
40794 getDialCode : function(v)
40798 if (v.length == 0) {
40799 return this.dialCodeHolder.dom.value;
40803 if (v.charAt(0) != "+") {
40806 var numericChars = "";
40807 for (var i = 1; i < v.length; i++) {
40808 var c = v.charAt(i);
40811 if (this.dialCodeMapping[numericChars]) {
40812 dialCode = v.substr(1, i);
40814 if (numericChars.length == 4) {
40824 this.setValue(this.defaultDialCode);
40828 hiddenEl : function()
40830 return this.el.select('input.hidden-tel-input',true).first();
40833 // after setting val
40834 onKeyUp : function(e){
40835 this.setValue(this.getValue());
40838 onKeyPress : function(e){
40839 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40846 * @class Roo.bootstrap.MoneyField
40847 * @extends Roo.bootstrap.ComboBox
40848 * Bootstrap MoneyField class
40851 * Create a new MoneyField.
40852 * @param {Object} config Configuration options
40855 Roo.bootstrap.MoneyField = function(config) {
40857 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40861 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40864 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40866 allowDecimals : true,
40868 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40870 decimalSeparator : ".",
40872 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40874 decimalPrecision : 0,
40876 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40878 allowNegative : true,
40880 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40884 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40886 minValue : Number.NEGATIVE_INFINITY,
40888 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40890 maxValue : Number.MAX_VALUE,
40892 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40894 minText : "The minimum value for this field is {0}",
40896 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40898 maxText : "The maximum value for this field is {0}",
40900 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40901 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40903 nanText : "{0} is not a valid number",
40905 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40909 * @cfg {String} defaults currency of the MoneyField
40910 * value should be in lkey
40912 defaultCurrency : false,
40914 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40916 thousandsDelimiter : false,
40918 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40929 getAutoCreate : function()
40931 var align = this.labelAlign || this.parentLabelAlign();
40943 cls : 'form-control roo-money-amount-input',
40944 autocomplete: 'new-password'
40947 var hiddenInput = {
40951 cls: 'hidden-number-input'
40954 if(this.max_length) {
40955 input.maxlength = this.max_length;
40959 hiddenInput.name = this.name;
40962 if (this.disabled) {
40963 input.disabled = true;
40966 var clg = 12 - this.inputlg;
40967 var cmd = 12 - this.inputmd;
40968 var csm = 12 - this.inputsm;
40969 var cxs = 12 - this.inputxs;
40973 cls : 'row roo-money-field',
40977 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40981 cls: 'roo-select2-container input-group',
40985 cls : 'form-control roo-money-currency-input',
40986 autocomplete: 'new-password',
40988 name : this.currencyName
40992 cls : 'input-group-addon',
41006 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41010 cls: this.hasFeedback ? 'has-feedback' : '',
41021 if (this.fieldLabel.length) {
41024 tooltip: 'This field is required'
41030 cls: 'control-label',
41036 html: this.fieldLabel
41039 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41045 if(this.indicatorpos == 'right') {
41046 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41053 if(align == 'left') {
41061 if(this.labelWidth > 12){
41062 label.style = "width: " + this.labelWidth + 'px';
41064 if(this.labelWidth < 13 && this.labelmd == 0){
41065 this.labelmd = this.labelWidth;
41067 if(this.labellg > 0){
41068 label.cls += ' col-lg-' + this.labellg;
41069 input.cls += ' col-lg-' + (12 - this.labellg);
41071 if(this.labelmd > 0){
41072 label.cls += ' col-md-' + this.labelmd;
41073 container.cls += ' col-md-' + (12 - this.labelmd);
41075 if(this.labelsm > 0){
41076 label.cls += ' col-sm-' + this.labelsm;
41077 container.cls += ' col-sm-' + (12 - this.labelsm);
41079 if(this.labelxs > 0){
41080 label.cls += ' col-xs-' + this.labelxs;
41081 container.cls += ' col-xs-' + (12 - this.labelxs);
41092 var settings = this;
41094 ['xs','sm','md','lg'].map(function(size){
41095 if (settings[size]) {
41096 cfg.cls += ' col-' + size + '-' + settings[size];
41103 initEvents : function()
41105 this.indicator = this.indicatorEl();
41107 this.initCurrencyEvent();
41109 this.initNumberEvent();
41112 initCurrencyEvent : function()
41115 throw "can not find store for combo";
41118 this.store = Roo.factory(this.store, Roo.data);
41119 this.store.parent = this;
41123 this.triggerEl = this.el.select('.input-group-addon', true).first();
41125 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41130 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41131 _this.list.setWidth(lw);
41134 this.list.on('mouseover', this.onViewOver, this);
41135 this.list.on('mousemove', this.onViewMove, this);
41136 this.list.on('scroll', this.onViewScroll, this);
41139 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41142 this.view = new Roo.View(this.list, this.tpl, {
41143 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41146 this.view.on('click', this.onViewClick, this);
41148 this.store.on('beforeload', this.onBeforeLoad, this);
41149 this.store.on('load', this.onLoad, this);
41150 this.store.on('loadexception', this.onLoadException, this);
41152 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41153 "up" : function(e){
41154 this.inKeyMode = true;
41158 "down" : function(e){
41159 if(!this.isExpanded()){
41160 this.onTriggerClick();
41162 this.inKeyMode = true;
41167 "enter" : function(e){
41170 if(this.fireEvent("specialkey", this, e)){
41171 this.onViewClick(false);
41177 "esc" : function(e){
41181 "tab" : function(e){
41184 if(this.fireEvent("specialkey", this, e)){
41185 this.onViewClick(false);
41193 doRelay : function(foo, bar, hname){
41194 if(hname == 'down' || this.scope.isExpanded()){
41195 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41203 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41207 initNumberEvent : function(e)
41209 this.inputEl().on("keydown" , this.fireKey, this);
41210 this.inputEl().on("focus", this.onFocus, this);
41211 this.inputEl().on("blur", this.onBlur, this);
41213 this.inputEl().relayEvent('keyup', this);
41215 if(this.indicator){
41216 this.indicator.addClass('invisible');
41219 this.originalValue = this.getValue();
41221 if(this.validationEvent == 'keyup'){
41222 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41223 this.inputEl().on('keyup', this.filterValidation, this);
41225 else if(this.validationEvent !== false){
41226 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41229 if(this.selectOnFocus){
41230 this.on("focus", this.preFocus, this);
41233 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41234 this.inputEl().on("keypress", this.filterKeys, this);
41236 this.inputEl().relayEvent('keypress', this);
41239 var allowed = "0123456789";
41241 if(this.allowDecimals){
41242 allowed += this.decimalSeparator;
41245 if(this.allowNegative){
41249 if(this.thousandsDelimiter) {
41253 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41255 var keyPress = function(e){
41257 var k = e.getKey();
41259 var c = e.getCharCode();
41262 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41263 allowed.indexOf(String.fromCharCode(c)) === -1
41269 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41273 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41278 this.inputEl().on("keypress", keyPress, this);
41282 onTriggerClick : function(e)
41289 this.loadNext = false;
41291 if(this.isExpanded()){
41296 this.hasFocus = true;
41298 if(this.triggerAction == 'all') {
41299 this.doQuery(this.allQuery, true);
41303 this.doQuery(this.getRawValue());
41306 getCurrency : function()
41308 var v = this.currencyEl().getValue();
41313 restrictHeight : function()
41315 this.list.alignTo(this.currencyEl(), this.listAlign);
41316 this.list.alignTo(this.currencyEl(), this.listAlign);
41319 onViewClick : function(view, doFocus, el, e)
41321 var index = this.view.getSelectedIndexes()[0];
41323 var r = this.store.getAt(index);
41326 this.onSelect(r, index);
41330 onSelect : function(record, index){
41332 if(this.fireEvent('beforeselect', this, record, index) !== false){
41334 this.setFromCurrencyData(index > -1 ? record.data : false);
41338 this.fireEvent('select', this, record, index);
41342 setFromCurrencyData : function(o)
41346 this.lastCurrency = o;
41348 if (this.currencyField) {
41349 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41351 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41354 this.lastSelectionText = currency;
41356 //setting default currency
41357 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41358 this.setCurrency(this.defaultCurrency);
41362 this.setCurrency(currency);
41365 setFromData : function(o)
41369 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41371 this.setFromCurrencyData(c);
41376 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41378 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41381 this.setValue(value);
41385 setCurrency : function(v)
41387 this.currencyValue = v;
41390 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41395 setValue : function(v)
41397 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41403 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41405 this.inputEl().dom.value = (v == '') ? '' :
41406 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41408 if(!this.allowZero && v === '0') {
41409 this.hiddenEl().dom.value = '';
41410 this.inputEl().dom.value = '';
41417 getRawValue : function()
41419 var v = this.inputEl().getValue();
41424 getValue : function()
41426 return this.fixPrecision(this.parseValue(this.getRawValue()));
41429 parseValue : function(value)
41431 if(this.thousandsDelimiter) {
41433 r = new RegExp(",", "g");
41434 value = value.replace(r, "");
41437 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41438 return isNaN(value) ? '' : value;
41442 fixPrecision : function(value)
41444 if(this.thousandsDelimiter) {
41446 r = new RegExp(",", "g");
41447 value = value.replace(r, "");
41450 var nan = isNaN(value);
41452 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41453 return nan ? '' : value;
41455 return parseFloat(value).toFixed(this.decimalPrecision);
41458 decimalPrecisionFcn : function(v)
41460 return Math.floor(v);
41463 validateValue : function(value)
41465 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41469 var num = this.parseValue(value);
41472 this.markInvalid(String.format(this.nanText, value));
41476 if(num < this.minValue){
41477 this.markInvalid(String.format(this.minText, this.minValue));
41481 if(num > this.maxValue){
41482 this.markInvalid(String.format(this.maxText, this.maxValue));
41489 validate : function()
41491 if(this.disabled || this.allowBlank){
41496 var currency = this.getCurrency();
41498 if(this.validateValue(this.getRawValue()) && currency.length){
41503 this.markInvalid();
41507 getName: function()
41512 beforeBlur : function()
41518 var v = this.parseValue(this.getRawValue());
41525 onBlur : function()
41529 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41530 //this.el.removeClass(this.focusClass);
41533 this.hasFocus = false;
41535 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41539 var v = this.getValue();
41541 if(String(v) !== String(this.startValue)){
41542 this.fireEvent('change', this, v, this.startValue);
41545 this.fireEvent("blur", this);
41548 inputEl : function()
41550 return this.el.select('.roo-money-amount-input', true).first();
41553 currencyEl : function()
41555 return this.el.select('.roo-money-currency-input', true).first();
41558 hiddenEl : function()
41560 return this.el.select('input.hidden-number-input',true).first();
41564 * @class Roo.bootstrap.BezierSignature
41565 * @extends Roo.bootstrap.Component
41566 * Bootstrap BezierSignature class
41567 * This script refer to:
41568 * Title: Signature Pad
41570 * Availability: https://github.com/szimek/signature_pad
41573 * Create a new BezierSignature
41574 * @param {Object} config The config object
41577 Roo.bootstrap.BezierSignature = function(config){
41578 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41584 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41591 mouse_btn_down: true,
41594 * @cfg {int} canvas height
41596 canvas_height: '200px',
41599 * @cfg {float|function} Radius of a single dot.
41604 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41609 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41614 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41619 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41624 * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
41626 bg_color: 'rgba(0, 0, 0, 0)',
41629 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41631 dot_color: 'black',
41634 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41636 velocity_filter_weight: 0.7,
41639 * @cfg {function} Callback when stroke begin.
41644 * @cfg {function} Callback when stroke end.
41648 getAutoCreate : function()
41650 var cls = 'roo-signature column';
41653 cls += ' ' + this.cls;
41663 for(var i = 0; i < col_sizes.length; i++) {
41664 if(this[col_sizes[i]]) {
41665 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41675 cls: 'roo-signature-body',
41679 cls: 'roo-signature-body-canvas',
41680 height: this.canvas_height,
41681 width: this.canvas_width
41688 style: 'display: none'
41696 initEvents: function()
41698 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41700 var canvas = this.canvasEl();
41702 // mouse && touch event swapping...
41703 canvas.dom.style.touchAction = 'none';
41704 canvas.dom.style.msTouchAction = 'none';
41706 this.mouse_btn_down = false;
41707 canvas.on('mousedown', this._handleMouseDown, this);
41708 canvas.on('mousemove', this._handleMouseMove, this);
41709 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41711 if (window.PointerEvent) {
41712 canvas.on('pointerdown', this._handleMouseDown, this);
41713 canvas.on('pointermove', this._handleMouseMove, this);
41714 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41717 if ('ontouchstart' in window) {
41718 canvas.on('touchstart', this._handleTouchStart, this);
41719 canvas.on('touchmove', this._handleTouchMove, this);
41720 canvas.on('touchend', this._handleTouchEnd, this);
41723 Roo.EventManager.onWindowResize(this.resize, this, true);
41725 // file input event
41726 this.fileEl().on('change', this.uploadImage, this);
41733 resize: function(){
41735 var canvas = this.canvasEl().dom;
41736 var ctx = this.canvasElCtx();
41737 var img_data = false;
41739 if(canvas.width > 0) {
41740 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41742 // setting canvas width will clean img data
41745 var style = window.getComputedStyle ?
41746 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41748 var padding_left = parseInt(style.paddingLeft) || 0;
41749 var padding_right = parseInt(style.paddingRight) || 0;
41751 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41754 ctx.putImageData(img_data, 0, 0);
41758 _handleMouseDown: function(e)
41760 if (e.browserEvent.which === 1) {
41761 this.mouse_btn_down = true;
41762 this.strokeBegin(e);
41766 _handleMouseMove: function (e)
41768 if (this.mouse_btn_down) {
41769 this.strokeMoveUpdate(e);
41773 _handleMouseUp: function (e)
41775 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41776 this.mouse_btn_down = false;
41781 _handleTouchStart: function (e) {
41783 e.preventDefault();
41784 if (e.browserEvent.targetTouches.length === 1) {
41785 // var touch = e.browserEvent.changedTouches[0];
41786 // this.strokeBegin(touch);
41788 this.strokeBegin(e); // assume e catching the correct xy...
41792 _handleTouchMove: function (e) {
41793 e.preventDefault();
41794 // var touch = event.targetTouches[0];
41795 // _this._strokeMoveUpdate(touch);
41796 this.strokeMoveUpdate(e);
41799 _handleTouchEnd: function (e) {
41800 var wasCanvasTouched = e.target === this.canvasEl().dom;
41801 if (wasCanvasTouched) {
41802 e.preventDefault();
41803 // var touch = event.changedTouches[0];
41804 // _this._strokeEnd(touch);
41809 reset: function () {
41810 this._lastPoints = [];
41811 this._lastVelocity = 0;
41812 this._lastWidth = (this.min_width + this.max_width) / 2;
41813 this.canvasElCtx().fillStyle = this.dot_color;
41816 strokeMoveUpdate: function(e)
41818 this.strokeUpdate(e);
41820 if (this.throttle) {
41821 this.throttleStroke(this.strokeUpdate, this.throttle);
41824 this.strokeUpdate(e);
41828 strokeBegin: function(e)
41830 var newPointGroup = {
41831 color: this.dot_color,
41835 if (typeof this.onBegin === 'function') {
41839 this.curve_data.push(newPointGroup);
41841 this.strokeUpdate(e);
41844 strokeUpdate: function(e)
41846 var rect = this.canvasEl().dom.getBoundingClientRect();
41847 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41848 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41849 var lastPoints = lastPointGroup.points;
41850 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41851 var isLastPointTooClose = lastPoint
41852 ? point.distanceTo(lastPoint) <= this.min_distance
41854 var color = lastPointGroup.color;
41855 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41856 var curve = this.addPoint(point);
41858 this.drawDot({color: color, point: point});
41861 this.drawCurve({color: color, curve: curve});
41871 strokeEnd: function(e)
41873 this.strokeUpdate(e);
41874 if (typeof this.onEnd === 'function') {
41879 addPoint: function (point) {
41880 var _lastPoints = this._lastPoints;
41881 _lastPoints.push(point);
41882 if (_lastPoints.length > 2) {
41883 if (_lastPoints.length === 3) {
41884 _lastPoints.unshift(_lastPoints[0]);
41886 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41887 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41888 _lastPoints.shift();
41894 calculateCurveWidths: function (startPoint, endPoint) {
41895 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41896 (1 - this.velocity_filter_weight) * this._lastVelocity;
41898 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41901 start: this._lastWidth
41904 this._lastVelocity = velocity;
41905 this._lastWidth = newWidth;
41909 drawDot: function (_a) {
41910 var color = _a.color, point = _a.point;
41911 var ctx = this.canvasElCtx();
41912 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41914 this.drawCurveSegment(point.x, point.y, width);
41916 ctx.fillStyle = color;
41920 drawCurve: function (_a) {
41921 var color = _a.color, curve = _a.curve;
41922 var ctx = this.canvasElCtx();
41923 var widthDelta = curve.endWidth - curve.startWidth;
41924 var drawSteps = Math.floor(curve.length()) * 2;
41926 ctx.fillStyle = color;
41927 for (var i = 0; i < drawSteps; i += 1) {
41928 var t = i / drawSteps;
41934 var x = uuu * curve.startPoint.x;
41935 x += 3 * uu * t * curve.control1.x;
41936 x += 3 * u * tt * curve.control2.x;
41937 x += ttt * curve.endPoint.x;
41938 var y = uuu * curve.startPoint.y;
41939 y += 3 * uu * t * curve.control1.y;
41940 y += 3 * u * tt * curve.control2.y;
41941 y += ttt * curve.endPoint.y;
41942 var width = curve.startWidth + ttt * widthDelta;
41943 this.drawCurveSegment(x, y, width);
41949 drawCurveSegment: function (x, y, width) {
41950 var ctx = this.canvasElCtx();
41952 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41953 this.is_empty = false;
41958 var ctx = this.canvasElCtx();
41959 var canvas = this.canvasEl().dom;
41960 ctx.fillStyle = this.bg_color;
41961 ctx.clearRect(0, 0, canvas.width, canvas.height);
41962 ctx.fillRect(0, 0, canvas.width, canvas.height);
41963 this.curve_data = [];
41965 this.is_empty = true;
41970 return this.el.select('input',true).first();
41973 canvasEl: function()
41975 return this.el.select('canvas',true).first();
41978 canvasElCtx: function()
41980 return this.el.select('canvas',true).first().dom.getContext('2d');
41983 getImage: function(type)
41985 if(this.is_empty) {
41990 return this.canvasEl().dom.toDataURL('image/'+type, 1);
41993 drawFromImage: function(img_src)
41995 var img = new Image();
41997 img.onload = function(){
41998 this.canvasElCtx().drawImage(img, 0, 0);
42003 this.is_empty = false;
42006 selectImage: function()
42008 this.fileEl().dom.click();
42011 uploadImage: function(e)
42013 var reader = new FileReader();
42015 reader.onload = function(e){
42016 var img = new Image();
42017 img.onload = function(){
42019 this.canvasElCtx().drawImage(img, 0, 0);
42021 img.src = e.target.result;
42024 reader.readAsDataURL(e.target.files[0]);
42027 // Bezier Point Constructor
42028 Point: (function () {
42029 function Point(x, y, time) {
42032 this.time = time || Date.now();
42034 Point.prototype.distanceTo = function (start) {
42035 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42037 Point.prototype.equals = function (other) {
42038 return this.x === other.x && this.y === other.y && this.time === other.time;
42040 Point.prototype.velocityFrom = function (start) {
42041 return this.time !== start.time
42042 ? this.distanceTo(start) / (this.time - start.time)
42049 // Bezier Constructor
42050 Bezier: (function () {
42051 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42052 this.startPoint = startPoint;
42053 this.control2 = control2;
42054 this.control1 = control1;
42055 this.endPoint = endPoint;
42056 this.startWidth = startWidth;
42057 this.endWidth = endWidth;
42059 Bezier.fromPoints = function (points, widths, scope) {
42060 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42061 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42062 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42064 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42065 var dx1 = s1.x - s2.x;
42066 var dy1 = s1.y - s2.y;
42067 var dx2 = s2.x - s3.x;
42068 var dy2 = s2.y - s3.y;
42069 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42070 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42071 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42072 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42073 var dxm = m1.x - m2.x;
42074 var dym = m1.y - m2.y;
42075 var k = l2 / (l1 + l2);
42076 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42077 var tx = s2.x - cm.x;
42078 var ty = s2.y - cm.y;
42080 c1: new scope.Point(m1.x + tx, m1.y + ty),
42081 c2: new scope.Point(m2.x + tx, m2.y + ty)
42084 Bezier.prototype.length = function () {
42089 for (var i = 0; i <= steps; i += 1) {
42091 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42092 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42094 var xdiff = cx - px;
42095 var ydiff = cy - py;
42096 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42103 Bezier.prototype.point = function (t, start, c1, c2, end) {
42104 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42105 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42106 + (3.0 * c2 * (1.0 - t) * t * t)
42107 + (end * t * t * t);
42112 throttleStroke: function(fn, wait) {
42113 if (wait === void 0) { wait = 250; }
42115 var timeout = null;
42119 var later = function () {
42120 previous = Date.now();
42122 result = fn.apply(storedContext, storedArgs);
42124 storedContext = null;
42128 return function wrapper() {
42130 for (var _i = 0; _i < arguments.length; _i++) {
42131 args[_i] = arguments[_i];
42133 var now = Date.now();
42134 var remaining = wait - (now - previous);
42135 storedContext = this;
42137 if (remaining <= 0 || remaining > wait) {
42139 clearTimeout(timeout);
42143 result = fn.apply(storedContext, storedArgs);
42145 storedContext = null;
42149 else if (!timeout) {
42150 timeout = window.setTimeout(later, remaining);