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' +
10660 (Roo.bootstrap.version == 3 ? ' dropdown-toggle' : ''),
10665 cls: 'combobox-clear',
10679 combobox.cls += ' roo-select2-container-multi';
10683 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10684 tooltip : 'This field is required'
10686 if (Roo.bootstrap.version == 4) {
10689 style : 'display:none'
10694 if (align ==='left' && this.fieldLabel.length) {
10696 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10703 cls : 'control-label',
10704 html : this.fieldLabel
10716 var labelCfg = cfg.cn[1];
10717 var contentCfg = cfg.cn[2];
10719 if(this.indicatorpos == 'right'){
10724 cls : 'control-label',
10728 html : this.fieldLabel
10742 labelCfg = cfg.cn[0];
10743 contentCfg = cfg.cn[1];
10746 if(this.labelWidth > 12){
10747 labelCfg.style = "width: " + this.labelWidth + 'px';
10750 if(this.labelWidth < 13 && this.labelmd == 0){
10751 this.labelmd = this.labelWidth;
10754 if(this.labellg > 0){
10755 labelCfg.cls += ' col-lg-' + this.labellg;
10756 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10759 if(this.labelmd > 0){
10760 labelCfg.cls += ' col-md-' + this.labelmd;
10761 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10764 if(this.labelsm > 0){
10765 labelCfg.cls += ' col-sm-' + this.labelsm;
10766 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10769 if(this.labelxs > 0){
10770 labelCfg.cls += ' col-xs-' + this.labelxs;
10771 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10774 } else if ( this.fieldLabel.length) {
10775 // Roo.log(" label");
10780 //cls : 'input-group-addon',
10781 html : this.fieldLabel
10789 if(this.indicatorpos == 'right'){
10797 html : this.fieldLabel
10811 // Roo.log(" no label && no align");
10818 ['xs','sm','md','lg'].map(function(size){
10819 if (settings[size]) {
10820 cfg.cls += ' col-' + size + '-' + settings[size];
10831 onResize : function(w, h){
10832 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10833 // if(typeof w == 'number'){
10834 // var x = w - this.trigger.getWidth();
10835 // this.inputEl().setWidth(this.adjustWidth('input', x));
10836 // this.trigger.setStyle('left', x+'px');
10841 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10844 getResizeEl : function(){
10845 return this.inputEl();
10849 getPositionEl : function(){
10850 return this.inputEl();
10854 alignErrorIcon : function(){
10855 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10859 initEvents : function(){
10863 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10864 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10865 if(!this.multiple && this.showToggleBtn){
10866 this.trigger = this.el.select('span.input-group-append',true).first();
10867 if(this.hideTrigger){
10868 this.trigger.setDisplayed(false);
10870 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10874 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10877 if(this.removable && !this.editable && !this.tickable){
10878 var close = this.closeTriggerEl();
10881 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10882 close.on('click', this.removeBtnClick, this, close);
10886 //this.trigger.addClassOnOver('x-form-trigger-over');
10887 //this.trigger.addClassOnClick('x-form-trigger-click');
10890 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10894 closeTriggerEl : function()
10896 var close = this.el.select('.roo-combo-removable-btn', true).first();
10897 return close ? close : false;
10900 removeBtnClick : function(e, h, el)
10902 e.preventDefault();
10904 if(this.fireEvent("remove", this) !== false){
10906 this.fireEvent("afterremove", this)
10910 createList : function()
10912 this.list = Roo.get(document.body).createChild({
10913 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10914 cls: 'typeahead typeahead-long dropdown-menu',
10915 style: 'display:none'
10918 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10923 initTrigger : function(){
10928 onDestroy : function(){
10930 this.trigger.removeAllListeners();
10931 // this.trigger.remove();
10934 // this.wrap.remove();
10936 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10940 onFocus : function(){
10941 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10943 if(!this.mimicing){
10944 this.wrap.addClass('x-trigger-wrap-focus');
10945 this.mimicing = true;
10946 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10947 if(this.monitorTab){
10948 this.el.on("keydown", this.checkTab, this);
10955 checkTab : function(e){
10956 if(e.getKey() == e.TAB){
10957 this.triggerBlur();
10962 onBlur : function(){
10967 mimicBlur : function(e, t){
10969 if(!this.wrap.contains(t) && this.validateBlur()){
10970 this.triggerBlur();
10976 triggerBlur : function(){
10977 this.mimicing = false;
10978 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10979 if(this.monitorTab){
10980 this.el.un("keydown", this.checkTab, this);
10982 //this.wrap.removeClass('x-trigger-wrap-focus');
10983 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10987 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10988 validateBlur : function(e, t){
10993 onDisable : function(){
10994 this.inputEl().dom.disabled = true;
10995 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10997 // this.wrap.addClass('x-item-disabled');
11002 onEnable : function(){
11003 this.inputEl().dom.disabled = false;
11004 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11006 // this.el.removeClass('x-item-disabled');
11011 onShow : function(){
11012 var ae = this.getActionEl();
11015 ae.dom.style.display = '';
11016 ae.dom.style.visibility = 'visible';
11022 onHide : function(){
11023 var ae = this.getActionEl();
11024 ae.dom.style.display = 'none';
11028 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11029 * by an implementing function.
11031 * @param {EventObject} e
11033 onTriggerClick : Roo.emptyFn
11037 * Ext JS Library 1.1.1
11038 * Copyright(c) 2006-2007, Ext JS, LLC.
11040 * Originally Released Under LGPL - original licence link has changed is not relivant.
11043 * <script type="text/javascript">
11048 * @class Roo.data.SortTypes
11050 * Defines the default sorting (casting?) comparison functions used when sorting data.
11052 Roo.data.SortTypes = {
11054 * Default sort that does nothing
11055 * @param {Mixed} s The value being converted
11056 * @return {Mixed} The comparison value
11058 none : function(s){
11063 * The regular expression used to strip tags
11067 stripTagsRE : /<\/?[^>]+>/gi,
11070 * Strips all HTML tags to sort on text only
11071 * @param {Mixed} s The value being converted
11072 * @return {String} The comparison value
11074 asText : function(s){
11075 return String(s).replace(this.stripTagsRE, "");
11079 * Strips all HTML tags to sort on text only - Case insensitive
11080 * @param {Mixed} s The value being converted
11081 * @return {String} The comparison value
11083 asUCText : function(s){
11084 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11088 * Case insensitive string
11089 * @param {Mixed} s The value being converted
11090 * @return {String} The comparison value
11092 asUCString : function(s) {
11093 return String(s).toUpperCase();
11098 * @param {Mixed} s The value being converted
11099 * @return {Number} The comparison value
11101 asDate : function(s) {
11105 if(s instanceof Date){
11106 return s.getTime();
11108 return Date.parse(String(s));
11113 * @param {Mixed} s The value being converted
11114 * @return {Float} The comparison value
11116 asFloat : function(s) {
11117 var val = parseFloat(String(s).replace(/,/g, ""));
11126 * @param {Mixed} s The value being converted
11127 * @return {Number} The comparison value
11129 asInt : function(s) {
11130 var val = parseInt(String(s).replace(/,/g, ""));
11138 * Ext JS Library 1.1.1
11139 * Copyright(c) 2006-2007, Ext JS, LLC.
11141 * Originally Released Under LGPL - original licence link has changed is not relivant.
11144 * <script type="text/javascript">
11148 * @class Roo.data.Record
11149 * Instances of this class encapsulate both record <em>definition</em> information, and record
11150 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11151 * to access Records cached in an {@link Roo.data.Store} object.<br>
11153 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11154 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11157 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11159 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11160 * {@link #create}. The parameters are the same.
11161 * @param {Array} data An associative Array of data values keyed by the field name.
11162 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11163 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11164 * not specified an integer id is generated.
11166 Roo.data.Record = function(data, id){
11167 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11172 * Generate a constructor for a specific record layout.
11173 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11174 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11175 * Each field definition object may contain the following properties: <ul>
11176 * <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,
11177 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11178 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11179 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11180 * is being used, then this is a string containing the javascript expression to reference the data relative to
11181 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11182 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11183 * this may be omitted.</p></li>
11184 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11185 * <ul><li>auto (Default, implies no conversion)</li>
11190 * <li>date</li></ul></p></li>
11191 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11192 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11193 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11194 * by the Reader into an object that will be stored in the Record. It is passed the
11195 * following parameters:<ul>
11196 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11198 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11200 * <br>usage:<br><pre><code>
11201 var TopicRecord = Roo.data.Record.create(
11202 {name: 'title', mapping: 'topic_title'},
11203 {name: 'author', mapping: 'username'},
11204 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11205 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11206 {name: 'lastPoster', mapping: 'user2'},
11207 {name: 'excerpt', mapping: 'post_text'}
11210 var myNewRecord = new TopicRecord({
11211 title: 'Do my job please',
11214 lastPost: new Date(),
11215 lastPoster: 'Animal',
11216 excerpt: 'No way dude!'
11218 myStore.add(myNewRecord);
11223 Roo.data.Record.create = function(o){
11224 var f = function(){
11225 f.superclass.constructor.apply(this, arguments);
11227 Roo.extend(f, Roo.data.Record);
11228 var p = f.prototype;
11229 p.fields = new Roo.util.MixedCollection(false, function(field){
11232 for(var i = 0, len = o.length; i < len; i++){
11233 p.fields.add(new Roo.data.Field(o[i]));
11235 f.getField = function(name){
11236 return p.fields.get(name);
11241 Roo.data.Record.AUTO_ID = 1000;
11242 Roo.data.Record.EDIT = 'edit';
11243 Roo.data.Record.REJECT = 'reject';
11244 Roo.data.Record.COMMIT = 'commit';
11246 Roo.data.Record.prototype = {
11248 * Readonly flag - true if this record has been modified.
11257 join : function(store){
11258 this.store = store;
11262 * Set the named field to the specified value.
11263 * @param {String} name The name of the field to set.
11264 * @param {Object} value The value to set the field to.
11266 set : function(name, value){
11267 if(this.data[name] == value){
11271 if(!this.modified){
11272 this.modified = {};
11274 if(typeof this.modified[name] == 'undefined'){
11275 this.modified[name] = this.data[name];
11277 this.data[name] = value;
11278 if(!this.editing && this.store){
11279 this.store.afterEdit(this);
11284 * Get the value of the named field.
11285 * @param {String} name The name of the field to get the value of.
11286 * @return {Object} The value of the field.
11288 get : function(name){
11289 return this.data[name];
11293 beginEdit : function(){
11294 this.editing = true;
11295 this.modified = {};
11299 cancelEdit : function(){
11300 this.editing = false;
11301 delete this.modified;
11305 endEdit : function(){
11306 this.editing = false;
11307 if(this.dirty && this.store){
11308 this.store.afterEdit(this);
11313 * Usually called by the {@link Roo.data.Store} which owns the Record.
11314 * Rejects all changes made to the Record since either creation, or the last commit operation.
11315 * Modified fields are reverted to their original values.
11317 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11318 * of reject operations.
11320 reject : function(){
11321 var m = this.modified;
11323 if(typeof m[n] != "function"){
11324 this.data[n] = m[n];
11327 this.dirty = false;
11328 delete this.modified;
11329 this.editing = false;
11331 this.store.afterReject(this);
11336 * Usually called by the {@link Roo.data.Store} which owns the Record.
11337 * Commits all changes made to the Record since either creation, or the last commit operation.
11339 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11340 * of commit operations.
11342 commit : function(){
11343 this.dirty = false;
11344 delete this.modified;
11345 this.editing = false;
11347 this.store.afterCommit(this);
11352 hasError : function(){
11353 return this.error != null;
11357 clearError : function(){
11362 * Creates a copy of this record.
11363 * @param {String} id (optional) A new record id if you don't want to use this record's id
11366 copy : function(newId) {
11367 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11371 * Ext JS Library 1.1.1
11372 * Copyright(c) 2006-2007, Ext JS, LLC.
11374 * Originally Released Under LGPL - original licence link has changed is not relivant.
11377 * <script type="text/javascript">
11383 * @class Roo.data.Store
11384 * @extends Roo.util.Observable
11385 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11386 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11388 * 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
11389 * has no knowledge of the format of the data returned by the Proxy.<br>
11391 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11392 * instances from the data object. These records are cached and made available through accessor functions.
11394 * Creates a new Store.
11395 * @param {Object} config A config object containing the objects needed for the Store to access data,
11396 * and read the data into Records.
11398 Roo.data.Store = function(config){
11399 this.data = new Roo.util.MixedCollection(false);
11400 this.data.getKey = function(o){
11403 this.baseParams = {};
11405 this.paramNames = {
11410 "multisort" : "_multisort"
11413 if(config && config.data){
11414 this.inlineData = config.data;
11415 delete config.data;
11418 Roo.apply(this, config);
11420 if(this.reader){ // reader passed
11421 this.reader = Roo.factory(this.reader, Roo.data);
11422 this.reader.xmodule = this.xmodule || false;
11423 if(!this.recordType){
11424 this.recordType = this.reader.recordType;
11426 if(this.reader.onMetaChange){
11427 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11431 if(this.recordType){
11432 this.fields = this.recordType.prototype.fields;
11434 this.modified = [];
11438 * @event datachanged
11439 * Fires when the data cache has changed, and a widget which is using this Store
11440 * as a Record cache should refresh its view.
11441 * @param {Store} this
11443 datachanged : true,
11445 * @event metachange
11446 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11447 * @param {Store} this
11448 * @param {Object} meta The JSON metadata
11453 * Fires when Records have been added to the Store
11454 * @param {Store} this
11455 * @param {Roo.data.Record[]} records The array of Records added
11456 * @param {Number} index The index at which the record(s) were added
11461 * Fires when a Record has been removed from the Store
11462 * @param {Store} this
11463 * @param {Roo.data.Record} record The Record that was removed
11464 * @param {Number} index The index at which the record was removed
11469 * Fires when a Record has been updated
11470 * @param {Store} this
11471 * @param {Roo.data.Record} record The Record that was updated
11472 * @param {String} operation The update operation being performed. Value may be one of:
11474 Roo.data.Record.EDIT
11475 Roo.data.Record.REJECT
11476 Roo.data.Record.COMMIT
11482 * Fires when the data cache has been cleared.
11483 * @param {Store} this
11487 * @event beforeload
11488 * Fires before a request is made for a new data object. If the beforeload handler returns false
11489 * the load action will be canceled.
11490 * @param {Store} this
11491 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11495 * @event beforeloadadd
11496 * Fires after a new set of Records has been loaded.
11497 * @param {Store} this
11498 * @param {Roo.data.Record[]} records The Records that were loaded
11499 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11501 beforeloadadd : true,
11504 * Fires after a new set of Records has been loaded, before they are added to the store.
11505 * @param {Store} this
11506 * @param {Roo.data.Record[]} records The Records that were loaded
11507 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11508 * @params {Object} return from reader
11512 * @event loadexception
11513 * Fires if an exception occurs in the Proxy during loading.
11514 * Called with the signature of the Proxy's "loadexception" event.
11515 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11518 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11519 * @param {Object} load options
11520 * @param {Object} jsonData from your request (normally this contains the Exception)
11522 loadexception : true
11526 this.proxy = Roo.factory(this.proxy, Roo.data);
11527 this.proxy.xmodule = this.xmodule || false;
11528 this.relayEvents(this.proxy, ["loadexception"]);
11530 this.sortToggle = {};
11531 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11533 Roo.data.Store.superclass.constructor.call(this);
11535 if(this.inlineData){
11536 this.loadData(this.inlineData);
11537 delete this.inlineData;
11541 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11543 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11544 * without a remote query - used by combo/forms at present.
11548 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11551 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11554 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11555 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11558 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11559 * on any HTTP request
11562 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11565 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11569 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11570 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11572 remoteSort : false,
11575 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11576 * loaded or when a record is removed. (defaults to false).
11578 pruneModifiedRecords : false,
11581 lastOptions : null,
11584 * Add Records to the Store and fires the add event.
11585 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11587 add : function(records){
11588 records = [].concat(records);
11589 for(var i = 0, len = records.length; i < len; i++){
11590 records[i].join(this);
11592 var index = this.data.length;
11593 this.data.addAll(records);
11594 this.fireEvent("add", this, records, index);
11598 * Remove a Record from the Store and fires the remove event.
11599 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11601 remove : function(record){
11602 var index = this.data.indexOf(record);
11603 this.data.removeAt(index);
11605 if(this.pruneModifiedRecords){
11606 this.modified.remove(record);
11608 this.fireEvent("remove", this, record, index);
11612 * Remove all Records from the Store and fires the clear event.
11614 removeAll : function(){
11616 if(this.pruneModifiedRecords){
11617 this.modified = [];
11619 this.fireEvent("clear", this);
11623 * Inserts Records to the Store at the given index and fires the add event.
11624 * @param {Number} index The start index at which to insert the passed Records.
11625 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11627 insert : function(index, records){
11628 records = [].concat(records);
11629 for(var i = 0, len = records.length; i < len; i++){
11630 this.data.insert(index, records[i]);
11631 records[i].join(this);
11633 this.fireEvent("add", this, records, index);
11637 * Get the index within the cache of the passed Record.
11638 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11639 * @return {Number} The index of the passed Record. Returns -1 if not found.
11641 indexOf : function(record){
11642 return this.data.indexOf(record);
11646 * Get the index within the cache of the Record with the passed id.
11647 * @param {String} id The id of the Record to find.
11648 * @return {Number} The index of the Record. Returns -1 if not found.
11650 indexOfId : function(id){
11651 return this.data.indexOfKey(id);
11655 * Get the Record with the specified id.
11656 * @param {String} id The id of the Record to find.
11657 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11659 getById : function(id){
11660 return this.data.key(id);
11664 * Get the Record at the specified index.
11665 * @param {Number} index The index of the Record to find.
11666 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11668 getAt : function(index){
11669 return this.data.itemAt(index);
11673 * Returns a range of Records between specified indices.
11674 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11675 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11676 * @return {Roo.data.Record[]} An array of Records
11678 getRange : function(start, end){
11679 return this.data.getRange(start, end);
11683 storeOptions : function(o){
11684 o = Roo.apply({}, o);
11687 this.lastOptions = o;
11691 * Loads the Record cache from the configured Proxy using the configured Reader.
11693 * If using remote paging, then the first load call must specify the <em>start</em>
11694 * and <em>limit</em> properties in the options.params property to establish the initial
11695 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11697 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11698 * and this call will return before the new data has been loaded. Perform any post-processing
11699 * in a callback function, or in a "load" event handler.</strong>
11701 * @param {Object} options An object containing properties which control loading options:<ul>
11702 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11703 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11704 * passed the following arguments:<ul>
11705 * <li>r : Roo.data.Record[]</li>
11706 * <li>options: Options object from the load call</li>
11707 * <li>success: Boolean success indicator</li></ul></li>
11708 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11709 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11712 load : function(options){
11713 options = options || {};
11714 if(this.fireEvent("beforeload", this, options) !== false){
11715 this.storeOptions(options);
11716 var p = Roo.apply(options.params || {}, this.baseParams);
11717 // if meta was not loaded from remote source.. try requesting it.
11718 if (!this.reader.metaFromRemote) {
11719 p._requestMeta = 1;
11721 if(this.sortInfo && this.remoteSort){
11722 var pn = this.paramNames;
11723 p[pn["sort"]] = this.sortInfo.field;
11724 p[pn["dir"]] = this.sortInfo.direction;
11726 if (this.multiSort) {
11727 var pn = this.paramNames;
11728 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11731 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11736 * Reloads the Record cache from the configured Proxy using the configured Reader and
11737 * the options from the last load operation performed.
11738 * @param {Object} options (optional) An object containing properties which may override the options
11739 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11740 * the most recently used options are reused).
11742 reload : function(options){
11743 this.load(Roo.applyIf(options||{}, this.lastOptions));
11747 // Called as a callback by the Reader during a load operation.
11748 loadRecords : function(o, options, success){
11749 if(!o || success === false){
11750 if(success !== false){
11751 this.fireEvent("load", this, [], options, o);
11753 if(options.callback){
11754 options.callback.call(options.scope || this, [], options, false);
11758 // if data returned failure - throw an exception.
11759 if (o.success === false) {
11760 // show a message if no listener is registered.
11761 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11762 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11764 // loadmask wil be hooked into this..
11765 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11768 var r = o.records, t = o.totalRecords || r.length;
11770 this.fireEvent("beforeloadadd", this, r, options, o);
11772 if(!options || options.add !== true){
11773 if(this.pruneModifiedRecords){
11774 this.modified = [];
11776 for(var i = 0, len = r.length; i < len; i++){
11780 this.data = this.snapshot;
11781 delete this.snapshot;
11784 this.data.addAll(r);
11785 this.totalLength = t;
11787 this.fireEvent("datachanged", this);
11789 this.totalLength = Math.max(t, this.data.length+r.length);
11793 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11795 var e = new Roo.data.Record({});
11797 e.set(this.parent.displayField, this.parent.emptyTitle);
11798 e.set(this.parent.valueField, '');
11803 this.fireEvent("load", this, r, options, o);
11804 if(options.callback){
11805 options.callback.call(options.scope || this, r, options, true);
11811 * Loads data from a passed data block. A Reader which understands the format of the data
11812 * must have been configured in the constructor.
11813 * @param {Object} data The data block from which to read the Records. The format of the data expected
11814 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11815 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11817 loadData : function(o, append){
11818 var r = this.reader.readRecords(o);
11819 this.loadRecords(r, {add: append}, true);
11823 * Gets the number of cached records.
11825 * <em>If using paging, this may not be the total size of the dataset. If the data object
11826 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11827 * the data set size</em>
11829 getCount : function(){
11830 return this.data.length || 0;
11834 * Gets the total number of records in the dataset as returned by the server.
11836 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11837 * the dataset size</em>
11839 getTotalCount : function(){
11840 return this.totalLength || 0;
11844 * Returns the sort state of the Store as an object with two properties:
11846 field {String} The name of the field by which the Records are sorted
11847 direction {String} The sort order, "ASC" or "DESC"
11850 getSortState : function(){
11851 return this.sortInfo;
11855 applySort : function(){
11856 if(this.sortInfo && !this.remoteSort){
11857 var s = this.sortInfo, f = s.field;
11858 var st = this.fields.get(f).sortType;
11859 var fn = function(r1, r2){
11860 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11861 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11863 this.data.sort(s.direction, fn);
11864 if(this.snapshot && this.snapshot != this.data){
11865 this.snapshot.sort(s.direction, fn);
11871 * Sets the default sort column and order to be used by the next load operation.
11872 * @param {String} fieldName The name of the field to sort by.
11873 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11875 setDefaultSort : function(field, dir){
11876 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11880 * Sort the Records.
11881 * If remote sorting is used, the sort is performed on the server, and the cache is
11882 * reloaded. If local sorting is used, the cache is sorted internally.
11883 * @param {String} fieldName The name of the field to sort by.
11884 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11886 sort : function(fieldName, dir){
11887 var f = this.fields.get(fieldName);
11889 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11891 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11892 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11897 this.sortToggle[f.name] = dir;
11898 this.sortInfo = {field: f.name, direction: dir};
11899 if(!this.remoteSort){
11901 this.fireEvent("datachanged", this);
11903 this.load(this.lastOptions);
11908 * Calls the specified function for each of the Records in the cache.
11909 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11910 * Returning <em>false</em> aborts and exits the iteration.
11911 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11913 each : function(fn, scope){
11914 this.data.each(fn, scope);
11918 * Gets all records modified since the last commit. Modified records are persisted across load operations
11919 * (e.g., during paging).
11920 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11922 getModifiedRecords : function(){
11923 return this.modified;
11927 createFilterFn : function(property, value, anyMatch){
11928 if(!value.exec){ // not a regex
11929 value = String(value);
11930 if(value.length == 0){
11933 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11935 return function(r){
11936 return value.test(r.data[property]);
11941 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11942 * @param {String} property A field on your records
11943 * @param {Number} start The record index to start at (defaults to 0)
11944 * @param {Number} end The last record index to include (defaults to length - 1)
11945 * @return {Number} The sum
11947 sum : function(property, start, end){
11948 var rs = this.data.items, v = 0;
11949 start = start || 0;
11950 end = (end || end === 0) ? end : rs.length-1;
11952 for(var i = start; i <= end; i++){
11953 v += (rs[i].data[property] || 0);
11959 * Filter the records by a specified property.
11960 * @param {String} field A field on your records
11961 * @param {String/RegExp} value Either a string that the field
11962 * should start with or a RegExp to test against the field
11963 * @param {Boolean} anyMatch True to match any part not just the beginning
11965 filter : function(property, value, anyMatch){
11966 var fn = this.createFilterFn(property, value, anyMatch);
11967 return fn ? this.filterBy(fn) : this.clearFilter();
11971 * Filter by a function. The specified function will be called with each
11972 * record in this data source. If the function returns true the record is included,
11973 * otherwise it is filtered.
11974 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11975 * @param {Object} scope (optional) The scope of the function (defaults to this)
11977 filterBy : function(fn, scope){
11978 this.snapshot = this.snapshot || this.data;
11979 this.data = this.queryBy(fn, scope||this);
11980 this.fireEvent("datachanged", this);
11984 * Query the records by a specified property.
11985 * @param {String} field A field on your records
11986 * @param {String/RegExp} value Either a string that the field
11987 * should start with or a RegExp to test against the field
11988 * @param {Boolean} anyMatch True to match any part not just the beginning
11989 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11991 query : function(property, value, anyMatch){
11992 var fn = this.createFilterFn(property, value, anyMatch);
11993 return fn ? this.queryBy(fn) : this.data.clone();
11997 * Query by a function. The specified function will be called with each
11998 * record in this data source. If the function returns true the record is included
12000 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12001 * @param {Object} scope (optional) The scope of the function (defaults to this)
12002 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12004 queryBy : function(fn, scope){
12005 var data = this.snapshot || this.data;
12006 return data.filterBy(fn, scope||this);
12010 * Collects unique values for a particular dataIndex from this store.
12011 * @param {String} dataIndex The property to collect
12012 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12013 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12014 * @return {Array} An array of the unique values
12016 collect : function(dataIndex, allowNull, bypassFilter){
12017 var d = (bypassFilter === true && this.snapshot) ?
12018 this.snapshot.items : this.data.items;
12019 var v, sv, r = [], l = {};
12020 for(var i = 0, len = d.length; i < len; i++){
12021 v = d[i].data[dataIndex];
12023 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12032 * Revert to a view of the Record cache with no filtering applied.
12033 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12035 clearFilter : function(suppressEvent){
12036 if(this.snapshot && this.snapshot != this.data){
12037 this.data = this.snapshot;
12038 delete this.snapshot;
12039 if(suppressEvent !== true){
12040 this.fireEvent("datachanged", this);
12046 afterEdit : function(record){
12047 if(this.modified.indexOf(record) == -1){
12048 this.modified.push(record);
12050 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12054 afterReject : function(record){
12055 this.modified.remove(record);
12056 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12060 afterCommit : function(record){
12061 this.modified.remove(record);
12062 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12066 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12067 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12069 commitChanges : function(){
12070 var m = this.modified.slice(0);
12071 this.modified = [];
12072 for(var i = 0, len = m.length; i < len; i++){
12078 * Cancel outstanding changes on all changed records.
12080 rejectChanges : function(){
12081 var m = this.modified.slice(0);
12082 this.modified = [];
12083 for(var i = 0, len = m.length; i < len; i++){
12088 onMetaChange : function(meta, rtype, o){
12089 this.recordType = rtype;
12090 this.fields = rtype.prototype.fields;
12091 delete this.snapshot;
12092 this.sortInfo = meta.sortInfo || this.sortInfo;
12093 this.modified = [];
12094 this.fireEvent('metachange', this, this.reader.meta);
12097 moveIndex : function(data, type)
12099 var index = this.indexOf(data);
12101 var newIndex = index + type;
12105 this.insert(newIndex, data);
12110 * Ext JS Library 1.1.1
12111 * Copyright(c) 2006-2007, Ext JS, LLC.
12113 * Originally Released Under LGPL - original licence link has changed is not relivant.
12116 * <script type="text/javascript">
12120 * @class Roo.data.SimpleStore
12121 * @extends Roo.data.Store
12122 * Small helper class to make creating Stores from Array data easier.
12123 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12124 * @cfg {Array} fields An array of field definition objects, or field name strings.
12125 * @cfg {Array} data The multi-dimensional array of data
12127 * @param {Object} config
12129 Roo.data.SimpleStore = function(config){
12130 Roo.data.SimpleStore.superclass.constructor.call(this, {
12132 reader: new Roo.data.ArrayReader({
12135 Roo.data.Record.create(config.fields)
12137 proxy : new Roo.data.MemoryProxy(config.data)
12141 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12143 * Ext JS Library 1.1.1
12144 * Copyright(c) 2006-2007, Ext JS, LLC.
12146 * Originally Released Under LGPL - original licence link has changed is not relivant.
12149 * <script type="text/javascript">
12154 * @extends Roo.data.Store
12155 * @class Roo.data.JsonStore
12156 * Small helper class to make creating Stores for JSON data easier. <br/>
12158 var store = new Roo.data.JsonStore({
12159 url: 'get-images.php',
12161 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12164 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12165 * JsonReader and HttpProxy (unless inline data is provided).</b>
12166 * @cfg {Array} fields An array of field definition objects, or field name strings.
12168 * @param {Object} config
12170 Roo.data.JsonStore = function(c){
12171 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12172 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12173 reader: new Roo.data.JsonReader(c, c.fields)
12176 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12178 * Ext JS Library 1.1.1
12179 * Copyright(c) 2006-2007, Ext JS, LLC.
12181 * Originally Released Under LGPL - original licence link has changed is not relivant.
12184 * <script type="text/javascript">
12188 Roo.data.Field = function(config){
12189 if(typeof config == "string"){
12190 config = {name: config};
12192 Roo.apply(this, config);
12195 this.type = "auto";
12198 var st = Roo.data.SortTypes;
12199 // named sortTypes are supported, here we look them up
12200 if(typeof this.sortType == "string"){
12201 this.sortType = st[this.sortType];
12204 // set default sortType for strings and dates
12205 if(!this.sortType){
12208 this.sortType = st.asUCString;
12211 this.sortType = st.asDate;
12214 this.sortType = st.none;
12219 var stripRe = /[\$,%]/g;
12221 // prebuilt conversion function for this field, instead of
12222 // switching every time we're reading a value
12224 var cv, dateFormat = this.dateFormat;
12229 cv = function(v){ return v; };
12232 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12236 return v !== undefined && v !== null && v !== '' ?
12237 parseInt(String(v).replace(stripRe, ""), 10) : '';
12242 return v !== undefined && v !== null && v !== '' ?
12243 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12248 cv = function(v){ return v === true || v === "true" || v == 1; };
12255 if(v instanceof Date){
12259 if(dateFormat == "timestamp"){
12260 return new Date(v*1000);
12262 return Date.parseDate(v, dateFormat);
12264 var parsed = Date.parse(v);
12265 return parsed ? new Date(parsed) : null;
12274 Roo.data.Field.prototype = {
12282 * Ext JS Library 1.1.1
12283 * Copyright(c) 2006-2007, Ext JS, LLC.
12285 * Originally Released Under LGPL - original licence link has changed is not relivant.
12288 * <script type="text/javascript">
12291 // Base class for reading structured data from a data source. This class is intended to be
12292 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12295 * @class Roo.data.DataReader
12296 * Base class for reading structured data from a data source. This class is intended to be
12297 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12300 Roo.data.DataReader = function(meta, recordType){
12304 this.recordType = recordType instanceof Array ?
12305 Roo.data.Record.create(recordType) : recordType;
12308 Roo.data.DataReader.prototype = {
12310 * Create an empty record
12311 * @param {Object} data (optional) - overlay some values
12312 * @return {Roo.data.Record} record created.
12314 newRow : function(d) {
12316 this.recordType.prototype.fields.each(function(c) {
12318 case 'int' : da[c.name] = 0; break;
12319 case 'date' : da[c.name] = new Date(); break;
12320 case 'float' : da[c.name] = 0.0; break;
12321 case 'boolean' : da[c.name] = false; break;
12322 default : da[c.name] = ""; break;
12326 return new this.recordType(Roo.apply(da, d));
12331 * Ext JS Library 1.1.1
12332 * Copyright(c) 2006-2007, Ext JS, LLC.
12334 * Originally Released Under LGPL - original licence link has changed is not relivant.
12337 * <script type="text/javascript">
12341 * @class Roo.data.DataProxy
12342 * @extends Roo.data.Observable
12343 * This class is an abstract base class for implementations which provide retrieval of
12344 * unformatted data objects.<br>
12346 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12347 * (of the appropriate type which knows how to parse the data object) to provide a block of
12348 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12350 * Custom implementations must implement the load method as described in
12351 * {@link Roo.data.HttpProxy#load}.
12353 Roo.data.DataProxy = function(){
12356 * @event beforeload
12357 * Fires before a network request is made to retrieve a data object.
12358 * @param {Object} This DataProxy object.
12359 * @param {Object} params The params parameter to the load function.
12364 * Fires before the load method's callback is called.
12365 * @param {Object} This DataProxy object.
12366 * @param {Object} o The data object.
12367 * @param {Object} arg The callback argument object passed to the load function.
12371 * @event loadexception
12372 * Fires if an Exception occurs during data retrieval.
12373 * @param {Object} This DataProxy object.
12374 * @param {Object} o The data object.
12375 * @param {Object} arg The callback argument object passed to the load function.
12376 * @param {Object} e The Exception.
12378 loadexception : true
12380 Roo.data.DataProxy.superclass.constructor.call(this);
12383 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12386 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12390 * Ext JS Library 1.1.1
12391 * Copyright(c) 2006-2007, Ext JS, LLC.
12393 * Originally Released Under LGPL - original licence link has changed is not relivant.
12396 * <script type="text/javascript">
12399 * @class Roo.data.MemoryProxy
12400 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12401 * to the Reader when its load method is called.
12403 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12405 Roo.data.MemoryProxy = function(data){
12409 Roo.data.MemoryProxy.superclass.constructor.call(this);
12413 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12416 * Load data from the requested source (in this case an in-memory
12417 * data object passed to the constructor), read the data object into
12418 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12419 * process that block using the passed callback.
12420 * @param {Object} params This parameter is not used by the MemoryProxy class.
12421 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12422 * object into a block of Roo.data.Records.
12423 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12424 * The function must be passed <ul>
12425 * <li>The Record block object</li>
12426 * <li>The "arg" argument from the load function</li>
12427 * <li>A boolean success indicator</li>
12429 * @param {Object} scope The scope in which to call the callback
12430 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12432 load : function(params, reader, callback, scope, arg){
12433 params = params || {};
12436 result = reader.readRecords(params.data ? params.data :this.data);
12438 this.fireEvent("loadexception", this, arg, null, e);
12439 callback.call(scope, null, arg, false);
12442 callback.call(scope, result, arg, true);
12446 update : function(params, records){
12451 * Ext JS Library 1.1.1
12452 * Copyright(c) 2006-2007, Ext JS, LLC.
12454 * Originally Released Under LGPL - original licence link has changed is not relivant.
12457 * <script type="text/javascript">
12460 * @class Roo.data.HttpProxy
12461 * @extends Roo.data.DataProxy
12462 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12463 * configured to reference a certain URL.<br><br>
12465 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12466 * from which the running page was served.<br><br>
12468 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12470 * Be aware that to enable the browser to parse an XML document, the server must set
12471 * the Content-Type header in the HTTP response to "text/xml".
12473 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12474 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12475 * will be used to make the request.
12477 Roo.data.HttpProxy = function(conn){
12478 Roo.data.HttpProxy.superclass.constructor.call(this);
12479 // is conn a conn config or a real conn?
12481 this.useAjax = !conn || !conn.events;
12485 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12486 // thse are take from connection...
12489 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12492 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12493 * extra parameters to each request made by this object. (defaults to undefined)
12496 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12497 * to each request made by this object. (defaults to undefined)
12500 * @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)
12503 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12506 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12512 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12516 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12517 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12518 * a finer-grained basis than the DataProxy events.
12520 getConnection : function(){
12521 return this.useAjax ? Roo.Ajax : this.conn;
12525 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12526 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12527 * process that block using the passed callback.
12528 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12529 * for the request to the remote server.
12530 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12531 * object into a block of Roo.data.Records.
12532 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12533 * The function must be passed <ul>
12534 * <li>The Record block object</li>
12535 * <li>The "arg" argument from the load function</li>
12536 * <li>A boolean success indicator</li>
12538 * @param {Object} scope The scope in which to call the callback
12539 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12541 load : function(params, reader, callback, scope, arg){
12542 if(this.fireEvent("beforeload", this, params) !== false){
12544 params : params || {},
12546 callback : callback,
12551 callback : this.loadResponse,
12555 Roo.applyIf(o, this.conn);
12556 if(this.activeRequest){
12557 Roo.Ajax.abort(this.activeRequest);
12559 this.activeRequest = Roo.Ajax.request(o);
12561 this.conn.request(o);
12564 callback.call(scope||this, null, arg, false);
12569 loadResponse : function(o, success, response){
12570 delete this.activeRequest;
12572 this.fireEvent("loadexception", this, o, response);
12573 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12578 result = o.reader.read(response);
12580 this.fireEvent("loadexception", this, o, response, e);
12581 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12585 this.fireEvent("load", this, o, o.request.arg);
12586 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12590 update : function(dataSet){
12595 updateResponse : function(dataSet){
12600 * Ext JS Library 1.1.1
12601 * Copyright(c) 2006-2007, Ext JS, LLC.
12603 * Originally Released Under LGPL - original licence link has changed is not relivant.
12606 * <script type="text/javascript">
12610 * @class Roo.data.ScriptTagProxy
12611 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12612 * other than the originating domain of the running page.<br><br>
12614 * <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
12615 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12617 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12618 * source code that is used as the source inside a <script> tag.<br><br>
12620 * In order for the browser to process the returned data, the server must wrap the data object
12621 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12622 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12623 * depending on whether the callback name was passed:
12626 boolean scriptTag = false;
12627 String cb = request.getParameter("callback");
12630 response.setContentType("text/javascript");
12632 response.setContentType("application/x-json");
12634 Writer out = response.getWriter();
12636 out.write(cb + "(");
12638 out.print(dataBlock.toJsonString());
12645 * @param {Object} config A configuration object.
12647 Roo.data.ScriptTagProxy = function(config){
12648 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12649 Roo.apply(this, config);
12650 this.head = document.getElementsByTagName("head")[0];
12653 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12655 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12657 * @cfg {String} url The URL from which to request the data object.
12660 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12664 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12665 * the server the name of the callback function set up by the load call to process the returned data object.
12666 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12667 * javascript output which calls this named function passing the data object as its only parameter.
12669 callbackParam : "callback",
12671 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12672 * name to the request.
12677 * Load data from the configured URL, read the data object into
12678 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12679 * process that block using the passed callback.
12680 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12681 * for the request to the remote server.
12682 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12683 * object into a block of Roo.data.Records.
12684 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12685 * The function must be passed <ul>
12686 * <li>The Record block object</li>
12687 * <li>The "arg" argument from the load function</li>
12688 * <li>A boolean success indicator</li>
12690 * @param {Object} scope The scope in which to call the callback
12691 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12693 load : function(params, reader, callback, scope, arg){
12694 if(this.fireEvent("beforeload", this, params) !== false){
12696 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12698 var url = this.url;
12699 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12701 url += "&_dc=" + (new Date().getTime());
12703 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12706 cb : "stcCallback"+transId,
12707 scriptId : "stcScript"+transId,
12711 callback : callback,
12717 window[trans.cb] = function(o){
12718 conn.handleResponse(o, trans);
12721 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12723 if(this.autoAbort !== false){
12727 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12729 var script = document.createElement("script");
12730 script.setAttribute("src", url);
12731 script.setAttribute("type", "text/javascript");
12732 script.setAttribute("id", trans.scriptId);
12733 this.head.appendChild(script);
12735 this.trans = trans;
12737 callback.call(scope||this, null, arg, false);
12742 isLoading : function(){
12743 return this.trans ? true : false;
12747 * Abort the current server request.
12749 abort : function(){
12750 if(this.isLoading()){
12751 this.destroyTrans(this.trans);
12756 destroyTrans : function(trans, isLoaded){
12757 this.head.removeChild(document.getElementById(trans.scriptId));
12758 clearTimeout(trans.timeoutId);
12760 window[trans.cb] = undefined;
12762 delete window[trans.cb];
12765 // if hasn't been loaded, wait for load to remove it to prevent script error
12766 window[trans.cb] = function(){
12767 window[trans.cb] = undefined;
12769 delete window[trans.cb];
12776 handleResponse : function(o, trans){
12777 this.trans = false;
12778 this.destroyTrans(trans, true);
12781 result = trans.reader.readRecords(o);
12783 this.fireEvent("loadexception", this, o, trans.arg, e);
12784 trans.callback.call(trans.scope||window, null, trans.arg, false);
12787 this.fireEvent("load", this, o, trans.arg);
12788 trans.callback.call(trans.scope||window, result, trans.arg, true);
12792 handleFailure : function(trans){
12793 this.trans = false;
12794 this.destroyTrans(trans, false);
12795 this.fireEvent("loadexception", this, null, trans.arg);
12796 trans.callback.call(trans.scope||window, null, trans.arg, false);
12800 * Ext JS Library 1.1.1
12801 * Copyright(c) 2006-2007, Ext JS, LLC.
12803 * Originally Released Under LGPL - original licence link has changed is not relivant.
12806 * <script type="text/javascript">
12810 * @class Roo.data.JsonReader
12811 * @extends Roo.data.DataReader
12812 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12813 * based on mappings in a provided Roo.data.Record constructor.
12815 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12816 * in the reply previously.
12821 var RecordDef = Roo.data.Record.create([
12822 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12823 {name: 'occupation'} // This field will use "occupation" as the mapping.
12825 var myReader = new Roo.data.JsonReader({
12826 totalProperty: "results", // The property which contains the total dataset size (optional)
12827 root: "rows", // The property which contains an Array of row objects
12828 id: "id" // The property within each row object that provides an ID for the record (optional)
12832 * This would consume a JSON file like this:
12834 { 'results': 2, 'rows': [
12835 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12836 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12839 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12840 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12841 * paged from the remote server.
12842 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12843 * @cfg {String} root name of the property which contains the Array of row objects.
12844 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12845 * @cfg {Array} fields Array of field definition objects
12847 * Create a new JsonReader
12848 * @param {Object} meta Metadata configuration options
12849 * @param {Object} recordType Either an Array of field definition objects,
12850 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12852 Roo.data.JsonReader = function(meta, recordType){
12855 // set some defaults:
12856 Roo.applyIf(meta, {
12857 totalProperty: 'total',
12858 successProperty : 'success',
12863 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12865 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12868 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12869 * Used by Store query builder to append _requestMeta to params.
12872 metaFromRemote : false,
12874 * This method is only used by a DataProxy which has retrieved data from a remote server.
12875 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12876 * @return {Object} data A data block which is used by an Roo.data.Store object as
12877 * a cache of Roo.data.Records.
12879 read : function(response){
12880 var json = response.responseText;
12882 var o = /* eval:var:o */ eval("("+json+")");
12884 throw {message: "JsonReader.read: Json object not found"};
12890 this.metaFromRemote = true;
12891 this.meta = o.metaData;
12892 this.recordType = Roo.data.Record.create(o.metaData.fields);
12893 this.onMetaChange(this.meta, this.recordType, o);
12895 return this.readRecords(o);
12898 // private function a store will implement
12899 onMetaChange : function(meta, recordType, o){
12906 simpleAccess: function(obj, subsc) {
12913 getJsonAccessor: function(){
12915 return function(expr) {
12917 return(re.test(expr))
12918 ? new Function("obj", "return obj." + expr)
12923 return Roo.emptyFn;
12928 * Create a data block containing Roo.data.Records from an XML document.
12929 * @param {Object} o An object which contains an Array of row objects in the property specified
12930 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12931 * which contains the total size of the dataset.
12932 * @return {Object} data A data block which is used by an Roo.data.Store object as
12933 * a cache of Roo.data.Records.
12935 readRecords : function(o){
12937 * After any data loads, the raw JSON data is available for further custom processing.
12941 var s = this.meta, Record = this.recordType,
12942 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12944 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12946 if(s.totalProperty) {
12947 this.getTotal = this.getJsonAccessor(s.totalProperty);
12949 if(s.successProperty) {
12950 this.getSuccess = this.getJsonAccessor(s.successProperty);
12952 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12954 var g = this.getJsonAccessor(s.id);
12955 this.getId = function(rec) {
12957 return (r === undefined || r === "") ? null : r;
12960 this.getId = function(){return null;};
12963 for(var jj = 0; jj < fl; jj++){
12965 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12966 this.ef[jj] = this.getJsonAccessor(map);
12970 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12971 if(s.totalProperty){
12972 var vt = parseInt(this.getTotal(o), 10);
12977 if(s.successProperty){
12978 var vs = this.getSuccess(o);
12979 if(vs === false || vs === 'false'){
12984 for(var i = 0; i < c; i++){
12987 var id = this.getId(n);
12988 for(var j = 0; j < fl; j++){
12990 var v = this.ef[j](n);
12992 Roo.log('missing convert for ' + f.name);
12996 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12998 var record = new Record(values, id);
13000 records[i] = record;
13006 totalRecords : totalRecords
13011 * Ext JS Library 1.1.1
13012 * Copyright(c) 2006-2007, Ext JS, LLC.
13014 * Originally Released Under LGPL - original licence link has changed is not relivant.
13017 * <script type="text/javascript">
13021 * @class Roo.data.ArrayReader
13022 * @extends Roo.data.DataReader
13023 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13024 * Each element of that Array represents a row of data fields. The
13025 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13026 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13030 var RecordDef = Roo.data.Record.create([
13031 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13032 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13034 var myReader = new Roo.data.ArrayReader({
13035 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13039 * This would consume an Array like this:
13041 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13045 * Create a new JsonReader
13046 * @param {Object} meta Metadata configuration options.
13047 * @param {Object|Array} recordType Either an Array of field definition objects
13049 * @cfg {Array} fields Array of field definition objects
13050 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13051 * as specified to {@link Roo.data.Record#create},
13052 * or an {@link Roo.data.Record} object
13055 * created using {@link Roo.data.Record#create}.
13057 Roo.data.ArrayReader = function(meta, recordType){
13060 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13063 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13065 * Create a data block containing Roo.data.Records from an XML document.
13066 * @param {Object} o An Array of row objects which represents the dataset.
13067 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13068 * a cache of Roo.data.Records.
13070 readRecords : function(o){
13071 var sid = this.meta ? this.meta.id : null;
13072 var recordType = this.recordType, fields = recordType.prototype.fields;
13075 for(var i = 0; i < root.length; i++){
13078 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13079 for(var j = 0, jlen = fields.length; j < jlen; j++){
13080 var f = fields.items[j];
13081 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13082 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13084 values[f.name] = v;
13086 var record = new recordType(values, id);
13088 records[records.length] = record;
13092 totalRecords : records.length
13101 * @class Roo.bootstrap.ComboBox
13102 * @extends Roo.bootstrap.TriggerField
13103 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13104 * @cfg {Boolean} append (true|false) default false
13105 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13106 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13107 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13108 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13109 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13110 * @cfg {Boolean} animate default true
13111 * @cfg {Boolean} emptyResultText only for touch device
13112 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13113 * @cfg {String} emptyTitle default ''
13115 * Create a new ComboBox.
13116 * @param {Object} config Configuration options
13118 Roo.bootstrap.ComboBox = function(config){
13119 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13123 * Fires when the dropdown list is expanded
13124 * @param {Roo.bootstrap.ComboBox} combo This combo box
13129 * Fires when the dropdown list is collapsed
13130 * @param {Roo.bootstrap.ComboBox} combo This combo box
13134 * @event beforeselect
13135 * Fires before a list item is selected. Return false to cancel the selection.
13136 * @param {Roo.bootstrap.ComboBox} combo This combo box
13137 * @param {Roo.data.Record} record The data record returned from the underlying store
13138 * @param {Number} index The index of the selected item in the dropdown list
13140 'beforeselect' : true,
13143 * Fires when a list item is selected
13144 * @param {Roo.bootstrap.ComboBox} combo This combo box
13145 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13146 * @param {Number} index The index of the selected item in the dropdown list
13150 * @event beforequery
13151 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13152 * The event object passed has these properties:
13153 * @param {Roo.bootstrap.ComboBox} combo This combo box
13154 * @param {String} query The query
13155 * @param {Boolean} forceAll true to force "all" query
13156 * @param {Boolean} cancel true to cancel the query
13157 * @param {Object} e The query event object
13159 'beforequery': true,
13162 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13163 * @param {Roo.bootstrap.ComboBox} combo This combo box
13168 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13169 * @param {Roo.bootstrap.ComboBox} combo This combo box
13170 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13175 * Fires when the remove value from the combobox array
13176 * @param {Roo.bootstrap.ComboBox} combo This combo box
13180 * @event afterremove
13181 * Fires when the remove value from the combobox array
13182 * @param {Roo.bootstrap.ComboBox} combo This combo box
13184 'afterremove' : true,
13186 * @event specialfilter
13187 * Fires when specialfilter
13188 * @param {Roo.bootstrap.ComboBox} combo This combo box
13190 'specialfilter' : true,
13193 * Fires when tick the element
13194 * @param {Roo.bootstrap.ComboBox} combo This combo box
13198 * @event touchviewdisplay
13199 * Fires when touch view require special display (default is using displayField)
13200 * @param {Roo.bootstrap.ComboBox} combo This combo box
13201 * @param {Object} cfg set html .
13203 'touchviewdisplay' : true
13208 this.tickItems = [];
13210 this.selectedIndex = -1;
13211 if(this.mode == 'local'){
13212 if(config.queryDelay === undefined){
13213 this.queryDelay = 10;
13215 if(config.minChars === undefined){
13221 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13224 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13225 * rendering into an Roo.Editor, defaults to false)
13228 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13229 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13232 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13235 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13236 * the dropdown list (defaults to undefined, with no header element)
13240 * @cfg {String/Roo.Template} tpl The template to use to render the output
13244 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13246 listWidth: undefined,
13248 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13249 * mode = 'remote' or 'text' if mode = 'local')
13251 displayField: undefined,
13254 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13255 * mode = 'remote' or 'value' if mode = 'local').
13256 * Note: use of a valueField requires the user make a selection
13257 * in order for a value to be mapped.
13259 valueField: undefined,
13261 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13266 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13267 * field's data value (defaults to the underlying DOM element's name)
13269 hiddenName: undefined,
13271 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13275 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13277 selectedClass: 'active',
13280 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13284 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13285 * anchor positions (defaults to 'tl-bl')
13287 listAlign: 'tl-bl?',
13289 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13293 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13294 * query specified by the allQuery config option (defaults to 'query')
13296 triggerAction: 'query',
13298 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13299 * (defaults to 4, does not apply if editable = false)
13303 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13304 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13308 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13309 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13313 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13314 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13318 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13319 * when editable = true (defaults to false)
13321 selectOnFocus:false,
13323 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13325 queryParam: 'query',
13327 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13328 * when mode = 'remote' (defaults to 'Loading...')
13330 loadingText: 'Loading...',
13332 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13336 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13340 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13341 * traditional select (defaults to true)
13345 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13349 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13353 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13354 * listWidth has a higher value)
13358 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13359 * allow the user to set arbitrary text into the field (defaults to false)
13361 forceSelection:false,
13363 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13364 * if typeAhead = true (defaults to 250)
13366 typeAheadDelay : 250,
13368 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13369 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13371 valueNotFoundText : undefined,
13373 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13375 blockFocus : false,
13378 * @cfg {Boolean} disableClear Disable showing of clear button.
13380 disableClear : false,
13382 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13384 alwaysQuery : false,
13387 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13392 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13394 invalidClass : "has-warning",
13397 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13399 validClass : "has-success",
13402 * @cfg {Boolean} specialFilter (true|false) special filter default false
13404 specialFilter : false,
13407 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13409 mobileTouchView : true,
13412 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13414 useNativeIOS : false,
13417 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13419 mobile_restrict_height : false,
13421 ios_options : false,
13433 btnPosition : 'right',
13434 triggerList : true,
13435 showToggleBtn : true,
13437 emptyResultText: 'Empty',
13438 triggerText : 'Select',
13441 // element that contains real text value.. (when hidden is used..)
13443 getAutoCreate : function()
13448 * Render classic select for iso
13451 if(Roo.isIOS && this.useNativeIOS){
13452 cfg = this.getAutoCreateNativeIOS();
13460 if(Roo.isTouch && this.mobileTouchView){
13461 cfg = this.getAutoCreateTouchView();
13468 if(!this.tickable){
13469 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13474 * ComboBox with tickable selections
13477 var align = this.labelAlign || this.parentLabelAlign();
13480 cls : 'form-group roo-combobox-tickable' //input-group
13483 var btn_text_select = '';
13484 var btn_text_done = '';
13485 var btn_text_cancel = '';
13487 if (this.btn_text_show) {
13488 btn_text_select = 'Select';
13489 btn_text_done = 'Done';
13490 btn_text_cancel = 'Cancel';
13495 cls : 'tickable-buttons',
13500 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13501 //html : this.triggerText
13502 html: btn_text_select
13508 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13510 html: btn_text_done
13516 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13518 html: btn_text_cancel
13524 buttons.cn.unshift({
13526 cls: 'roo-select2-search-field-input'
13532 Roo.each(buttons.cn, function(c){
13534 c.cls += ' btn-' + _this.size;
13537 if (_this.disabled) {
13544 style : 'display: contents',
13549 cls: 'form-hidden-field'
13553 cls: 'roo-select2-choices',
13557 cls: 'roo-select2-search-field',
13568 cls: 'roo-select2-container input-group roo-select2-container-multi',
13574 // cls: 'typeahead typeahead-long dropdown-menu',
13575 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13580 if(this.hasFeedback && !this.allowBlank){
13584 cls: 'glyphicon form-control-feedback'
13587 combobox.cn.push(feedback);
13592 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13593 tooltip : 'This field is required'
13595 if (Roo.bootstrap.version == 4) {
13598 style : 'display:none'
13601 if (align ==='left' && this.fieldLabel.length) {
13603 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13610 cls : 'control-label col-form-label',
13611 html : this.fieldLabel
13623 var labelCfg = cfg.cn[1];
13624 var contentCfg = cfg.cn[2];
13627 if(this.indicatorpos == 'right'){
13633 cls : 'control-label col-form-label',
13637 html : this.fieldLabel
13653 labelCfg = cfg.cn[0];
13654 contentCfg = cfg.cn[1];
13658 if(this.labelWidth > 12){
13659 labelCfg.style = "width: " + this.labelWidth + 'px';
13662 if(this.labelWidth < 13 && this.labelmd == 0){
13663 this.labelmd = this.labelWidth;
13666 if(this.labellg > 0){
13667 labelCfg.cls += ' col-lg-' + this.labellg;
13668 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13671 if(this.labelmd > 0){
13672 labelCfg.cls += ' col-md-' + this.labelmd;
13673 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13676 if(this.labelsm > 0){
13677 labelCfg.cls += ' col-sm-' + this.labelsm;
13678 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13681 if(this.labelxs > 0){
13682 labelCfg.cls += ' col-xs-' + this.labelxs;
13683 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13687 } else if ( this.fieldLabel.length) {
13688 // Roo.log(" label");
13693 //cls : 'input-group-addon',
13694 html : this.fieldLabel
13699 if(this.indicatorpos == 'right'){
13703 //cls : 'input-group-addon',
13704 html : this.fieldLabel
13714 // Roo.log(" no label && no align");
13721 ['xs','sm','md','lg'].map(function(size){
13722 if (settings[size]) {
13723 cfg.cls += ' col-' + size + '-' + settings[size];
13731 _initEventsCalled : false,
13734 initEvents: function()
13736 if (this._initEventsCalled) { // as we call render... prevent looping...
13739 this._initEventsCalled = true;
13742 throw "can not find store for combo";
13745 this.indicator = this.indicatorEl();
13747 this.store = Roo.factory(this.store, Roo.data);
13748 this.store.parent = this;
13750 // if we are building from html. then this element is so complex, that we can not really
13751 // use the rendered HTML.
13752 // so we have to trash and replace the previous code.
13753 if (Roo.XComponent.build_from_html) {
13754 // remove this element....
13755 var e = this.el.dom, k=0;
13756 while (e ) { e = e.previousSibling; ++k;}
13761 this.rendered = false;
13763 this.render(this.parent().getChildContainer(true), k);
13766 if(Roo.isIOS && this.useNativeIOS){
13767 this.initIOSView();
13775 if(Roo.isTouch && this.mobileTouchView){
13776 this.initTouchView();
13781 this.initTickableEvents();
13785 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13787 if(this.hiddenName){
13789 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13791 this.hiddenField.dom.value =
13792 this.hiddenValue !== undefined ? this.hiddenValue :
13793 this.value !== undefined ? this.value : '';
13795 // prevent input submission
13796 this.el.dom.removeAttribute('name');
13797 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13802 // this.el.dom.setAttribute('autocomplete', 'off');
13805 var cls = 'x-combo-list';
13807 //this.list = new Roo.Layer({
13808 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13814 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13815 _this.list.setWidth(lw);
13818 this.list.on('mouseover', this.onViewOver, this);
13819 this.list.on('mousemove', this.onViewMove, this);
13820 this.list.on('scroll', this.onViewScroll, this);
13823 this.list.swallowEvent('mousewheel');
13824 this.assetHeight = 0;
13827 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13828 this.assetHeight += this.header.getHeight();
13831 this.innerList = this.list.createChild({cls:cls+'-inner'});
13832 this.innerList.on('mouseover', this.onViewOver, this);
13833 this.innerList.on('mousemove', this.onViewMove, this);
13834 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13836 if(this.allowBlank && !this.pageSize && !this.disableClear){
13837 this.footer = this.list.createChild({cls:cls+'-ft'});
13838 this.pageTb = new Roo.Toolbar(this.footer);
13842 this.footer = this.list.createChild({cls:cls+'-ft'});
13843 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13844 {pageSize: this.pageSize});
13848 if (this.pageTb && this.allowBlank && !this.disableClear) {
13850 this.pageTb.add(new Roo.Toolbar.Fill(), {
13851 cls: 'x-btn-icon x-btn-clear',
13853 handler: function()
13856 _this.clearValue();
13857 _this.onSelect(false, -1);
13862 this.assetHeight += this.footer.getHeight();
13867 this.tpl = Roo.bootstrap.version == 4 ?
13868 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13869 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13872 this.view = new Roo.View(this.list, this.tpl, {
13873 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13875 //this.view.wrapEl.setDisplayed(false);
13876 this.view.on('click', this.onViewClick, this);
13879 this.store.on('beforeload', this.onBeforeLoad, this);
13880 this.store.on('load', this.onLoad, this);
13881 this.store.on('loadexception', this.onLoadException, this);
13883 if(this.resizable){
13884 this.resizer = new Roo.Resizable(this.list, {
13885 pinned:true, handles:'se'
13887 this.resizer.on('resize', function(r, w, h){
13888 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13889 this.listWidth = w;
13890 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13891 this.restrictHeight();
13893 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13896 if(!this.editable){
13897 this.editable = true;
13898 this.setEditable(false);
13903 if (typeof(this.events.add.listeners) != 'undefined') {
13905 this.addicon = this.wrap.createChild(
13906 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13908 this.addicon.on('click', function(e) {
13909 this.fireEvent('add', this);
13912 if (typeof(this.events.edit.listeners) != 'undefined') {
13914 this.editicon = this.wrap.createChild(
13915 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13916 if (this.addicon) {
13917 this.editicon.setStyle('margin-left', '40px');
13919 this.editicon.on('click', function(e) {
13921 // we fire even if inothing is selected..
13922 this.fireEvent('edit', this, this.lastData );
13928 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13929 "up" : function(e){
13930 this.inKeyMode = true;
13934 "down" : function(e){
13935 if(!this.isExpanded()){
13936 this.onTriggerClick();
13938 this.inKeyMode = true;
13943 "enter" : function(e){
13944 // this.onViewClick();
13948 if(this.fireEvent("specialkey", this, e)){
13949 this.onViewClick(false);
13955 "esc" : function(e){
13959 "tab" : function(e){
13962 if(this.fireEvent("specialkey", this, e)){
13963 this.onViewClick(false);
13971 doRelay : function(foo, bar, hname){
13972 if(hname == 'down' || this.scope.isExpanded()){
13973 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13982 this.queryDelay = Math.max(this.queryDelay || 10,
13983 this.mode == 'local' ? 10 : 250);
13986 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13988 if(this.typeAhead){
13989 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13991 if(this.editable !== false){
13992 this.inputEl().on("keyup", this.onKeyUp, this);
13994 if(this.forceSelection){
13995 this.inputEl().on('blur', this.doForce, this);
13999 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14000 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14004 initTickableEvents: function()
14008 if(this.hiddenName){
14010 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14012 this.hiddenField.dom.value =
14013 this.hiddenValue !== undefined ? this.hiddenValue :
14014 this.value !== undefined ? this.value : '';
14016 // prevent input submission
14017 this.el.dom.removeAttribute('name');
14018 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14023 // this.list = this.el.select('ul.dropdown-menu',true).first();
14025 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14026 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14027 if(this.triggerList){
14028 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14031 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14032 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14034 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14035 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14037 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14038 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14040 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14041 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14042 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14045 this.cancelBtn.hide();
14050 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14051 _this.list.setWidth(lw);
14054 this.list.on('mouseover', this.onViewOver, this);
14055 this.list.on('mousemove', this.onViewMove, this);
14057 this.list.on('scroll', this.onViewScroll, this);
14060 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14061 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14064 this.view = new Roo.View(this.list, this.tpl, {
14069 selectedClass: this.selectedClass
14072 //this.view.wrapEl.setDisplayed(false);
14073 this.view.on('click', this.onViewClick, this);
14077 this.store.on('beforeload', this.onBeforeLoad, this);
14078 this.store.on('load', this.onLoad, this);
14079 this.store.on('loadexception', this.onLoadException, this);
14082 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14083 "up" : function(e){
14084 this.inKeyMode = true;
14088 "down" : function(e){
14089 this.inKeyMode = true;
14093 "enter" : function(e){
14094 if(this.fireEvent("specialkey", this, e)){
14095 this.onViewClick(false);
14101 "esc" : function(e){
14102 this.onTickableFooterButtonClick(e, false, false);
14105 "tab" : function(e){
14106 this.fireEvent("specialkey", this, e);
14108 this.onTickableFooterButtonClick(e, false, false);
14115 doRelay : function(e, fn, key){
14116 if(this.scope.isExpanded()){
14117 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14126 this.queryDelay = Math.max(this.queryDelay || 10,
14127 this.mode == 'local' ? 10 : 250);
14130 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14132 if(this.typeAhead){
14133 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14136 if(this.editable !== false){
14137 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14140 this.indicator = this.indicatorEl();
14142 if(this.indicator){
14143 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14144 this.indicator.hide();
14149 onDestroy : function(){
14151 this.view.setStore(null);
14152 this.view.el.removeAllListeners();
14153 this.view.el.remove();
14154 this.view.purgeListeners();
14157 this.list.dom.innerHTML = '';
14161 this.store.un('beforeload', this.onBeforeLoad, this);
14162 this.store.un('load', this.onLoad, this);
14163 this.store.un('loadexception', this.onLoadException, this);
14165 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14169 fireKey : function(e){
14170 if(e.isNavKeyPress() && !this.list.isVisible()){
14171 this.fireEvent("specialkey", this, e);
14176 onResize: function(w, h){
14177 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14179 // if(typeof w != 'number'){
14180 // // we do not handle it!?!?
14183 // var tw = this.trigger.getWidth();
14184 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14185 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14187 // this.inputEl().setWidth( this.adjustWidth('input', x));
14189 // //this.trigger.setStyle('left', x+'px');
14191 // if(this.list && this.listWidth === undefined){
14192 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14193 // this.list.setWidth(lw);
14194 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14202 * Allow or prevent the user from directly editing the field text. If false is passed,
14203 * the user will only be able to select from the items defined in the dropdown list. This method
14204 * is the runtime equivalent of setting the 'editable' config option at config time.
14205 * @param {Boolean} value True to allow the user to directly edit the field text
14207 setEditable : function(value){
14208 if(value == this.editable){
14211 this.editable = value;
14213 this.inputEl().dom.setAttribute('readOnly', true);
14214 this.inputEl().on('mousedown', this.onTriggerClick, this);
14215 this.inputEl().addClass('x-combo-noedit');
14217 this.inputEl().dom.setAttribute('readOnly', false);
14218 this.inputEl().un('mousedown', this.onTriggerClick, this);
14219 this.inputEl().removeClass('x-combo-noedit');
14225 onBeforeLoad : function(combo,opts){
14226 if(!this.hasFocus){
14230 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14232 this.restrictHeight();
14233 this.selectedIndex = -1;
14237 onLoad : function(){
14239 this.hasQuery = false;
14241 if(!this.hasFocus){
14245 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14246 this.loading.hide();
14249 if(this.store.getCount() > 0){
14252 this.restrictHeight();
14253 if(this.lastQuery == this.allQuery){
14254 if(this.editable && !this.tickable){
14255 this.inputEl().dom.select();
14259 !this.selectByValue(this.value, true) &&
14262 !this.store.lastOptions ||
14263 typeof(this.store.lastOptions.add) == 'undefined' ||
14264 this.store.lastOptions.add != true
14267 this.select(0, true);
14270 if(this.autoFocus){
14273 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14274 this.taTask.delay(this.typeAheadDelay);
14278 this.onEmptyResults();
14284 onLoadException : function()
14286 this.hasQuery = false;
14288 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14289 this.loading.hide();
14292 if(this.tickable && this.editable){
14297 // only causes errors at present
14298 //Roo.log(this.store.reader.jsonData);
14299 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14301 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14307 onTypeAhead : function(){
14308 if(this.store.getCount() > 0){
14309 var r = this.store.getAt(0);
14310 var newValue = r.data[this.displayField];
14311 var len = newValue.length;
14312 var selStart = this.getRawValue().length;
14314 if(selStart != len){
14315 this.setRawValue(newValue);
14316 this.selectText(selStart, newValue.length);
14322 onSelect : function(record, index){
14324 if(this.fireEvent('beforeselect', this, record, index) !== false){
14326 this.setFromData(index > -1 ? record.data : false);
14329 this.fireEvent('select', this, record, index);
14334 * Returns the currently selected field value or empty string if no value is set.
14335 * @return {String} value The selected value
14337 getValue : function()
14339 if(Roo.isIOS && this.useNativeIOS){
14340 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14344 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14347 if(this.valueField){
14348 return typeof this.value != 'undefined' ? this.value : '';
14350 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14354 getRawValue : function()
14356 if(Roo.isIOS && this.useNativeIOS){
14357 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14360 var v = this.inputEl().getValue();
14366 * Clears any text/value currently set in the field
14368 clearValue : function(){
14370 if(this.hiddenField){
14371 this.hiddenField.dom.value = '';
14374 this.setRawValue('');
14375 this.lastSelectionText = '';
14376 this.lastData = false;
14378 var close = this.closeTriggerEl();
14389 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14390 * will be displayed in the field. If the value does not match the data value of an existing item,
14391 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14392 * Otherwise the field will be blank (although the value will still be set).
14393 * @param {String} value The value to match
14395 setValue : function(v)
14397 if(Roo.isIOS && this.useNativeIOS){
14398 this.setIOSValue(v);
14408 if(this.valueField){
14409 var r = this.findRecord(this.valueField, v);
14411 text = r.data[this.displayField];
14412 }else if(this.valueNotFoundText !== undefined){
14413 text = this.valueNotFoundText;
14416 this.lastSelectionText = text;
14417 if(this.hiddenField){
14418 this.hiddenField.dom.value = v;
14420 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14423 var close = this.closeTriggerEl();
14426 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14432 * @property {Object} the last set data for the element
14437 * Sets the value of the field based on a object which is related to the record format for the store.
14438 * @param {Object} value the value to set as. or false on reset?
14440 setFromData : function(o){
14447 var dv = ''; // display value
14448 var vv = ''; // value value..
14450 if (this.displayField) {
14451 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14453 // this is an error condition!!!
14454 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14457 if(this.valueField){
14458 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14461 var close = this.closeTriggerEl();
14464 if(dv.length || vv * 1 > 0){
14466 this.blockFocus=true;
14472 if(this.hiddenField){
14473 this.hiddenField.dom.value = vv;
14475 this.lastSelectionText = dv;
14476 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14480 // no hidden field.. - we store the value in 'value', but still display
14481 // display field!!!!
14482 this.lastSelectionText = dv;
14483 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14490 reset : function(){
14491 // overridden so that last data is reset..
14498 this.setValue(this.originalValue);
14499 //this.clearInvalid();
14500 this.lastData = false;
14502 this.view.clearSelections();
14508 findRecord : function(prop, value){
14510 if(this.store.getCount() > 0){
14511 this.store.each(function(r){
14512 if(r.data[prop] == value){
14522 getName: function()
14524 // returns hidden if it's set..
14525 if (!this.rendered) {return ''};
14526 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14530 onViewMove : function(e, t){
14531 this.inKeyMode = false;
14535 onViewOver : function(e, t){
14536 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14539 var item = this.view.findItemFromChild(t);
14542 var index = this.view.indexOf(item);
14543 this.select(index, false);
14548 onViewClick : function(view, doFocus, el, e)
14550 var index = this.view.getSelectedIndexes()[0];
14552 var r = this.store.getAt(index);
14556 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14563 Roo.each(this.tickItems, function(v,k){
14565 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14567 _this.tickItems.splice(k, 1);
14569 if(typeof(e) == 'undefined' && view == false){
14570 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14582 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14583 this.tickItems.push(r.data);
14586 if(typeof(e) == 'undefined' && view == false){
14587 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14594 this.onSelect(r, index);
14596 if(doFocus !== false && !this.blockFocus){
14597 this.inputEl().focus();
14602 restrictHeight : function(){
14603 //this.innerList.dom.style.height = '';
14604 //var inner = this.innerList.dom;
14605 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14606 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14607 //this.list.beginUpdate();
14608 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14609 this.list.alignTo(this.inputEl(), this.listAlign);
14610 this.list.alignTo(this.inputEl(), this.listAlign);
14611 //this.list.endUpdate();
14615 onEmptyResults : function(){
14617 if(this.tickable && this.editable){
14618 this.hasFocus = false;
14619 this.restrictHeight();
14627 * Returns true if the dropdown list is expanded, else false.
14629 isExpanded : function(){
14630 return this.list.isVisible();
14634 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14635 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14636 * @param {String} value The data value of the item to select
14637 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14638 * selected item if it is not currently in view (defaults to true)
14639 * @return {Boolean} True if the value matched an item in the list, else false
14641 selectByValue : function(v, scrollIntoView){
14642 if(v !== undefined && v !== null){
14643 var r = this.findRecord(this.valueField || this.displayField, v);
14645 this.select(this.store.indexOf(r), scrollIntoView);
14653 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14654 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14655 * @param {Number} index The zero-based index of the list item to select
14656 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14657 * selected item if it is not currently in view (defaults to true)
14659 select : function(index, scrollIntoView){
14660 this.selectedIndex = index;
14661 this.view.select(index);
14662 if(scrollIntoView !== false){
14663 var el = this.view.getNode(index);
14665 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14668 this.list.scrollChildIntoView(el, false);
14674 selectNext : function(){
14675 var ct = this.store.getCount();
14677 if(this.selectedIndex == -1){
14679 }else if(this.selectedIndex < ct-1){
14680 this.select(this.selectedIndex+1);
14686 selectPrev : function(){
14687 var ct = this.store.getCount();
14689 if(this.selectedIndex == -1){
14691 }else if(this.selectedIndex != 0){
14692 this.select(this.selectedIndex-1);
14698 onKeyUp : function(e){
14699 if(this.editable !== false && !e.isSpecialKey()){
14700 this.lastKey = e.getKey();
14701 this.dqTask.delay(this.queryDelay);
14706 validateBlur : function(){
14707 return !this.list || !this.list.isVisible();
14711 initQuery : function(){
14713 var v = this.getRawValue();
14715 if(this.tickable && this.editable){
14716 v = this.tickableInputEl().getValue();
14723 doForce : function(){
14724 if(this.inputEl().dom.value.length > 0){
14725 this.inputEl().dom.value =
14726 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14732 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14733 * query allowing the query action to be canceled if needed.
14734 * @param {String} query The SQL query to execute
14735 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14736 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14737 * saved in the current store (defaults to false)
14739 doQuery : function(q, forceAll){
14741 if(q === undefined || q === null){
14746 forceAll: forceAll,
14750 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14755 forceAll = qe.forceAll;
14756 if(forceAll === true || (q.length >= this.minChars)){
14758 this.hasQuery = true;
14760 if(this.lastQuery != q || this.alwaysQuery){
14761 this.lastQuery = q;
14762 if(this.mode == 'local'){
14763 this.selectedIndex = -1;
14765 this.store.clearFilter();
14768 if(this.specialFilter){
14769 this.fireEvent('specialfilter', this);
14774 this.store.filter(this.displayField, q);
14777 this.store.fireEvent("datachanged", this.store);
14784 this.store.baseParams[this.queryParam] = q;
14786 var options = {params : this.getParams(q)};
14789 options.add = true;
14790 options.params.start = this.page * this.pageSize;
14793 this.store.load(options);
14796 * this code will make the page width larger, at the beginning, the list not align correctly,
14797 * we should expand the list on onLoad
14798 * so command out it
14803 this.selectedIndex = -1;
14808 this.loadNext = false;
14812 getParams : function(q){
14814 //p[this.queryParam] = q;
14818 p.limit = this.pageSize;
14824 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14826 collapse : function(){
14827 if(!this.isExpanded()){
14833 this.hasFocus = false;
14837 this.cancelBtn.hide();
14838 this.trigger.show();
14841 this.tickableInputEl().dom.value = '';
14842 this.tickableInputEl().blur();
14847 Roo.get(document).un('mousedown', this.collapseIf, this);
14848 Roo.get(document).un('mousewheel', this.collapseIf, this);
14849 if (!this.editable) {
14850 Roo.get(document).un('keydown', this.listKeyPress, this);
14852 this.fireEvent('collapse', this);
14858 collapseIf : function(e){
14859 var in_combo = e.within(this.el);
14860 var in_list = e.within(this.list);
14861 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14863 if (in_combo || in_list || is_list) {
14864 //e.stopPropagation();
14869 this.onTickableFooterButtonClick(e, false, false);
14877 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14879 expand : function(){
14881 if(this.isExpanded() || !this.hasFocus){
14885 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14886 this.list.setWidth(lw);
14892 this.restrictHeight();
14896 this.tickItems = Roo.apply([], this.item);
14899 this.cancelBtn.show();
14900 this.trigger.hide();
14903 this.tickableInputEl().focus();
14908 Roo.get(document).on('mousedown', this.collapseIf, this);
14909 Roo.get(document).on('mousewheel', this.collapseIf, this);
14910 if (!this.editable) {
14911 Roo.get(document).on('keydown', this.listKeyPress, this);
14914 this.fireEvent('expand', this);
14918 // Implements the default empty TriggerField.onTriggerClick function
14919 onTriggerClick : function(e)
14921 Roo.log('trigger click');
14923 if(this.disabled || !this.triggerList){
14928 this.loadNext = false;
14930 if(this.isExpanded()){
14932 if (!this.blockFocus) {
14933 this.inputEl().focus();
14937 this.hasFocus = true;
14938 if(this.triggerAction == 'all') {
14939 this.doQuery(this.allQuery, true);
14941 this.doQuery(this.getRawValue());
14943 if (!this.blockFocus) {
14944 this.inputEl().focus();
14949 onTickableTriggerClick : function(e)
14956 this.loadNext = false;
14957 this.hasFocus = true;
14959 if(this.triggerAction == 'all') {
14960 this.doQuery(this.allQuery, true);
14962 this.doQuery(this.getRawValue());
14966 onSearchFieldClick : function(e)
14968 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14969 this.onTickableFooterButtonClick(e, false, false);
14973 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14978 this.loadNext = false;
14979 this.hasFocus = true;
14981 if(this.triggerAction == 'all') {
14982 this.doQuery(this.allQuery, true);
14984 this.doQuery(this.getRawValue());
14988 listKeyPress : function(e)
14990 //Roo.log('listkeypress');
14991 // scroll to first matching element based on key pres..
14992 if (e.isSpecialKey()) {
14995 var k = String.fromCharCode(e.getKey()).toUpperCase();
14998 var csel = this.view.getSelectedNodes();
14999 var cselitem = false;
15001 var ix = this.view.indexOf(csel[0]);
15002 cselitem = this.store.getAt(ix);
15003 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15009 this.store.each(function(v) {
15011 // start at existing selection.
15012 if (cselitem.id == v.id) {
15018 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15019 match = this.store.indexOf(v);
15025 if (match === false) {
15026 return true; // no more action?
15029 this.view.select(match);
15030 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15031 sn.scrollIntoView(sn.dom.parentNode, false);
15034 onViewScroll : function(e, t){
15036 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){
15040 this.hasQuery = true;
15042 this.loading = this.list.select('.loading', true).first();
15044 if(this.loading === null){
15045 this.list.createChild({
15047 cls: 'loading roo-select2-more-results roo-select2-active',
15048 html: 'Loading more results...'
15051 this.loading = this.list.select('.loading', true).first();
15053 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15055 this.loading.hide();
15058 this.loading.show();
15063 this.loadNext = true;
15065 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15070 addItem : function(o)
15072 var dv = ''; // display value
15074 if (this.displayField) {
15075 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15077 // this is an error condition!!!
15078 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15085 var choice = this.choices.createChild({
15087 cls: 'roo-select2-search-choice',
15096 cls: 'roo-select2-search-choice-close fa fa-times',
15101 }, this.searchField);
15103 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15105 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15113 this.inputEl().dom.value = '';
15118 onRemoveItem : function(e, _self, o)
15120 e.preventDefault();
15122 this.lastItem = Roo.apply([], this.item);
15124 var index = this.item.indexOf(o.data) * 1;
15127 Roo.log('not this item?!');
15131 this.item.splice(index, 1);
15136 this.fireEvent('remove', this, e);
15142 syncValue : function()
15144 if(!this.item.length){
15151 Roo.each(this.item, function(i){
15152 if(_this.valueField){
15153 value.push(i[_this.valueField]);
15160 this.value = value.join(',');
15162 if(this.hiddenField){
15163 this.hiddenField.dom.value = this.value;
15166 this.store.fireEvent("datachanged", this.store);
15171 clearItem : function()
15173 if(!this.multiple){
15179 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15187 if(this.tickable && !Roo.isTouch){
15188 this.view.refresh();
15192 inputEl: function ()
15194 if(Roo.isIOS && this.useNativeIOS){
15195 return this.el.select('select.roo-ios-select', true).first();
15198 if(Roo.isTouch && this.mobileTouchView){
15199 return this.el.select('input.form-control',true).first();
15203 return this.searchField;
15206 return this.el.select('input.form-control',true).first();
15209 onTickableFooterButtonClick : function(e, btn, el)
15211 e.preventDefault();
15213 this.lastItem = Roo.apply([], this.item);
15215 if(btn && btn.name == 'cancel'){
15216 this.tickItems = Roo.apply([], this.item);
15225 Roo.each(this.tickItems, function(o){
15233 validate : function()
15235 if(this.getVisibilityEl().hasClass('hidden')){
15239 var v = this.getRawValue();
15242 v = this.getValue();
15245 if(this.disabled || this.allowBlank || v.length){
15250 this.markInvalid();
15254 tickableInputEl : function()
15256 if(!this.tickable || !this.editable){
15257 return this.inputEl();
15260 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15264 getAutoCreateTouchView : function()
15269 cls: 'form-group' //input-group
15275 type : this.inputType,
15276 cls : 'form-control x-combo-noedit',
15277 autocomplete: 'new-password',
15278 placeholder : this.placeholder || '',
15283 input.name = this.name;
15287 input.cls += ' input-' + this.size;
15290 if (this.disabled) {
15291 input.disabled = true;
15302 inputblock.cls += ' input-group';
15304 inputblock.cn.unshift({
15306 cls : 'input-group-addon input-group-prepend input-group-text',
15311 if(this.removable && !this.multiple){
15312 inputblock.cls += ' roo-removable';
15314 inputblock.cn.push({
15317 cls : 'roo-combo-removable-btn close'
15321 if(this.hasFeedback && !this.allowBlank){
15323 inputblock.cls += ' has-feedback';
15325 inputblock.cn.push({
15327 cls: 'glyphicon form-control-feedback'
15334 inputblock.cls += (this.before) ? '' : ' input-group';
15336 inputblock.cn.push({
15338 cls : 'input-group-addon input-group-append input-group-text',
15344 var ibwrap = inputblock;
15349 cls: 'roo-select2-choices',
15353 cls: 'roo-select2-search-field',
15366 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15371 cls: 'form-hidden-field'
15377 if(!this.multiple && this.showToggleBtn){
15384 if (this.caret != false) {
15387 cls: 'fa fa-' + this.caret
15394 cls : 'input-group-addon input-group-append input-group-text btn' +
15395 (Roo.bootstrap.version == 3 ? ' dropdown-toggle' : ''),
15400 cls: 'combobox-clear',
15414 combobox.cls += ' roo-select2-container-multi';
15417 var align = this.labelAlign || this.parentLabelAlign();
15419 if (align ==='left' && this.fieldLabel.length) {
15424 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15425 tooltip : 'This field is required'
15429 cls : 'control-label col-form-label',
15430 html : this.fieldLabel
15441 var labelCfg = cfg.cn[1];
15442 var contentCfg = cfg.cn[2];
15445 if(this.indicatorpos == 'right'){
15450 cls : 'control-label col-form-label',
15454 html : this.fieldLabel
15458 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15459 tooltip : 'This field is required'
15472 labelCfg = cfg.cn[0];
15473 contentCfg = cfg.cn[1];
15478 if(this.labelWidth > 12){
15479 labelCfg.style = "width: " + this.labelWidth + 'px';
15482 if(this.labelWidth < 13 && this.labelmd == 0){
15483 this.labelmd = this.labelWidth;
15486 if(this.labellg > 0){
15487 labelCfg.cls += ' col-lg-' + this.labellg;
15488 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15491 if(this.labelmd > 0){
15492 labelCfg.cls += ' col-md-' + this.labelmd;
15493 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15496 if(this.labelsm > 0){
15497 labelCfg.cls += ' col-sm-' + this.labelsm;
15498 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15501 if(this.labelxs > 0){
15502 labelCfg.cls += ' col-xs-' + this.labelxs;
15503 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15507 } else if ( this.fieldLabel.length) {
15511 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15512 tooltip : 'This field is required'
15516 cls : 'control-label',
15517 html : this.fieldLabel
15528 if(this.indicatorpos == 'right'){
15532 cls : 'control-label',
15533 html : this.fieldLabel,
15537 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15538 tooltip : 'This field is required'
15555 var settings = this;
15557 ['xs','sm','md','lg'].map(function(size){
15558 if (settings[size]) {
15559 cfg.cls += ' col-' + size + '-' + settings[size];
15566 initTouchView : function()
15568 this.renderTouchView();
15570 this.touchViewEl.on('scroll', function(){
15571 this.el.dom.scrollTop = 0;
15574 this.originalValue = this.getValue();
15576 this.triggerEl = this.el.select('span.input-group-append',true).first();
15578 this.inputEl().on("click", this.showTouchView, this);
15579 if (this.triggerEl) {
15580 this.triggerEl.on("click", this.showTouchView, this);
15584 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15585 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15587 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15589 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15590 this.store.on('load', this.onTouchViewLoad, this);
15591 this.store.on('loadexception', this.onTouchViewLoadException, this);
15593 if(this.hiddenName){
15595 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15597 this.hiddenField.dom.value =
15598 this.hiddenValue !== undefined ? this.hiddenValue :
15599 this.value !== undefined ? this.value : '';
15601 this.el.dom.removeAttribute('name');
15602 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15606 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15607 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15610 if(this.removable && !this.multiple){
15611 var close = this.closeTriggerEl();
15613 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15614 close.on('click', this.removeBtnClick, this, close);
15618 * fix the bug in Safari iOS8
15620 this.inputEl().on("focus", function(e){
15621 document.activeElement.blur();
15624 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15631 renderTouchView : function()
15633 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15634 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15636 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15637 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15639 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15640 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15641 this.touchViewBodyEl.setStyle('overflow', 'auto');
15643 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15644 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15646 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15647 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15651 showTouchView : function()
15657 this.touchViewHeaderEl.hide();
15659 if(this.modalTitle.length){
15660 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15661 this.touchViewHeaderEl.show();
15664 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15665 this.touchViewEl.show();
15667 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15669 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15670 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15672 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15674 if(this.modalTitle.length){
15675 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15678 this.touchViewBodyEl.setHeight(bodyHeight);
15682 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15684 this.touchViewEl.addClass('in');
15687 if(this._touchViewMask){
15688 Roo.get(document.body).addClass("x-body-masked");
15689 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15690 this._touchViewMask.setStyle('z-index', 10000);
15691 this._touchViewMask.addClass('show');
15694 this.doTouchViewQuery();
15698 hideTouchView : function()
15700 this.touchViewEl.removeClass('in');
15704 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15706 this.touchViewEl.setStyle('display', 'none');
15709 if(this._touchViewMask){
15710 this._touchViewMask.removeClass('show');
15711 Roo.get(document.body).removeClass("x-body-masked");
15715 setTouchViewValue : function()
15722 Roo.each(this.tickItems, function(o){
15727 this.hideTouchView();
15730 doTouchViewQuery : function()
15739 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15743 if(!this.alwaysQuery || this.mode == 'local'){
15744 this.onTouchViewLoad();
15751 onTouchViewBeforeLoad : function(combo,opts)
15757 onTouchViewLoad : function()
15759 if(this.store.getCount() < 1){
15760 this.onTouchViewEmptyResults();
15764 this.clearTouchView();
15766 var rawValue = this.getRawValue();
15768 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15770 this.tickItems = [];
15772 this.store.data.each(function(d, rowIndex){
15773 var row = this.touchViewListGroup.createChild(template);
15775 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15776 row.addClass(d.data.cls);
15779 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15782 html : d.data[this.displayField]
15785 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15786 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15789 row.removeClass('selected');
15790 if(!this.multiple && this.valueField &&
15791 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15794 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15795 row.addClass('selected');
15798 if(this.multiple && this.valueField &&
15799 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15803 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15804 this.tickItems.push(d.data);
15807 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15811 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15813 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15815 if(this.modalTitle.length){
15816 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15819 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15821 if(this.mobile_restrict_height && listHeight < bodyHeight){
15822 this.touchViewBodyEl.setHeight(listHeight);
15827 if(firstChecked && listHeight > bodyHeight){
15828 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15833 onTouchViewLoadException : function()
15835 this.hideTouchView();
15838 onTouchViewEmptyResults : function()
15840 this.clearTouchView();
15842 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15844 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15848 clearTouchView : function()
15850 this.touchViewListGroup.dom.innerHTML = '';
15853 onTouchViewClick : function(e, el, o)
15855 e.preventDefault();
15858 var rowIndex = o.rowIndex;
15860 var r = this.store.getAt(rowIndex);
15862 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15864 if(!this.multiple){
15865 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15866 c.dom.removeAttribute('checked');
15869 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15871 this.setFromData(r.data);
15873 var close = this.closeTriggerEl();
15879 this.hideTouchView();
15881 this.fireEvent('select', this, r, rowIndex);
15886 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15887 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15888 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15892 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15893 this.addItem(r.data);
15894 this.tickItems.push(r.data);
15898 getAutoCreateNativeIOS : function()
15901 cls: 'form-group' //input-group,
15906 cls : 'roo-ios-select'
15910 combobox.name = this.name;
15913 if (this.disabled) {
15914 combobox.disabled = true;
15917 var settings = this;
15919 ['xs','sm','md','lg'].map(function(size){
15920 if (settings[size]) {
15921 cfg.cls += ' col-' + size + '-' + settings[size];
15931 initIOSView : function()
15933 this.store.on('load', this.onIOSViewLoad, this);
15938 onIOSViewLoad : function()
15940 if(this.store.getCount() < 1){
15944 this.clearIOSView();
15946 if(this.allowBlank) {
15948 var default_text = '-- SELECT --';
15950 if(this.placeholder.length){
15951 default_text = this.placeholder;
15954 if(this.emptyTitle.length){
15955 default_text += ' - ' + this.emptyTitle + ' -';
15958 var opt = this.inputEl().createChild({
15961 html : default_text
15965 o[this.valueField] = 0;
15966 o[this.displayField] = default_text;
15968 this.ios_options.push({
15975 this.store.data.each(function(d, rowIndex){
15979 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15980 html = d.data[this.displayField];
15985 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15986 value = d.data[this.valueField];
15995 if(this.value == d.data[this.valueField]){
15996 option['selected'] = true;
15999 var opt = this.inputEl().createChild(option);
16001 this.ios_options.push({
16008 this.inputEl().on('change', function(){
16009 this.fireEvent('select', this);
16014 clearIOSView: function()
16016 this.inputEl().dom.innerHTML = '';
16018 this.ios_options = [];
16021 setIOSValue: function(v)
16025 if(!this.ios_options){
16029 Roo.each(this.ios_options, function(opts){
16031 opts.el.dom.removeAttribute('selected');
16033 if(opts.data[this.valueField] != v){
16037 opts.el.dom.setAttribute('selected', true);
16043 * @cfg {Boolean} grow
16047 * @cfg {Number} growMin
16051 * @cfg {Number} growMax
16060 Roo.apply(Roo.bootstrap.ComboBox, {
16064 cls: 'modal-header',
16086 cls: 'list-group-item',
16090 cls: 'roo-combobox-list-group-item-value'
16094 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16108 listItemCheckbox : {
16110 cls: 'list-group-item',
16114 cls: 'roo-combobox-list-group-item-value'
16118 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16134 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16139 cls: 'modal-footer',
16147 cls: 'col-xs-6 text-left',
16150 cls: 'btn btn-danger roo-touch-view-cancel',
16156 cls: 'col-xs-6 text-right',
16159 cls: 'btn btn-success roo-touch-view-ok',
16170 Roo.apply(Roo.bootstrap.ComboBox, {
16172 touchViewTemplate : {
16174 cls: 'modal fade roo-combobox-touch-view',
16178 cls: 'modal-dialog',
16179 style : 'position:fixed', // we have to fix position....
16183 cls: 'modal-content',
16185 Roo.bootstrap.ComboBox.header,
16186 Roo.bootstrap.ComboBox.body,
16187 Roo.bootstrap.ComboBox.footer
16196 * Ext JS Library 1.1.1
16197 * Copyright(c) 2006-2007, Ext JS, LLC.
16199 * Originally Released Under LGPL - original licence link has changed is not relivant.
16202 * <script type="text/javascript">
16207 * @extends Roo.util.Observable
16208 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16209 * This class also supports single and multi selection modes. <br>
16210 * Create a data model bound view:
16212 var store = new Roo.data.Store(...);
16214 var view = new Roo.View({
16216 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16218 singleSelect: true,
16219 selectedClass: "ydataview-selected",
16223 // listen for node click?
16224 view.on("click", function(vw, index, node, e){
16225 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16229 dataModel.load("foobar.xml");
16231 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16233 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16234 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16236 * Note: old style constructor is still suported (container, template, config)
16239 * Create a new View
16240 * @param {Object} config The config object
16243 Roo.View = function(config, depreciated_tpl, depreciated_config){
16245 this.parent = false;
16247 if (typeof(depreciated_tpl) == 'undefined') {
16248 // new way.. - universal constructor.
16249 Roo.apply(this, config);
16250 this.el = Roo.get(this.el);
16253 this.el = Roo.get(config);
16254 this.tpl = depreciated_tpl;
16255 Roo.apply(this, depreciated_config);
16257 this.wrapEl = this.el.wrap().wrap();
16258 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16261 if(typeof(this.tpl) == "string"){
16262 this.tpl = new Roo.Template(this.tpl);
16264 // support xtype ctors..
16265 this.tpl = new Roo.factory(this.tpl, Roo);
16269 this.tpl.compile();
16274 * @event beforeclick
16275 * Fires before a click is processed. Returns false to cancel the default action.
16276 * @param {Roo.View} this
16277 * @param {Number} index The index of the target node
16278 * @param {HTMLElement} node The target node
16279 * @param {Roo.EventObject} e The raw event object
16281 "beforeclick" : true,
16284 * Fires when a template node is clicked.
16285 * @param {Roo.View} this
16286 * @param {Number} index The index of the target node
16287 * @param {HTMLElement} node The target node
16288 * @param {Roo.EventObject} e The raw event object
16293 * Fires when a template node is double clicked.
16294 * @param {Roo.View} this
16295 * @param {Number} index The index of the target node
16296 * @param {HTMLElement} node The target node
16297 * @param {Roo.EventObject} e The raw event object
16301 * @event contextmenu
16302 * Fires when a template node is right clicked.
16303 * @param {Roo.View} this
16304 * @param {Number} index The index of the target node
16305 * @param {HTMLElement} node The target node
16306 * @param {Roo.EventObject} e The raw event object
16308 "contextmenu" : true,
16310 * @event selectionchange
16311 * Fires when the selected nodes change.
16312 * @param {Roo.View} this
16313 * @param {Array} selections Array of the selected nodes
16315 "selectionchange" : true,
16318 * @event beforeselect
16319 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16320 * @param {Roo.View} this
16321 * @param {HTMLElement} node The node to be selected
16322 * @param {Array} selections Array of currently selected nodes
16324 "beforeselect" : true,
16326 * @event preparedata
16327 * Fires on every row to render, to allow you to change the data.
16328 * @param {Roo.View} this
16329 * @param {Object} data to be rendered (change this)
16331 "preparedata" : true
16339 "click": this.onClick,
16340 "dblclick": this.onDblClick,
16341 "contextmenu": this.onContextMenu,
16345 this.selections = [];
16347 this.cmp = new Roo.CompositeElementLite([]);
16349 this.store = Roo.factory(this.store, Roo.data);
16350 this.setStore(this.store, true);
16353 if ( this.footer && this.footer.xtype) {
16355 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16357 this.footer.dataSource = this.store;
16358 this.footer.container = fctr;
16359 this.footer = Roo.factory(this.footer, Roo);
16360 fctr.insertFirst(this.el);
16362 // this is a bit insane - as the paging toolbar seems to detach the el..
16363 // dom.parentNode.parentNode.parentNode
16364 // they get detached?
16368 Roo.View.superclass.constructor.call(this);
16373 Roo.extend(Roo.View, Roo.util.Observable, {
16376 * @cfg {Roo.data.Store} store Data store to load data from.
16381 * @cfg {String|Roo.Element} el The container element.
16386 * @cfg {String|Roo.Template} tpl The template used by this View
16390 * @cfg {String} dataName the named area of the template to use as the data area
16391 * Works with domtemplates roo-name="name"
16395 * @cfg {String} selectedClass The css class to add to selected nodes
16397 selectedClass : "x-view-selected",
16399 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16404 * @cfg {String} text to display on mask (default Loading)
16408 * @cfg {Boolean} multiSelect Allow multiple selection
16410 multiSelect : false,
16412 * @cfg {Boolean} singleSelect Allow single selection
16414 singleSelect: false,
16417 * @cfg {Boolean} toggleSelect - selecting
16419 toggleSelect : false,
16422 * @cfg {Boolean} tickable - selecting
16427 * Returns the element this view is bound to.
16428 * @return {Roo.Element}
16430 getEl : function(){
16431 return this.wrapEl;
16437 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16439 refresh : function(){
16440 //Roo.log('refresh');
16443 // if we are using something like 'domtemplate', then
16444 // the what gets used is:
16445 // t.applySubtemplate(NAME, data, wrapping data..)
16446 // the outer template then get' applied with
16447 // the store 'extra data'
16448 // and the body get's added to the
16449 // roo-name="data" node?
16450 // <span class='roo-tpl-{name}'></span> ?????
16454 this.clearSelections();
16455 this.el.update("");
16457 var records = this.store.getRange();
16458 if(records.length < 1) {
16460 // is this valid?? = should it render a template??
16462 this.el.update(this.emptyText);
16466 if (this.dataName) {
16467 this.el.update(t.apply(this.store.meta)); //????
16468 el = this.el.child('.roo-tpl-' + this.dataName);
16471 for(var i = 0, len = records.length; i < len; i++){
16472 var data = this.prepareData(records[i].data, i, records[i]);
16473 this.fireEvent("preparedata", this, data, i, records[i]);
16475 var d = Roo.apply({}, data);
16478 Roo.apply(d, {'roo-id' : Roo.id()});
16482 Roo.each(this.parent.item, function(item){
16483 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16486 Roo.apply(d, {'roo-data-checked' : 'checked'});
16490 html[html.length] = Roo.util.Format.trim(
16492 t.applySubtemplate(this.dataName, d, this.store.meta) :
16499 el.update(html.join(""));
16500 this.nodes = el.dom.childNodes;
16501 this.updateIndexes(0);
16506 * Function to override to reformat the data that is sent to
16507 * the template for each node.
16508 * DEPRICATED - use the preparedata event handler.
16509 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16510 * a JSON object for an UpdateManager bound view).
16512 prepareData : function(data, index, record)
16514 this.fireEvent("preparedata", this, data, index, record);
16518 onUpdate : function(ds, record){
16519 // Roo.log('on update');
16520 this.clearSelections();
16521 var index = this.store.indexOf(record);
16522 var n = this.nodes[index];
16523 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16524 n.parentNode.removeChild(n);
16525 this.updateIndexes(index, index);
16531 onAdd : function(ds, records, index)
16533 //Roo.log(['on Add', ds, records, index] );
16534 this.clearSelections();
16535 if(this.nodes.length == 0){
16539 var n = this.nodes[index];
16540 for(var i = 0, len = records.length; i < len; i++){
16541 var d = this.prepareData(records[i].data, i, records[i]);
16543 this.tpl.insertBefore(n, d);
16546 this.tpl.append(this.el, d);
16549 this.updateIndexes(index);
16552 onRemove : function(ds, record, index){
16553 // Roo.log('onRemove');
16554 this.clearSelections();
16555 var el = this.dataName ?
16556 this.el.child('.roo-tpl-' + this.dataName) :
16559 el.dom.removeChild(this.nodes[index]);
16560 this.updateIndexes(index);
16564 * Refresh an individual node.
16565 * @param {Number} index
16567 refreshNode : function(index){
16568 this.onUpdate(this.store, this.store.getAt(index));
16571 updateIndexes : function(startIndex, endIndex){
16572 var ns = this.nodes;
16573 startIndex = startIndex || 0;
16574 endIndex = endIndex || ns.length - 1;
16575 for(var i = startIndex; i <= endIndex; i++){
16576 ns[i].nodeIndex = i;
16581 * Changes the data store this view uses and refresh the view.
16582 * @param {Store} store
16584 setStore : function(store, initial){
16585 if(!initial && this.store){
16586 this.store.un("datachanged", this.refresh);
16587 this.store.un("add", this.onAdd);
16588 this.store.un("remove", this.onRemove);
16589 this.store.un("update", this.onUpdate);
16590 this.store.un("clear", this.refresh);
16591 this.store.un("beforeload", this.onBeforeLoad);
16592 this.store.un("load", this.onLoad);
16593 this.store.un("loadexception", this.onLoad);
16597 store.on("datachanged", this.refresh, this);
16598 store.on("add", this.onAdd, this);
16599 store.on("remove", this.onRemove, this);
16600 store.on("update", this.onUpdate, this);
16601 store.on("clear", this.refresh, this);
16602 store.on("beforeload", this.onBeforeLoad, this);
16603 store.on("load", this.onLoad, this);
16604 store.on("loadexception", this.onLoad, this);
16612 * onbeforeLoad - masks the loading area.
16615 onBeforeLoad : function(store,opts)
16617 //Roo.log('onBeforeLoad');
16619 this.el.update("");
16621 this.el.mask(this.mask ? this.mask : "Loading" );
16623 onLoad : function ()
16630 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16631 * @param {HTMLElement} node
16632 * @return {HTMLElement} The template node
16634 findItemFromChild : function(node){
16635 var el = this.dataName ?
16636 this.el.child('.roo-tpl-' + this.dataName,true) :
16639 if(!node || node.parentNode == el){
16642 var p = node.parentNode;
16643 while(p && p != el){
16644 if(p.parentNode == el){
16653 onClick : function(e){
16654 var item = this.findItemFromChild(e.getTarget());
16656 var index = this.indexOf(item);
16657 if(this.onItemClick(item, index, e) !== false){
16658 this.fireEvent("click", this, index, item, e);
16661 this.clearSelections();
16666 onContextMenu : function(e){
16667 var item = this.findItemFromChild(e.getTarget());
16669 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16674 onDblClick : function(e){
16675 var item = this.findItemFromChild(e.getTarget());
16677 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16681 onItemClick : function(item, index, e)
16683 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16686 if (this.toggleSelect) {
16687 var m = this.isSelected(item) ? 'unselect' : 'select';
16690 _t[m](item, true, false);
16693 if(this.multiSelect || this.singleSelect){
16694 if(this.multiSelect && e.shiftKey && this.lastSelection){
16695 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16697 this.select(item, this.multiSelect && e.ctrlKey);
16698 this.lastSelection = item;
16701 if(!this.tickable){
16702 e.preventDefault();
16710 * Get the number of selected nodes.
16713 getSelectionCount : function(){
16714 return this.selections.length;
16718 * Get the currently selected nodes.
16719 * @return {Array} An array of HTMLElements
16721 getSelectedNodes : function(){
16722 return this.selections;
16726 * Get the indexes of the selected nodes.
16729 getSelectedIndexes : function(){
16730 var indexes = [], s = this.selections;
16731 for(var i = 0, len = s.length; i < len; i++){
16732 indexes.push(s[i].nodeIndex);
16738 * Clear all selections
16739 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16741 clearSelections : function(suppressEvent){
16742 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16743 this.cmp.elements = this.selections;
16744 this.cmp.removeClass(this.selectedClass);
16745 this.selections = [];
16746 if(!suppressEvent){
16747 this.fireEvent("selectionchange", this, this.selections);
16753 * Returns true if the passed node is selected
16754 * @param {HTMLElement/Number} node The node or node index
16755 * @return {Boolean}
16757 isSelected : function(node){
16758 var s = this.selections;
16762 node = this.getNode(node);
16763 return s.indexOf(node) !== -1;
16768 * @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
16769 * @param {Boolean} keepExisting (optional) true to keep existing selections
16770 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16772 select : function(nodeInfo, keepExisting, suppressEvent){
16773 if(nodeInfo instanceof Array){
16775 this.clearSelections(true);
16777 for(var i = 0, len = nodeInfo.length; i < len; i++){
16778 this.select(nodeInfo[i], true, true);
16782 var node = this.getNode(nodeInfo);
16783 if(!node || this.isSelected(node)){
16784 return; // already selected.
16787 this.clearSelections(true);
16790 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16791 Roo.fly(node).addClass(this.selectedClass);
16792 this.selections.push(node);
16793 if(!suppressEvent){
16794 this.fireEvent("selectionchange", this, this.selections);
16802 * @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
16803 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16804 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16806 unselect : function(nodeInfo, keepExisting, suppressEvent)
16808 if(nodeInfo instanceof Array){
16809 Roo.each(this.selections, function(s) {
16810 this.unselect(s, nodeInfo);
16814 var node = this.getNode(nodeInfo);
16815 if(!node || !this.isSelected(node)){
16816 //Roo.log("not selected");
16817 return; // not selected.
16821 Roo.each(this.selections, function(s) {
16823 Roo.fly(node).removeClass(this.selectedClass);
16830 this.selections= ns;
16831 this.fireEvent("selectionchange", this, this.selections);
16835 * Gets a template node.
16836 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16837 * @return {HTMLElement} The node or null if it wasn't found
16839 getNode : function(nodeInfo){
16840 if(typeof nodeInfo == "string"){
16841 return document.getElementById(nodeInfo);
16842 }else if(typeof nodeInfo == "number"){
16843 return this.nodes[nodeInfo];
16849 * Gets a range template nodes.
16850 * @param {Number} startIndex
16851 * @param {Number} endIndex
16852 * @return {Array} An array of nodes
16854 getNodes : function(start, end){
16855 var ns = this.nodes;
16856 start = start || 0;
16857 end = typeof end == "undefined" ? ns.length - 1 : end;
16860 for(var i = start; i <= end; i++){
16864 for(var i = start; i >= end; i--){
16872 * Finds the index of the passed node
16873 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16874 * @return {Number} The index of the node or -1
16876 indexOf : function(node){
16877 node = this.getNode(node);
16878 if(typeof node.nodeIndex == "number"){
16879 return node.nodeIndex;
16881 var ns = this.nodes;
16882 for(var i = 0, len = ns.length; i < len; i++){
16893 * based on jquery fullcalendar
16897 Roo.bootstrap = Roo.bootstrap || {};
16899 * @class Roo.bootstrap.Calendar
16900 * @extends Roo.bootstrap.Component
16901 * Bootstrap Calendar class
16902 * @cfg {Boolean} loadMask (true|false) default false
16903 * @cfg {Object} header generate the user specific header of the calendar, default false
16906 * Create a new Container
16907 * @param {Object} config The config object
16912 Roo.bootstrap.Calendar = function(config){
16913 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16917 * Fires when a date is selected
16918 * @param {DatePicker} this
16919 * @param {Date} date The selected date
16923 * @event monthchange
16924 * Fires when the displayed month changes
16925 * @param {DatePicker} this
16926 * @param {Date} date The selected month
16928 'monthchange': true,
16930 * @event evententer
16931 * Fires when mouse over an event
16932 * @param {Calendar} this
16933 * @param {event} Event
16935 'evententer': true,
16937 * @event eventleave
16938 * Fires when the mouse leaves an
16939 * @param {Calendar} this
16942 'eventleave': true,
16944 * @event eventclick
16945 * Fires when the mouse click an
16946 * @param {Calendar} this
16955 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16958 * @cfg {Number} startDay
16959 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16967 getAutoCreate : function(){
16970 var fc_button = function(name, corner, style, content ) {
16971 return Roo.apply({},{
16973 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16975 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16978 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16989 style : 'width:100%',
16996 cls : 'fc-header-left',
16998 fc_button('prev', 'left', 'arrow', '‹' ),
16999 fc_button('next', 'right', 'arrow', '›' ),
17000 { tag: 'span', cls: 'fc-header-space' },
17001 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17009 cls : 'fc-header-center',
17013 cls: 'fc-header-title',
17016 html : 'month / year'
17024 cls : 'fc-header-right',
17026 /* fc_button('month', 'left', '', 'month' ),
17027 fc_button('week', '', '', 'week' ),
17028 fc_button('day', 'right', '', 'day' )
17040 header = this.header;
17043 var cal_heads = function() {
17045 // fixme - handle this.
17047 for (var i =0; i < Date.dayNames.length; i++) {
17048 var d = Date.dayNames[i];
17051 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17052 html : d.substring(0,3)
17056 ret[0].cls += ' fc-first';
17057 ret[6].cls += ' fc-last';
17060 var cal_cell = function(n) {
17063 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17068 cls: 'fc-day-number',
17072 cls: 'fc-day-content',
17076 style: 'position: relative;' // height: 17px;
17088 var cal_rows = function() {
17091 for (var r = 0; r < 6; r++) {
17098 for (var i =0; i < Date.dayNames.length; i++) {
17099 var d = Date.dayNames[i];
17100 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17103 row.cn[0].cls+=' fc-first';
17104 row.cn[0].cn[0].style = 'min-height:90px';
17105 row.cn[6].cls+=' fc-last';
17109 ret[0].cls += ' fc-first';
17110 ret[4].cls += ' fc-prev-last';
17111 ret[5].cls += ' fc-last';
17118 cls: 'fc-border-separate',
17119 style : 'width:100%',
17127 cls : 'fc-first fc-last',
17145 cls : 'fc-content',
17146 style : "position: relative;",
17149 cls : 'fc-view fc-view-month fc-grid',
17150 style : 'position: relative',
17151 unselectable : 'on',
17154 cls : 'fc-event-container',
17155 style : 'position:absolute;z-index:8;top:0;left:0;'
17173 initEvents : function()
17176 throw "can not find store for calendar";
17182 style: "text-align:center",
17186 style: "background-color:white;width:50%;margin:250 auto",
17190 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17201 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17203 var size = this.el.select('.fc-content', true).first().getSize();
17204 this.maskEl.setSize(size.width, size.height);
17205 this.maskEl.enableDisplayMode("block");
17206 if(!this.loadMask){
17207 this.maskEl.hide();
17210 this.store = Roo.factory(this.store, Roo.data);
17211 this.store.on('load', this.onLoad, this);
17212 this.store.on('beforeload', this.onBeforeLoad, this);
17216 this.cells = this.el.select('.fc-day',true);
17217 //Roo.log(this.cells);
17218 this.textNodes = this.el.query('.fc-day-number');
17219 this.cells.addClassOnOver('fc-state-hover');
17221 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17222 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17223 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17224 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17226 this.on('monthchange', this.onMonthChange, this);
17228 this.update(new Date().clearTime());
17231 resize : function() {
17232 var sz = this.el.getSize();
17234 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17235 this.el.select('.fc-day-content div',true).setHeight(34);
17240 showPrevMonth : function(e){
17241 this.update(this.activeDate.add("mo", -1));
17243 showToday : function(e){
17244 this.update(new Date().clearTime());
17247 showNextMonth : function(e){
17248 this.update(this.activeDate.add("mo", 1));
17252 showPrevYear : function(){
17253 this.update(this.activeDate.add("y", -1));
17257 showNextYear : function(){
17258 this.update(this.activeDate.add("y", 1));
17263 update : function(date)
17265 var vd = this.activeDate;
17266 this.activeDate = date;
17267 // if(vd && this.el){
17268 // var t = date.getTime();
17269 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17270 // Roo.log('using add remove');
17272 // this.fireEvent('monthchange', this, date);
17274 // this.cells.removeClass("fc-state-highlight");
17275 // this.cells.each(function(c){
17276 // if(c.dateValue == t){
17277 // c.addClass("fc-state-highlight");
17278 // setTimeout(function(){
17279 // try{c.dom.firstChild.focus();}catch(e){}
17289 var days = date.getDaysInMonth();
17291 var firstOfMonth = date.getFirstDateOfMonth();
17292 var startingPos = firstOfMonth.getDay()-this.startDay;
17294 if(startingPos < this.startDay){
17298 var pm = date.add(Date.MONTH, -1);
17299 var prevStart = pm.getDaysInMonth()-startingPos;
17301 this.cells = this.el.select('.fc-day',true);
17302 this.textNodes = this.el.query('.fc-day-number');
17303 this.cells.addClassOnOver('fc-state-hover');
17305 var cells = this.cells.elements;
17306 var textEls = this.textNodes;
17308 Roo.each(cells, function(cell){
17309 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17312 days += startingPos;
17314 // convert everything to numbers so it's fast
17315 var day = 86400000;
17316 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17319 //Roo.log(prevStart);
17321 var today = new Date().clearTime().getTime();
17322 var sel = date.clearTime().getTime();
17323 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17324 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17325 var ddMatch = this.disabledDatesRE;
17326 var ddText = this.disabledDatesText;
17327 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17328 var ddaysText = this.disabledDaysText;
17329 var format = this.format;
17331 var setCellClass = function(cal, cell){
17335 //Roo.log('set Cell Class');
17337 var t = d.getTime();
17341 cell.dateValue = t;
17343 cell.className += " fc-today";
17344 cell.className += " fc-state-highlight";
17345 cell.title = cal.todayText;
17348 // disable highlight in other month..
17349 //cell.className += " fc-state-highlight";
17354 cell.className = " fc-state-disabled";
17355 cell.title = cal.minText;
17359 cell.className = " fc-state-disabled";
17360 cell.title = cal.maxText;
17364 if(ddays.indexOf(d.getDay()) != -1){
17365 cell.title = ddaysText;
17366 cell.className = " fc-state-disabled";
17369 if(ddMatch && format){
17370 var fvalue = d.dateFormat(format);
17371 if(ddMatch.test(fvalue)){
17372 cell.title = ddText.replace("%0", fvalue);
17373 cell.className = " fc-state-disabled";
17377 if (!cell.initialClassName) {
17378 cell.initialClassName = cell.dom.className;
17381 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17386 for(; i < startingPos; i++) {
17387 textEls[i].innerHTML = (++prevStart);
17388 d.setDate(d.getDate()+1);
17390 cells[i].className = "fc-past fc-other-month";
17391 setCellClass(this, cells[i]);
17396 for(; i < days; i++){
17397 intDay = i - startingPos + 1;
17398 textEls[i].innerHTML = (intDay);
17399 d.setDate(d.getDate()+1);
17401 cells[i].className = ''; // "x-date-active";
17402 setCellClass(this, cells[i]);
17406 for(; i < 42; i++) {
17407 textEls[i].innerHTML = (++extraDays);
17408 d.setDate(d.getDate()+1);
17410 cells[i].className = "fc-future fc-other-month";
17411 setCellClass(this, cells[i]);
17414 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17416 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17418 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17419 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17421 if(totalRows != 6){
17422 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17423 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17426 this.fireEvent('monthchange', this, date);
17430 if(!this.internalRender){
17431 var main = this.el.dom.firstChild;
17432 var w = main.offsetWidth;
17433 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17434 Roo.fly(main).setWidth(w);
17435 this.internalRender = true;
17436 // opera does not respect the auto grow header center column
17437 // then, after it gets a width opera refuses to recalculate
17438 // without a second pass
17439 if(Roo.isOpera && !this.secondPass){
17440 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17441 this.secondPass = true;
17442 this.update.defer(10, this, [date]);
17449 findCell : function(dt) {
17450 dt = dt.clearTime().getTime();
17452 this.cells.each(function(c){
17453 //Roo.log("check " +c.dateValue + '?=' + dt);
17454 if(c.dateValue == dt){
17464 findCells : function(ev) {
17465 var s = ev.start.clone().clearTime().getTime();
17467 var e= ev.end.clone().clearTime().getTime();
17470 this.cells.each(function(c){
17471 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17473 if(c.dateValue > e){
17476 if(c.dateValue < s){
17485 // findBestRow: function(cells)
17489 // for (var i =0 ; i < cells.length;i++) {
17490 // ret = Math.max(cells[i].rows || 0,ret);
17497 addItem : function(ev)
17499 // look for vertical location slot in
17500 var cells = this.findCells(ev);
17502 // ev.row = this.findBestRow(cells);
17504 // work out the location.
17508 for(var i =0; i < cells.length; i++) {
17510 cells[i].row = cells[0].row;
17513 cells[i].row = cells[i].row + 1;
17523 if (crow.start.getY() == cells[i].getY()) {
17525 crow.end = cells[i];
17542 cells[0].events.push(ev);
17544 this.calevents.push(ev);
17547 clearEvents: function() {
17549 if(!this.calevents){
17553 Roo.each(this.cells.elements, function(c){
17559 Roo.each(this.calevents, function(e) {
17560 Roo.each(e.els, function(el) {
17561 el.un('mouseenter' ,this.onEventEnter, this);
17562 el.un('mouseleave' ,this.onEventLeave, this);
17567 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17573 renderEvents: function()
17577 this.cells.each(function(c) {
17586 if(c.row != c.events.length){
17587 r = 4 - (4 - (c.row - c.events.length));
17590 c.events = ev.slice(0, r);
17591 c.more = ev.slice(r);
17593 if(c.more.length && c.more.length == 1){
17594 c.events.push(c.more.pop());
17597 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17601 this.cells.each(function(c) {
17603 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17606 for (var e = 0; e < c.events.length; e++){
17607 var ev = c.events[e];
17608 var rows = ev.rows;
17610 for(var i = 0; i < rows.length; i++) {
17612 // how many rows should it span..
17615 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17616 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17618 unselectable : "on",
17621 cls: 'fc-event-inner',
17625 // cls: 'fc-event-time',
17626 // html : cells.length > 1 ? '' : ev.time
17630 cls: 'fc-event-title',
17631 html : String.format('{0}', ev.title)
17638 cls: 'ui-resizable-handle ui-resizable-e',
17639 html : '  '
17646 cfg.cls += ' fc-event-start';
17648 if ((i+1) == rows.length) {
17649 cfg.cls += ' fc-event-end';
17652 var ctr = _this.el.select('.fc-event-container',true).first();
17653 var cg = ctr.createChild(cfg);
17655 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17656 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17658 var r = (c.more.length) ? 1 : 0;
17659 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17660 cg.setWidth(ebox.right - sbox.x -2);
17662 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17663 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17664 cg.on('click', _this.onEventClick, _this, ev);
17675 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17676 style : 'position: absolute',
17677 unselectable : "on",
17680 cls: 'fc-event-inner',
17684 cls: 'fc-event-title',
17692 cls: 'ui-resizable-handle ui-resizable-e',
17693 html : '  '
17699 var ctr = _this.el.select('.fc-event-container',true).first();
17700 var cg = ctr.createChild(cfg);
17702 var sbox = c.select('.fc-day-content',true).first().getBox();
17703 var ebox = c.select('.fc-day-content',true).first().getBox();
17705 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17706 cg.setWidth(ebox.right - sbox.x -2);
17708 cg.on('click', _this.onMoreEventClick, _this, c.more);
17718 onEventEnter: function (e, el,event,d) {
17719 this.fireEvent('evententer', this, el, event);
17722 onEventLeave: function (e, el,event,d) {
17723 this.fireEvent('eventleave', this, el, event);
17726 onEventClick: function (e, el,event,d) {
17727 this.fireEvent('eventclick', this, el, event);
17730 onMonthChange: function () {
17734 onMoreEventClick: function(e, el, more)
17738 this.calpopover.placement = 'right';
17739 this.calpopover.setTitle('More');
17741 this.calpopover.setContent('');
17743 var ctr = this.calpopover.el.select('.popover-content', true).first();
17745 Roo.each(more, function(m){
17747 cls : 'fc-event-hori fc-event-draggable',
17750 var cg = ctr.createChild(cfg);
17752 cg.on('click', _this.onEventClick, _this, m);
17755 this.calpopover.show(el);
17760 onLoad: function ()
17762 this.calevents = [];
17765 if(this.store.getCount() > 0){
17766 this.store.data.each(function(d){
17769 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17770 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17771 time : d.data.start_time,
17772 title : d.data.title,
17773 description : d.data.description,
17774 venue : d.data.venue
17779 this.renderEvents();
17781 if(this.calevents.length && this.loadMask){
17782 this.maskEl.hide();
17786 onBeforeLoad: function()
17788 this.clearEvents();
17790 this.maskEl.show();
17804 * @class Roo.bootstrap.Popover
17805 * @extends Roo.bootstrap.Component
17806 * Bootstrap Popover class
17807 * @cfg {String} html contents of the popover (or false to use children..)
17808 * @cfg {String} title of popover (or false to hide)
17809 * @cfg {String} placement how it is placed
17810 * @cfg {String} trigger click || hover (or false to trigger manually)
17811 * @cfg {String} over what (parent or false to trigger manually.)
17812 * @cfg {Number} delay - delay before showing
17815 * Create a new Popover
17816 * @param {Object} config The config object
17819 Roo.bootstrap.Popover = function(config){
17820 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17826 * After the popover show
17828 * @param {Roo.bootstrap.Popover} this
17833 * After the popover hide
17835 * @param {Roo.bootstrap.Popover} this
17841 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17843 title: 'Fill in a title',
17846 placement : 'right',
17847 trigger : 'hover', // hover
17853 can_build_overlaid : false,
17855 getChildContainer : function()
17857 return this.el.select('.popover-content',true).first();
17860 getAutoCreate : function(){
17863 cls : 'popover roo-dynamic',
17864 style: 'display:block',
17870 cls : 'popover-inner',
17874 cls: 'popover-title popover-header',
17878 cls : 'popover-content popover-body',
17889 setTitle: function(str)
17892 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17894 setContent: function(str)
17897 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17899 // as it get's added to the bottom of the page.
17900 onRender : function(ct, position)
17902 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17904 var cfg = Roo.apply({}, this.getAutoCreate());
17908 cfg.cls += ' ' + this.cls;
17911 cfg.style = this.style;
17913 //Roo.log("adding to ");
17914 this.el = Roo.get(document.body).createChild(cfg, position);
17915 // Roo.log(this.el);
17920 initEvents : function()
17922 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17923 this.el.enableDisplayMode('block');
17925 if (this.over === false) {
17928 if (this.triggers === false) {
17931 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17932 var triggers = this.trigger ? this.trigger.split(' ') : [];
17933 Roo.each(triggers, function(trigger) {
17935 if (trigger == 'click') {
17936 on_el.on('click', this.toggle, this);
17937 } else if (trigger != 'manual') {
17938 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17939 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17941 on_el.on(eventIn ,this.enter, this);
17942 on_el.on(eventOut, this.leave, this);
17953 toggle : function () {
17954 this.hoverState == 'in' ? this.leave() : this.enter();
17957 enter : function () {
17959 clearTimeout(this.timeout);
17961 this.hoverState = 'in';
17963 if (!this.delay || !this.delay.show) {
17968 this.timeout = setTimeout(function () {
17969 if (_t.hoverState == 'in') {
17972 }, this.delay.show)
17975 leave : function() {
17976 clearTimeout(this.timeout);
17978 this.hoverState = 'out';
17980 if (!this.delay || !this.delay.hide) {
17985 this.timeout = setTimeout(function () {
17986 if (_t.hoverState == 'out') {
17989 }, this.delay.hide)
17992 show : function (on_el)
17995 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17999 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18000 if (this.html !== false) {
18001 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18003 this.el.removeClass([
18004 'fade','top','bottom', 'left', 'right','in',
18005 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18007 if (!this.title.length) {
18008 this.el.select('.popover-title',true).hide();
18011 var placement = typeof this.placement == 'function' ?
18012 this.placement.call(this, this.el, on_el) :
18015 var autoToken = /\s?auto?\s?/i;
18016 var autoPlace = autoToken.test(placement);
18018 placement = placement.replace(autoToken, '') || 'top';
18022 //this.el.setXY([0,0]);
18024 this.el.dom.style.display='block';
18025 this.el.addClass(placement);
18027 //this.el.appendTo(on_el);
18029 var p = this.getPosition();
18030 var box = this.el.getBox();
18035 var align = Roo.bootstrap.Popover.alignment[placement];
18038 this.el.alignTo(on_el, align[0],align[1]);
18039 //var arrow = this.el.select('.arrow',true).first();
18040 //arrow.set(align[2],
18042 this.el.addClass('in');
18045 if (this.el.hasClass('fade')) {
18049 this.hoverState = 'in';
18051 this.fireEvent('show', this);
18056 this.el.setXY([0,0]);
18057 this.el.removeClass('in');
18059 this.hoverState = null;
18061 this.fireEvent('hide', this);
18066 Roo.bootstrap.Popover.alignment = {
18067 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18068 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18069 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18070 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18081 * @class Roo.bootstrap.Progress
18082 * @extends Roo.bootstrap.Component
18083 * Bootstrap Progress class
18084 * @cfg {Boolean} striped striped of the progress bar
18085 * @cfg {Boolean} active animated of the progress bar
18089 * Create a new Progress
18090 * @param {Object} config The config object
18093 Roo.bootstrap.Progress = function(config){
18094 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18097 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18102 getAutoCreate : function(){
18110 cfg.cls += ' progress-striped';
18114 cfg.cls += ' active';
18133 * @class Roo.bootstrap.ProgressBar
18134 * @extends Roo.bootstrap.Component
18135 * Bootstrap ProgressBar class
18136 * @cfg {Number} aria_valuenow aria-value now
18137 * @cfg {Number} aria_valuemin aria-value min
18138 * @cfg {Number} aria_valuemax aria-value max
18139 * @cfg {String} label label for the progress bar
18140 * @cfg {String} panel (success | info | warning | danger )
18141 * @cfg {String} role role of the progress bar
18142 * @cfg {String} sr_only text
18146 * Create a new ProgressBar
18147 * @param {Object} config The config object
18150 Roo.bootstrap.ProgressBar = function(config){
18151 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18154 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18158 aria_valuemax : 100,
18164 getAutoCreate : function()
18169 cls: 'progress-bar',
18170 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18182 cfg.role = this.role;
18185 if(this.aria_valuenow){
18186 cfg['aria-valuenow'] = this.aria_valuenow;
18189 if(this.aria_valuemin){
18190 cfg['aria-valuemin'] = this.aria_valuemin;
18193 if(this.aria_valuemax){
18194 cfg['aria-valuemax'] = this.aria_valuemax;
18197 if(this.label && !this.sr_only){
18198 cfg.html = this.label;
18202 cfg.cls += ' progress-bar-' + this.panel;
18208 update : function(aria_valuenow)
18210 this.aria_valuenow = aria_valuenow;
18212 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18227 * @class Roo.bootstrap.TabGroup
18228 * @extends Roo.bootstrap.Column
18229 * Bootstrap Column class
18230 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18231 * @cfg {Boolean} carousel true to make the group behave like a carousel
18232 * @cfg {Boolean} bullets show bullets for the panels
18233 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18234 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18235 * @cfg {Boolean} showarrow (true|false) show arrow default true
18238 * Create a new TabGroup
18239 * @param {Object} config The config object
18242 Roo.bootstrap.TabGroup = function(config){
18243 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18245 this.navId = Roo.id();
18248 Roo.bootstrap.TabGroup.register(this);
18252 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18255 transition : false,
18260 slideOnTouch : false,
18263 getAutoCreate : function()
18265 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18267 cfg.cls += ' tab-content';
18269 if (this.carousel) {
18270 cfg.cls += ' carousel slide';
18273 cls : 'carousel-inner',
18277 if(this.bullets && !Roo.isTouch){
18280 cls : 'carousel-bullets',
18284 if(this.bullets_cls){
18285 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18292 cfg.cn[0].cn.push(bullets);
18295 if(this.showarrow){
18296 cfg.cn[0].cn.push({
18298 class : 'carousel-arrow',
18302 class : 'carousel-prev',
18306 class : 'fa fa-chevron-left'
18312 class : 'carousel-next',
18316 class : 'fa fa-chevron-right'
18329 initEvents: function()
18331 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18332 // this.el.on("touchstart", this.onTouchStart, this);
18335 if(this.autoslide){
18338 this.slideFn = window.setInterval(function() {
18339 _this.showPanelNext();
18343 if(this.showarrow){
18344 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18345 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18351 // onTouchStart : function(e, el, o)
18353 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18357 // this.showPanelNext();
18361 getChildContainer : function()
18363 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18367 * register a Navigation item
18368 * @param {Roo.bootstrap.NavItem} the navitem to add
18370 register : function(item)
18372 this.tabs.push( item);
18373 item.navId = this.navId; // not really needed..
18378 getActivePanel : function()
18381 Roo.each(this.tabs, function(t) {
18391 getPanelByName : function(n)
18394 Roo.each(this.tabs, function(t) {
18395 if (t.tabId == n) {
18403 indexOfPanel : function(p)
18406 Roo.each(this.tabs, function(t,i) {
18407 if (t.tabId == p.tabId) {
18416 * show a specific panel
18417 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18418 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18420 showPanel : function (pan)
18422 if(this.transition || typeof(pan) == 'undefined'){
18423 Roo.log("waiting for the transitionend");
18427 if (typeof(pan) == 'number') {
18428 pan = this.tabs[pan];
18431 if (typeof(pan) == 'string') {
18432 pan = this.getPanelByName(pan);
18435 var cur = this.getActivePanel();
18438 Roo.log('pan or acitve pan is undefined');
18442 if (pan.tabId == this.getActivePanel().tabId) {
18446 if (false === cur.fireEvent('beforedeactivate')) {
18450 if(this.bullets > 0 && !Roo.isTouch){
18451 this.setActiveBullet(this.indexOfPanel(pan));
18454 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18456 //class="carousel-item carousel-item-next carousel-item-left"
18458 this.transition = true;
18459 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18460 var lr = dir == 'next' ? 'left' : 'right';
18461 pan.el.addClass(dir); // or prev
18462 pan.el.addClass('carousel-item-' + dir); // or prev
18463 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18464 cur.el.addClass(lr); // or right
18465 pan.el.addClass(lr);
18466 cur.el.addClass('carousel-item-' +lr); // or right
18467 pan.el.addClass('carousel-item-' +lr);
18471 cur.el.on('transitionend', function() {
18472 Roo.log("trans end?");
18474 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18475 pan.setActive(true);
18477 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18478 cur.setActive(false);
18480 _this.transition = false;
18482 }, this, { single: true } );
18487 cur.setActive(false);
18488 pan.setActive(true);
18493 showPanelNext : function()
18495 var i = this.indexOfPanel(this.getActivePanel());
18497 if (i >= this.tabs.length - 1 && !this.autoslide) {
18501 if (i >= this.tabs.length - 1 && this.autoslide) {
18505 this.showPanel(this.tabs[i+1]);
18508 showPanelPrev : function()
18510 var i = this.indexOfPanel(this.getActivePanel());
18512 if (i < 1 && !this.autoslide) {
18516 if (i < 1 && this.autoslide) {
18517 i = this.tabs.length;
18520 this.showPanel(this.tabs[i-1]);
18524 addBullet: function()
18526 if(!this.bullets || Roo.isTouch){
18529 var ctr = this.el.select('.carousel-bullets',true).first();
18530 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18531 var bullet = ctr.createChild({
18532 cls : 'bullet bullet-' + i
18533 },ctr.dom.lastChild);
18538 bullet.on('click', (function(e, el, o, ii, t){
18540 e.preventDefault();
18542 this.showPanel(ii);
18544 if(this.autoslide && this.slideFn){
18545 clearInterval(this.slideFn);
18546 this.slideFn = window.setInterval(function() {
18547 _this.showPanelNext();
18551 }).createDelegate(this, [i, bullet], true));
18556 setActiveBullet : function(i)
18562 Roo.each(this.el.select('.bullet', true).elements, function(el){
18563 el.removeClass('selected');
18566 var bullet = this.el.select('.bullet-' + i, true).first();
18572 bullet.addClass('selected');
18583 Roo.apply(Roo.bootstrap.TabGroup, {
18587 * register a Navigation Group
18588 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18590 register : function(navgrp)
18592 this.groups[navgrp.navId] = navgrp;
18596 * fetch a Navigation Group based on the navigation ID
18597 * if one does not exist , it will get created.
18598 * @param {string} the navgroup to add
18599 * @returns {Roo.bootstrap.NavGroup} the navgroup
18601 get: function(navId) {
18602 if (typeof(this.groups[navId]) == 'undefined') {
18603 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18605 return this.groups[navId] ;
18620 * @class Roo.bootstrap.TabPanel
18621 * @extends Roo.bootstrap.Component
18622 * Bootstrap TabPanel class
18623 * @cfg {Boolean} active panel active
18624 * @cfg {String} html panel content
18625 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18626 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18627 * @cfg {String} href click to link..
18631 * Create a new TabPanel
18632 * @param {Object} config The config object
18635 Roo.bootstrap.TabPanel = function(config){
18636 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18640 * Fires when the active status changes
18641 * @param {Roo.bootstrap.TabPanel} this
18642 * @param {Boolean} state the new state
18647 * @event beforedeactivate
18648 * Fires before a tab is de-activated - can be used to do validation on a form.
18649 * @param {Roo.bootstrap.TabPanel} this
18650 * @return {Boolean} false if there is an error
18653 'beforedeactivate': true
18656 this.tabId = this.tabId || Roo.id();
18660 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18668 getAutoCreate : function(){
18673 // item is needed for carousel - not sure if it has any effect otherwise
18674 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18675 html: this.html || ''
18679 cfg.cls += ' active';
18683 cfg.tabId = this.tabId;
18691 initEvents: function()
18693 var p = this.parent();
18695 this.navId = this.navId || p.navId;
18697 if (typeof(this.navId) != 'undefined') {
18698 // not really needed.. but just in case.. parent should be a NavGroup.
18699 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18703 var i = tg.tabs.length - 1;
18705 if(this.active && tg.bullets > 0 && i < tg.bullets){
18706 tg.setActiveBullet(i);
18710 this.el.on('click', this.onClick, this);
18713 this.el.on("touchstart", this.onTouchStart, this);
18714 this.el.on("touchmove", this.onTouchMove, this);
18715 this.el.on("touchend", this.onTouchEnd, this);
18720 onRender : function(ct, position)
18722 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18725 setActive : function(state)
18727 Roo.log("panel - set active " + this.tabId + "=" + state);
18729 this.active = state;
18731 this.el.removeClass('active');
18733 } else if (!this.el.hasClass('active')) {
18734 this.el.addClass('active');
18737 this.fireEvent('changed', this, state);
18740 onClick : function(e)
18742 e.preventDefault();
18744 if(!this.href.length){
18748 window.location.href = this.href;
18757 onTouchStart : function(e)
18759 this.swiping = false;
18761 this.startX = e.browserEvent.touches[0].clientX;
18762 this.startY = e.browserEvent.touches[0].clientY;
18765 onTouchMove : function(e)
18767 this.swiping = true;
18769 this.endX = e.browserEvent.touches[0].clientX;
18770 this.endY = e.browserEvent.touches[0].clientY;
18773 onTouchEnd : function(e)
18780 var tabGroup = this.parent();
18782 if(this.endX > this.startX){ // swiping right
18783 tabGroup.showPanelPrev();
18787 if(this.startX > this.endX){ // swiping left
18788 tabGroup.showPanelNext();
18807 * @class Roo.bootstrap.DateField
18808 * @extends Roo.bootstrap.Input
18809 * Bootstrap DateField class
18810 * @cfg {Number} weekStart default 0
18811 * @cfg {String} viewMode default empty, (months|years)
18812 * @cfg {String} minViewMode default empty, (months|years)
18813 * @cfg {Number} startDate default -Infinity
18814 * @cfg {Number} endDate default Infinity
18815 * @cfg {Boolean} todayHighlight default false
18816 * @cfg {Boolean} todayBtn default false
18817 * @cfg {Boolean} calendarWeeks default false
18818 * @cfg {Object} daysOfWeekDisabled default empty
18819 * @cfg {Boolean} singleMode default false (true | false)
18821 * @cfg {Boolean} keyboardNavigation default true
18822 * @cfg {String} language default en
18825 * Create a new DateField
18826 * @param {Object} config The config object
18829 Roo.bootstrap.DateField = function(config){
18830 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18834 * Fires when this field show.
18835 * @param {Roo.bootstrap.DateField} this
18836 * @param {Mixed} date The date value
18841 * Fires when this field hide.
18842 * @param {Roo.bootstrap.DateField} this
18843 * @param {Mixed} date The date value
18848 * Fires when select a date.
18849 * @param {Roo.bootstrap.DateField} this
18850 * @param {Mixed} date The date value
18854 * @event beforeselect
18855 * Fires when before select a date.
18856 * @param {Roo.bootstrap.DateField} this
18857 * @param {Mixed} date The date value
18859 beforeselect : true
18863 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18866 * @cfg {String} format
18867 * The default date format string which can be overriden for localization support. The format must be
18868 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18872 * @cfg {String} altFormats
18873 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18874 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18876 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18884 todayHighlight : false,
18890 keyboardNavigation: true,
18892 calendarWeeks: false,
18894 startDate: -Infinity,
18898 daysOfWeekDisabled: [],
18902 singleMode : false,
18904 UTCDate: function()
18906 return new Date(Date.UTC.apply(Date, arguments));
18909 UTCToday: function()
18911 var today = new Date();
18912 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18915 getDate: function() {
18916 var d = this.getUTCDate();
18917 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18920 getUTCDate: function() {
18924 setDate: function(d) {
18925 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18928 setUTCDate: function(d) {
18930 this.setValue(this.formatDate(this.date));
18933 onRender: function(ct, position)
18936 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18938 this.language = this.language || 'en';
18939 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18940 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18942 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18943 this.format = this.format || 'm/d/y';
18944 this.isInline = false;
18945 this.isInput = true;
18946 this.component = this.el.select('.add-on', true).first() || false;
18947 this.component = (this.component && this.component.length === 0) ? false : this.component;
18948 this.hasInput = this.component && this.inputEl().length;
18950 if (typeof(this.minViewMode === 'string')) {
18951 switch (this.minViewMode) {
18953 this.minViewMode = 1;
18956 this.minViewMode = 2;
18959 this.minViewMode = 0;
18964 if (typeof(this.viewMode === 'string')) {
18965 switch (this.viewMode) {
18978 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18980 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18982 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18984 this.picker().on('mousedown', this.onMousedown, this);
18985 this.picker().on('click', this.onClick, this);
18987 this.picker().addClass('datepicker-dropdown');
18989 this.startViewMode = this.viewMode;
18991 if(this.singleMode){
18992 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18993 v.setVisibilityMode(Roo.Element.DISPLAY);
18997 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18998 v.setStyle('width', '189px');
19002 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19003 if(!this.calendarWeeks){
19008 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19009 v.attr('colspan', function(i, val){
19010 return parseInt(val) + 1;
19015 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19017 this.setStartDate(this.startDate);
19018 this.setEndDate(this.endDate);
19020 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19027 if(this.isInline) {
19032 picker : function()
19034 return this.pickerEl;
19035 // return this.el.select('.datepicker', true).first();
19038 fillDow: function()
19040 var dowCnt = this.weekStart;
19049 if(this.calendarWeeks){
19057 while (dowCnt < this.weekStart + 7) {
19061 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19065 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19068 fillMonths: function()
19071 var months = this.picker().select('>.datepicker-months td', true).first();
19073 months.dom.innerHTML = '';
19079 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19082 months.createChild(month);
19089 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;
19091 if (this.date < this.startDate) {
19092 this.viewDate = new Date(this.startDate);
19093 } else if (this.date > this.endDate) {
19094 this.viewDate = new Date(this.endDate);
19096 this.viewDate = new Date(this.date);
19104 var d = new Date(this.viewDate),
19105 year = d.getUTCFullYear(),
19106 month = d.getUTCMonth(),
19107 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19108 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19109 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19110 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19111 currentDate = this.date && this.date.valueOf(),
19112 today = this.UTCToday();
19114 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19116 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19118 // this.picker.select('>tfoot th.today').
19119 // .text(dates[this.language].today)
19120 // .toggle(this.todayBtn !== false);
19122 this.updateNavArrows();
19125 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19127 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19129 prevMonth.setUTCDate(day);
19131 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19133 var nextMonth = new Date(prevMonth);
19135 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19137 nextMonth = nextMonth.valueOf();
19139 var fillMonths = false;
19141 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19143 while(prevMonth.valueOf() <= nextMonth) {
19146 if (prevMonth.getUTCDay() === this.weekStart) {
19148 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19156 if(this.calendarWeeks){
19157 // ISO 8601: First week contains first thursday.
19158 // ISO also states week starts on Monday, but we can be more abstract here.
19160 // Start of current week: based on weekstart/current date
19161 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19162 // Thursday of this week
19163 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19164 // First Thursday of year, year from thursday
19165 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19166 // Calendar week: ms between thursdays, div ms per day, div 7 days
19167 calWeek = (th - yth) / 864e5 / 7 + 1;
19169 fillMonths.cn.push({
19177 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19179 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19182 if (this.todayHighlight &&
19183 prevMonth.getUTCFullYear() == today.getFullYear() &&
19184 prevMonth.getUTCMonth() == today.getMonth() &&
19185 prevMonth.getUTCDate() == today.getDate()) {
19186 clsName += ' today';
19189 if (currentDate && prevMonth.valueOf() === currentDate) {
19190 clsName += ' active';
19193 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19194 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19195 clsName += ' disabled';
19198 fillMonths.cn.push({
19200 cls: 'day ' + clsName,
19201 html: prevMonth.getDate()
19204 prevMonth.setDate(prevMonth.getDate()+1);
19207 var currentYear = this.date && this.date.getUTCFullYear();
19208 var currentMonth = this.date && this.date.getUTCMonth();
19210 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19212 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19213 v.removeClass('active');
19215 if(currentYear === year && k === currentMonth){
19216 v.addClass('active');
19219 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19220 v.addClass('disabled');
19226 year = parseInt(year/10, 10) * 10;
19228 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19230 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19233 for (var i = -1; i < 11; i++) {
19234 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19236 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19244 showMode: function(dir)
19247 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19250 Roo.each(this.picker().select('>div',true).elements, function(v){
19251 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19254 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19259 if(this.isInline) {
19263 this.picker().removeClass(['bottom', 'top']);
19265 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19267 * place to the top of element!
19271 this.picker().addClass('top');
19272 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19277 this.picker().addClass('bottom');
19279 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19282 parseDate : function(value)
19284 if(!value || value instanceof Date){
19287 var v = Date.parseDate(value, this.format);
19288 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19289 v = Date.parseDate(value, 'Y-m-d');
19291 if(!v && this.altFormats){
19292 if(!this.altFormatsArray){
19293 this.altFormatsArray = this.altFormats.split("|");
19295 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19296 v = Date.parseDate(value, this.altFormatsArray[i]);
19302 formatDate : function(date, fmt)
19304 return (!date || !(date instanceof Date)) ?
19305 date : date.dateFormat(fmt || this.format);
19308 onFocus : function()
19310 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19314 onBlur : function()
19316 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19318 var d = this.inputEl().getValue();
19325 showPopup : function()
19327 this.picker().show();
19331 this.fireEvent('showpopup', this, this.date);
19334 hidePopup : function()
19336 if(this.isInline) {
19339 this.picker().hide();
19340 this.viewMode = this.startViewMode;
19343 this.fireEvent('hidepopup', this, this.date);
19347 onMousedown: function(e)
19349 e.stopPropagation();
19350 e.preventDefault();
19355 Roo.bootstrap.DateField.superclass.keyup.call(this);
19359 setValue: function(v)
19361 if(this.fireEvent('beforeselect', this, v) !== false){
19362 var d = new Date(this.parseDate(v) ).clearTime();
19364 if(isNaN(d.getTime())){
19365 this.date = this.viewDate = '';
19366 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19370 v = this.formatDate(d);
19372 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19374 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19378 this.fireEvent('select', this, this.date);
19382 getValue: function()
19384 return this.formatDate(this.date);
19387 fireKey: function(e)
19389 if (!this.picker().isVisible()){
19390 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19396 var dateChanged = false,
19398 newDate, newViewDate;
19403 e.preventDefault();
19407 if (!this.keyboardNavigation) {
19410 dir = e.keyCode == 37 ? -1 : 1;
19413 newDate = this.moveYear(this.date, dir);
19414 newViewDate = this.moveYear(this.viewDate, dir);
19415 } else if (e.shiftKey){
19416 newDate = this.moveMonth(this.date, dir);
19417 newViewDate = this.moveMonth(this.viewDate, dir);
19419 newDate = new Date(this.date);
19420 newDate.setUTCDate(this.date.getUTCDate() + dir);
19421 newViewDate = new Date(this.viewDate);
19422 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19424 if (this.dateWithinRange(newDate)){
19425 this.date = newDate;
19426 this.viewDate = newViewDate;
19427 this.setValue(this.formatDate(this.date));
19429 e.preventDefault();
19430 dateChanged = true;
19435 if (!this.keyboardNavigation) {
19438 dir = e.keyCode == 38 ? -1 : 1;
19440 newDate = this.moveYear(this.date, dir);
19441 newViewDate = this.moveYear(this.viewDate, dir);
19442 } else if (e.shiftKey){
19443 newDate = this.moveMonth(this.date, dir);
19444 newViewDate = this.moveMonth(this.viewDate, dir);
19446 newDate = new Date(this.date);
19447 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19448 newViewDate = new Date(this.viewDate);
19449 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19451 if (this.dateWithinRange(newDate)){
19452 this.date = newDate;
19453 this.viewDate = newViewDate;
19454 this.setValue(this.formatDate(this.date));
19456 e.preventDefault();
19457 dateChanged = true;
19461 this.setValue(this.formatDate(this.date));
19463 e.preventDefault();
19466 this.setValue(this.formatDate(this.date));
19480 onClick: function(e)
19482 e.stopPropagation();
19483 e.preventDefault();
19485 var target = e.getTarget();
19487 if(target.nodeName.toLowerCase() === 'i'){
19488 target = Roo.get(target).dom.parentNode;
19491 var nodeName = target.nodeName;
19492 var className = target.className;
19493 var html = target.innerHTML;
19494 //Roo.log(nodeName);
19496 switch(nodeName.toLowerCase()) {
19498 switch(className) {
19504 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19505 switch(this.viewMode){
19507 this.viewDate = this.moveMonth(this.viewDate, dir);
19511 this.viewDate = this.moveYear(this.viewDate, dir);
19517 var date = new Date();
19518 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19520 this.setValue(this.formatDate(this.date));
19527 if (className.indexOf('disabled') < 0) {
19528 this.viewDate.setUTCDate(1);
19529 if (className.indexOf('month') > -1) {
19530 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19532 var year = parseInt(html, 10) || 0;
19533 this.viewDate.setUTCFullYear(year);
19537 if(this.singleMode){
19538 this.setValue(this.formatDate(this.viewDate));
19549 //Roo.log(className);
19550 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19551 var day = parseInt(html, 10) || 1;
19552 var year = this.viewDate.getUTCFullYear(),
19553 month = this.viewDate.getUTCMonth();
19555 if (className.indexOf('old') > -1) {
19562 } else if (className.indexOf('new') > -1) {
19570 //Roo.log([year,month,day]);
19571 this.date = this.UTCDate(year, month, day,0,0,0,0);
19572 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19574 //Roo.log(this.formatDate(this.date));
19575 this.setValue(this.formatDate(this.date));
19582 setStartDate: function(startDate)
19584 this.startDate = startDate || -Infinity;
19585 if (this.startDate !== -Infinity) {
19586 this.startDate = this.parseDate(this.startDate);
19589 this.updateNavArrows();
19592 setEndDate: function(endDate)
19594 this.endDate = endDate || Infinity;
19595 if (this.endDate !== Infinity) {
19596 this.endDate = this.parseDate(this.endDate);
19599 this.updateNavArrows();
19602 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19604 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19605 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19606 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19608 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19609 return parseInt(d, 10);
19612 this.updateNavArrows();
19615 updateNavArrows: function()
19617 if(this.singleMode){
19621 var d = new Date(this.viewDate),
19622 year = d.getUTCFullYear(),
19623 month = d.getUTCMonth();
19625 Roo.each(this.picker().select('.prev', true).elements, function(v){
19627 switch (this.viewMode) {
19630 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19636 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19643 Roo.each(this.picker().select('.next', true).elements, function(v){
19645 switch (this.viewMode) {
19648 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19654 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19662 moveMonth: function(date, dir)
19667 var new_date = new Date(date.valueOf()),
19668 day = new_date.getUTCDate(),
19669 month = new_date.getUTCMonth(),
19670 mag = Math.abs(dir),
19672 dir = dir > 0 ? 1 : -1;
19675 // If going back one month, make sure month is not current month
19676 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19678 return new_date.getUTCMonth() == month;
19680 // If going forward one month, make sure month is as expected
19681 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19683 return new_date.getUTCMonth() != new_month;
19685 new_month = month + dir;
19686 new_date.setUTCMonth(new_month);
19687 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19688 if (new_month < 0 || new_month > 11) {
19689 new_month = (new_month + 12) % 12;
19692 // For magnitudes >1, move one month at a time...
19693 for (var i=0; i<mag; i++) {
19694 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19695 new_date = this.moveMonth(new_date, dir);
19697 // ...then reset the day, keeping it in the new month
19698 new_month = new_date.getUTCMonth();
19699 new_date.setUTCDate(day);
19701 return new_month != new_date.getUTCMonth();
19704 // Common date-resetting loop -- if date is beyond end of month, make it
19707 new_date.setUTCDate(--day);
19708 new_date.setUTCMonth(new_month);
19713 moveYear: function(date, dir)
19715 return this.moveMonth(date, dir*12);
19718 dateWithinRange: function(date)
19720 return date >= this.startDate && date <= this.endDate;
19726 this.picker().remove();
19729 validateValue : function(value)
19731 if(this.getVisibilityEl().hasClass('hidden')){
19735 if(value.length < 1) {
19736 if(this.allowBlank){
19742 if(value.length < this.minLength){
19745 if(value.length > this.maxLength){
19749 var vt = Roo.form.VTypes;
19750 if(!vt[this.vtype](value, this)){
19754 if(typeof this.validator == "function"){
19755 var msg = this.validator(value);
19761 if(this.regex && !this.regex.test(value)){
19765 if(typeof(this.parseDate(value)) == 'undefined'){
19769 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19773 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19783 this.date = this.viewDate = '';
19785 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19790 Roo.apply(Roo.bootstrap.DateField, {
19801 html: '<i class="fa fa-arrow-left"/>'
19811 html: '<i class="fa fa-arrow-right"/>'
19853 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19854 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19855 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19856 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19857 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19870 navFnc: 'FullYear',
19875 navFnc: 'FullYear',
19880 Roo.apply(Roo.bootstrap.DateField, {
19884 cls: 'datepicker dropdown-menu roo-dynamic',
19888 cls: 'datepicker-days',
19892 cls: 'table-condensed',
19894 Roo.bootstrap.DateField.head,
19898 Roo.bootstrap.DateField.footer
19905 cls: 'datepicker-months',
19909 cls: 'table-condensed',
19911 Roo.bootstrap.DateField.head,
19912 Roo.bootstrap.DateField.content,
19913 Roo.bootstrap.DateField.footer
19920 cls: 'datepicker-years',
19924 cls: 'table-condensed',
19926 Roo.bootstrap.DateField.head,
19927 Roo.bootstrap.DateField.content,
19928 Roo.bootstrap.DateField.footer
19947 * @class Roo.bootstrap.TimeField
19948 * @extends Roo.bootstrap.Input
19949 * Bootstrap DateField class
19953 * Create a new TimeField
19954 * @param {Object} config The config object
19957 Roo.bootstrap.TimeField = function(config){
19958 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19962 * Fires when this field show.
19963 * @param {Roo.bootstrap.DateField} thisthis
19964 * @param {Mixed} date The date value
19969 * Fires when this field hide.
19970 * @param {Roo.bootstrap.DateField} this
19971 * @param {Mixed} date The date value
19976 * Fires when select a date.
19977 * @param {Roo.bootstrap.DateField} this
19978 * @param {Mixed} date The date value
19984 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19987 * @cfg {String} format
19988 * The default time format string which can be overriden for localization support. The format must be
19989 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19993 onRender: function(ct, position)
19996 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19998 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20000 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20002 this.pop = this.picker().select('>.datepicker-time',true).first();
20003 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20005 this.picker().on('mousedown', this.onMousedown, this);
20006 this.picker().on('click', this.onClick, this);
20008 this.picker().addClass('datepicker-dropdown');
20013 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20014 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20015 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20016 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20017 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20018 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20022 fireKey: function(e){
20023 if (!this.picker().isVisible()){
20024 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20030 e.preventDefault();
20038 this.onTogglePeriod();
20041 this.onIncrementMinutes();
20044 this.onDecrementMinutes();
20053 onClick: function(e) {
20054 e.stopPropagation();
20055 e.preventDefault();
20058 picker : function()
20060 return this.el.select('.datepicker', true).first();
20063 fillTime: function()
20065 var time = this.pop.select('tbody', true).first();
20067 time.dom.innerHTML = '';
20082 cls: 'hours-up glyphicon glyphicon-chevron-up'
20102 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20123 cls: 'timepicker-hour',
20138 cls: 'timepicker-minute',
20153 cls: 'btn btn-primary period',
20175 cls: 'hours-down glyphicon glyphicon-chevron-down'
20195 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20213 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20220 var hours = this.time.getHours();
20221 var minutes = this.time.getMinutes();
20234 hours = hours - 12;
20238 hours = '0' + hours;
20242 minutes = '0' + minutes;
20245 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20246 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20247 this.pop.select('button', true).first().dom.innerHTML = period;
20253 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20255 var cls = ['bottom'];
20257 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20264 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20269 this.picker().addClass(cls.join('-'));
20273 Roo.each(cls, function(c){
20275 _this.picker().setTop(_this.inputEl().getHeight());
20279 _this.picker().setTop(0 - _this.picker().getHeight());
20284 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20288 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20295 onFocus : function()
20297 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20301 onBlur : function()
20303 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20309 this.picker().show();
20314 this.fireEvent('show', this, this.date);
20319 this.picker().hide();
20322 this.fireEvent('hide', this, this.date);
20325 setTime : function()
20328 this.setValue(this.time.format(this.format));
20330 this.fireEvent('select', this, this.date);
20335 onMousedown: function(e){
20336 e.stopPropagation();
20337 e.preventDefault();
20340 onIncrementHours: function()
20342 Roo.log('onIncrementHours');
20343 this.time = this.time.add(Date.HOUR, 1);
20348 onDecrementHours: function()
20350 Roo.log('onDecrementHours');
20351 this.time = this.time.add(Date.HOUR, -1);
20355 onIncrementMinutes: function()
20357 Roo.log('onIncrementMinutes');
20358 this.time = this.time.add(Date.MINUTE, 1);
20362 onDecrementMinutes: function()
20364 Roo.log('onDecrementMinutes');
20365 this.time = this.time.add(Date.MINUTE, -1);
20369 onTogglePeriod: function()
20371 Roo.log('onTogglePeriod');
20372 this.time = this.time.add(Date.HOUR, 12);
20379 Roo.apply(Roo.bootstrap.TimeField, {
20409 cls: 'btn btn-info ok',
20421 Roo.apply(Roo.bootstrap.TimeField, {
20425 cls: 'datepicker dropdown-menu',
20429 cls: 'datepicker-time',
20433 cls: 'table-condensed',
20435 Roo.bootstrap.TimeField.content,
20436 Roo.bootstrap.TimeField.footer
20455 * @class Roo.bootstrap.MonthField
20456 * @extends Roo.bootstrap.Input
20457 * Bootstrap MonthField class
20459 * @cfg {String} language default en
20462 * Create a new MonthField
20463 * @param {Object} config The config object
20466 Roo.bootstrap.MonthField = function(config){
20467 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20472 * Fires when this field show.
20473 * @param {Roo.bootstrap.MonthField} this
20474 * @param {Mixed} date The date value
20479 * Fires when this field hide.
20480 * @param {Roo.bootstrap.MonthField} this
20481 * @param {Mixed} date The date value
20486 * Fires when select a date.
20487 * @param {Roo.bootstrap.MonthField} this
20488 * @param {String} oldvalue The old value
20489 * @param {String} newvalue The new value
20495 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20497 onRender: function(ct, position)
20500 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20502 this.language = this.language || 'en';
20503 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20504 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20506 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20507 this.isInline = false;
20508 this.isInput = true;
20509 this.component = this.el.select('.add-on', true).first() || false;
20510 this.component = (this.component && this.component.length === 0) ? false : this.component;
20511 this.hasInput = this.component && this.inputEL().length;
20513 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20515 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20517 this.picker().on('mousedown', this.onMousedown, this);
20518 this.picker().on('click', this.onClick, this);
20520 this.picker().addClass('datepicker-dropdown');
20522 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20523 v.setStyle('width', '189px');
20530 if(this.isInline) {
20536 setValue: function(v, suppressEvent)
20538 var o = this.getValue();
20540 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20544 if(suppressEvent !== true){
20545 this.fireEvent('select', this, o, v);
20550 getValue: function()
20555 onClick: function(e)
20557 e.stopPropagation();
20558 e.preventDefault();
20560 var target = e.getTarget();
20562 if(target.nodeName.toLowerCase() === 'i'){
20563 target = Roo.get(target).dom.parentNode;
20566 var nodeName = target.nodeName;
20567 var className = target.className;
20568 var html = target.innerHTML;
20570 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20574 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20576 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20582 picker : function()
20584 return this.pickerEl;
20587 fillMonths: function()
20590 var months = this.picker().select('>.datepicker-months td', true).first();
20592 months.dom.innerHTML = '';
20598 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20601 months.createChild(month);
20610 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20611 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20614 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20615 e.removeClass('active');
20617 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20618 e.addClass('active');
20625 if(this.isInline) {
20629 this.picker().removeClass(['bottom', 'top']);
20631 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20633 * place to the top of element!
20637 this.picker().addClass('top');
20638 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20643 this.picker().addClass('bottom');
20645 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20648 onFocus : function()
20650 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20654 onBlur : function()
20656 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20658 var d = this.inputEl().getValue();
20667 this.picker().show();
20668 this.picker().select('>.datepicker-months', true).first().show();
20672 this.fireEvent('show', this, this.date);
20677 if(this.isInline) {
20680 this.picker().hide();
20681 this.fireEvent('hide', this, this.date);
20685 onMousedown: function(e)
20687 e.stopPropagation();
20688 e.preventDefault();
20693 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20697 fireKey: function(e)
20699 if (!this.picker().isVisible()){
20700 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20711 e.preventDefault();
20715 dir = e.keyCode == 37 ? -1 : 1;
20717 this.vIndex = this.vIndex + dir;
20719 if(this.vIndex < 0){
20723 if(this.vIndex > 11){
20727 if(isNaN(this.vIndex)){
20731 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20737 dir = e.keyCode == 38 ? -1 : 1;
20739 this.vIndex = this.vIndex + dir * 4;
20741 if(this.vIndex < 0){
20745 if(this.vIndex > 11){
20749 if(isNaN(this.vIndex)){
20753 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20758 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20759 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20763 e.preventDefault();
20766 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20767 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20783 this.picker().remove();
20788 Roo.apply(Roo.bootstrap.MonthField, {
20807 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20808 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20813 Roo.apply(Roo.bootstrap.MonthField, {
20817 cls: 'datepicker dropdown-menu roo-dynamic',
20821 cls: 'datepicker-months',
20825 cls: 'table-condensed',
20827 Roo.bootstrap.DateField.content
20847 * @class Roo.bootstrap.CheckBox
20848 * @extends Roo.bootstrap.Input
20849 * Bootstrap CheckBox class
20851 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20852 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20853 * @cfg {String} boxLabel The text that appears beside the checkbox
20854 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20855 * @cfg {Boolean} checked initnal the element
20856 * @cfg {Boolean} inline inline the element (default false)
20857 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20858 * @cfg {String} tooltip label tooltip
20861 * Create a new CheckBox
20862 * @param {Object} config The config object
20865 Roo.bootstrap.CheckBox = function(config){
20866 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20871 * Fires when the element is checked or unchecked.
20872 * @param {Roo.bootstrap.CheckBox} this This input
20873 * @param {Boolean} checked The new checked value
20878 * Fires when the element is click.
20879 * @param {Roo.bootstrap.CheckBox} this This input
20886 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20888 inputType: 'checkbox',
20897 getAutoCreate : function()
20899 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20905 cfg.cls = 'form-group ' + this.inputType; //input-group
20908 cfg.cls += ' ' + this.inputType + '-inline';
20914 type : this.inputType,
20915 value : this.inputValue,
20916 cls : 'roo-' + this.inputType, //'form-box',
20917 placeholder : this.placeholder || ''
20921 if(this.inputType != 'radio'){
20925 cls : 'roo-hidden-value',
20926 value : this.checked ? this.inputValue : this.valueOff
20931 if (this.weight) { // Validity check?
20932 cfg.cls += " " + this.inputType + "-" + this.weight;
20935 if (this.disabled) {
20936 input.disabled=true;
20940 input.checked = this.checked;
20945 input.name = this.name;
20947 if(this.inputType != 'radio'){
20948 hidden.name = this.name;
20949 input.name = '_hidden_' + this.name;
20954 input.cls += ' input-' + this.size;
20959 ['xs','sm','md','lg'].map(function(size){
20960 if (settings[size]) {
20961 cfg.cls += ' col-' + size + '-' + settings[size];
20965 var inputblock = input;
20967 if (this.before || this.after) {
20970 cls : 'input-group',
20975 inputblock.cn.push({
20977 cls : 'input-group-addon',
20982 inputblock.cn.push(input);
20984 if(this.inputType != 'radio'){
20985 inputblock.cn.push(hidden);
20989 inputblock.cn.push({
20991 cls : 'input-group-addon',
20998 if (align ==='left' && this.fieldLabel.length) {
20999 // Roo.log("left and has label");
21004 cls : 'control-label',
21005 html : this.fieldLabel
21015 if(this.labelWidth > 12){
21016 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21019 if(this.labelWidth < 13 && this.labelmd == 0){
21020 this.labelmd = this.labelWidth;
21023 if(this.labellg > 0){
21024 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21025 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21028 if(this.labelmd > 0){
21029 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21030 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21033 if(this.labelsm > 0){
21034 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21035 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21038 if(this.labelxs > 0){
21039 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21040 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21043 } else if ( this.fieldLabel.length) {
21044 // Roo.log(" label");
21048 tag: this.boxLabel ? 'span' : 'label',
21050 cls: 'control-label box-input-label',
21051 //cls : 'input-group-addon',
21052 html : this.fieldLabel
21061 // Roo.log(" no label && no align");
21062 cfg.cn = [ inputblock ] ;
21068 var boxLabelCfg = {
21070 //'for': id, // box label is handled by onclick - so no for...
21072 html: this.boxLabel
21076 boxLabelCfg.tooltip = this.tooltip;
21079 cfg.cn.push(boxLabelCfg);
21082 if(this.inputType != 'radio'){
21083 cfg.cn.push(hidden);
21091 * return the real input element.
21093 inputEl: function ()
21095 return this.el.select('input.roo-' + this.inputType,true).first();
21097 hiddenEl: function ()
21099 return this.el.select('input.roo-hidden-value',true).first();
21102 labelEl: function()
21104 return this.el.select('label.control-label',true).first();
21106 /* depricated... */
21110 return this.labelEl();
21113 boxLabelEl: function()
21115 return this.el.select('label.box-label',true).first();
21118 initEvents : function()
21120 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21122 this.inputEl().on('click', this.onClick, this);
21124 if (this.boxLabel) {
21125 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21128 this.startValue = this.getValue();
21131 Roo.bootstrap.CheckBox.register(this);
21135 onClick : function(e)
21137 if(this.fireEvent('click', this, e) !== false){
21138 this.setChecked(!this.checked);
21143 setChecked : function(state,suppressEvent)
21145 this.startValue = this.getValue();
21147 if(this.inputType == 'radio'){
21149 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150 e.dom.checked = false;
21153 this.inputEl().dom.checked = true;
21155 this.inputEl().dom.value = this.inputValue;
21157 if(suppressEvent !== true){
21158 this.fireEvent('check', this, true);
21166 this.checked = state;
21168 this.inputEl().dom.checked = state;
21171 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21173 if(suppressEvent !== true){
21174 this.fireEvent('check', this, state);
21180 getValue : function()
21182 if(this.inputType == 'radio'){
21183 return this.getGroupValue();
21186 return this.hiddenEl().dom.value;
21190 getGroupValue : function()
21192 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21196 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21199 setValue : function(v,suppressEvent)
21201 if(this.inputType == 'radio'){
21202 this.setGroupValue(v, suppressEvent);
21206 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21211 setGroupValue : function(v, suppressEvent)
21213 this.startValue = this.getValue();
21215 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21216 e.dom.checked = false;
21218 if(e.dom.value == v){
21219 e.dom.checked = true;
21223 if(suppressEvent !== true){
21224 this.fireEvent('check', this, true);
21232 validate : function()
21234 if(this.getVisibilityEl().hasClass('hidden')){
21240 (this.inputType == 'radio' && this.validateRadio()) ||
21241 (this.inputType == 'checkbox' && this.validateCheckbox())
21247 this.markInvalid();
21251 validateRadio : function()
21253 if(this.getVisibilityEl().hasClass('hidden')){
21257 if(this.allowBlank){
21263 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21264 if(!e.dom.checked){
21276 validateCheckbox : function()
21279 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21280 //return (this.getValue() == this.inputValue) ? true : false;
21283 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21291 for(var i in group){
21292 if(group[i].el.isVisible(true)){
21300 for(var i in group){
21305 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21312 * Mark this field as valid
21314 markValid : function()
21318 this.fireEvent('valid', this);
21320 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21323 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21330 if(this.inputType == 'radio'){
21331 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21332 var fg = e.findParent('.form-group', false, true);
21333 if (Roo.bootstrap.version == 3) {
21334 fg.removeClass([_this.invalidClass, _this.validClass]);
21335 fg.addClass(_this.validClass);
21337 fg.removeClass(['is-valid', 'is-invalid']);
21338 fg.addClass('is-valid');
21346 var fg = this.el.findParent('.form-group', false, true);
21347 if (Roo.bootstrap.version == 3) {
21348 fg.removeClass([this.invalidClass, this.validClass]);
21349 fg.addClass(this.validClass);
21351 fg.removeClass(['is-valid', 'is-invalid']);
21352 fg.addClass('is-valid');
21357 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21363 for(var i in group){
21364 var fg = group[i].el.findParent('.form-group', false, true);
21365 if (Roo.bootstrap.version == 3) {
21366 fg.removeClass([this.invalidClass, this.validClass]);
21367 fg.addClass(this.validClass);
21369 fg.removeClass(['is-valid', 'is-invalid']);
21370 fg.addClass('is-valid');
21376 * Mark this field as invalid
21377 * @param {String} msg The validation message
21379 markInvalid : function(msg)
21381 if(this.allowBlank){
21387 this.fireEvent('invalid', this, msg);
21389 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21392 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21396 label.markInvalid();
21399 if(this.inputType == 'radio'){
21401 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21402 var fg = e.findParent('.form-group', false, true);
21403 if (Roo.bootstrap.version == 3) {
21404 fg.removeClass([_this.invalidClass, _this.validClass]);
21405 fg.addClass(_this.invalidClass);
21407 fg.removeClass(['is-invalid', 'is-valid']);
21408 fg.addClass('is-invalid');
21416 var fg = this.el.findParent('.form-group', false, true);
21417 if (Roo.bootstrap.version == 3) {
21418 fg.removeClass([_this.invalidClass, _this.validClass]);
21419 fg.addClass(_this.invalidClass);
21421 fg.removeClass(['is-invalid', 'is-valid']);
21422 fg.addClass('is-invalid');
21427 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21433 for(var i in group){
21434 var fg = group[i].el.findParent('.form-group', false, true);
21435 if (Roo.bootstrap.version == 3) {
21436 fg.removeClass([_this.invalidClass, _this.validClass]);
21437 fg.addClass(_this.invalidClass);
21439 fg.removeClass(['is-invalid', 'is-valid']);
21440 fg.addClass('is-invalid');
21446 clearInvalid : function()
21448 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21450 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21452 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21454 if (label && label.iconEl) {
21455 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21456 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21460 disable : function()
21462 if(this.inputType != 'radio'){
21463 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21470 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21471 _this.getActionEl().addClass(this.disabledClass);
21472 e.dom.disabled = true;
21476 this.disabled = true;
21477 this.fireEvent("disable", this);
21481 enable : function()
21483 if(this.inputType != 'radio'){
21484 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21491 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21492 _this.getActionEl().removeClass(this.disabledClass);
21493 e.dom.disabled = false;
21497 this.disabled = false;
21498 this.fireEvent("enable", this);
21502 setBoxLabel : function(v)
21507 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21513 Roo.apply(Roo.bootstrap.CheckBox, {
21518 * register a CheckBox Group
21519 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21521 register : function(checkbox)
21523 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21524 this.groups[checkbox.groupId] = {};
21527 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21531 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21535 * fetch a CheckBox Group based on the group ID
21536 * @param {string} the group ID
21537 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21539 get: function(groupId) {
21540 if (typeof(this.groups[groupId]) == 'undefined') {
21544 return this.groups[groupId] ;
21557 * @class Roo.bootstrap.Radio
21558 * @extends Roo.bootstrap.Component
21559 * Bootstrap Radio class
21560 * @cfg {String} boxLabel - the label associated
21561 * @cfg {String} value - the value of radio
21564 * Create a new Radio
21565 * @param {Object} config The config object
21567 Roo.bootstrap.Radio = function(config){
21568 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21572 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21578 getAutoCreate : function()
21582 cls : 'form-group radio',
21587 html : this.boxLabel
21595 initEvents : function()
21597 this.parent().register(this);
21599 this.el.on('click', this.onClick, this);
21603 onClick : function(e)
21605 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21606 this.setChecked(true);
21610 setChecked : function(state, suppressEvent)
21612 this.parent().setValue(this.value, suppressEvent);
21616 setBoxLabel : function(v)
21621 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21636 * @class Roo.bootstrap.SecurePass
21637 * @extends Roo.bootstrap.Input
21638 * Bootstrap SecurePass class
21642 * Create a new SecurePass
21643 * @param {Object} config The config object
21646 Roo.bootstrap.SecurePass = function (config) {
21647 // these go here, so the translation tool can replace them..
21649 PwdEmpty: "Please type a password, and then retype it to confirm.",
21650 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21651 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21652 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21653 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21654 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21655 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21656 TooWeak: "Your password is Too Weak."
21658 this.meterLabel = "Password strength:";
21659 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21660 this.meterClass = [
21661 "roo-password-meter-tooweak",
21662 "roo-password-meter-weak",
21663 "roo-password-meter-medium",
21664 "roo-password-meter-strong",
21665 "roo-password-meter-grey"
21670 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21673 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21675 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21677 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21678 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21679 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21680 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21681 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21682 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21683 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21693 * @cfg {String/Object} Label for the strength meter (defaults to
21694 * 'Password strength:')
21699 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21700 * ['Weak', 'Medium', 'Strong'])
21703 pwdStrengths: false,
21716 initEvents: function ()
21718 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21720 if (this.el.is('input[type=password]') && Roo.isSafari) {
21721 this.el.on('keydown', this.SafariOnKeyDown, this);
21724 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21727 onRender: function (ct, position)
21729 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21730 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21731 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21733 this.trigger.createChild({
21738 cls: 'roo-password-meter-grey col-xs-12',
21741 //width: this.meterWidth + 'px'
21745 cls: 'roo-password-meter-text'
21751 if (this.hideTrigger) {
21752 this.trigger.setDisplayed(false);
21754 this.setSize(this.width || '', this.height || '');
21757 onDestroy: function ()
21759 if (this.trigger) {
21760 this.trigger.removeAllListeners();
21761 this.trigger.remove();
21764 this.wrap.remove();
21766 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21769 checkStrength: function ()
21771 var pwd = this.inputEl().getValue();
21772 if (pwd == this._lastPwd) {
21777 if (this.ClientSideStrongPassword(pwd)) {
21779 } else if (this.ClientSideMediumPassword(pwd)) {
21781 } else if (this.ClientSideWeakPassword(pwd)) {
21787 Roo.log('strength1: ' + strength);
21789 //var pm = this.trigger.child('div/div/div').dom;
21790 var pm = this.trigger.child('div/div');
21791 pm.removeClass(this.meterClass);
21792 pm.addClass(this.meterClass[strength]);
21795 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21797 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21799 this._lastPwd = pwd;
21803 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21805 this._lastPwd = '';
21807 var pm = this.trigger.child('div/div');
21808 pm.removeClass(this.meterClass);
21809 pm.addClass('roo-password-meter-grey');
21812 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21815 this.inputEl().dom.type='password';
21818 validateValue: function (value)
21821 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21824 if (value.length == 0) {
21825 if (this.allowBlank) {
21826 this.clearInvalid();
21830 this.markInvalid(this.errors.PwdEmpty);
21831 this.errorMsg = this.errors.PwdEmpty;
21839 if ('[\x21-\x7e]*'.match(value)) {
21840 this.markInvalid(this.errors.PwdBadChar);
21841 this.errorMsg = this.errors.PwdBadChar;
21844 if (value.length < 6) {
21845 this.markInvalid(this.errors.PwdShort);
21846 this.errorMsg = this.errors.PwdShort;
21849 if (value.length > 16) {
21850 this.markInvalid(this.errors.PwdLong);
21851 this.errorMsg = this.errors.PwdLong;
21855 if (this.ClientSideStrongPassword(value)) {
21857 } else if (this.ClientSideMediumPassword(value)) {
21859 } else if (this.ClientSideWeakPassword(value)) {
21866 if (strength < 2) {
21867 //this.markInvalid(this.errors.TooWeak);
21868 this.errorMsg = this.errors.TooWeak;
21873 console.log('strength2: ' + strength);
21875 //var pm = this.trigger.child('div/div/div').dom;
21877 var pm = this.trigger.child('div/div');
21878 pm.removeClass(this.meterClass);
21879 pm.addClass(this.meterClass[strength]);
21881 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21883 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21885 this.errorMsg = '';
21889 CharacterSetChecks: function (type)
21892 this.fResult = false;
21895 isctype: function (character, type)
21898 case this.kCapitalLetter:
21899 if (character >= 'A' && character <= 'Z') {
21904 case this.kSmallLetter:
21905 if (character >= 'a' && character <= 'z') {
21911 if (character >= '0' && character <= '9') {
21916 case this.kPunctuation:
21917 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21928 IsLongEnough: function (pwd, size)
21930 return !(pwd == null || isNaN(size) || pwd.length < size);
21933 SpansEnoughCharacterSets: function (word, nb)
21935 if (!this.IsLongEnough(word, nb))
21940 var characterSetChecks = new Array(
21941 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21942 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21945 for (var index = 0; index < word.length; ++index) {
21946 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21947 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21948 characterSetChecks[nCharSet].fResult = true;
21955 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21956 if (characterSetChecks[nCharSet].fResult) {
21961 if (nCharSets < nb) {
21967 ClientSideStrongPassword: function (pwd)
21969 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21972 ClientSideMediumPassword: function (pwd)
21974 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21977 ClientSideWeakPassword: function (pwd)
21979 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21982 })//<script type="text/javascript">
21985 * Based Ext JS Library 1.1.1
21986 * Copyright(c) 2006-2007, Ext JS, LLC.
21992 * @class Roo.HtmlEditorCore
21993 * @extends Roo.Component
21994 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21996 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21999 Roo.HtmlEditorCore = function(config){
22002 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22007 * @event initialize
22008 * Fires when the editor is fully initialized (including the iframe)
22009 * @param {Roo.HtmlEditorCore} this
22014 * Fires when the editor is first receives the focus. Any insertion must wait
22015 * until after this event.
22016 * @param {Roo.HtmlEditorCore} this
22020 * @event beforesync
22021 * Fires before the textarea is updated with content from the editor iframe. Return false
22022 * to cancel the sync.
22023 * @param {Roo.HtmlEditorCore} this
22024 * @param {String} html
22028 * @event beforepush
22029 * Fires before the iframe editor is updated with content from the textarea. Return false
22030 * to cancel the push.
22031 * @param {Roo.HtmlEditorCore} this
22032 * @param {String} html
22037 * Fires when the textarea is updated with content from the editor iframe.
22038 * @param {Roo.HtmlEditorCore} this
22039 * @param {String} html
22044 * Fires when the iframe editor is updated with content from the textarea.
22045 * @param {Roo.HtmlEditorCore} this
22046 * @param {String} html
22051 * @event editorevent
22052 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22053 * @param {Roo.HtmlEditorCore} this
22059 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22061 // defaults : white / black...
22062 this.applyBlacklists();
22069 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22073 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22079 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22084 * @cfg {Number} height (in pixels)
22088 * @cfg {Number} width (in pixels)
22093 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22096 stylesheets: false,
22101 // private properties
22102 validationEvent : false,
22104 initialized : false,
22106 sourceEditMode : false,
22107 onFocus : Roo.emptyFn,
22109 hideMode:'offsets',
22113 // blacklist + whitelisted elements..
22120 * Protected method that will not generally be called directly. It
22121 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22122 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22124 getDocMarkup : function(){
22128 // inherit styels from page...??
22129 if (this.stylesheets === false) {
22131 Roo.get(document.head).select('style').each(function(node) {
22132 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22135 Roo.get(document.head).select('link').each(function(node) {
22136 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22139 } else if (!this.stylesheets.length) {
22141 st = '<style type="text/css">' +
22142 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22145 st = '<style type="text/css">' +
22150 st += '<style type="text/css">' +
22151 'IMG { cursor: pointer } ' +
22154 var cls = 'roo-htmleditor-body';
22156 if(this.bodyCls.length){
22157 cls += ' ' + this.bodyCls;
22160 return '<html><head>' + st +
22161 //<style type="text/css">' +
22162 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22164 ' </head><body class="' + cls + '"></body></html>';
22168 onRender : function(ct, position)
22171 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22172 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22175 this.el.dom.style.border = '0 none';
22176 this.el.dom.setAttribute('tabIndex', -1);
22177 this.el.addClass('x-hidden hide');
22181 if(Roo.isIE){ // fix IE 1px bogus margin
22182 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22186 this.frameId = Roo.id();
22190 var iframe = this.owner.wrap.createChild({
22192 cls: 'form-control', // bootstrap..
22194 name: this.frameId,
22195 frameBorder : 'no',
22196 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22201 this.iframe = iframe.dom;
22203 this.assignDocWin();
22205 this.doc.designMode = 'on';
22208 this.doc.write(this.getDocMarkup());
22212 var task = { // must defer to wait for browser to be ready
22214 //console.log("run task?" + this.doc.readyState);
22215 this.assignDocWin();
22216 if(this.doc.body || this.doc.readyState == 'complete'){
22218 this.doc.designMode="on";
22222 Roo.TaskMgr.stop(task);
22223 this.initEditor.defer(10, this);
22230 Roo.TaskMgr.start(task);
22235 onResize : function(w, h)
22237 Roo.log('resize: ' +w + ',' + h );
22238 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22242 if(typeof w == 'number'){
22244 this.iframe.style.width = w + 'px';
22246 if(typeof h == 'number'){
22248 this.iframe.style.height = h + 'px';
22250 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22257 * Toggles the editor between standard and source edit mode.
22258 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22260 toggleSourceEdit : function(sourceEditMode){
22262 this.sourceEditMode = sourceEditMode === true;
22264 if(this.sourceEditMode){
22266 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22269 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22270 //this.iframe.className = '';
22273 //this.setSize(this.owner.wrap.getSize());
22274 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22281 * Protected method that will not generally be called directly. If you need/want
22282 * custom HTML cleanup, this is the method you should override.
22283 * @param {String} html The HTML to be cleaned
22284 * return {String} The cleaned HTML
22286 cleanHtml : function(html){
22287 html = String(html);
22288 if(html.length > 5){
22289 if(Roo.isSafari){ // strip safari nonsense
22290 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22293 if(html == ' '){
22300 * HTML Editor -> Textarea
22301 * Protected method that will not generally be called directly. Syncs the contents
22302 * of the editor iframe with the textarea.
22304 syncValue : function(){
22305 if(this.initialized){
22306 var bd = (this.doc.body || this.doc.documentElement);
22307 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22308 var html = bd.innerHTML;
22310 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22311 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22313 html = '<div style="'+m[0]+'">' + html + '</div>';
22316 html = this.cleanHtml(html);
22317 // fix up the special chars.. normaly like back quotes in word...
22318 // however we do not want to do this with chinese..
22319 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22320 var cc = b.charCodeAt();
22322 (cc >= 0x4E00 && cc < 0xA000 ) ||
22323 (cc >= 0x3400 && cc < 0x4E00 ) ||
22324 (cc >= 0xf900 && cc < 0xfb00 )
22330 if(this.owner.fireEvent('beforesync', this, html) !== false){
22331 this.el.dom.value = html;
22332 this.owner.fireEvent('sync', this, html);
22338 * Protected method that will not generally be called directly. Pushes the value of the textarea
22339 * into the iframe editor.
22341 pushValue : function(){
22342 if(this.initialized){
22343 var v = this.el.dom.value.trim();
22345 // if(v.length < 1){
22349 if(this.owner.fireEvent('beforepush', this, v) !== false){
22350 var d = (this.doc.body || this.doc.documentElement);
22352 this.cleanUpPaste();
22353 this.el.dom.value = d.innerHTML;
22354 this.owner.fireEvent('push', this, v);
22360 deferFocus : function(){
22361 this.focus.defer(10, this);
22365 focus : function(){
22366 if(this.win && !this.sourceEditMode){
22373 assignDocWin: function()
22375 var iframe = this.iframe;
22378 this.doc = iframe.contentWindow.document;
22379 this.win = iframe.contentWindow;
22381 // if (!Roo.get(this.frameId)) {
22384 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22385 // this.win = Roo.get(this.frameId).dom.contentWindow;
22387 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22391 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22392 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22397 initEditor : function(){
22398 //console.log("INIT EDITOR");
22399 this.assignDocWin();
22403 this.doc.designMode="on";
22405 this.doc.write(this.getDocMarkup());
22408 var dbody = (this.doc.body || this.doc.documentElement);
22409 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22410 // this copies styles from the containing element into thsi one..
22411 // not sure why we need all of this..
22412 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22414 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22415 //ss['background-attachment'] = 'fixed'; // w3c
22416 dbody.bgProperties = 'fixed'; // ie
22417 //Roo.DomHelper.applyStyles(dbody, ss);
22418 Roo.EventManager.on(this.doc, {
22419 //'mousedown': this.onEditorEvent,
22420 'mouseup': this.onEditorEvent,
22421 'dblclick': this.onEditorEvent,
22422 'click': this.onEditorEvent,
22423 'keyup': this.onEditorEvent,
22428 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22430 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22431 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22433 this.initialized = true;
22435 this.owner.fireEvent('initialize', this);
22440 onDestroy : function(){
22446 //for (var i =0; i < this.toolbars.length;i++) {
22447 // // fixme - ask toolbars for heights?
22448 // this.toolbars[i].onDestroy();
22451 //this.wrap.dom.innerHTML = '';
22452 //this.wrap.remove();
22457 onFirstFocus : function(){
22459 this.assignDocWin();
22462 this.activated = true;
22465 if(Roo.isGecko){ // prevent silly gecko errors
22467 var s = this.win.getSelection();
22468 if(!s.focusNode || s.focusNode.nodeType != 3){
22469 var r = s.getRangeAt(0);
22470 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22475 this.execCmd('useCSS', true);
22476 this.execCmd('styleWithCSS', false);
22479 this.owner.fireEvent('activate', this);
22483 adjustFont: function(btn){
22484 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22485 //if(Roo.isSafari){ // safari
22488 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22489 if(Roo.isSafari){ // safari
22490 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22491 v = (v < 10) ? 10 : v;
22492 v = (v > 48) ? 48 : v;
22493 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22498 v = Math.max(1, v+adjust);
22500 this.execCmd('FontSize', v );
22503 onEditorEvent : function(e)
22505 this.owner.fireEvent('editorevent', this, e);
22506 // this.updateToolbar();
22507 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22510 insertTag : function(tg)
22512 // could be a bit smarter... -> wrap the current selected tRoo..
22513 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22515 range = this.createRange(this.getSelection());
22516 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22517 wrappingNode.appendChild(range.extractContents());
22518 range.insertNode(wrappingNode);
22525 this.execCmd("formatblock", tg);
22529 insertText : function(txt)
22533 var range = this.createRange();
22534 range.deleteContents();
22535 //alert(Sender.getAttribute('label'));
22537 range.insertNode(this.doc.createTextNode(txt));
22543 * Executes a Midas editor command on the editor document and performs necessary focus and
22544 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22545 * @param {String} cmd The Midas command
22546 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22548 relayCmd : function(cmd, value){
22550 this.execCmd(cmd, value);
22551 this.owner.fireEvent('editorevent', this);
22552 //this.updateToolbar();
22553 this.owner.deferFocus();
22557 * Executes a Midas editor command directly on the editor document.
22558 * For visual commands, you should use {@link #relayCmd} instead.
22559 * <b>This should only be called after the editor is initialized.</b>
22560 * @param {String} cmd The Midas command
22561 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22563 execCmd : function(cmd, value){
22564 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22571 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22573 * @param {String} text | dom node..
22575 insertAtCursor : function(text)
22578 if(!this.activated){
22584 var r = this.doc.selection.createRange();
22595 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22599 // from jquery ui (MIT licenced)
22601 var win = this.win;
22603 if (win.getSelection && win.getSelection().getRangeAt) {
22604 range = win.getSelection().getRangeAt(0);
22605 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22606 range.insertNode(node);
22607 } else if (win.document.selection && win.document.selection.createRange) {
22608 // no firefox support
22609 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22610 win.document.selection.createRange().pasteHTML(txt);
22612 // no firefox support
22613 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22614 this.execCmd('InsertHTML', txt);
22623 mozKeyPress : function(e){
22625 var c = e.getCharCode(), cmd;
22628 c = String.fromCharCode(c).toLowerCase();
22642 this.cleanUpPaste.defer(100, this);
22650 e.preventDefault();
22658 fixKeys : function(){ // load time branching for fastest keydown performance
22660 return function(e){
22661 var k = e.getKey(), r;
22664 r = this.doc.selection.createRange();
22667 r.pasteHTML('    ');
22674 r = this.doc.selection.createRange();
22676 var target = r.parentElement();
22677 if(!target || target.tagName.toLowerCase() != 'li'){
22679 r.pasteHTML('<br />');
22685 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22686 this.cleanUpPaste.defer(100, this);
22692 }else if(Roo.isOpera){
22693 return function(e){
22694 var k = e.getKey();
22698 this.execCmd('InsertHTML','    ');
22701 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22702 this.cleanUpPaste.defer(100, this);
22707 }else if(Roo.isSafari){
22708 return function(e){
22709 var k = e.getKey();
22713 this.execCmd('InsertText','\t');
22717 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22718 this.cleanUpPaste.defer(100, this);
22726 getAllAncestors: function()
22728 var p = this.getSelectedNode();
22731 a.push(p); // push blank onto stack..
22732 p = this.getParentElement();
22736 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22740 a.push(this.doc.body);
22744 lastSelNode : false,
22747 getSelection : function()
22749 this.assignDocWin();
22750 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22753 getSelectedNode: function()
22755 // this may only work on Gecko!!!
22757 // should we cache this!!!!
22762 var range = this.createRange(this.getSelection()).cloneRange();
22765 var parent = range.parentElement();
22767 var testRange = range.duplicate();
22768 testRange.moveToElementText(parent);
22769 if (testRange.inRange(range)) {
22772 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22775 parent = parent.parentElement;
22780 // is ancestor a text element.
22781 var ac = range.commonAncestorContainer;
22782 if (ac.nodeType == 3) {
22783 ac = ac.parentNode;
22786 var ar = ac.childNodes;
22789 var other_nodes = [];
22790 var has_other_nodes = false;
22791 for (var i=0;i<ar.length;i++) {
22792 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22795 // fullly contained node.
22797 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22802 // probably selected..
22803 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22804 other_nodes.push(ar[i]);
22808 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22813 has_other_nodes = true;
22815 if (!nodes.length && other_nodes.length) {
22816 nodes= other_nodes;
22818 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22824 createRange: function(sel)
22826 // this has strange effects when using with
22827 // top toolbar - not sure if it's a great idea.
22828 //this.editor.contentWindow.focus();
22829 if (typeof sel != "undefined") {
22831 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22833 return this.doc.createRange();
22836 return this.doc.createRange();
22839 getParentElement: function()
22842 this.assignDocWin();
22843 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22845 var range = this.createRange(sel);
22848 var p = range.commonAncestorContainer;
22849 while (p.nodeType == 3) { // text node
22860 * Range intersection.. the hard stuff...
22864 * [ -- selected range --- ]
22868 * if end is before start or hits it. fail.
22869 * if start is after end or hits it fail.
22871 * if either hits (but other is outside. - then it's not
22877 // @see http://www.thismuchiknow.co.uk/?p=64.
22878 rangeIntersectsNode : function(range, node)
22880 var nodeRange = node.ownerDocument.createRange();
22882 nodeRange.selectNode(node);
22884 nodeRange.selectNodeContents(node);
22887 var rangeStartRange = range.cloneRange();
22888 rangeStartRange.collapse(true);
22890 var rangeEndRange = range.cloneRange();
22891 rangeEndRange.collapse(false);
22893 var nodeStartRange = nodeRange.cloneRange();
22894 nodeStartRange.collapse(true);
22896 var nodeEndRange = nodeRange.cloneRange();
22897 nodeEndRange.collapse(false);
22899 return rangeStartRange.compareBoundaryPoints(
22900 Range.START_TO_START, nodeEndRange) == -1 &&
22901 rangeEndRange.compareBoundaryPoints(
22902 Range.START_TO_START, nodeStartRange) == 1;
22906 rangeCompareNode : function(range, node)
22908 var nodeRange = node.ownerDocument.createRange();
22910 nodeRange.selectNode(node);
22912 nodeRange.selectNodeContents(node);
22916 range.collapse(true);
22918 nodeRange.collapse(true);
22920 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22921 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22923 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22925 var nodeIsBefore = ss == 1;
22926 var nodeIsAfter = ee == -1;
22928 if (nodeIsBefore && nodeIsAfter) {
22931 if (!nodeIsBefore && nodeIsAfter) {
22932 return 1; //right trailed.
22935 if (nodeIsBefore && !nodeIsAfter) {
22936 return 2; // left trailed.
22942 // private? - in a new class?
22943 cleanUpPaste : function()
22945 // cleans up the whole document..
22946 Roo.log('cleanuppaste');
22948 this.cleanUpChildren(this.doc.body);
22949 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22950 if (clean != this.doc.body.innerHTML) {
22951 this.doc.body.innerHTML = clean;
22956 cleanWordChars : function(input) {// change the chars to hex code
22957 var he = Roo.HtmlEditorCore;
22959 var output = input;
22960 Roo.each(he.swapCodes, function(sw) {
22961 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22963 output = output.replace(swapper, sw[1]);
22970 cleanUpChildren : function (n)
22972 if (!n.childNodes.length) {
22975 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22976 this.cleanUpChild(n.childNodes[i]);
22983 cleanUpChild : function (node)
22986 //console.log(node);
22987 if (node.nodeName == "#text") {
22988 // clean up silly Windows -- stuff?
22991 if (node.nodeName == "#comment") {
22992 node.parentNode.removeChild(node);
22993 // clean up silly Windows -- stuff?
22996 var lcname = node.tagName.toLowerCase();
22997 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22998 // whitelist of tags..
23000 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23002 node.parentNode.removeChild(node);
23007 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23009 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23010 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23012 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23013 // remove_keep_children = true;
23016 if (remove_keep_children) {
23017 this.cleanUpChildren(node);
23018 // inserts everything just before this node...
23019 while (node.childNodes.length) {
23020 var cn = node.childNodes[0];
23021 node.removeChild(cn);
23022 node.parentNode.insertBefore(cn, node);
23024 node.parentNode.removeChild(node);
23028 if (!node.attributes || !node.attributes.length) {
23029 this.cleanUpChildren(node);
23033 function cleanAttr(n,v)
23036 if (v.match(/^\./) || v.match(/^\//)) {
23039 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23042 if (v.match(/^#/)) {
23045 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23046 node.removeAttribute(n);
23050 var cwhite = this.cwhite;
23051 var cblack = this.cblack;
23053 function cleanStyle(n,v)
23055 if (v.match(/expression/)) { //XSS?? should we even bother..
23056 node.removeAttribute(n);
23060 var parts = v.split(/;/);
23063 Roo.each(parts, function(p) {
23064 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23068 var l = p.split(':').shift().replace(/\s+/g,'');
23069 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23071 if ( cwhite.length && cblack.indexOf(l) > -1) {
23072 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23073 //node.removeAttribute(n);
23077 // only allow 'c whitelisted system attributes'
23078 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23079 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23080 //node.removeAttribute(n);
23090 if (clean.length) {
23091 node.setAttribute(n, clean.join(';'));
23093 node.removeAttribute(n);
23099 for (var i = node.attributes.length-1; i > -1 ; i--) {
23100 var a = node.attributes[i];
23103 if (a.name.toLowerCase().substr(0,2)=='on') {
23104 node.removeAttribute(a.name);
23107 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23108 node.removeAttribute(a.name);
23111 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23112 cleanAttr(a.name,a.value); // fixme..
23115 if (a.name == 'style') {
23116 cleanStyle(a.name,a.value);
23119 /// clean up MS crap..
23120 // tecnically this should be a list of valid class'es..
23123 if (a.name == 'class') {
23124 if (a.value.match(/^Mso/)) {
23125 node.className = '';
23128 if (a.value.match(/^body$/)) {
23129 node.className = '';
23140 this.cleanUpChildren(node);
23146 * Clean up MS wordisms...
23148 cleanWord : function(node)
23153 this.cleanWord(this.doc.body);
23156 if (node.nodeName == "#text") {
23157 // clean up silly Windows -- stuff?
23160 if (node.nodeName == "#comment") {
23161 node.parentNode.removeChild(node);
23162 // clean up silly Windows -- stuff?
23166 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23167 node.parentNode.removeChild(node);
23171 // remove - but keep children..
23172 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23173 while (node.childNodes.length) {
23174 var cn = node.childNodes[0];
23175 node.removeChild(cn);
23176 node.parentNode.insertBefore(cn, node);
23178 node.parentNode.removeChild(node);
23179 this.iterateChildren(node, this.cleanWord);
23183 if (node.className.length) {
23185 var cn = node.className.split(/\W+/);
23187 Roo.each(cn, function(cls) {
23188 if (cls.match(/Mso[a-zA-Z]+/)) {
23193 node.className = cna.length ? cna.join(' ') : '';
23195 node.removeAttribute("class");
23199 if (node.hasAttribute("lang")) {
23200 node.removeAttribute("lang");
23203 if (node.hasAttribute("style")) {
23205 var styles = node.getAttribute("style").split(";");
23207 Roo.each(styles, function(s) {
23208 if (!s.match(/:/)) {
23211 var kv = s.split(":");
23212 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23215 // what ever is left... we allow.
23218 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23219 if (!nstyle.length) {
23220 node.removeAttribute('style');
23223 this.iterateChildren(node, this.cleanWord);
23229 * iterateChildren of a Node, calling fn each time, using this as the scole..
23230 * @param {DomNode} node node to iterate children of.
23231 * @param {Function} fn method of this class to call on each item.
23233 iterateChildren : function(node, fn)
23235 if (!node.childNodes.length) {
23238 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23239 fn.call(this, node.childNodes[i])
23245 * cleanTableWidths.
23247 * Quite often pasting from word etc.. results in tables with column and widths.
23248 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23251 cleanTableWidths : function(node)
23256 this.cleanTableWidths(this.doc.body);
23261 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23264 Roo.log(node.tagName);
23265 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23266 this.iterateChildren(node, this.cleanTableWidths);
23269 if (node.hasAttribute('width')) {
23270 node.removeAttribute('width');
23274 if (node.hasAttribute("style")) {
23277 var styles = node.getAttribute("style").split(";");
23279 Roo.each(styles, function(s) {
23280 if (!s.match(/:/)) {
23283 var kv = s.split(":");
23284 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23287 // what ever is left... we allow.
23290 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23291 if (!nstyle.length) {
23292 node.removeAttribute('style');
23296 this.iterateChildren(node, this.cleanTableWidths);
23304 domToHTML : function(currentElement, depth, nopadtext) {
23306 depth = depth || 0;
23307 nopadtext = nopadtext || false;
23309 if (!currentElement) {
23310 return this.domToHTML(this.doc.body);
23313 //Roo.log(currentElement);
23315 var allText = false;
23316 var nodeName = currentElement.nodeName;
23317 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23319 if (nodeName == '#text') {
23321 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23326 if (nodeName != 'BODY') {
23329 // Prints the node tagName, such as <A>, <IMG>, etc
23332 for(i = 0; i < currentElement.attributes.length;i++) {
23334 var aname = currentElement.attributes.item(i).name;
23335 if (!currentElement.attributes.item(i).value.length) {
23338 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23341 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23350 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23353 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23358 // Traverse the tree
23360 var currentElementChild = currentElement.childNodes.item(i);
23361 var allText = true;
23362 var innerHTML = '';
23364 while (currentElementChild) {
23365 // Formatting code (indent the tree so it looks nice on the screen)
23366 var nopad = nopadtext;
23367 if (lastnode == 'SPAN') {
23371 if (currentElementChild.nodeName == '#text') {
23372 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23373 toadd = nopadtext ? toadd : toadd.trim();
23374 if (!nopad && toadd.length > 80) {
23375 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23377 innerHTML += toadd;
23380 currentElementChild = currentElement.childNodes.item(i);
23386 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23388 // Recursively traverse the tree structure of the child node
23389 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23390 lastnode = currentElementChild.nodeName;
23392 currentElementChild=currentElement.childNodes.item(i);
23398 // The remaining code is mostly for formatting the tree
23399 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23404 ret+= "</"+tagName+">";
23410 applyBlacklists : function()
23412 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23413 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23417 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23418 if (b.indexOf(tag) > -1) {
23421 this.white.push(tag);
23425 Roo.each(w, function(tag) {
23426 if (b.indexOf(tag) > -1) {
23429 if (this.white.indexOf(tag) > -1) {
23432 this.white.push(tag);
23437 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23438 if (w.indexOf(tag) > -1) {
23441 this.black.push(tag);
23445 Roo.each(b, function(tag) {
23446 if (w.indexOf(tag) > -1) {
23449 if (this.black.indexOf(tag) > -1) {
23452 this.black.push(tag);
23457 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23458 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23462 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23463 if (b.indexOf(tag) > -1) {
23466 this.cwhite.push(tag);
23470 Roo.each(w, function(tag) {
23471 if (b.indexOf(tag) > -1) {
23474 if (this.cwhite.indexOf(tag) > -1) {
23477 this.cwhite.push(tag);
23482 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23483 if (w.indexOf(tag) > -1) {
23486 this.cblack.push(tag);
23490 Roo.each(b, function(tag) {
23491 if (w.indexOf(tag) > -1) {
23494 if (this.cblack.indexOf(tag) > -1) {
23497 this.cblack.push(tag);
23502 setStylesheets : function(stylesheets)
23504 if(typeof(stylesheets) == 'string'){
23505 Roo.get(this.iframe.contentDocument.head).createChild({
23507 rel : 'stylesheet',
23516 Roo.each(stylesheets, function(s) {
23521 Roo.get(_this.iframe.contentDocument.head).createChild({
23523 rel : 'stylesheet',
23532 removeStylesheets : function()
23536 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23541 setStyle : function(style)
23543 Roo.get(this.iframe.contentDocument.head).createChild({
23552 // hide stuff that is not compatible
23566 * @event specialkey
23570 * @cfg {String} fieldClass @hide
23573 * @cfg {String} focusClass @hide
23576 * @cfg {String} autoCreate @hide
23579 * @cfg {String} inputType @hide
23582 * @cfg {String} invalidClass @hide
23585 * @cfg {String} invalidText @hide
23588 * @cfg {String} msgFx @hide
23591 * @cfg {String} validateOnBlur @hide
23595 Roo.HtmlEditorCore.white = [
23596 'area', 'br', 'img', 'input', 'hr', 'wbr',
23598 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23599 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23600 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23601 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23602 'table', 'ul', 'xmp',
23604 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23607 'dir', 'menu', 'ol', 'ul', 'dl',
23613 Roo.HtmlEditorCore.black = [
23614 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23616 'base', 'basefont', 'bgsound', 'blink', 'body',
23617 'frame', 'frameset', 'head', 'html', 'ilayer',
23618 'iframe', 'layer', 'link', 'meta', 'object',
23619 'script', 'style' ,'title', 'xml' // clean later..
23621 Roo.HtmlEditorCore.clean = [
23622 'script', 'style', 'title', 'xml'
23624 Roo.HtmlEditorCore.remove = [
23629 Roo.HtmlEditorCore.ablack = [
23633 Roo.HtmlEditorCore.aclean = [
23634 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23638 Roo.HtmlEditorCore.pwhite= [
23639 'http', 'https', 'mailto'
23642 // white listed style attributes.
23643 Roo.HtmlEditorCore.cwhite= [
23644 // 'text-align', /// default is to allow most things..
23650 // black listed style attributes.
23651 Roo.HtmlEditorCore.cblack= [
23652 // 'font-size' -- this can be set by the project
23656 Roo.HtmlEditorCore.swapCodes =[
23675 * @class Roo.bootstrap.HtmlEditor
23676 * @extends Roo.bootstrap.TextArea
23677 * Bootstrap HtmlEditor class
23680 * Create a new HtmlEditor
23681 * @param {Object} config The config object
23684 Roo.bootstrap.HtmlEditor = function(config){
23685 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23686 if (!this.toolbars) {
23687 this.toolbars = [];
23690 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23693 * @event initialize
23694 * Fires when the editor is fully initialized (including the iframe)
23695 * @param {HtmlEditor} this
23700 * Fires when the editor is first receives the focus. Any insertion must wait
23701 * until after this event.
23702 * @param {HtmlEditor} this
23706 * @event beforesync
23707 * Fires before the textarea is updated with content from the editor iframe. Return false
23708 * to cancel the sync.
23709 * @param {HtmlEditor} this
23710 * @param {String} html
23714 * @event beforepush
23715 * Fires before the iframe editor is updated with content from the textarea. Return false
23716 * to cancel the push.
23717 * @param {HtmlEditor} this
23718 * @param {String} html
23723 * Fires when the textarea is updated with content from the editor iframe.
23724 * @param {HtmlEditor} this
23725 * @param {String} html
23730 * Fires when the iframe editor is updated with content from the textarea.
23731 * @param {HtmlEditor} this
23732 * @param {String} html
23736 * @event editmodechange
23737 * Fires when the editor switches edit modes
23738 * @param {HtmlEditor} this
23739 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23741 editmodechange: true,
23743 * @event editorevent
23744 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23745 * @param {HtmlEditor} this
23749 * @event firstfocus
23750 * Fires when on first focus - needed by toolbars..
23751 * @param {HtmlEditor} this
23756 * Auto save the htmlEditor value as a file into Events
23757 * @param {HtmlEditor} this
23761 * @event savedpreview
23762 * preview the saved version of htmlEditor
23763 * @param {HtmlEditor} this
23770 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23774 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23779 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23784 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23789 * @cfg {Number} height (in pixels)
23793 * @cfg {Number} width (in pixels)
23798 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23801 stylesheets: false,
23806 // private properties
23807 validationEvent : false,
23809 initialized : false,
23812 onFocus : Roo.emptyFn,
23814 hideMode:'offsets',
23816 tbContainer : false,
23820 toolbarContainer :function() {
23821 return this.wrap.select('.x-html-editor-tb',true).first();
23825 * Protected method that will not generally be called directly. It
23826 * is called when the editor creates its toolbar. Override this method if you need to
23827 * add custom toolbar buttons.
23828 * @param {HtmlEditor} editor
23830 createToolbar : function(){
23831 Roo.log('renewing');
23832 Roo.log("create toolbars");
23834 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23835 this.toolbars[0].render(this.toolbarContainer());
23839 // if (!editor.toolbars || !editor.toolbars.length) {
23840 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23843 // for (var i =0 ; i < editor.toolbars.length;i++) {
23844 // editor.toolbars[i] = Roo.factory(
23845 // typeof(editor.toolbars[i]) == 'string' ?
23846 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23847 // Roo.bootstrap.HtmlEditor);
23848 // editor.toolbars[i].init(editor);
23854 onRender : function(ct, position)
23856 // Roo.log("Call onRender: " + this.xtype);
23858 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23860 this.wrap = this.inputEl().wrap({
23861 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23864 this.editorcore.onRender(ct, position);
23866 if (this.resizable) {
23867 this.resizeEl = new Roo.Resizable(this.wrap, {
23871 minHeight : this.height,
23872 height: this.height,
23873 handles : this.resizable,
23876 resize : function(r, w, h) {
23877 _t.onResize(w,h); // -something
23883 this.createToolbar(this);
23886 if(!this.width && this.resizable){
23887 this.setSize(this.wrap.getSize());
23889 if (this.resizeEl) {
23890 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23891 // should trigger onReize..
23897 onResize : function(w, h)
23899 Roo.log('resize: ' +w + ',' + h );
23900 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23904 if(this.inputEl() ){
23905 if(typeof w == 'number'){
23906 var aw = w - this.wrap.getFrameWidth('lr');
23907 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23910 if(typeof h == 'number'){
23911 var tbh = -11; // fixme it needs to tool bar size!
23912 for (var i =0; i < this.toolbars.length;i++) {
23913 // fixme - ask toolbars for heights?
23914 tbh += this.toolbars[i].el.getHeight();
23915 //if (this.toolbars[i].footer) {
23916 // tbh += this.toolbars[i].footer.el.getHeight();
23924 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23925 ah -= 5; // knock a few pixes off for look..
23926 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23930 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23931 this.editorcore.onResize(ew,eh);
23936 * Toggles the editor between standard and source edit mode.
23937 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23939 toggleSourceEdit : function(sourceEditMode)
23941 this.editorcore.toggleSourceEdit(sourceEditMode);
23943 if(this.editorcore.sourceEditMode){
23944 Roo.log('editor - showing textarea');
23947 // Roo.log(this.syncValue());
23949 this.inputEl().removeClass(['hide', 'x-hidden']);
23950 this.inputEl().dom.removeAttribute('tabIndex');
23951 this.inputEl().focus();
23953 Roo.log('editor - hiding textarea');
23955 // Roo.log(this.pushValue());
23958 this.inputEl().addClass(['hide', 'x-hidden']);
23959 this.inputEl().dom.setAttribute('tabIndex', -1);
23960 //this.deferFocus();
23963 if(this.resizable){
23964 this.setSize(this.wrap.getSize());
23967 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23970 // private (for BoxComponent)
23971 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23973 // private (for BoxComponent)
23974 getResizeEl : function(){
23978 // private (for BoxComponent)
23979 getPositionEl : function(){
23984 initEvents : function(){
23985 this.originalValue = this.getValue();
23989 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23992 // markInvalid : Roo.emptyFn,
23994 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23997 // clearInvalid : Roo.emptyFn,
23999 setValue : function(v){
24000 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24001 this.editorcore.pushValue();
24006 deferFocus : function(){
24007 this.focus.defer(10, this);
24011 focus : function(){
24012 this.editorcore.focus();
24018 onDestroy : function(){
24024 for (var i =0; i < this.toolbars.length;i++) {
24025 // fixme - ask toolbars for heights?
24026 this.toolbars[i].onDestroy();
24029 this.wrap.dom.innerHTML = '';
24030 this.wrap.remove();
24035 onFirstFocus : function(){
24036 //Roo.log("onFirstFocus");
24037 this.editorcore.onFirstFocus();
24038 for (var i =0; i < this.toolbars.length;i++) {
24039 this.toolbars[i].onFirstFocus();
24045 syncValue : function()
24047 this.editorcore.syncValue();
24050 pushValue : function()
24052 this.editorcore.pushValue();
24056 // hide stuff that is not compatible
24070 * @event specialkey
24074 * @cfg {String} fieldClass @hide
24077 * @cfg {String} focusClass @hide
24080 * @cfg {String} autoCreate @hide
24083 * @cfg {String} inputType @hide
24087 * @cfg {String} invalidText @hide
24090 * @cfg {String} msgFx @hide
24093 * @cfg {String} validateOnBlur @hide
24102 Roo.namespace('Roo.bootstrap.htmleditor');
24104 * @class Roo.bootstrap.HtmlEditorToolbar1
24110 new Roo.bootstrap.HtmlEditor({
24113 new Roo.bootstrap.HtmlEditorToolbar1({
24114 disable : { fonts: 1 , format: 1, ..., ... , ...],
24120 * @cfg {Object} disable List of elements to disable..
24121 * @cfg {Array} btns List of additional buttons.
24125 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24128 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24131 Roo.apply(this, config);
24133 // default disabled, based on 'good practice'..
24134 this.disable = this.disable || {};
24135 Roo.applyIf(this.disable, {
24138 specialElements : true
24140 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24142 this.editor = config.editor;
24143 this.editorcore = config.editor.editorcore;
24145 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24147 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24148 // dont call parent... till later.
24150 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24155 editorcore : false,
24160 "h1","h2","h3","h4","h5","h6",
24162 "abbr", "acronym", "address", "cite", "samp", "var",
24166 onRender : function(ct, position)
24168 // Roo.log("Call onRender: " + this.xtype);
24170 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24172 this.el.dom.style.marginBottom = '0';
24174 var editorcore = this.editorcore;
24175 var editor= this.editor;
24178 var btn = function(id,cmd , toggle, handler, html){
24180 var event = toggle ? 'toggle' : 'click';
24185 xns: Roo.bootstrap,
24189 enableToggle:toggle !== false,
24191 pressed : toggle ? false : null,
24194 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24195 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24201 // var cb_box = function...
24206 xns: Roo.bootstrap,
24211 xns: Roo.bootstrap,
24215 Roo.each(this.formats, function(f) {
24216 style.menu.items.push({
24218 xns: Roo.bootstrap,
24219 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24224 editorcore.insertTag(this.tagname);
24231 children.push(style);
24233 btn('bold',false,true);
24234 btn('italic',false,true);
24235 btn('align-left', 'justifyleft',true);
24236 btn('align-center', 'justifycenter',true);
24237 btn('align-right' , 'justifyright',true);
24238 btn('link', false, false, function(btn) {
24239 //Roo.log("create link?");
24240 var url = prompt(this.createLinkText, this.defaultLinkValue);
24241 if(url && url != 'http:/'+'/'){
24242 this.editorcore.relayCmd('createlink', url);
24245 btn('list','insertunorderedlist',true);
24246 btn('pencil', false,true, function(btn){
24248 this.toggleSourceEdit(btn.pressed);
24251 if (this.editor.btns.length > 0) {
24252 for (var i = 0; i<this.editor.btns.length; i++) {
24253 children.push(this.editor.btns[i]);
24261 xns: Roo.bootstrap,
24266 xns: Roo.bootstrap,
24271 cog.menu.items.push({
24273 xns: Roo.bootstrap,
24274 html : Clean styles,
24279 editorcore.insertTag(this.tagname);
24288 this.xtype = 'NavSimplebar';
24290 for(var i=0;i< children.length;i++) {
24292 this.buttons.add(this.addxtypeChild(children[i]));
24296 editor.on('editorevent', this.updateToolbar, this);
24298 onBtnClick : function(id)
24300 this.editorcore.relayCmd(id);
24301 this.editorcore.focus();
24305 * Protected method that will not generally be called directly. It triggers
24306 * a toolbar update by reading the markup state of the current selection in the editor.
24308 updateToolbar: function(){
24310 if(!this.editorcore.activated){
24311 this.editor.onFirstFocus(); // is this neeed?
24315 var btns = this.buttons;
24316 var doc = this.editorcore.doc;
24317 btns.get('bold').setActive(doc.queryCommandState('bold'));
24318 btns.get('italic').setActive(doc.queryCommandState('italic'));
24319 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24321 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24322 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24323 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24325 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24326 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24329 var ans = this.editorcore.getAllAncestors();
24330 if (this.formatCombo) {
24333 var store = this.formatCombo.store;
24334 this.formatCombo.setValue("");
24335 for (var i =0; i < ans.length;i++) {
24336 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24338 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24346 // hides menus... - so this cant be on a menu...
24347 Roo.bootstrap.MenuMgr.hideAll();
24349 Roo.bootstrap.MenuMgr.hideAll();
24350 //this.editorsyncValue();
24352 onFirstFocus: function() {
24353 this.buttons.each(function(item){
24357 toggleSourceEdit : function(sourceEditMode){
24360 if(sourceEditMode){
24361 Roo.log("disabling buttons");
24362 this.buttons.each( function(item){
24363 if(item.cmd != 'pencil'){
24369 Roo.log("enabling buttons");
24370 if(this.editorcore.initialized){
24371 this.buttons.each( function(item){
24377 Roo.log("calling toggole on editor");
24378 // tell the editor that it's been pressed..
24379 this.editor.toggleSourceEdit(sourceEditMode);
24389 * @class Roo.bootstrap.Table.AbstractSelectionModel
24390 * @extends Roo.util.Observable
24391 * Abstract base class for grid SelectionModels. It provides the interface that should be
24392 * implemented by descendant classes. This class should not be directly instantiated.
24395 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24396 this.locked = false;
24397 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24401 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24402 /** @ignore Called by the grid automatically. Do not call directly. */
24403 init : function(grid){
24409 * Locks the selections.
24412 this.locked = true;
24416 * Unlocks the selections.
24418 unlock : function(){
24419 this.locked = false;
24423 * Returns true if the selections are locked.
24424 * @return {Boolean}
24426 isLocked : function(){
24427 return this.locked;
24431 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24432 * @class Roo.bootstrap.Table.RowSelectionModel
24433 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24434 * It supports multiple selections and keyboard selection/navigation.
24436 * @param {Object} config
24439 Roo.bootstrap.Table.RowSelectionModel = function(config){
24440 Roo.apply(this, config);
24441 this.selections = new Roo.util.MixedCollection(false, function(o){
24446 this.lastActive = false;
24450 * @event selectionchange
24451 * Fires when the selection changes
24452 * @param {SelectionModel} this
24454 "selectionchange" : true,
24456 * @event afterselectionchange
24457 * Fires after the selection changes (eg. by key press or clicking)
24458 * @param {SelectionModel} this
24460 "afterselectionchange" : true,
24462 * @event beforerowselect
24463 * Fires when a row is selected being selected, return false to cancel.
24464 * @param {SelectionModel} this
24465 * @param {Number} rowIndex The selected index
24466 * @param {Boolean} keepExisting False if other selections will be cleared
24468 "beforerowselect" : true,
24471 * Fires when a row is selected.
24472 * @param {SelectionModel} this
24473 * @param {Number} rowIndex The selected index
24474 * @param {Roo.data.Record} r The record
24476 "rowselect" : true,
24478 * @event rowdeselect
24479 * Fires when a row is deselected.
24480 * @param {SelectionModel} this
24481 * @param {Number} rowIndex The selected index
24483 "rowdeselect" : true
24485 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24486 this.locked = false;
24489 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24491 * @cfg {Boolean} singleSelect
24492 * True to allow selection of only one row at a time (defaults to false)
24494 singleSelect : false,
24497 initEvents : function()
24500 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24501 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24502 //}else{ // allow click to work like normal
24503 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24505 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24506 this.grid.on("rowclick", this.handleMouseDown, this);
24508 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24509 "up" : function(e){
24511 this.selectPrevious(e.shiftKey);
24512 }else if(this.last !== false && this.lastActive !== false){
24513 var last = this.last;
24514 this.selectRange(this.last, this.lastActive-1);
24515 this.grid.getView().focusRow(this.lastActive);
24516 if(last !== false){
24520 this.selectFirstRow();
24522 this.fireEvent("afterselectionchange", this);
24524 "down" : function(e){
24526 this.selectNext(e.shiftKey);
24527 }else if(this.last !== false && this.lastActive !== false){
24528 var last = this.last;
24529 this.selectRange(this.last, this.lastActive+1);
24530 this.grid.getView().focusRow(this.lastActive);
24531 if(last !== false){
24535 this.selectFirstRow();
24537 this.fireEvent("afterselectionchange", this);
24541 this.grid.store.on('load', function(){
24542 this.selections.clear();
24545 var view = this.grid.view;
24546 view.on("refresh", this.onRefresh, this);
24547 view.on("rowupdated", this.onRowUpdated, this);
24548 view.on("rowremoved", this.onRemove, this);
24553 onRefresh : function()
24555 var ds = this.grid.store, i, v = this.grid.view;
24556 var s = this.selections;
24557 s.each(function(r){
24558 if((i = ds.indexOfId(r.id)) != -1){
24567 onRemove : function(v, index, r){
24568 this.selections.remove(r);
24572 onRowUpdated : function(v, index, r){
24573 if(this.isSelected(r)){
24574 v.onRowSelect(index);
24580 * @param {Array} records The records to select
24581 * @param {Boolean} keepExisting (optional) True to keep existing selections
24583 selectRecords : function(records, keepExisting)
24586 this.clearSelections();
24588 var ds = this.grid.store;
24589 for(var i = 0, len = records.length; i < len; i++){
24590 this.selectRow(ds.indexOf(records[i]), true);
24595 * Gets the number of selected rows.
24598 getCount : function(){
24599 return this.selections.length;
24603 * Selects the first row in the grid.
24605 selectFirstRow : function(){
24610 * Select the last row.
24611 * @param {Boolean} keepExisting (optional) True to keep existing selections
24613 selectLastRow : function(keepExisting){
24614 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24615 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24619 * Selects the row immediately following the last selected row.
24620 * @param {Boolean} keepExisting (optional) True to keep existing selections
24622 selectNext : function(keepExisting)
24624 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24625 this.selectRow(this.last+1, keepExisting);
24626 this.grid.getView().focusRow(this.last);
24631 * Selects the row that precedes the last selected row.
24632 * @param {Boolean} keepExisting (optional) True to keep existing selections
24634 selectPrevious : function(keepExisting){
24636 this.selectRow(this.last-1, keepExisting);
24637 this.grid.getView().focusRow(this.last);
24642 * Returns the selected records
24643 * @return {Array} Array of selected records
24645 getSelections : function(){
24646 return [].concat(this.selections.items);
24650 * Returns the first selected record.
24653 getSelected : function(){
24654 return this.selections.itemAt(0);
24659 * Clears all selections.
24661 clearSelections : function(fast)
24667 var ds = this.grid.store;
24668 var s = this.selections;
24669 s.each(function(r){
24670 this.deselectRow(ds.indexOfId(r.id));
24674 this.selections.clear();
24681 * Selects all rows.
24683 selectAll : function(){
24687 this.selections.clear();
24688 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24689 this.selectRow(i, true);
24694 * Returns True if there is a selection.
24695 * @return {Boolean}
24697 hasSelection : function(){
24698 return this.selections.length > 0;
24702 * Returns True if the specified row is selected.
24703 * @param {Number/Record} record The record or index of the record to check
24704 * @return {Boolean}
24706 isSelected : function(index){
24707 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24708 return (r && this.selections.key(r.id) ? true : false);
24712 * Returns True if the specified record id is selected.
24713 * @param {String} id The id of record to check
24714 * @return {Boolean}
24716 isIdSelected : function(id){
24717 return (this.selections.key(id) ? true : false);
24722 handleMouseDBClick : function(e, t){
24726 handleMouseDown : function(e, t)
24728 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24729 if(this.isLocked() || rowIndex < 0 ){
24732 if(e.shiftKey && this.last !== false){
24733 var last = this.last;
24734 this.selectRange(last, rowIndex, e.ctrlKey);
24735 this.last = last; // reset the last
24739 var isSelected = this.isSelected(rowIndex);
24740 //Roo.log("select row:" + rowIndex);
24742 this.deselectRow(rowIndex);
24744 this.selectRow(rowIndex, true);
24748 if(e.button !== 0 && isSelected){
24749 alert('rowIndex 2: ' + rowIndex);
24750 view.focusRow(rowIndex);
24751 }else if(e.ctrlKey && isSelected){
24752 this.deselectRow(rowIndex);
24753 }else if(!isSelected){
24754 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24755 view.focusRow(rowIndex);
24759 this.fireEvent("afterselectionchange", this);
24762 handleDragableRowClick : function(grid, rowIndex, e)
24764 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24765 this.selectRow(rowIndex, false);
24766 grid.view.focusRow(rowIndex);
24767 this.fireEvent("afterselectionchange", this);
24772 * Selects multiple rows.
24773 * @param {Array} rows Array of the indexes of the row to select
24774 * @param {Boolean} keepExisting (optional) True to keep existing selections
24776 selectRows : function(rows, keepExisting){
24778 this.clearSelections();
24780 for(var i = 0, len = rows.length; i < len; i++){
24781 this.selectRow(rows[i], true);
24786 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24787 * @param {Number} startRow The index of the first row in the range
24788 * @param {Number} endRow The index of the last row in the range
24789 * @param {Boolean} keepExisting (optional) True to retain existing selections
24791 selectRange : function(startRow, endRow, keepExisting){
24796 this.clearSelections();
24798 if(startRow <= endRow){
24799 for(var i = startRow; i <= endRow; i++){
24800 this.selectRow(i, true);
24803 for(var i = startRow; i >= endRow; i--){
24804 this.selectRow(i, true);
24810 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24811 * @param {Number} startRow The index of the first row in the range
24812 * @param {Number} endRow The index of the last row in the range
24814 deselectRange : function(startRow, endRow, preventViewNotify){
24818 for(var i = startRow; i <= endRow; i++){
24819 this.deselectRow(i, preventViewNotify);
24825 * @param {Number} row The index of the row to select
24826 * @param {Boolean} keepExisting (optional) True to keep existing selections
24828 selectRow : function(index, keepExisting, preventViewNotify)
24830 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24833 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24834 if(!keepExisting || this.singleSelect){
24835 this.clearSelections();
24838 var r = this.grid.store.getAt(index);
24839 //console.log('selectRow - record id :' + r.id);
24841 this.selections.add(r);
24842 this.last = this.lastActive = index;
24843 if(!preventViewNotify){
24844 var proxy = new Roo.Element(
24845 this.grid.getRowDom(index)
24847 proxy.addClass('bg-info info');
24849 this.fireEvent("rowselect", this, index, r);
24850 this.fireEvent("selectionchange", this);
24856 * @param {Number} row The index of the row to deselect
24858 deselectRow : function(index, preventViewNotify)
24863 if(this.last == index){
24866 if(this.lastActive == index){
24867 this.lastActive = false;
24870 var r = this.grid.store.getAt(index);
24875 this.selections.remove(r);
24876 //.console.log('deselectRow - record id :' + r.id);
24877 if(!preventViewNotify){
24879 var proxy = new Roo.Element(
24880 this.grid.getRowDom(index)
24882 proxy.removeClass('bg-info info');
24884 this.fireEvent("rowdeselect", this, index);
24885 this.fireEvent("selectionchange", this);
24889 restoreLast : function(){
24891 this.last = this._last;
24896 acceptsNav : function(row, col, cm){
24897 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24901 onEditorKey : function(field, e){
24902 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24907 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24909 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24911 }else if(k == e.ENTER && !e.ctrlKey){
24915 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24917 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24919 }else if(k == e.ESC){
24923 g.startEditing(newCell[0], newCell[1]);
24929 * Ext JS Library 1.1.1
24930 * Copyright(c) 2006-2007, Ext JS, LLC.
24932 * Originally Released Under LGPL - original licence link has changed is not relivant.
24935 * <script type="text/javascript">
24939 * @class Roo.bootstrap.PagingToolbar
24940 * @extends Roo.bootstrap.NavSimplebar
24941 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24943 * Create a new PagingToolbar
24944 * @param {Object} config The config object
24945 * @param {Roo.data.Store} store
24947 Roo.bootstrap.PagingToolbar = function(config)
24949 // old args format still supported... - xtype is prefered..
24950 // created from xtype...
24952 this.ds = config.dataSource;
24954 if (config.store && !this.ds) {
24955 this.store= Roo.factory(config.store, Roo.data);
24956 this.ds = this.store;
24957 this.ds.xmodule = this.xmodule || false;
24960 this.toolbarItems = [];
24961 if (config.items) {
24962 this.toolbarItems = config.items;
24965 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24970 this.bind(this.ds);
24973 if (Roo.bootstrap.version == 4) {
24974 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24976 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24981 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24983 * @cfg {Roo.data.Store} dataSource
24984 * The underlying data store providing the paged data
24987 * @cfg {String/HTMLElement/Element} container
24988 * container The id or element that will contain the toolbar
24991 * @cfg {Boolean} displayInfo
24992 * True to display the displayMsg (defaults to false)
24995 * @cfg {Number} pageSize
24996 * The number of records to display per page (defaults to 20)
25000 * @cfg {String} displayMsg
25001 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25003 displayMsg : 'Displaying {0} - {1} of {2}',
25005 * @cfg {String} emptyMsg
25006 * The message to display when no records are found (defaults to "No data to display")
25008 emptyMsg : 'No data to display',
25010 * Customizable piece of the default paging text (defaults to "Page")
25013 beforePageText : "Page",
25015 * Customizable piece of the default paging text (defaults to "of %0")
25018 afterPageText : "of {0}",
25020 * Customizable piece of the default paging text (defaults to "First Page")
25023 firstText : "First Page",
25025 * Customizable piece of the default paging text (defaults to "Previous Page")
25028 prevText : "Previous Page",
25030 * Customizable piece of the default paging text (defaults to "Next Page")
25033 nextText : "Next Page",
25035 * Customizable piece of the default paging text (defaults to "Last Page")
25038 lastText : "Last Page",
25040 * Customizable piece of the default paging text (defaults to "Refresh")
25043 refreshText : "Refresh",
25047 onRender : function(ct, position)
25049 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25050 this.navgroup.parentId = this.id;
25051 this.navgroup.onRender(this.el, null);
25052 // add the buttons to the navgroup
25054 if(this.displayInfo){
25055 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25056 this.displayEl = this.el.select('.x-paging-info', true).first();
25057 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25058 // this.displayEl = navel.el.select('span',true).first();
25064 Roo.each(_this.buttons, function(e){ // this might need to use render????
25065 Roo.factory(e).render(_this.el);
25069 Roo.each(_this.toolbarItems, function(e) {
25070 _this.navgroup.addItem(e);
25074 this.first = this.navgroup.addItem({
25075 tooltip: this.firstText,
25076 cls: "prev btn-outline-secondary",
25077 html : ' <i class="fa fa-step-backward"></i>',
25079 preventDefault: true,
25080 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25083 this.prev = this.navgroup.addItem({
25084 tooltip: this.prevText,
25085 cls: "prev btn-outline-secondary",
25086 html : ' <i class="fa fa-backward"></i>',
25088 preventDefault: true,
25089 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25091 //this.addSeparator();
25094 var field = this.navgroup.addItem( {
25096 cls : 'x-paging-position btn-outline-secondary',
25098 html : this.beforePageText +
25099 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25100 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25103 this.field = field.el.select('input', true).first();
25104 this.field.on("keydown", this.onPagingKeydown, this);
25105 this.field.on("focus", function(){this.dom.select();});
25108 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25109 //this.field.setHeight(18);
25110 //this.addSeparator();
25111 this.next = this.navgroup.addItem({
25112 tooltip: this.nextText,
25113 cls: "next btn-outline-secondary",
25114 html : ' <i class="fa fa-forward"></i>',
25116 preventDefault: true,
25117 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25119 this.last = this.navgroup.addItem({
25120 tooltip: this.lastText,
25121 html : ' <i class="fa fa-step-forward"></i>',
25122 cls: "next btn-outline-secondary",
25124 preventDefault: true,
25125 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25127 //this.addSeparator();
25128 this.loading = this.navgroup.addItem({
25129 tooltip: this.refreshText,
25130 cls: "btn-outline-secondary",
25131 html : ' <i class="fa fa-refresh"></i>',
25132 preventDefault: true,
25133 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25139 updateInfo : function(){
25140 if(this.displayEl){
25141 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25142 var msg = count == 0 ?
25146 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25148 this.displayEl.update(msg);
25153 onLoad : function(ds, r, o)
25155 this.cursor = o.params.start ? o.params.start : 0;
25157 var d = this.getPageData(),
25162 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25163 this.field.dom.value = ap;
25164 this.first.setDisabled(ap == 1);
25165 this.prev.setDisabled(ap == 1);
25166 this.next.setDisabled(ap == ps);
25167 this.last.setDisabled(ap == ps);
25168 this.loading.enable();
25173 getPageData : function(){
25174 var total = this.ds.getTotalCount();
25177 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25178 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25183 onLoadError : function(){
25184 this.loading.enable();
25188 onPagingKeydown : function(e){
25189 var k = e.getKey();
25190 var d = this.getPageData();
25192 var v = this.field.dom.value, pageNum;
25193 if(!v || isNaN(pageNum = parseInt(v, 10))){
25194 this.field.dom.value = d.activePage;
25197 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25198 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25201 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))
25203 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25204 this.field.dom.value = pageNum;
25205 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25208 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25210 var v = this.field.dom.value, pageNum;
25211 var increment = (e.shiftKey) ? 10 : 1;
25212 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25215 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25216 this.field.dom.value = d.activePage;
25219 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25221 this.field.dom.value = parseInt(v, 10) + increment;
25222 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25223 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25230 beforeLoad : function(){
25232 this.loading.disable();
25237 onClick : function(which){
25246 ds.load({params:{start: 0, limit: this.pageSize}});
25249 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25252 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25255 var total = ds.getTotalCount();
25256 var extra = total % this.pageSize;
25257 var lastStart = extra ? (total - extra) : total-this.pageSize;
25258 ds.load({params:{start: lastStart, limit: this.pageSize}});
25261 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25267 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25268 * @param {Roo.data.Store} store The data store to unbind
25270 unbind : function(ds){
25271 ds.un("beforeload", this.beforeLoad, this);
25272 ds.un("load", this.onLoad, this);
25273 ds.un("loadexception", this.onLoadError, this);
25274 ds.un("remove", this.updateInfo, this);
25275 ds.un("add", this.updateInfo, this);
25276 this.ds = undefined;
25280 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25281 * @param {Roo.data.Store} store The data store to bind
25283 bind : function(ds){
25284 ds.on("beforeload", this.beforeLoad, this);
25285 ds.on("load", this.onLoad, this);
25286 ds.on("loadexception", this.onLoadError, this);
25287 ds.on("remove", this.updateInfo, this);
25288 ds.on("add", this.updateInfo, this);
25299 * @class Roo.bootstrap.MessageBar
25300 * @extends Roo.bootstrap.Component
25301 * Bootstrap MessageBar class
25302 * @cfg {String} html contents of the MessageBar
25303 * @cfg {String} weight (info | success | warning | danger) default info
25304 * @cfg {String} beforeClass insert the bar before the given class
25305 * @cfg {Boolean} closable (true | false) default false
25306 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25309 * Create a new Element
25310 * @param {Object} config The config object
25313 Roo.bootstrap.MessageBar = function(config){
25314 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25317 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25323 beforeClass: 'bootstrap-sticky-wrap',
25325 getAutoCreate : function(){
25329 cls: 'alert alert-dismissable alert-' + this.weight,
25334 html: this.html || ''
25340 cfg.cls += ' alert-messages-fixed';
25354 onRender : function(ct, position)
25356 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25359 var cfg = Roo.apply({}, this.getAutoCreate());
25363 cfg.cls += ' ' + this.cls;
25366 cfg.style = this.style;
25368 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25370 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25373 this.el.select('>button.close').on('click', this.hide, this);
25379 if (!this.rendered) {
25385 this.fireEvent('show', this);
25391 if (!this.rendered) {
25397 this.fireEvent('hide', this);
25400 update : function()
25402 // var e = this.el.dom.firstChild;
25404 // if(this.closable){
25405 // e = e.nextSibling;
25408 // e.data = this.html || '';
25410 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25426 * @class Roo.bootstrap.Graph
25427 * @extends Roo.bootstrap.Component
25428 * Bootstrap Graph class
25432 @cfg {String} graphtype bar | vbar | pie
25433 @cfg {number} g_x coodinator | centre x (pie)
25434 @cfg {number} g_y coodinator | centre y (pie)
25435 @cfg {number} g_r radius (pie)
25436 @cfg {number} g_height height of the chart (respected by all elements in the set)
25437 @cfg {number} g_width width of the chart (respected by all elements in the set)
25438 @cfg {Object} title The title of the chart
25441 -opts (object) options for the chart
25443 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25444 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25446 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.
25447 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25449 o stretch (boolean)
25451 -opts (object) options for the pie
25454 o startAngle (number)
25455 o endAngle (number)
25459 * Create a new Input
25460 * @param {Object} config The config object
25463 Roo.bootstrap.Graph = function(config){
25464 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25470 * The img click event for the img.
25471 * @param {Roo.EventObject} e
25477 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25488 //g_colors: this.colors,
25495 getAutoCreate : function(){
25506 onRender : function(ct,position){
25509 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25511 if (typeof(Raphael) == 'undefined') {
25512 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25516 this.raphael = Raphael(this.el.dom);
25518 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25519 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25520 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25521 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25523 r.text(160, 10, "Single Series Chart").attr(txtattr);
25524 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25525 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25526 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25528 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25529 r.barchart(330, 10, 300, 220, data1);
25530 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25531 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25534 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25535 // r.barchart(30, 30, 560, 250, xdata, {
25536 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25537 // axis : "0 0 1 1",
25538 // axisxlabels : xdata
25539 // //yvalues : cols,
25542 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25544 // this.load(null,xdata,{
25545 // axis : "0 0 1 1",
25546 // axisxlabels : xdata
25551 load : function(graphtype,xdata,opts)
25553 this.raphael.clear();
25555 graphtype = this.graphtype;
25560 var r = this.raphael,
25561 fin = function () {
25562 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25564 fout = function () {
25565 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25567 pfin = function() {
25568 this.sector.stop();
25569 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25572 this.label[0].stop();
25573 this.label[0].attr({ r: 7.5 });
25574 this.label[1].attr({ "font-weight": 800 });
25577 pfout = function() {
25578 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25581 this.label[0].animate({ r: 5 }, 500, "bounce");
25582 this.label[1].attr({ "font-weight": 400 });
25588 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25591 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25594 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25595 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25597 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25604 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25609 setTitle: function(o)
25614 initEvents: function() {
25617 this.el.on('click', this.onClick, this);
25621 onClick : function(e)
25623 Roo.log('img onclick');
25624 this.fireEvent('click', this, e);
25636 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25639 * @class Roo.bootstrap.dash.NumberBox
25640 * @extends Roo.bootstrap.Component
25641 * Bootstrap NumberBox class
25642 * @cfg {String} headline Box headline
25643 * @cfg {String} content Box content
25644 * @cfg {String} icon Box icon
25645 * @cfg {String} footer Footer text
25646 * @cfg {String} fhref Footer href
25649 * Create a new NumberBox
25650 * @param {Object} config The config object
25654 Roo.bootstrap.dash.NumberBox = function(config){
25655 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25659 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25668 getAutoCreate : function(){
25672 cls : 'small-box ',
25680 cls : 'roo-headline',
25681 html : this.headline
25685 cls : 'roo-content',
25686 html : this.content
25700 cls : 'ion ' + this.icon
25709 cls : 'small-box-footer',
25710 href : this.fhref || '#',
25714 cfg.cn.push(footer);
25721 onRender : function(ct,position){
25722 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25729 setHeadline: function (value)
25731 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25734 setFooter: function (value, href)
25736 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25739 this.el.select('a.small-box-footer',true).first().attr('href', href);
25744 setContent: function (value)
25746 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25749 initEvents: function()
25763 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25766 * @class Roo.bootstrap.dash.TabBox
25767 * @extends Roo.bootstrap.Component
25768 * Bootstrap TabBox class
25769 * @cfg {String} title Title of the TabBox
25770 * @cfg {String} icon Icon of the TabBox
25771 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25772 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25775 * Create a new TabBox
25776 * @param {Object} config The config object
25780 Roo.bootstrap.dash.TabBox = function(config){
25781 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25786 * When a pane is added
25787 * @param {Roo.bootstrap.dash.TabPane} pane
25791 * @event activatepane
25792 * When a pane is activated
25793 * @param {Roo.bootstrap.dash.TabPane} pane
25795 "activatepane" : true
25803 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25808 tabScrollable : false,
25810 getChildContainer : function()
25812 return this.el.select('.tab-content', true).first();
25815 getAutoCreate : function(){
25819 cls: 'pull-left header',
25827 cls: 'fa ' + this.icon
25833 cls: 'nav nav-tabs pull-right',
25839 if(this.tabScrollable){
25846 cls: 'nav nav-tabs pull-right',
25857 cls: 'nav-tabs-custom',
25862 cls: 'tab-content no-padding',
25870 initEvents : function()
25872 //Roo.log('add add pane handler');
25873 this.on('addpane', this.onAddPane, this);
25876 * Updates the box title
25877 * @param {String} html to set the title to.
25879 setTitle : function(value)
25881 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25883 onAddPane : function(pane)
25885 this.panes.push(pane);
25886 //Roo.log('addpane');
25888 // tabs are rendere left to right..
25889 if(!this.showtabs){
25893 var ctr = this.el.select('.nav-tabs', true).first();
25896 var existing = ctr.select('.nav-tab',true);
25897 var qty = existing.getCount();;
25900 var tab = ctr.createChild({
25902 cls : 'nav-tab' + (qty ? '' : ' active'),
25910 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25913 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25915 pane.el.addClass('active');
25920 onTabClick : function(ev,un,ob,pane)
25922 //Roo.log('tab - prev default');
25923 ev.preventDefault();
25926 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25927 pane.tab.addClass('active');
25928 //Roo.log(pane.title);
25929 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25930 // technically we should have a deactivate event.. but maybe add later.
25931 // and it should not de-activate the selected tab...
25932 this.fireEvent('activatepane', pane);
25933 pane.el.addClass('active');
25934 pane.fireEvent('activate');
25939 getActivePane : function()
25942 Roo.each(this.panes, function(p) {
25943 if(p.el.hasClass('active')){
25964 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25966 * @class Roo.bootstrap.TabPane
25967 * @extends Roo.bootstrap.Component
25968 * Bootstrap TabPane class
25969 * @cfg {Boolean} active (false | true) Default false
25970 * @cfg {String} title title of panel
25974 * Create a new TabPane
25975 * @param {Object} config The config object
25978 Roo.bootstrap.dash.TabPane = function(config){
25979 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25985 * When a pane is activated
25986 * @param {Roo.bootstrap.dash.TabPane} pane
25993 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25998 // the tabBox that this is attached to.
26001 getAutoCreate : function()
26009 cfg.cls += ' active';
26014 initEvents : function()
26016 //Roo.log('trigger add pane handler');
26017 this.parent().fireEvent('addpane', this)
26021 * Updates the tab title
26022 * @param {String} html to set the title to.
26024 setTitle: function(str)
26030 this.tab.select('a', true).first().dom.innerHTML = str;
26047 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26050 * @class Roo.bootstrap.menu.Menu
26051 * @extends Roo.bootstrap.Component
26052 * Bootstrap Menu class - container for Menu
26053 * @cfg {String} html Text of the menu
26054 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26055 * @cfg {String} icon Font awesome icon
26056 * @cfg {String} pos Menu align to (top | bottom) default bottom
26060 * Create a new Menu
26061 * @param {Object} config The config object
26065 Roo.bootstrap.menu.Menu = function(config){
26066 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26070 * @event beforeshow
26071 * Fires before this menu is displayed
26072 * @param {Roo.bootstrap.menu.Menu} this
26076 * @event beforehide
26077 * Fires before this menu is hidden
26078 * @param {Roo.bootstrap.menu.Menu} this
26083 * Fires after this menu is displayed
26084 * @param {Roo.bootstrap.menu.Menu} this
26089 * Fires after this menu is hidden
26090 * @param {Roo.bootstrap.menu.Menu} this
26095 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26096 * @param {Roo.bootstrap.menu.Menu} this
26097 * @param {Roo.EventObject} e
26104 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26108 weight : 'default',
26113 getChildContainer : function() {
26114 if(this.isSubMenu){
26118 return this.el.select('ul.dropdown-menu', true).first();
26121 getAutoCreate : function()
26126 cls : 'roo-menu-text',
26134 cls : 'fa ' + this.icon
26145 cls : 'dropdown-button btn btn-' + this.weight,
26150 cls : 'dropdown-toggle btn btn-' + this.weight,
26160 cls : 'dropdown-menu'
26166 if(this.pos == 'top'){
26167 cfg.cls += ' dropup';
26170 if(this.isSubMenu){
26173 cls : 'dropdown-menu'
26180 onRender : function(ct, position)
26182 this.isSubMenu = ct.hasClass('dropdown-submenu');
26184 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26187 initEvents : function()
26189 if(this.isSubMenu){
26193 this.hidden = true;
26195 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26196 this.triggerEl.on('click', this.onTriggerPress, this);
26198 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26199 this.buttonEl.on('click', this.onClick, this);
26205 if(this.isSubMenu){
26209 return this.el.select('ul.dropdown-menu', true).first();
26212 onClick : function(e)
26214 this.fireEvent("click", this, e);
26217 onTriggerPress : function(e)
26219 if (this.isVisible()) {
26226 isVisible : function(){
26227 return !this.hidden;
26232 this.fireEvent("beforeshow", this);
26234 this.hidden = false;
26235 this.el.addClass('open');
26237 Roo.get(document).on("mouseup", this.onMouseUp, this);
26239 this.fireEvent("show", this);
26246 this.fireEvent("beforehide", this);
26248 this.hidden = true;
26249 this.el.removeClass('open');
26251 Roo.get(document).un("mouseup", this.onMouseUp);
26253 this.fireEvent("hide", this);
26256 onMouseUp : function()
26270 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26273 * @class Roo.bootstrap.menu.Item
26274 * @extends Roo.bootstrap.Component
26275 * Bootstrap MenuItem class
26276 * @cfg {Boolean} submenu (true | false) default false
26277 * @cfg {String} html text of the item
26278 * @cfg {String} href the link
26279 * @cfg {Boolean} disable (true | false) default false
26280 * @cfg {Boolean} preventDefault (true | false) default true
26281 * @cfg {String} icon Font awesome icon
26282 * @cfg {String} pos Submenu align to (left | right) default right
26286 * Create a new Item
26287 * @param {Object} config The config object
26291 Roo.bootstrap.menu.Item = function(config){
26292 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26296 * Fires when the mouse is hovering over this menu
26297 * @param {Roo.bootstrap.menu.Item} this
26298 * @param {Roo.EventObject} e
26303 * Fires when the mouse exits this menu
26304 * @param {Roo.bootstrap.menu.Item} this
26305 * @param {Roo.EventObject} e
26311 * The raw click event for the entire grid.
26312 * @param {Roo.EventObject} e
26318 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26323 preventDefault: true,
26328 getAutoCreate : function()
26333 cls : 'roo-menu-item-text',
26341 cls : 'fa ' + this.icon
26350 href : this.href || '#',
26357 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26361 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26363 if(this.pos == 'left'){
26364 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26371 initEvents : function()
26373 this.el.on('mouseover', this.onMouseOver, this);
26374 this.el.on('mouseout', this.onMouseOut, this);
26376 this.el.select('a', true).first().on('click', this.onClick, this);
26380 onClick : function(e)
26382 if(this.preventDefault){
26383 e.preventDefault();
26386 this.fireEvent("click", this, e);
26389 onMouseOver : function(e)
26391 if(this.submenu && this.pos == 'left'){
26392 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26395 this.fireEvent("mouseover", this, e);
26398 onMouseOut : function(e)
26400 this.fireEvent("mouseout", this, e);
26412 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26415 * @class Roo.bootstrap.menu.Separator
26416 * @extends Roo.bootstrap.Component
26417 * Bootstrap Separator class
26420 * Create a new Separator
26421 * @param {Object} config The config object
26425 Roo.bootstrap.menu.Separator = function(config){
26426 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26429 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26431 getAutoCreate : function(){
26452 * @class Roo.bootstrap.Tooltip
26453 * Bootstrap Tooltip class
26454 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26455 * to determine which dom element triggers the tooltip.
26457 * It needs to add support for additional attributes like tooltip-position
26460 * Create a new Toolti
26461 * @param {Object} config The config object
26464 Roo.bootstrap.Tooltip = function(config){
26465 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26467 this.alignment = Roo.bootstrap.Tooltip.alignment;
26469 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26470 this.alignment = config.alignment;
26475 Roo.apply(Roo.bootstrap.Tooltip, {
26477 * @function init initialize tooltip monitoring.
26481 currentTip : false,
26482 currentRegion : false,
26488 Roo.get(document).on('mouseover', this.enter ,this);
26489 Roo.get(document).on('mouseout', this.leave, this);
26492 this.currentTip = new Roo.bootstrap.Tooltip();
26495 enter : function(ev)
26497 var dom = ev.getTarget();
26499 //Roo.log(['enter',dom]);
26500 var el = Roo.fly(dom);
26501 if (this.currentEl) {
26503 //Roo.log(this.currentEl);
26504 //Roo.log(this.currentEl.contains(dom));
26505 if (this.currentEl == el) {
26508 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26514 if (this.currentTip.el) {
26515 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26519 if(!el || el.dom == document){
26525 // you can not look for children, as if el is the body.. then everythign is the child..
26526 if (!el.attr('tooltip')) { //
26527 if (!el.select("[tooltip]").elements.length) {
26530 // is the mouse over this child...?
26531 bindEl = el.select("[tooltip]").first();
26532 var xy = ev.getXY();
26533 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26534 //Roo.log("not in region.");
26537 //Roo.log("child element over..");
26540 this.currentEl = bindEl;
26541 this.currentTip.bind(bindEl);
26542 this.currentRegion = Roo.lib.Region.getRegion(dom);
26543 this.currentTip.enter();
26546 leave : function(ev)
26548 var dom = ev.getTarget();
26549 //Roo.log(['leave',dom]);
26550 if (!this.currentEl) {
26555 if (dom != this.currentEl.dom) {
26558 var xy = ev.getXY();
26559 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26562 // only activate leave if mouse cursor is outside... bounding box..
26567 if (this.currentTip) {
26568 this.currentTip.leave();
26570 //Roo.log('clear currentEl');
26571 this.currentEl = false;
26576 'left' : ['r-l', [-2,0], 'right'],
26577 'right' : ['l-r', [2,0], 'left'],
26578 'bottom' : ['t-b', [0,2], 'top'],
26579 'top' : [ 'b-t', [0,-2], 'bottom']
26585 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26590 delay : null, // can be { show : 300 , hide: 500}
26594 hoverState : null, //???
26596 placement : 'bottom',
26600 getAutoCreate : function(){
26607 cls : 'tooltip-arrow'
26610 cls : 'tooltip-inner'
26617 bind : function(el)
26623 enter : function () {
26625 if (this.timeout != null) {
26626 clearTimeout(this.timeout);
26629 this.hoverState = 'in';
26630 //Roo.log("enter - show");
26631 if (!this.delay || !this.delay.show) {
26636 this.timeout = setTimeout(function () {
26637 if (_t.hoverState == 'in') {
26640 }, this.delay.show);
26644 clearTimeout(this.timeout);
26646 this.hoverState = 'out';
26647 if (!this.delay || !this.delay.hide) {
26653 this.timeout = setTimeout(function () {
26654 //Roo.log("leave - timeout");
26656 if (_t.hoverState == 'out') {
26658 Roo.bootstrap.Tooltip.currentEl = false;
26663 show : function (msg)
26666 this.render(document.body);
26669 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26671 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26673 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26675 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26677 var placement = typeof this.placement == 'function' ?
26678 this.placement.call(this, this.el, on_el) :
26681 var autoToken = /\s?auto?\s?/i;
26682 var autoPlace = autoToken.test(placement);
26684 placement = placement.replace(autoToken, '') || 'top';
26688 //this.el.setXY([0,0]);
26690 //this.el.dom.style.display='block';
26692 //this.el.appendTo(on_el);
26694 var p = this.getPosition();
26695 var box = this.el.getBox();
26701 var align = this.alignment[placement];
26703 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26705 if(placement == 'top' || placement == 'bottom'){
26707 placement = 'right';
26710 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26711 placement = 'left';
26714 var scroll = Roo.select('body', true).first().getScroll();
26716 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26720 align = this.alignment[placement];
26723 this.el.alignTo(this.bindEl, align[0],align[1]);
26724 //var arrow = this.el.select('.arrow',true).first();
26725 //arrow.set(align[2],
26727 this.el.addClass(placement);
26729 this.el.addClass('in fade');
26731 this.hoverState = null;
26733 if (this.el.hasClass('fade')) {
26744 //this.el.setXY([0,0]);
26745 this.el.removeClass('in');
26761 * @class Roo.bootstrap.LocationPicker
26762 * @extends Roo.bootstrap.Component
26763 * Bootstrap LocationPicker class
26764 * @cfg {Number} latitude Position when init default 0
26765 * @cfg {Number} longitude Position when init default 0
26766 * @cfg {Number} zoom default 15
26767 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26768 * @cfg {Boolean} mapTypeControl default false
26769 * @cfg {Boolean} disableDoubleClickZoom default false
26770 * @cfg {Boolean} scrollwheel default true
26771 * @cfg {Boolean} streetViewControl default false
26772 * @cfg {Number} radius default 0
26773 * @cfg {String} locationName
26774 * @cfg {Boolean} draggable default true
26775 * @cfg {Boolean} enableAutocomplete default false
26776 * @cfg {Boolean} enableReverseGeocode default true
26777 * @cfg {String} markerTitle
26780 * Create a new LocationPicker
26781 * @param {Object} config The config object
26785 Roo.bootstrap.LocationPicker = function(config){
26787 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26792 * Fires when the picker initialized.
26793 * @param {Roo.bootstrap.LocationPicker} this
26794 * @param {Google Location} location
26798 * @event positionchanged
26799 * Fires when the picker position changed.
26800 * @param {Roo.bootstrap.LocationPicker} this
26801 * @param {Google Location} location
26803 positionchanged : true,
26806 * Fires when the map resize.
26807 * @param {Roo.bootstrap.LocationPicker} this
26812 * Fires when the map show.
26813 * @param {Roo.bootstrap.LocationPicker} this
26818 * Fires when the map hide.
26819 * @param {Roo.bootstrap.LocationPicker} this
26824 * Fires when click the map.
26825 * @param {Roo.bootstrap.LocationPicker} this
26826 * @param {Map event} e
26830 * @event mapRightClick
26831 * Fires when right click the map.
26832 * @param {Roo.bootstrap.LocationPicker} this
26833 * @param {Map event} e
26835 mapRightClick : true,
26837 * @event markerClick
26838 * Fires when click the marker.
26839 * @param {Roo.bootstrap.LocationPicker} this
26840 * @param {Map event} e
26842 markerClick : true,
26844 * @event markerRightClick
26845 * Fires when right click the marker.
26846 * @param {Roo.bootstrap.LocationPicker} this
26847 * @param {Map event} e
26849 markerRightClick : true,
26851 * @event OverlayViewDraw
26852 * Fires when OverlayView Draw
26853 * @param {Roo.bootstrap.LocationPicker} this
26855 OverlayViewDraw : true,
26857 * @event OverlayViewOnAdd
26858 * Fires when OverlayView Draw
26859 * @param {Roo.bootstrap.LocationPicker} this
26861 OverlayViewOnAdd : true,
26863 * @event OverlayViewOnRemove
26864 * Fires when OverlayView Draw
26865 * @param {Roo.bootstrap.LocationPicker} this
26867 OverlayViewOnRemove : true,
26869 * @event OverlayViewShow
26870 * Fires when OverlayView Draw
26871 * @param {Roo.bootstrap.LocationPicker} this
26872 * @param {Pixel} cpx
26874 OverlayViewShow : true,
26876 * @event OverlayViewHide
26877 * Fires when OverlayView Draw
26878 * @param {Roo.bootstrap.LocationPicker} this
26880 OverlayViewHide : true,
26882 * @event loadexception
26883 * Fires when load google lib failed.
26884 * @param {Roo.bootstrap.LocationPicker} this
26886 loadexception : true
26891 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26893 gMapContext: false,
26899 mapTypeControl: false,
26900 disableDoubleClickZoom: false,
26902 streetViewControl: false,
26906 enableAutocomplete: false,
26907 enableReverseGeocode: true,
26910 getAutoCreate: function()
26915 cls: 'roo-location-picker'
26921 initEvents: function(ct, position)
26923 if(!this.el.getWidth() || this.isApplied()){
26927 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26932 initial: function()
26934 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26935 this.fireEvent('loadexception', this);
26939 if(!this.mapTypeId){
26940 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26943 this.gMapContext = this.GMapContext();
26945 this.initOverlayView();
26947 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26951 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26952 _this.setPosition(_this.gMapContext.marker.position);
26955 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26956 _this.fireEvent('mapClick', this, event);
26960 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26961 _this.fireEvent('mapRightClick', this, event);
26965 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26966 _this.fireEvent('markerClick', this, event);
26970 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26971 _this.fireEvent('markerRightClick', this, event);
26975 this.setPosition(this.gMapContext.location);
26977 this.fireEvent('initial', this, this.gMapContext.location);
26980 initOverlayView: function()
26984 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26988 _this.fireEvent('OverlayViewDraw', _this);
26993 _this.fireEvent('OverlayViewOnAdd', _this);
26996 onRemove: function()
26998 _this.fireEvent('OverlayViewOnRemove', _this);
27001 show: function(cpx)
27003 _this.fireEvent('OverlayViewShow', _this, cpx);
27008 _this.fireEvent('OverlayViewHide', _this);
27014 fromLatLngToContainerPixel: function(event)
27016 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27019 isApplied: function()
27021 return this.getGmapContext() == false ? false : true;
27024 getGmapContext: function()
27026 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27029 GMapContext: function()
27031 var position = new google.maps.LatLng(this.latitude, this.longitude);
27033 var _map = new google.maps.Map(this.el.dom, {
27036 mapTypeId: this.mapTypeId,
27037 mapTypeControl: this.mapTypeControl,
27038 disableDoubleClickZoom: this.disableDoubleClickZoom,
27039 scrollwheel: this.scrollwheel,
27040 streetViewControl: this.streetViewControl,
27041 locationName: this.locationName,
27042 draggable: this.draggable,
27043 enableAutocomplete: this.enableAutocomplete,
27044 enableReverseGeocode: this.enableReverseGeocode
27047 var _marker = new google.maps.Marker({
27048 position: position,
27050 title: this.markerTitle,
27051 draggable: this.draggable
27058 location: position,
27059 radius: this.radius,
27060 locationName: this.locationName,
27061 addressComponents: {
27062 formatted_address: null,
27063 addressLine1: null,
27064 addressLine2: null,
27066 streetNumber: null,
27070 stateOrProvince: null
27073 domContainer: this.el.dom,
27074 geodecoder: new google.maps.Geocoder()
27078 drawCircle: function(center, radius, options)
27080 if (this.gMapContext.circle != null) {
27081 this.gMapContext.circle.setMap(null);
27085 options = Roo.apply({}, options, {
27086 strokeColor: "#0000FF",
27087 strokeOpacity: .35,
27089 fillColor: "#0000FF",
27093 options.map = this.gMapContext.map;
27094 options.radius = radius;
27095 options.center = center;
27096 this.gMapContext.circle = new google.maps.Circle(options);
27097 return this.gMapContext.circle;
27103 setPosition: function(location)
27105 this.gMapContext.location = location;
27106 this.gMapContext.marker.setPosition(location);
27107 this.gMapContext.map.panTo(location);
27108 this.drawCircle(location, this.gMapContext.radius, {});
27112 if (this.gMapContext.settings.enableReverseGeocode) {
27113 this.gMapContext.geodecoder.geocode({
27114 latLng: this.gMapContext.location
27115 }, function(results, status) {
27117 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27118 _this.gMapContext.locationName = results[0].formatted_address;
27119 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27121 _this.fireEvent('positionchanged', this, location);
27128 this.fireEvent('positionchanged', this, location);
27133 google.maps.event.trigger(this.gMapContext.map, "resize");
27135 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27137 this.fireEvent('resize', this);
27140 setPositionByLatLng: function(latitude, longitude)
27142 this.setPosition(new google.maps.LatLng(latitude, longitude));
27145 getCurrentPosition: function()
27148 latitude: this.gMapContext.location.lat(),
27149 longitude: this.gMapContext.location.lng()
27153 getAddressName: function()
27155 return this.gMapContext.locationName;
27158 getAddressComponents: function()
27160 return this.gMapContext.addressComponents;
27163 address_component_from_google_geocode: function(address_components)
27167 for (var i = 0; i < address_components.length; i++) {
27168 var component = address_components[i];
27169 if (component.types.indexOf("postal_code") >= 0) {
27170 result.postalCode = component.short_name;
27171 } else if (component.types.indexOf("street_number") >= 0) {
27172 result.streetNumber = component.short_name;
27173 } else if (component.types.indexOf("route") >= 0) {
27174 result.streetName = component.short_name;
27175 } else if (component.types.indexOf("neighborhood") >= 0) {
27176 result.city = component.short_name;
27177 } else if (component.types.indexOf("locality") >= 0) {
27178 result.city = component.short_name;
27179 } else if (component.types.indexOf("sublocality") >= 0) {
27180 result.district = component.short_name;
27181 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27182 result.stateOrProvince = component.short_name;
27183 } else if (component.types.indexOf("country") >= 0) {
27184 result.country = component.short_name;
27188 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27189 result.addressLine2 = "";
27193 setZoomLevel: function(zoom)
27195 this.gMapContext.map.setZoom(zoom);
27208 this.fireEvent('show', this);
27219 this.fireEvent('hide', this);
27224 Roo.apply(Roo.bootstrap.LocationPicker, {
27226 OverlayView : function(map, options)
27228 options = options || {};
27235 * @class Roo.bootstrap.Alert
27236 * @extends Roo.bootstrap.Component
27237 * Bootstrap Alert class - shows an alert area box
27239 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27240 Enter a valid email address
27243 * @cfg {String} title The title of alert
27244 * @cfg {String} html The content of alert
27245 * @cfg {String} weight ( success | info | warning | danger )
27246 * @cfg {String} faicon font-awesomeicon
27249 * Create a new alert
27250 * @param {Object} config The config object
27254 Roo.bootstrap.Alert = function(config){
27255 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27259 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27266 getAutoCreate : function()
27275 cls : 'roo-alert-icon'
27280 cls : 'roo-alert-title',
27285 cls : 'roo-alert-text',
27292 cfg.cn[0].cls += ' fa ' + this.faicon;
27296 cfg.cls += ' alert-' + this.weight;
27302 initEvents: function()
27304 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27307 setTitle : function(str)
27309 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27312 setText : function(str)
27314 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27317 setWeight : function(weight)
27320 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27323 this.weight = weight;
27325 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27328 setIcon : function(icon)
27331 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27334 this.faicon = icon;
27336 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27357 * @class Roo.bootstrap.UploadCropbox
27358 * @extends Roo.bootstrap.Component
27359 * Bootstrap UploadCropbox class
27360 * @cfg {String} emptyText show when image has been loaded
27361 * @cfg {String} rotateNotify show when image too small to rotate
27362 * @cfg {Number} errorTimeout default 3000
27363 * @cfg {Number} minWidth default 300
27364 * @cfg {Number} minHeight default 300
27365 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27366 * @cfg {Boolean} isDocument (true|false) default false
27367 * @cfg {String} url action url
27368 * @cfg {String} paramName default 'imageUpload'
27369 * @cfg {String} method default POST
27370 * @cfg {Boolean} loadMask (true|false) default true
27371 * @cfg {Boolean} loadingText default 'Loading...'
27374 * Create a new UploadCropbox
27375 * @param {Object} config The config object
27378 Roo.bootstrap.UploadCropbox = function(config){
27379 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27383 * @event beforeselectfile
27384 * Fire before select file
27385 * @param {Roo.bootstrap.UploadCropbox} this
27387 "beforeselectfile" : true,
27390 * Fire after initEvent
27391 * @param {Roo.bootstrap.UploadCropbox} this
27396 * Fire after initEvent
27397 * @param {Roo.bootstrap.UploadCropbox} this
27398 * @param {String} data
27403 * Fire when preparing the file data
27404 * @param {Roo.bootstrap.UploadCropbox} this
27405 * @param {Object} file
27410 * Fire when get exception
27411 * @param {Roo.bootstrap.UploadCropbox} this
27412 * @param {XMLHttpRequest} xhr
27414 "exception" : true,
27416 * @event beforeloadcanvas
27417 * Fire before load the canvas
27418 * @param {Roo.bootstrap.UploadCropbox} this
27419 * @param {String} src
27421 "beforeloadcanvas" : true,
27424 * Fire when trash image
27425 * @param {Roo.bootstrap.UploadCropbox} this
27430 * Fire when download the image
27431 * @param {Roo.bootstrap.UploadCropbox} this
27435 * @event footerbuttonclick
27436 * Fire when footerbuttonclick
27437 * @param {Roo.bootstrap.UploadCropbox} this
27438 * @param {String} type
27440 "footerbuttonclick" : true,
27444 * @param {Roo.bootstrap.UploadCropbox} this
27449 * Fire when rotate the image
27450 * @param {Roo.bootstrap.UploadCropbox} this
27451 * @param {String} pos
27456 * Fire when inspect the file
27457 * @param {Roo.bootstrap.UploadCropbox} this
27458 * @param {Object} file
27463 * Fire when xhr upload the file
27464 * @param {Roo.bootstrap.UploadCropbox} this
27465 * @param {Object} data
27470 * Fire when arrange the file data
27471 * @param {Roo.bootstrap.UploadCropbox} this
27472 * @param {Object} formData
27477 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27480 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27482 emptyText : 'Click to upload image',
27483 rotateNotify : 'Image is too small to rotate',
27484 errorTimeout : 3000,
27498 cropType : 'image/jpeg',
27500 canvasLoaded : false,
27501 isDocument : false,
27503 paramName : 'imageUpload',
27505 loadingText : 'Loading...',
27508 getAutoCreate : function()
27512 cls : 'roo-upload-cropbox',
27516 cls : 'roo-upload-cropbox-selector',
27521 cls : 'roo-upload-cropbox-body',
27522 style : 'cursor:pointer',
27526 cls : 'roo-upload-cropbox-preview'
27530 cls : 'roo-upload-cropbox-thumb'
27534 cls : 'roo-upload-cropbox-empty-notify',
27535 html : this.emptyText
27539 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27540 html : this.rotateNotify
27546 cls : 'roo-upload-cropbox-footer',
27549 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27559 onRender : function(ct, position)
27561 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27563 if (this.buttons.length) {
27565 Roo.each(this.buttons, function(bb) {
27567 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27569 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27575 this.maskEl = this.el;
27579 initEvents : function()
27581 this.urlAPI = (window.createObjectURL && window) ||
27582 (window.URL && URL.revokeObjectURL && URL) ||
27583 (window.webkitURL && webkitURL);
27585 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27586 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27588 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27589 this.selectorEl.hide();
27591 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27592 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27594 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27595 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27596 this.thumbEl.hide();
27598 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27599 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27601 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27602 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27603 this.errorEl.hide();
27605 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27606 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27607 this.footerEl.hide();
27609 this.setThumbBoxSize();
27615 this.fireEvent('initial', this);
27622 window.addEventListener("resize", function() { _this.resize(); } );
27624 this.bodyEl.on('click', this.beforeSelectFile, this);
27627 this.bodyEl.on('touchstart', this.onTouchStart, this);
27628 this.bodyEl.on('touchmove', this.onTouchMove, this);
27629 this.bodyEl.on('touchend', this.onTouchEnd, this);
27633 this.bodyEl.on('mousedown', this.onMouseDown, this);
27634 this.bodyEl.on('mousemove', this.onMouseMove, this);
27635 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27636 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27637 Roo.get(document).on('mouseup', this.onMouseUp, this);
27640 this.selectorEl.on('change', this.onFileSelected, this);
27646 this.baseScale = 1;
27648 this.baseRotate = 1;
27649 this.dragable = false;
27650 this.pinching = false;
27653 this.cropData = false;
27654 this.notifyEl.dom.innerHTML = this.emptyText;
27656 this.selectorEl.dom.value = '';
27660 resize : function()
27662 if(this.fireEvent('resize', this) != false){
27663 this.setThumbBoxPosition();
27664 this.setCanvasPosition();
27668 onFooterButtonClick : function(e, el, o, type)
27671 case 'rotate-left' :
27672 this.onRotateLeft(e);
27674 case 'rotate-right' :
27675 this.onRotateRight(e);
27678 this.beforeSelectFile(e);
27693 this.fireEvent('footerbuttonclick', this, type);
27696 beforeSelectFile : function(e)
27698 e.preventDefault();
27700 if(this.fireEvent('beforeselectfile', this) != false){
27701 this.selectorEl.dom.click();
27705 onFileSelected : function(e)
27707 e.preventDefault();
27709 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27713 var file = this.selectorEl.dom.files[0];
27715 if(this.fireEvent('inspect', this, file) != false){
27716 this.prepare(file);
27721 trash : function(e)
27723 this.fireEvent('trash', this);
27726 download : function(e)
27728 this.fireEvent('download', this);
27731 loadCanvas : function(src)
27733 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27737 this.imageEl = document.createElement('img');
27741 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27743 this.imageEl.src = src;
27747 onLoadCanvas : function()
27749 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27750 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27752 this.bodyEl.un('click', this.beforeSelectFile, this);
27754 this.notifyEl.hide();
27755 this.thumbEl.show();
27756 this.footerEl.show();
27758 this.baseRotateLevel();
27760 if(this.isDocument){
27761 this.setThumbBoxSize();
27764 this.setThumbBoxPosition();
27766 this.baseScaleLevel();
27772 this.canvasLoaded = true;
27775 this.maskEl.unmask();
27780 setCanvasPosition : function()
27782 if(!this.canvasEl){
27786 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27787 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27789 this.previewEl.setLeft(pw);
27790 this.previewEl.setTop(ph);
27794 onMouseDown : function(e)
27798 this.dragable = true;
27799 this.pinching = false;
27801 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27802 this.dragable = false;
27806 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27807 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27811 onMouseMove : function(e)
27815 if(!this.canvasLoaded){
27819 if (!this.dragable){
27823 var minX = Math.ceil(this.thumbEl.getLeft(true));
27824 var minY = Math.ceil(this.thumbEl.getTop(true));
27826 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27827 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27829 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27830 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27832 x = x - this.mouseX;
27833 y = y - this.mouseY;
27835 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27836 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27838 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27839 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27841 this.previewEl.setLeft(bgX);
27842 this.previewEl.setTop(bgY);
27844 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27845 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27848 onMouseUp : function(e)
27852 this.dragable = false;
27855 onMouseWheel : function(e)
27859 this.startScale = this.scale;
27861 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27863 if(!this.zoomable()){
27864 this.scale = this.startScale;
27873 zoomable : function()
27875 var minScale = this.thumbEl.getWidth() / this.minWidth;
27877 if(this.minWidth < this.minHeight){
27878 minScale = this.thumbEl.getHeight() / this.minHeight;
27881 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27882 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27886 (this.rotate == 0 || this.rotate == 180) &&
27888 width > this.imageEl.OriginWidth ||
27889 height > this.imageEl.OriginHeight ||
27890 (width < this.minWidth && height < this.minHeight)
27898 (this.rotate == 90 || this.rotate == 270) &&
27900 width > this.imageEl.OriginWidth ||
27901 height > this.imageEl.OriginHeight ||
27902 (width < this.minHeight && height < this.minWidth)
27909 !this.isDocument &&
27910 (this.rotate == 0 || this.rotate == 180) &&
27912 width < this.minWidth ||
27913 width > this.imageEl.OriginWidth ||
27914 height < this.minHeight ||
27915 height > this.imageEl.OriginHeight
27922 !this.isDocument &&
27923 (this.rotate == 90 || this.rotate == 270) &&
27925 width < this.minHeight ||
27926 width > this.imageEl.OriginWidth ||
27927 height < this.minWidth ||
27928 height > this.imageEl.OriginHeight
27938 onRotateLeft : function(e)
27940 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27942 var minScale = this.thumbEl.getWidth() / this.minWidth;
27944 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27945 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27947 this.startScale = this.scale;
27949 while (this.getScaleLevel() < minScale){
27951 this.scale = this.scale + 1;
27953 if(!this.zoomable()){
27958 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27959 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27964 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27971 this.scale = this.startScale;
27973 this.onRotateFail();
27978 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27980 if(this.isDocument){
27981 this.setThumbBoxSize();
27982 this.setThumbBoxPosition();
27983 this.setCanvasPosition();
27988 this.fireEvent('rotate', this, 'left');
27992 onRotateRight : function(e)
27994 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27996 var minScale = this.thumbEl.getWidth() / this.minWidth;
27998 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27999 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28001 this.startScale = this.scale;
28003 while (this.getScaleLevel() < minScale){
28005 this.scale = this.scale + 1;
28007 if(!this.zoomable()){
28012 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28013 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28018 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28025 this.scale = this.startScale;
28027 this.onRotateFail();
28032 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28034 if(this.isDocument){
28035 this.setThumbBoxSize();
28036 this.setThumbBoxPosition();
28037 this.setCanvasPosition();
28042 this.fireEvent('rotate', this, 'right');
28045 onRotateFail : function()
28047 this.errorEl.show(true);
28051 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28056 this.previewEl.dom.innerHTML = '';
28058 var canvasEl = document.createElement("canvas");
28060 var contextEl = canvasEl.getContext("2d");
28062 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28063 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28064 var center = this.imageEl.OriginWidth / 2;
28066 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28067 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28068 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28069 center = this.imageEl.OriginHeight / 2;
28072 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28074 contextEl.translate(center, center);
28075 contextEl.rotate(this.rotate * Math.PI / 180);
28077 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28079 this.canvasEl = document.createElement("canvas");
28081 this.contextEl = this.canvasEl.getContext("2d");
28083 switch (this.rotate) {
28086 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28087 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28089 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28094 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28095 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28097 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28098 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);
28102 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28107 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28108 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28110 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28111 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);
28115 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);
28120 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28121 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28123 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28124 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28128 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);
28135 this.previewEl.appendChild(this.canvasEl);
28137 this.setCanvasPosition();
28142 if(!this.canvasLoaded){
28146 var imageCanvas = document.createElement("canvas");
28148 var imageContext = imageCanvas.getContext("2d");
28150 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28151 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28153 var center = imageCanvas.width / 2;
28155 imageContext.translate(center, center);
28157 imageContext.rotate(this.rotate * Math.PI / 180);
28159 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28161 var canvas = document.createElement("canvas");
28163 var context = canvas.getContext("2d");
28165 canvas.width = this.minWidth;
28166 canvas.height = this.minHeight;
28168 switch (this.rotate) {
28171 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28172 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28174 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28175 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28177 var targetWidth = this.minWidth - 2 * x;
28178 var targetHeight = this.minHeight - 2 * y;
28182 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28183 scale = targetWidth / width;
28186 if(x > 0 && y == 0){
28187 scale = targetHeight / height;
28190 if(x > 0 && y > 0){
28191 scale = targetWidth / width;
28193 if(width < height){
28194 scale = targetHeight / height;
28198 context.scale(scale, scale);
28200 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28201 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28203 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28204 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28206 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28211 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28212 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28214 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28215 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28217 var targetWidth = this.minWidth - 2 * x;
28218 var targetHeight = this.minHeight - 2 * y;
28222 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28223 scale = targetWidth / width;
28226 if(x > 0 && y == 0){
28227 scale = targetHeight / height;
28230 if(x > 0 && y > 0){
28231 scale = targetWidth / width;
28233 if(width < height){
28234 scale = targetHeight / height;
28238 context.scale(scale, scale);
28240 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28241 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28243 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28244 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28246 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28248 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28253 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28254 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28256 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28257 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28259 var targetWidth = this.minWidth - 2 * x;
28260 var targetHeight = this.minHeight - 2 * y;
28264 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28265 scale = targetWidth / width;
28268 if(x > 0 && y == 0){
28269 scale = targetHeight / height;
28272 if(x > 0 && y > 0){
28273 scale = targetWidth / width;
28275 if(width < height){
28276 scale = targetHeight / height;
28280 context.scale(scale, scale);
28282 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28283 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28285 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28286 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28288 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28289 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28291 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28296 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28297 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28299 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28300 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28302 var targetWidth = this.minWidth - 2 * x;
28303 var targetHeight = this.minHeight - 2 * y;
28307 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28308 scale = targetWidth / width;
28311 if(x > 0 && y == 0){
28312 scale = targetHeight / height;
28315 if(x > 0 && y > 0){
28316 scale = targetWidth / width;
28318 if(width < height){
28319 scale = targetHeight / height;
28323 context.scale(scale, scale);
28325 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28326 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28328 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28329 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28331 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28333 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28340 this.cropData = canvas.toDataURL(this.cropType);
28342 if(this.fireEvent('crop', this, this.cropData) !== false){
28343 this.process(this.file, this.cropData);
28350 setThumbBoxSize : function()
28354 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28355 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28356 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28358 this.minWidth = width;
28359 this.minHeight = height;
28361 if(this.rotate == 90 || this.rotate == 270){
28362 this.minWidth = height;
28363 this.minHeight = width;
28368 width = Math.ceil(this.minWidth * height / this.minHeight);
28370 if(this.minWidth > this.minHeight){
28372 height = Math.ceil(this.minHeight * width / this.minWidth);
28375 this.thumbEl.setStyle({
28376 width : width + 'px',
28377 height : height + 'px'
28384 setThumbBoxPosition : function()
28386 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28387 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28389 this.thumbEl.setLeft(x);
28390 this.thumbEl.setTop(y);
28394 baseRotateLevel : function()
28396 this.baseRotate = 1;
28399 typeof(this.exif) != 'undefined' &&
28400 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28401 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28403 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28406 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28410 baseScaleLevel : function()
28414 if(this.isDocument){
28416 if(this.baseRotate == 6 || this.baseRotate == 8){
28418 height = this.thumbEl.getHeight();
28419 this.baseScale = height / this.imageEl.OriginWidth;
28421 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28422 width = this.thumbEl.getWidth();
28423 this.baseScale = width / this.imageEl.OriginHeight;
28429 height = this.thumbEl.getHeight();
28430 this.baseScale = height / this.imageEl.OriginHeight;
28432 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28433 width = this.thumbEl.getWidth();
28434 this.baseScale = width / this.imageEl.OriginWidth;
28440 if(this.baseRotate == 6 || this.baseRotate == 8){
28442 width = this.thumbEl.getHeight();
28443 this.baseScale = width / this.imageEl.OriginHeight;
28445 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28446 height = this.thumbEl.getWidth();
28447 this.baseScale = height / this.imageEl.OriginHeight;
28450 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28451 height = this.thumbEl.getWidth();
28452 this.baseScale = height / this.imageEl.OriginHeight;
28454 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28455 width = this.thumbEl.getHeight();
28456 this.baseScale = width / this.imageEl.OriginWidth;
28463 width = this.thumbEl.getWidth();
28464 this.baseScale = width / this.imageEl.OriginWidth;
28466 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28467 height = this.thumbEl.getHeight();
28468 this.baseScale = height / this.imageEl.OriginHeight;
28471 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28473 height = this.thumbEl.getHeight();
28474 this.baseScale = height / this.imageEl.OriginHeight;
28476 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28477 width = this.thumbEl.getWidth();
28478 this.baseScale = width / this.imageEl.OriginWidth;
28486 getScaleLevel : function()
28488 return this.baseScale * Math.pow(1.1, this.scale);
28491 onTouchStart : function(e)
28493 if(!this.canvasLoaded){
28494 this.beforeSelectFile(e);
28498 var touches = e.browserEvent.touches;
28504 if(touches.length == 1){
28505 this.onMouseDown(e);
28509 if(touches.length != 2){
28515 for(var i = 0, finger; finger = touches[i]; i++){
28516 coords.push(finger.pageX, finger.pageY);
28519 var x = Math.pow(coords[0] - coords[2], 2);
28520 var y = Math.pow(coords[1] - coords[3], 2);
28522 this.startDistance = Math.sqrt(x + y);
28524 this.startScale = this.scale;
28526 this.pinching = true;
28527 this.dragable = false;
28531 onTouchMove : function(e)
28533 if(!this.pinching && !this.dragable){
28537 var touches = e.browserEvent.touches;
28544 this.onMouseMove(e);
28550 for(var i = 0, finger; finger = touches[i]; i++){
28551 coords.push(finger.pageX, finger.pageY);
28554 var x = Math.pow(coords[0] - coords[2], 2);
28555 var y = Math.pow(coords[1] - coords[3], 2);
28557 this.endDistance = Math.sqrt(x + y);
28559 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28561 if(!this.zoomable()){
28562 this.scale = this.startScale;
28570 onTouchEnd : function(e)
28572 this.pinching = false;
28573 this.dragable = false;
28577 process : function(file, crop)
28580 this.maskEl.mask(this.loadingText);
28583 this.xhr = new XMLHttpRequest();
28585 file.xhr = this.xhr;
28587 this.xhr.open(this.method, this.url, true);
28590 "Accept": "application/json",
28591 "Cache-Control": "no-cache",
28592 "X-Requested-With": "XMLHttpRequest"
28595 for (var headerName in headers) {
28596 var headerValue = headers[headerName];
28598 this.xhr.setRequestHeader(headerName, headerValue);
28604 this.xhr.onload = function()
28606 _this.xhrOnLoad(_this.xhr);
28609 this.xhr.onerror = function()
28611 _this.xhrOnError(_this.xhr);
28614 var formData = new FormData();
28616 formData.append('returnHTML', 'NO');
28619 formData.append('crop', crop);
28622 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28623 formData.append(this.paramName, file, file.name);
28626 if(typeof(file.filename) != 'undefined'){
28627 formData.append('filename', file.filename);
28630 if(typeof(file.mimetype) != 'undefined'){
28631 formData.append('mimetype', file.mimetype);
28634 if(this.fireEvent('arrange', this, formData) != false){
28635 this.xhr.send(formData);
28639 xhrOnLoad : function(xhr)
28642 this.maskEl.unmask();
28645 if (xhr.readyState !== 4) {
28646 this.fireEvent('exception', this, xhr);
28650 var response = Roo.decode(xhr.responseText);
28652 if(!response.success){
28653 this.fireEvent('exception', this, xhr);
28657 var response = Roo.decode(xhr.responseText);
28659 this.fireEvent('upload', this, response);
28663 xhrOnError : function()
28666 this.maskEl.unmask();
28669 Roo.log('xhr on error');
28671 var response = Roo.decode(xhr.responseText);
28677 prepare : function(file)
28680 this.maskEl.mask(this.loadingText);
28686 if(typeof(file) === 'string'){
28687 this.loadCanvas(file);
28691 if(!file || !this.urlAPI){
28696 this.cropType = file.type;
28700 if(this.fireEvent('prepare', this, this.file) != false){
28702 var reader = new FileReader();
28704 reader.onload = function (e) {
28705 if (e.target.error) {
28706 Roo.log(e.target.error);
28710 var buffer = e.target.result,
28711 dataView = new DataView(buffer),
28713 maxOffset = dataView.byteLength - 4,
28717 if (dataView.getUint16(0) === 0xffd8) {
28718 while (offset < maxOffset) {
28719 markerBytes = dataView.getUint16(offset);
28721 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28722 markerLength = dataView.getUint16(offset + 2) + 2;
28723 if (offset + markerLength > dataView.byteLength) {
28724 Roo.log('Invalid meta data: Invalid segment size.');
28728 if(markerBytes == 0xffe1){
28729 _this.parseExifData(
28736 offset += markerLength;
28746 var url = _this.urlAPI.createObjectURL(_this.file);
28748 _this.loadCanvas(url);
28753 reader.readAsArrayBuffer(this.file);
28759 parseExifData : function(dataView, offset, length)
28761 var tiffOffset = offset + 10,
28765 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28766 // No Exif data, might be XMP data instead
28770 // Check for the ASCII code for "Exif" (0x45786966):
28771 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28772 // No Exif data, might be XMP data instead
28775 if (tiffOffset + 8 > dataView.byteLength) {
28776 Roo.log('Invalid Exif data: Invalid segment size.');
28779 // Check for the two null bytes:
28780 if (dataView.getUint16(offset + 8) !== 0x0000) {
28781 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28784 // Check the byte alignment:
28785 switch (dataView.getUint16(tiffOffset)) {
28787 littleEndian = true;
28790 littleEndian = false;
28793 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28796 // Check for the TIFF tag marker (0x002A):
28797 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28798 Roo.log('Invalid Exif data: Missing TIFF marker.');
28801 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28802 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28804 this.parseExifTags(
28807 tiffOffset + dirOffset,
28812 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28817 if (dirOffset + 6 > dataView.byteLength) {
28818 Roo.log('Invalid Exif data: Invalid directory offset.');
28821 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28822 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28823 if (dirEndOffset + 4 > dataView.byteLength) {
28824 Roo.log('Invalid Exif data: Invalid directory size.');
28827 for (i = 0; i < tagsNumber; i += 1) {
28831 dirOffset + 2 + 12 * i, // tag offset
28835 // Return the offset to the next directory:
28836 return dataView.getUint32(dirEndOffset, littleEndian);
28839 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28841 var tag = dataView.getUint16(offset, littleEndian);
28843 this.exif[tag] = this.getExifValue(
28847 dataView.getUint16(offset + 2, littleEndian), // tag type
28848 dataView.getUint32(offset + 4, littleEndian), // tag length
28853 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28855 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28864 Roo.log('Invalid Exif data: Invalid tag type.');
28868 tagSize = tagType.size * length;
28869 // Determine if the value is contained in the dataOffset bytes,
28870 // or if the value at the dataOffset is a pointer to the actual data:
28871 dataOffset = tagSize > 4 ?
28872 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28873 if (dataOffset + tagSize > dataView.byteLength) {
28874 Roo.log('Invalid Exif data: Invalid data offset.');
28877 if (length === 1) {
28878 return tagType.getValue(dataView, dataOffset, littleEndian);
28881 for (i = 0; i < length; i += 1) {
28882 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28885 if (tagType.ascii) {
28887 // Concatenate the chars:
28888 for (i = 0; i < values.length; i += 1) {
28890 // Ignore the terminating NULL byte(s):
28891 if (c === '\u0000') {
28903 Roo.apply(Roo.bootstrap.UploadCropbox, {
28905 'Orientation': 0x0112
28909 1: 0, //'top-left',
28911 3: 180, //'bottom-right',
28912 // 4: 'bottom-left',
28914 6: 90, //'right-top',
28915 // 7: 'right-bottom',
28916 8: 270 //'left-bottom'
28920 // byte, 8-bit unsigned int:
28922 getValue: function (dataView, dataOffset) {
28923 return dataView.getUint8(dataOffset);
28927 // ascii, 8-bit byte:
28929 getValue: function (dataView, dataOffset) {
28930 return String.fromCharCode(dataView.getUint8(dataOffset));
28935 // short, 16 bit int:
28937 getValue: function (dataView, dataOffset, littleEndian) {
28938 return dataView.getUint16(dataOffset, littleEndian);
28942 // long, 32 bit int:
28944 getValue: function (dataView, dataOffset, littleEndian) {
28945 return dataView.getUint32(dataOffset, littleEndian);
28949 // rational = two long values, first is numerator, second is denominator:
28951 getValue: function (dataView, dataOffset, littleEndian) {
28952 return dataView.getUint32(dataOffset, littleEndian) /
28953 dataView.getUint32(dataOffset + 4, littleEndian);
28957 // slong, 32 bit signed int:
28959 getValue: function (dataView, dataOffset, littleEndian) {
28960 return dataView.getInt32(dataOffset, littleEndian);
28964 // srational, two slongs, first is numerator, second is denominator:
28966 getValue: function (dataView, dataOffset, littleEndian) {
28967 return dataView.getInt32(dataOffset, littleEndian) /
28968 dataView.getInt32(dataOffset + 4, littleEndian);
28978 cls : 'btn-group roo-upload-cropbox-rotate-left',
28979 action : 'rotate-left',
28983 cls : 'btn btn-default',
28984 html : '<i class="fa fa-undo"></i>'
28990 cls : 'btn-group roo-upload-cropbox-picture',
28991 action : 'picture',
28995 cls : 'btn btn-default',
28996 html : '<i class="fa fa-picture-o"></i>'
29002 cls : 'btn-group roo-upload-cropbox-rotate-right',
29003 action : 'rotate-right',
29007 cls : 'btn btn-default',
29008 html : '<i class="fa fa-repeat"></i>'
29016 cls : 'btn-group roo-upload-cropbox-rotate-left',
29017 action : 'rotate-left',
29021 cls : 'btn btn-default',
29022 html : '<i class="fa fa-undo"></i>'
29028 cls : 'btn-group roo-upload-cropbox-download',
29029 action : 'download',
29033 cls : 'btn btn-default',
29034 html : '<i class="fa fa-download"></i>'
29040 cls : 'btn-group roo-upload-cropbox-crop',
29045 cls : 'btn btn-default',
29046 html : '<i class="fa fa-crop"></i>'
29052 cls : 'btn-group roo-upload-cropbox-trash',
29057 cls : 'btn btn-default',
29058 html : '<i class="fa fa-trash"></i>'
29064 cls : 'btn-group roo-upload-cropbox-rotate-right',
29065 action : 'rotate-right',
29069 cls : 'btn btn-default',
29070 html : '<i class="fa fa-repeat"></i>'
29078 cls : 'btn-group roo-upload-cropbox-rotate-left',
29079 action : 'rotate-left',
29083 cls : 'btn btn-default',
29084 html : '<i class="fa fa-undo"></i>'
29090 cls : 'btn-group roo-upload-cropbox-rotate-right',
29091 action : 'rotate-right',
29095 cls : 'btn btn-default',
29096 html : '<i class="fa fa-repeat"></i>'
29109 * @class Roo.bootstrap.DocumentManager
29110 * @extends Roo.bootstrap.Component
29111 * Bootstrap DocumentManager class
29112 * @cfg {String} paramName default 'imageUpload'
29113 * @cfg {String} toolTipName default 'filename'
29114 * @cfg {String} method default POST
29115 * @cfg {String} url action url
29116 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29117 * @cfg {Boolean} multiple multiple upload default true
29118 * @cfg {Number} thumbSize default 300
29119 * @cfg {String} fieldLabel
29120 * @cfg {Number} labelWidth default 4
29121 * @cfg {String} labelAlign (left|top) default left
29122 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29123 * @cfg {Number} labellg set the width of label (1-12)
29124 * @cfg {Number} labelmd set the width of label (1-12)
29125 * @cfg {Number} labelsm set the width of label (1-12)
29126 * @cfg {Number} labelxs set the width of label (1-12)
29129 * Create a new DocumentManager
29130 * @param {Object} config The config object
29133 Roo.bootstrap.DocumentManager = function(config){
29134 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29137 this.delegates = [];
29142 * Fire when initial the DocumentManager
29143 * @param {Roo.bootstrap.DocumentManager} this
29148 * inspect selected file
29149 * @param {Roo.bootstrap.DocumentManager} this
29150 * @param {File} file
29155 * Fire when xhr load exception
29156 * @param {Roo.bootstrap.DocumentManager} this
29157 * @param {XMLHttpRequest} xhr
29159 "exception" : true,
29161 * @event afterupload
29162 * Fire when xhr load exception
29163 * @param {Roo.bootstrap.DocumentManager} this
29164 * @param {XMLHttpRequest} xhr
29166 "afterupload" : true,
29169 * prepare the form data
29170 * @param {Roo.bootstrap.DocumentManager} this
29171 * @param {Object} formData
29176 * Fire when remove the file
29177 * @param {Roo.bootstrap.DocumentManager} this
29178 * @param {Object} file
29183 * Fire after refresh the file
29184 * @param {Roo.bootstrap.DocumentManager} this
29189 * Fire after click the image
29190 * @param {Roo.bootstrap.DocumentManager} this
29191 * @param {Object} file
29196 * Fire when upload a image and editable set to true
29197 * @param {Roo.bootstrap.DocumentManager} this
29198 * @param {Object} file
29202 * @event beforeselectfile
29203 * Fire before select file
29204 * @param {Roo.bootstrap.DocumentManager} this
29206 "beforeselectfile" : true,
29209 * Fire before process file
29210 * @param {Roo.bootstrap.DocumentManager} this
29211 * @param {Object} file
29215 * @event previewrendered
29216 * Fire when preview rendered
29217 * @param {Roo.bootstrap.DocumentManager} this
29218 * @param {Object} file
29220 "previewrendered" : true,
29223 "previewResize" : true
29228 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29237 paramName : 'imageUpload',
29238 toolTipName : 'filename',
29241 labelAlign : 'left',
29251 getAutoCreate : function()
29253 var managerWidget = {
29255 cls : 'roo-document-manager',
29259 cls : 'roo-document-manager-selector',
29264 cls : 'roo-document-manager-uploader',
29268 cls : 'roo-document-manager-upload-btn',
29269 html : '<i class="fa fa-plus"></i>'
29280 cls : 'column col-md-12',
29285 if(this.fieldLabel.length){
29290 cls : 'column col-md-12',
29291 html : this.fieldLabel
29295 cls : 'column col-md-12',
29300 if(this.labelAlign == 'left'){
29305 html : this.fieldLabel
29314 if(this.labelWidth > 12){
29315 content[0].style = "width: " + this.labelWidth + 'px';
29318 if(this.labelWidth < 13 && this.labelmd == 0){
29319 this.labelmd = this.labelWidth;
29322 if(this.labellg > 0){
29323 content[0].cls += ' col-lg-' + this.labellg;
29324 content[1].cls += ' col-lg-' + (12 - this.labellg);
29327 if(this.labelmd > 0){
29328 content[0].cls += ' col-md-' + this.labelmd;
29329 content[1].cls += ' col-md-' + (12 - this.labelmd);
29332 if(this.labelsm > 0){
29333 content[0].cls += ' col-sm-' + this.labelsm;
29334 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29337 if(this.labelxs > 0){
29338 content[0].cls += ' col-xs-' + this.labelxs;
29339 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29347 cls : 'row clearfix',
29355 initEvents : function()
29357 this.managerEl = this.el.select('.roo-document-manager', true).first();
29358 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29360 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29361 this.selectorEl.hide();
29364 this.selectorEl.attr('multiple', 'multiple');
29367 this.selectorEl.on('change', this.onFileSelected, this);
29369 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29370 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29372 this.uploader.on('click', this.onUploaderClick, this);
29374 this.renderProgressDialog();
29378 window.addEventListener("resize", function() { _this.refresh(); } );
29380 this.fireEvent('initial', this);
29383 renderProgressDialog : function()
29387 this.progressDialog = new Roo.bootstrap.Modal({
29388 cls : 'roo-document-manager-progress-dialog',
29389 allow_close : false,
29400 btnclick : function() {
29401 _this.uploadCancel();
29407 this.progressDialog.render(Roo.get(document.body));
29409 this.progress = new Roo.bootstrap.Progress({
29410 cls : 'roo-document-manager-progress',
29415 this.progress.render(this.progressDialog.getChildContainer());
29417 this.progressBar = new Roo.bootstrap.ProgressBar({
29418 cls : 'roo-document-manager-progress-bar',
29421 aria_valuemax : 12,
29425 this.progressBar.render(this.progress.getChildContainer());
29428 onUploaderClick : function(e)
29430 e.preventDefault();
29432 if(this.fireEvent('beforeselectfile', this) != false){
29433 this.selectorEl.dom.click();
29438 onFileSelected : function(e)
29440 e.preventDefault();
29442 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29446 Roo.each(this.selectorEl.dom.files, function(file){
29447 if(this.fireEvent('inspect', this, file) != false){
29448 this.files.push(file);
29458 this.selectorEl.dom.value = '';
29460 if(!this.files || !this.files.length){
29464 if(this.boxes > 0 && this.files.length > this.boxes){
29465 this.files = this.files.slice(0, this.boxes);
29468 this.uploader.show();
29470 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29471 this.uploader.hide();
29480 Roo.each(this.files, function(file){
29482 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29483 var f = this.renderPreview(file);
29488 if(file.type.indexOf('image') != -1){
29489 this.delegates.push(
29491 _this.process(file);
29492 }).createDelegate(this)
29500 _this.process(file);
29501 }).createDelegate(this)
29506 this.files = files;
29508 this.delegates = this.delegates.concat(docs);
29510 if(!this.delegates.length){
29515 this.progressBar.aria_valuemax = this.delegates.length;
29522 arrange : function()
29524 if(!this.delegates.length){
29525 this.progressDialog.hide();
29530 var delegate = this.delegates.shift();
29532 this.progressDialog.show();
29534 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29536 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29541 refresh : function()
29543 this.uploader.show();
29545 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29546 this.uploader.hide();
29549 Roo.isTouch ? this.closable(false) : this.closable(true);
29551 this.fireEvent('refresh', this);
29554 onRemove : function(e, el, o)
29556 e.preventDefault();
29558 this.fireEvent('remove', this, o);
29562 remove : function(o)
29566 Roo.each(this.files, function(file){
29567 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29576 this.files = files;
29583 Roo.each(this.files, function(file){
29588 file.target.remove();
29597 onClick : function(e, el, o)
29599 e.preventDefault();
29601 this.fireEvent('click', this, o);
29605 closable : function(closable)
29607 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29609 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29621 xhrOnLoad : function(xhr)
29623 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29627 if (xhr.readyState !== 4) {
29629 this.fireEvent('exception', this, xhr);
29633 var response = Roo.decode(xhr.responseText);
29635 if(!response.success){
29637 this.fireEvent('exception', this, xhr);
29641 var file = this.renderPreview(response.data);
29643 this.files.push(file);
29647 this.fireEvent('afterupload', this, xhr);
29651 xhrOnError : function(xhr)
29653 Roo.log('xhr on error');
29655 var response = Roo.decode(xhr.responseText);
29662 process : function(file)
29664 if(this.fireEvent('process', this, file) !== false){
29665 if(this.editable && file.type.indexOf('image') != -1){
29666 this.fireEvent('edit', this, file);
29670 this.uploadStart(file, false);
29677 uploadStart : function(file, crop)
29679 this.xhr = new XMLHttpRequest();
29681 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29686 file.xhr = this.xhr;
29688 this.managerEl.createChild({
29690 cls : 'roo-document-manager-loading',
29694 tooltip : file.name,
29695 cls : 'roo-document-manager-thumb',
29696 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29702 this.xhr.open(this.method, this.url, true);
29705 "Accept": "application/json",
29706 "Cache-Control": "no-cache",
29707 "X-Requested-With": "XMLHttpRequest"
29710 for (var headerName in headers) {
29711 var headerValue = headers[headerName];
29713 this.xhr.setRequestHeader(headerName, headerValue);
29719 this.xhr.onload = function()
29721 _this.xhrOnLoad(_this.xhr);
29724 this.xhr.onerror = function()
29726 _this.xhrOnError(_this.xhr);
29729 var formData = new FormData();
29731 formData.append('returnHTML', 'NO');
29734 formData.append('crop', crop);
29737 formData.append(this.paramName, file, file.name);
29744 if(this.fireEvent('prepare', this, formData, options) != false){
29746 if(options.manually){
29750 this.xhr.send(formData);
29754 this.uploadCancel();
29757 uploadCancel : function()
29763 this.delegates = [];
29765 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29772 renderPreview : function(file)
29774 if(typeof(file.target) != 'undefined' && file.target){
29778 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29780 var previewEl = this.managerEl.createChild({
29782 cls : 'roo-document-manager-preview',
29786 tooltip : file[this.toolTipName],
29787 cls : 'roo-document-manager-thumb',
29788 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29793 html : '<i class="fa fa-times-circle"></i>'
29798 var close = previewEl.select('button.close', true).first();
29800 close.on('click', this.onRemove, this, file);
29802 file.target = previewEl;
29804 var image = previewEl.select('img', true).first();
29808 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29810 image.on('click', this.onClick, this, file);
29812 this.fireEvent('previewrendered', this, file);
29818 onPreviewLoad : function(file, image)
29820 if(typeof(file.target) == 'undefined' || !file.target){
29824 var width = image.dom.naturalWidth || image.dom.width;
29825 var height = image.dom.naturalHeight || image.dom.height;
29827 if(!this.previewResize) {
29831 if(width > height){
29832 file.target.addClass('wide');
29836 file.target.addClass('tall');
29841 uploadFromSource : function(file, crop)
29843 this.xhr = new XMLHttpRequest();
29845 this.managerEl.createChild({
29847 cls : 'roo-document-manager-loading',
29851 tooltip : file.name,
29852 cls : 'roo-document-manager-thumb',
29853 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29859 this.xhr.open(this.method, this.url, true);
29862 "Accept": "application/json",
29863 "Cache-Control": "no-cache",
29864 "X-Requested-With": "XMLHttpRequest"
29867 for (var headerName in headers) {
29868 var headerValue = headers[headerName];
29870 this.xhr.setRequestHeader(headerName, headerValue);
29876 this.xhr.onload = function()
29878 _this.xhrOnLoad(_this.xhr);
29881 this.xhr.onerror = function()
29883 _this.xhrOnError(_this.xhr);
29886 var formData = new FormData();
29888 formData.append('returnHTML', 'NO');
29890 formData.append('crop', crop);
29892 if(typeof(file.filename) != 'undefined'){
29893 formData.append('filename', file.filename);
29896 if(typeof(file.mimetype) != 'undefined'){
29897 formData.append('mimetype', file.mimetype);
29902 if(this.fireEvent('prepare', this, formData) != false){
29903 this.xhr.send(formData);
29913 * @class Roo.bootstrap.DocumentViewer
29914 * @extends Roo.bootstrap.Component
29915 * Bootstrap DocumentViewer class
29916 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29917 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29920 * Create a new DocumentViewer
29921 * @param {Object} config The config object
29924 Roo.bootstrap.DocumentViewer = function(config){
29925 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29930 * Fire after initEvent
29931 * @param {Roo.bootstrap.DocumentViewer} this
29937 * @param {Roo.bootstrap.DocumentViewer} this
29942 * Fire after download button
29943 * @param {Roo.bootstrap.DocumentViewer} this
29948 * Fire after trash button
29949 * @param {Roo.bootstrap.DocumentViewer} this
29956 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29958 showDownload : true,
29962 getAutoCreate : function()
29966 cls : 'roo-document-viewer',
29970 cls : 'roo-document-viewer-body',
29974 cls : 'roo-document-viewer-thumb',
29978 cls : 'roo-document-viewer-image'
29986 cls : 'roo-document-viewer-footer',
29989 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29993 cls : 'btn-group roo-document-viewer-download',
29997 cls : 'btn btn-default',
29998 html : '<i class="fa fa-download"></i>'
30004 cls : 'btn-group roo-document-viewer-trash',
30008 cls : 'btn btn-default',
30009 html : '<i class="fa fa-trash"></i>'
30022 initEvents : function()
30024 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30025 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30027 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30028 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30030 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30031 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30033 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30034 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30036 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30037 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30039 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30040 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30042 this.bodyEl.on('click', this.onClick, this);
30043 this.downloadBtn.on('click', this.onDownload, this);
30044 this.trashBtn.on('click', this.onTrash, this);
30046 this.downloadBtn.hide();
30047 this.trashBtn.hide();
30049 if(this.showDownload){
30050 this.downloadBtn.show();
30053 if(this.showTrash){
30054 this.trashBtn.show();
30057 if(!this.showDownload && !this.showTrash) {
30058 this.footerEl.hide();
30063 initial : function()
30065 this.fireEvent('initial', this);
30069 onClick : function(e)
30071 e.preventDefault();
30073 this.fireEvent('click', this);
30076 onDownload : function(e)
30078 e.preventDefault();
30080 this.fireEvent('download', this);
30083 onTrash : function(e)
30085 e.preventDefault();
30087 this.fireEvent('trash', this);
30099 * @class Roo.bootstrap.NavProgressBar
30100 * @extends Roo.bootstrap.Component
30101 * Bootstrap NavProgressBar class
30104 * Create a new nav progress bar
30105 * @param {Object} config The config object
30108 Roo.bootstrap.NavProgressBar = function(config){
30109 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30111 this.bullets = this.bullets || [];
30113 // Roo.bootstrap.NavProgressBar.register(this);
30117 * Fires when the active item changes
30118 * @param {Roo.bootstrap.NavProgressBar} this
30119 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30120 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30127 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30132 getAutoCreate : function()
30134 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30138 cls : 'roo-navigation-bar-group',
30142 cls : 'roo-navigation-top-bar'
30146 cls : 'roo-navigation-bullets-bar',
30150 cls : 'roo-navigation-bar'
30157 cls : 'roo-navigation-bottom-bar'
30167 initEvents: function()
30172 onRender : function(ct, position)
30174 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30176 if(this.bullets.length){
30177 Roo.each(this.bullets, function(b){
30186 addItem : function(cfg)
30188 var item = new Roo.bootstrap.NavProgressItem(cfg);
30190 item.parentId = this.id;
30191 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30194 var top = new Roo.bootstrap.Element({
30196 cls : 'roo-navigation-bar-text'
30199 var bottom = new Roo.bootstrap.Element({
30201 cls : 'roo-navigation-bar-text'
30204 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30205 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30207 var topText = new Roo.bootstrap.Element({
30209 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30212 var bottomText = new Roo.bootstrap.Element({
30214 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30217 topText.onRender(top.el, null);
30218 bottomText.onRender(bottom.el, null);
30221 item.bottomEl = bottom;
30224 this.barItems.push(item);
30229 getActive : function()
30231 var active = false;
30233 Roo.each(this.barItems, function(v){
30235 if (!v.isActive()) {
30247 setActiveItem : function(item)
30251 Roo.each(this.barItems, function(v){
30252 if (v.rid == item.rid) {
30256 if (v.isActive()) {
30257 v.setActive(false);
30262 item.setActive(true);
30264 this.fireEvent('changed', this, item, prev);
30267 getBarItem: function(rid)
30271 Roo.each(this.barItems, function(e) {
30272 if (e.rid != rid) {
30283 indexOfItem : function(item)
30287 Roo.each(this.barItems, function(v, i){
30289 if (v.rid != item.rid) {
30300 setActiveNext : function()
30302 var i = this.indexOfItem(this.getActive());
30304 if (i > this.barItems.length) {
30308 this.setActiveItem(this.barItems[i+1]);
30311 setActivePrev : function()
30313 var i = this.indexOfItem(this.getActive());
30319 this.setActiveItem(this.barItems[i-1]);
30322 format : function()
30324 if(!this.barItems.length){
30328 var width = 100 / this.barItems.length;
30330 Roo.each(this.barItems, function(i){
30331 i.el.setStyle('width', width + '%');
30332 i.topEl.el.setStyle('width', width + '%');
30333 i.bottomEl.el.setStyle('width', width + '%');
30342 * Nav Progress Item
30347 * @class Roo.bootstrap.NavProgressItem
30348 * @extends Roo.bootstrap.Component
30349 * Bootstrap NavProgressItem class
30350 * @cfg {String} rid the reference id
30351 * @cfg {Boolean} active (true|false) Is item active default false
30352 * @cfg {Boolean} disabled (true|false) Is item active default false
30353 * @cfg {String} html
30354 * @cfg {String} position (top|bottom) text position default bottom
30355 * @cfg {String} icon show icon instead of number
30358 * Create a new NavProgressItem
30359 * @param {Object} config The config object
30361 Roo.bootstrap.NavProgressItem = function(config){
30362 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30367 * The raw click event for the entire grid.
30368 * @param {Roo.bootstrap.NavProgressItem} this
30369 * @param {Roo.EventObject} e
30376 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30382 position : 'bottom',
30385 getAutoCreate : function()
30387 var iconCls = 'roo-navigation-bar-item-icon';
30389 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30393 cls: 'roo-navigation-bar-item',
30403 cfg.cls += ' active';
30406 cfg.cls += ' disabled';
30412 disable : function()
30414 this.setDisabled(true);
30417 enable : function()
30419 this.setDisabled(false);
30422 initEvents: function()
30424 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30426 this.iconEl.on('click', this.onClick, this);
30429 onClick : function(e)
30431 e.preventDefault();
30437 if(this.fireEvent('click', this, e) === false){
30441 this.parent().setActiveItem(this);
30444 isActive: function ()
30446 return this.active;
30449 setActive : function(state)
30451 if(this.active == state){
30455 this.active = state;
30458 this.el.addClass('active');
30462 this.el.removeClass('active');
30467 setDisabled : function(state)
30469 if(this.disabled == state){
30473 this.disabled = state;
30476 this.el.addClass('disabled');
30480 this.el.removeClass('disabled');
30483 tooltipEl : function()
30485 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30498 * @class Roo.bootstrap.FieldLabel
30499 * @extends Roo.bootstrap.Component
30500 * Bootstrap FieldLabel class
30501 * @cfg {String} html contents of the element
30502 * @cfg {String} tag tag of the element default label
30503 * @cfg {String} cls class of the element
30504 * @cfg {String} target label target
30505 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30506 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30507 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30508 * @cfg {String} iconTooltip default "This field is required"
30509 * @cfg {String} indicatorpos (left|right) default left
30512 * Create a new FieldLabel
30513 * @param {Object} config The config object
30516 Roo.bootstrap.FieldLabel = function(config){
30517 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30522 * Fires after the field has been marked as invalid.
30523 * @param {Roo.form.FieldLabel} this
30524 * @param {String} msg The validation message
30529 * Fires after the field has been validated with no errors.
30530 * @param {Roo.form.FieldLabel} this
30536 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30543 invalidClass : 'has-warning',
30544 validClass : 'has-success',
30545 iconTooltip : 'This field is required',
30546 indicatorpos : 'left',
30548 getAutoCreate : function(){
30551 if (!this.allowBlank) {
30557 cls : 'roo-bootstrap-field-label ' + this.cls,
30562 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30563 tooltip : this.iconTooltip
30572 if(this.indicatorpos == 'right'){
30575 cls : 'roo-bootstrap-field-label ' + this.cls,
30584 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30585 tooltip : this.iconTooltip
30594 initEvents: function()
30596 Roo.bootstrap.Element.superclass.initEvents.call(this);
30598 this.indicator = this.indicatorEl();
30600 if(this.indicator){
30601 this.indicator.removeClass('visible');
30602 this.indicator.addClass('invisible');
30605 Roo.bootstrap.FieldLabel.register(this);
30608 indicatorEl : function()
30610 var indicator = this.el.select('i.roo-required-indicator',true).first();
30621 * Mark this field as valid
30623 markValid : function()
30625 if(this.indicator){
30626 this.indicator.removeClass('visible');
30627 this.indicator.addClass('invisible');
30629 if (Roo.bootstrap.version == 3) {
30630 this.el.removeClass(this.invalidClass);
30631 this.el.addClass(this.validClass);
30633 this.el.removeClass('is-invalid');
30634 this.el.addClass('is-valid');
30638 this.fireEvent('valid', this);
30642 * Mark this field as invalid
30643 * @param {String} msg The validation message
30645 markInvalid : function(msg)
30647 if(this.indicator){
30648 this.indicator.removeClass('invisible');
30649 this.indicator.addClass('visible');
30651 if (Roo.bootstrap.version == 3) {
30652 this.el.removeClass(this.validClass);
30653 this.el.addClass(this.invalidClass);
30655 this.el.removeClass('is-valid');
30656 this.el.addClass('is-invalid');
30660 this.fireEvent('invalid', this, msg);
30666 Roo.apply(Roo.bootstrap.FieldLabel, {
30671 * register a FieldLabel Group
30672 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30674 register : function(label)
30676 if(this.groups.hasOwnProperty(label.target)){
30680 this.groups[label.target] = label;
30684 * fetch a FieldLabel Group based on the target
30685 * @param {string} target
30686 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30688 get: function(target) {
30689 if (typeof(this.groups[target]) == 'undefined') {
30693 return this.groups[target] ;
30702 * page DateSplitField.
30708 * @class Roo.bootstrap.DateSplitField
30709 * @extends Roo.bootstrap.Component
30710 * Bootstrap DateSplitField class
30711 * @cfg {string} fieldLabel - the label associated
30712 * @cfg {Number} labelWidth set the width of label (0-12)
30713 * @cfg {String} labelAlign (top|left)
30714 * @cfg {Boolean} dayAllowBlank (true|false) default false
30715 * @cfg {Boolean} monthAllowBlank (true|false) default false
30716 * @cfg {Boolean} yearAllowBlank (true|false) default false
30717 * @cfg {string} dayPlaceholder
30718 * @cfg {string} monthPlaceholder
30719 * @cfg {string} yearPlaceholder
30720 * @cfg {string} dayFormat default 'd'
30721 * @cfg {string} monthFormat default 'm'
30722 * @cfg {string} yearFormat default 'Y'
30723 * @cfg {Number} labellg set the width of label (1-12)
30724 * @cfg {Number} labelmd set the width of label (1-12)
30725 * @cfg {Number} labelsm set the width of label (1-12)
30726 * @cfg {Number} labelxs set the width of label (1-12)
30730 * Create a new DateSplitField
30731 * @param {Object} config The config object
30734 Roo.bootstrap.DateSplitField = function(config){
30735 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30741 * getting the data of years
30742 * @param {Roo.bootstrap.DateSplitField} this
30743 * @param {Object} years
30748 * getting the data of days
30749 * @param {Roo.bootstrap.DateSplitField} this
30750 * @param {Object} days
30755 * Fires after the field has been marked as invalid.
30756 * @param {Roo.form.Field} this
30757 * @param {String} msg The validation message
30762 * Fires after the field has been validated with no errors.
30763 * @param {Roo.form.Field} this
30769 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30772 labelAlign : 'top',
30774 dayAllowBlank : false,
30775 monthAllowBlank : false,
30776 yearAllowBlank : false,
30777 dayPlaceholder : '',
30778 monthPlaceholder : '',
30779 yearPlaceholder : '',
30783 isFormField : true,
30789 getAutoCreate : function()
30793 cls : 'row roo-date-split-field-group',
30798 cls : 'form-hidden-field roo-date-split-field-group-value',
30804 var labelCls = 'col-md-12';
30805 var contentCls = 'col-md-4';
30807 if(this.fieldLabel){
30811 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30815 html : this.fieldLabel
30820 if(this.labelAlign == 'left'){
30822 if(this.labelWidth > 12){
30823 label.style = "width: " + this.labelWidth + 'px';
30826 if(this.labelWidth < 13 && this.labelmd == 0){
30827 this.labelmd = this.labelWidth;
30830 if(this.labellg > 0){
30831 labelCls = ' col-lg-' + this.labellg;
30832 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30835 if(this.labelmd > 0){
30836 labelCls = ' col-md-' + this.labelmd;
30837 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30840 if(this.labelsm > 0){
30841 labelCls = ' col-sm-' + this.labelsm;
30842 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30845 if(this.labelxs > 0){
30846 labelCls = ' col-xs-' + this.labelxs;
30847 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30851 label.cls += ' ' + labelCls;
30853 cfg.cn.push(label);
30856 Roo.each(['day', 'month', 'year'], function(t){
30859 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30866 inputEl: function ()
30868 return this.el.select('.roo-date-split-field-group-value', true).first();
30871 onRender : function(ct, position)
30875 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30877 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30879 this.dayField = new Roo.bootstrap.ComboBox({
30880 allowBlank : this.dayAllowBlank,
30881 alwaysQuery : true,
30882 displayField : 'value',
30885 forceSelection : true,
30887 placeholder : this.dayPlaceholder,
30888 selectOnFocus : true,
30889 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30890 triggerAction : 'all',
30892 valueField : 'value',
30893 store : new Roo.data.SimpleStore({
30894 data : (function() {
30896 _this.fireEvent('days', _this, days);
30899 fields : [ 'value' ]
30902 select : function (_self, record, index)
30904 _this.setValue(_this.getValue());
30909 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30911 this.monthField = new Roo.bootstrap.MonthField({
30912 after : '<i class=\"fa fa-calendar\"></i>',
30913 allowBlank : this.monthAllowBlank,
30914 placeholder : this.monthPlaceholder,
30917 render : function (_self)
30919 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30920 e.preventDefault();
30924 select : function (_self, oldvalue, newvalue)
30926 _this.setValue(_this.getValue());
30931 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30933 this.yearField = new Roo.bootstrap.ComboBox({
30934 allowBlank : this.yearAllowBlank,
30935 alwaysQuery : true,
30936 displayField : 'value',
30939 forceSelection : true,
30941 placeholder : this.yearPlaceholder,
30942 selectOnFocus : true,
30943 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30944 triggerAction : 'all',
30946 valueField : 'value',
30947 store : new Roo.data.SimpleStore({
30948 data : (function() {
30950 _this.fireEvent('years', _this, years);
30953 fields : [ 'value' ]
30956 select : function (_self, record, index)
30958 _this.setValue(_this.getValue());
30963 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30966 setValue : function(v, format)
30968 this.inputEl.dom.value = v;
30970 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30972 var d = Date.parseDate(v, f);
30979 this.setDay(d.format(this.dayFormat));
30980 this.setMonth(d.format(this.monthFormat));
30981 this.setYear(d.format(this.yearFormat));
30988 setDay : function(v)
30990 this.dayField.setValue(v);
30991 this.inputEl.dom.value = this.getValue();
30996 setMonth : function(v)
30998 this.monthField.setValue(v, true);
30999 this.inputEl.dom.value = this.getValue();
31004 setYear : function(v)
31006 this.yearField.setValue(v);
31007 this.inputEl.dom.value = this.getValue();
31012 getDay : function()
31014 return this.dayField.getValue();
31017 getMonth : function()
31019 return this.monthField.getValue();
31022 getYear : function()
31024 return this.yearField.getValue();
31027 getValue : function()
31029 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31031 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31041 this.inputEl.dom.value = '';
31046 validate : function()
31048 var d = this.dayField.validate();
31049 var m = this.monthField.validate();
31050 var y = this.yearField.validate();
31055 (!this.dayAllowBlank && !d) ||
31056 (!this.monthAllowBlank && !m) ||
31057 (!this.yearAllowBlank && !y)
31062 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31071 this.markInvalid();
31076 markValid : function()
31079 var label = this.el.select('label', true).first();
31080 var icon = this.el.select('i.fa-star', true).first();
31086 this.fireEvent('valid', this);
31090 * Mark this field as invalid
31091 * @param {String} msg The validation message
31093 markInvalid : function(msg)
31096 var label = this.el.select('label', true).first();
31097 var icon = this.el.select('i.fa-star', true).first();
31099 if(label && !icon){
31100 this.el.select('.roo-date-split-field-label', true).createChild({
31102 cls : 'text-danger fa fa-lg fa-star',
31103 tooltip : 'This field is required',
31104 style : 'margin-right:5px;'
31108 this.fireEvent('invalid', this, msg);
31111 clearInvalid : function()
31113 var label = this.el.select('label', true).first();
31114 var icon = this.el.select('i.fa-star', true).first();
31120 this.fireEvent('valid', this);
31123 getName: function()
31133 * http://masonry.desandro.com
31135 * The idea is to render all the bricks based on vertical width...
31137 * The original code extends 'outlayer' - we might need to use that....
31143 * @class Roo.bootstrap.LayoutMasonry
31144 * @extends Roo.bootstrap.Component
31145 * Bootstrap Layout Masonry class
31148 * Create a new Element
31149 * @param {Object} config The config object
31152 Roo.bootstrap.LayoutMasonry = function(config){
31154 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31158 Roo.bootstrap.LayoutMasonry.register(this);
31164 * Fire after layout the items
31165 * @param {Roo.bootstrap.LayoutMasonry} this
31166 * @param {Roo.EventObject} e
31173 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31176 * @cfg {Boolean} isLayoutInstant = no animation?
31178 isLayoutInstant : false, // needed?
31181 * @cfg {Number} boxWidth width of the columns
31186 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31191 * @cfg {Number} padWidth padding below box..
31196 * @cfg {Number} gutter gutter width..
31201 * @cfg {Number} maxCols maximum number of columns
31207 * @cfg {Boolean} isAutoInitial defalut true
31209 isAutoInitial : true,
31214 * @cfg {Boolean} isHorizontal defalut false
31216 isHorizontal : false,
31218 currentSize : null,
31224 bricks: null, //CompositeElement
31228 _isLayoutInited : false,
31230 // isAlternative : false, // only use for vertical layout...
31233 * @cfg {Number} alternativePadWidth padding below box..
31235 alternativePadWidth : 50,
31237 selectedBrick : [],
31239 getAutoCreate : function(){
31241 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31245 cls: 'blog-masonary-wrapper ' + this.cls,
31247 cls : 'mas-boxes masonary'
31254 getChildContainer: function( )
31256 if (this.boxesEl) {
31257 return this.boxesEl;
31260 this.boxesEl = this.el.select('.mas-boxes').first();
31262 return this.boxesEl;
31266 initEvents : function()
31270 if(this.isAutoInitial){
31271 Roo.log('hook children rendered');
31272 this.on('childrenrendered', function() {
31273 Roo.log('children rendered');
31279 initial : function()
31281 this.selectedBrick = [];
31283 this.currentSize = this.el.getBox(true);
31285 Roo.EventManager.onWindowResize(this.resize, this);
31287 if(!this.isAutoInitial){
31295 //this.layout.defer(500,this);
31299 resize : function()
31301 var cs = this.el.getBox(true);
31304 this.currentSize.width == cs.width &&
31305 this.currentSize.x == cs.x &&
31306 this.currentSize.height == cs.height &&
31307 this.currentSize.y == cs.y
31309 Roo.log("no change in with or X or Y");
31313 this.currentSize = cs;
31319 layout : function()
31321 this._resetLayout();
31323 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31325 this.layoutItems( isInstant );
31327 this._isLayoutInited = true;
31329 this.fireEvent('layout', this);
31333 _resetLayout : function()
31335 if(this.isHorizontal){
31336 this.horizontalMeasureColumns();
31340 this.verticalMeasureColumns();
31344 verticalMeasureColumns : function()
31346 this.getContainerWidth();
31348 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31349 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31353 var boxWidth = this.boxWidth + this.padWidth;
31355 if(this.containerWidth < this.boxWidth){
31356 boxWidth = this.containerWidth
31359 var containerWidth = this.containerWidth;
31361 var cols = Math.floor(containerWidth / boxWidth);
31363 this.cols = Math.max( cols, 1 );
31365 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31367 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31369 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31371 this.colWidth = boxWidth + avail - this.padWidth;
31373 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31374 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31377 horizontalMeasureColumns : function()
31379 this.getContainerWidth();
31381 var boxWidth = this.boxWidth;
31383 if(this.containerWidth < boxWidth){
31384 boxWidth = this.containerWidth;
31387 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31389 this.el.setHeight(boxWidth);
31393 getContainerWidth : function()
31395 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31398 layoutItems : function( isInstant )
31400 Roo.log(this.bricks);
31402 var items = Roo.apply([], this.bricks);
31404 if(this.isHorizontal){
31405 this._horizontalLayoutItems( items , isInstant );
31409 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31410 // this._verticalAlternativeLayoutItems( items , isInstant );
31414 this._verticalLayoutItems( items , isInstant );
31418 _verticalLayoutItems : function ( items , isInstant)
31420 if ( !items || !items.length ) {
31425 ['xs', 'xs', 'xs', 'tall'],
31426 ['xs', 'xs', 'tall'],
31427 ['xs', 'xs', 'sm'],
31428 ['xs', 'xs', 'xs'],
31434 ['sm', 'xs', 'xs'],
31438 ['tall', 'xs', 'xs', 'xs'],
31439 ['tall', 'xs', 'xs'],
31451 Roo.each(items, function(item, k){
31453 switch (item.size) {
31454 // these layouts take up a full box,
31465 boxes.push([item]);
31488 var filterPattern = function(box, length)
31496 var pattern = box.slice(0, length);
31500 Roo.each(pattern, function(i){
31501 format.push(i.size);
31504 Roo.each(standard, function(s){
31506 if(String(s) != String(format)){
31515 if(!match && length == 1){
31520 filterPattern(box, length - 1);
31524 queue.push(pattern);
31526 box = box.slice(length, box.length);
31528 filterPattern(box, 4);
31534 Roo.each(boxes, function(box, k){
31540 if(box.length == 1){
31545 filterPattern(box, 4);
31549 this._processVerticalLayoutQueue( queue, isInstant );
31553 // _verticalAlternativeLayoutItems : function( items , isInstant )
31555 // if ( !items || !items.length ) {
31559 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31563 _horizontalLayoutItems : function ( items , isInstant)
31565 if ( !items || !items.length || items.length < 3) {
31571 var eItems = items.slice(0, 3);
31573 items = items.slice(3, items.length);
31576 ['xs', 'xs', 'xs', 'wide'],
31577 ['xs', 'xs', 'wide'],
31578 ['xs', 'xs', 'sm'],
31579 ['xs', 'xs', 'xs'],
31585 ['sm', 'xs', 'xs'],
31589 ['wide', 'xs', 'xs', 'xs'],
31590 ['wide', 'xs', 'xs'],
31603 Roo.each(items, function(item, k){
31605 switch (item.size) {
31616 boxes.push([item]);
31640 var filterPattern = function(box, length)
31648 var pattern = box.slice(0, length);
31652 Roo.each(pattern, function(i){
31653 format.push(i.size);
31656 Roo.each(standard, function(s){
31658 if(String(s) != String(format)){
31667 if(!match && length == 1){
31672 filterPattern(box, length - 1);
31676 queue.push(pattern);
31678 box = box.slice(length, box.length);
31680 filterPattern(box, 4);
31686 Roo.each(boxes, function(box, k){
31692 if(box.length == 1){
31697 filterPattern(box, 4);
31704 var pos = this.el.getBox(true);
31708 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31710 var hit_end = false;
31712 Roo.each(queue, function(box){
31716 Roo.each(box, function(b){
31718 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31728 Roo.each(box, function(b){
31730 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31733 mx = Math.max(mx, b.x);
31737 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31741 Roo.each(box, function(b){
31743 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31757 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31760 /** Sets position of item in DOM
31761 * @param {Element} item
31762 * @param {Number} x - horizontal position
31763 * @param {Number} y - vertical position
31764 * @param {Boolean} isInstant - disables transitions
31766 _processVerticalLayoutQueue : function( queue, isInstant )
31768 var pos = this.el.getBox(true);
31773 for (var i = 0; i < this.cols; i++){
31777 Roo.each(queue, function(box, k){
31779 var col = k % this.cols;
31781 Roo.each(box, function(b,kk){
31783 b.el.position('absolute');
31785 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31786 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31788 if(b.size == 'md-left' || b.size == 'md-right'){
31789 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31790 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31793 b.el.setWidth(width);
31794 b.el.setHeight(height);
31796 b.el.select('iframe',true).setSize(width,height);
31800 for (var i = 0; i < this.cols; i++){
31802 if(maxY[i] < maxY[col]){
31807 col = Math.min(col, i);
31811 x = pos.x + col * (this.colWidth + this.padWidth);
31815 var positions = [];
31817 switch (box.length){
31819 positions = this.getVerticalOneBoxColPositions(x, y, box);
31822 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31825 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31828 positions = this.getVerticalFourBoxColPositions(x, y, box);
31834 Roo.each(box, function(b,kk){
31836 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31838 var sz = b.el.getSize();
31840 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31848 for (var i = 0; i < this.cols; i++){
31849 mY = Math.max(mY, maxY[i]);
31852 this.el.setHeight(mY - pos.y);
31856 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31858 // var pos = this.el.getBox(true);
31861 // var maxX = pos.right;
31863 // var maxHeight = 0;
31865 // Roo.each(items, function(item, k){
31869 // item.el.position('absolute');
31871 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31873 // item.el.setWidth(width);
31875 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31877 // item.el.setHeight(height);
31880 // item.el.setXY([x, y], isInstant ? false : true);
31882 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31885 // y = y + height + this.alternativePadWidth;
31887 // maxHeight = maxHeight + height + this.alternativePadWidth;
31891 // this.el.setHeight(maxHeight);
31895 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31897 var pos = this.el.getBox(true);
31902 var maxX = pos.right;
31904 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31906 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31908 Roo.each(queue, function(box, k){
31910 Roo.each(box, function(b, kk){
31912 b.el.position('absolute');
31914 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31915 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31917 if(b.size == 'md-left' || b.size == 'md-right'){
31918 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31919 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31922 b.el.setWidth(width);
31923 b.el.setHeight(height);
31931 var positions = [];
31933 switch (box.length){
31935 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31938 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31941 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31944 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31950 Roo.each(box, function(b,kk){
31952 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31954 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31962 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31964 Roo.each(eItems, function(b,k){
31966 b.size = (k == 0) ? 'sm' : 'xs';
31967 b.x = (k == 0) ? 2 : 1;
31968 b.y = (k == 0) ? 2 : 1;
31970 b.el.position('absolute');
31972 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31974 b.el.setWidth(width);
31976 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31978 b.el.setHeight(height);
31982 var positions = [];
31985 x : maxX - this.unitWidth * 2 - this.gutter,
31990 x : maxX - this.unitWidth,
31991 y : minY + (this.unitWidth + this.gutter) * 2
31995 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31999 Roo.each(eItems, function(b,k){
32001 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32007 getVerticalOneBoxColPositions : function(x, y, box)
32011 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32013 if(box[0].size == 'md-left'){
32017 if(box[0].size == 'md-right'){
32022 x : x + (this.unitWidth + this.gutter) * rand,
32029 getVerticalTwoBoxColPositions : function(x, y, box)
32033 if(box[0].size == 'xs'){
32037 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32041 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32055 x : x + (this.unitWidth + this.gutter) * 2,
32056 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32063 getVerticalThreeBoxColPositions : function(x, y, box)
32067 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32075 x : x + (this.unitWidth + this.gutter) * 1,
32080 x : x + (this.unitWidth + this.gutter) * 2,
32088 if(box[0].size == 'xs' && box[1].size == 'xs'){
32097 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32101 x : x + (this.unitWidth + this.gutter) * 1,
32115 x : x + (this.unitWidth + this.gutter) * 2,
32120 x : x + (this.unitWidth + this.gutter) * 2,
32121 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32128 getVerticalFourBoxColPositions : function(x, y, box)
32132 if(box[0].size == 'xs'){
32141 y : y + (this.unitHeight + this.gutter) * 1
32146 y : y + (this.unitHeight + this.gutter) * 2
32150 x : x + (this.unitWidth + this.gutter) * 1,
32164 x : x + (this.unitWidth + this.gutter) * 2,
32169 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32170 y : y + (this.unitHeight + this.gutter) * 1
32174 x : x + (this.unitWidth + this.gutter) * 2,
32175 y : y + (this.unitWidth + this.gutter) * 2
32182 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32186 if(box[0].size == 'md-left'){
32188 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32195 if(box[0].size == 'md-right'){
32197 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32198 y : minY + (this.unitWidth + this.gutter) * 1
32204 var rand = Math.floor(Math.random() * (4 - box[0].y));
32207 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32208 y : minY + (this.unitWidth + this.gutter) * rand
32215 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32219 if(box[0].size == 'xs'){
32222 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32227 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32228 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32236 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32241 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32242 y : minY + (this.unitWidth + this.gutter) * 2
32249 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32253 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32256 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32261 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32262 y : minY + (this.unitWidth + this.gutter) * 1
32266 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32267 y : minY + (this.unitWidth + this.gutter) * 2
32274 if(box[0].size == 'xs' && box[1].size == 'xs'){
32277 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32282 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32287 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32288 y : minY + (this.unitWidth + this.gutter) * 1
32296 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32301 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32302 y : minY + (this.unitWidth + this.gutter) * 2
32306 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32307 y : minY + (this.unitWidth + this.gutter) * 2
32314 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32318 if(box[0].size == 'xs'){
32321 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32326 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32331 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),
32336 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32337 y : minY + (this.unitWidth + this.gutter) * 1
32345 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32350 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32351 y : minY + (this.unitWidth + this.gutter) * 2
32355 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32356 y : minY + (this.unitWidth + this.gutter) * 2
32360 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),
32361 y : minY + (this.unitWidth + this.gutter) * 2
32369 * remove a Masonry Brick
32370 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32372 removeBrick : function(brick_id)
32378 for (var i = 0; i<this.bricks.length; i++) {
32379 if (this.bricks[i].id == brick_id) {
32380 this.bricks.splice(i,1);
32381 this.el.dom.removeChild(Roo.get(brick_id).dom);
32388 * adds a Masonry Brick
32389 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32391 addBrick : function(cfg)
32393 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32394 //this.register(cn);
32395 cn.parentId = this.id;
32396 cn.render(this.el);
32401 * register a Masonry Brick
32402 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32405 register : function(brick)
32407 this.bricks.push(brick);
32408 brick.masonryId = this.id;
32412 * clear all the Masonry Brick
32414 clearAll : function()
32417 //this.getChildContainer().dom.innerHTML = "";
32418 this.el.dom.innerHTML = '';
32421 getSelected : function()
32423 if (!this.selectedBrick) {
32427 return this.selectedBrick;
32431 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32435 * register a Masonry Layout
32436 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32439 register : function(layout)
32441 this.groups[layout.id] = layout;
32444 * fetch a Masonry Layout based on the masonry layout ID
32445 * @param {string} the masonry layout to add
32446 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32449 get: function(layout_id) {
32450 if (typeof(this.groups[layout_id]) == 'undefined') {
32453 return this.groups[layout_id] ;
32465 * http://masonry.desandro.com
32467 * The idea is to render all the bricks based on vertical width...
32469 * The original code extends 'outlayer' - we might need to use that....
32475 * @class Roo.bootstrap.LayoutMasonryAuto
32476 * @extends Roo.bootstrap.Component
32477 * Bootstrap Layout Masonry class
32480 * Create a new Element
32481 * @param {Object} config The config object
32484 Roo.bootstrap.LayoutMasonryAuto = function(config){
32485 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32488 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32491 * @cfg {Boolean} isFitWidth - resize the width..
32493 isFitWidth : false, // options..
32495 * @cfg {Boolean} isOriginLeft = left align?
32497 isOriginLeft : true,
32499 * @cfg {Boolean} isOriginTop = top align?
32501 isOriginTop : false,
32503 * @cfg {Boolean} isLayoutInstant = no animation?
32505 isLayoutInstant : false, // needed?
32507 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32509 isResizingContainer : true,
32511 * @cfg {Number} columnWidth width of the columns
32517 * @cfg {Number} maxCols maximum number of columns
32522 * @cfg {Number} padHeight padding below box..
32528 * @cfg {Boolean} isAutoInitial defalut true
32531 isAutoInitial : true,
32537 initialColumnWidth : 0,
32538 currentSize : null,
32540 colYs : null, // array.
32547 bricks: null, //CompositeElement
32548 cols : 0, // array?
32549 // element : null, // wrapped now this.el
32550 _isLayoutInited : null,
32553 getAutoCreate : function(){
32557 cls: 'blog-masonary-wrapper ' + this.cls,
32559 cls : 'mas-boxes masonary'
32566 getChildContainer: function( )
32568 if (this.boxesEl) {
32569 return this.boxesEl;
32572 this.boxesEl = this.el.select('.mas-boxes').first();
32574 return this.boxesEl;
32578 initEvents : function()
32582 if(this.isAutoInitial){
32583 Roo.log('hook children rendered');
32584 this.on('childrenrendered', function() {
32585 Roo.log('children rendered');
32592 initial : function()
32594 this.reloadItems();
32596 this.currentSize = this.el.getBox(true);
32598 /// was window resize... - let's see if this works..
32599 Roo.EventManager.onWindowResize(this.resize, this);
32601 if(!this.isAutoInitial){
32606 this.layout.defer(500,this);
32609 reloadItems: function()
32611 this.bricks = this.el.select('.masonry-brick', true);
32613 this.bricks.each(function(b) {
32614 //Roo.log(b.getSize());
32615 if (!b.attr('originalwidth')) {
32616 b.attr('originalwidth', b.getSize().width);
32621 Roo.log(this.bricks.elements.length);
32624 resize : function()
32627 var cs = this.el.getBox(true);
32629 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32630 Roo.log("no change in with or X");
32633 this.currentSize = cs;
32637 layout : function()
32640 this._resetLayout();
32641 //this._manageStamps();
32643 // don't animate first layout
32644 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32645 this.layoutItems( isInstant );
32647 // flag for initalized
32648 this._isLayoutInited = true;
32651 layoutItems : function( isInstant )
32653 //var items = this._getItemsForLayout( this.items );
32654 // original code supports filtering layout items.. we just ignore it..
32656 this._layoutItems( this.bricks , isInstant );
32658 this._postLayout();
32660 _layoutItems : function ( items , isInstant)
32662 //this.fireEvent( 'layout', this, items );
32665 if ( !items || !items.elements.length ) {
32666 // no items, emit event with empty array
32671 items.each(function(item) {
32672 Roo.log("layout item");
32674 // get x/y object from method
32675 var position = this._getItemLayoutPosition( item );
32677 position.item = item;
32678 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32679 queue.push( position );
32682 this._processLayoutQueue( queue );
32684 /** Sets position of item in DOM
32685 * @param {Element} item
32686 * @param {Number} x - horizontal position
32687 * @param {Number} y - vertical position
32688 * @param {Boolean} isInstant - disables transitions
32690 _processLayoutQueue : function( queue )
32692 for ( var i=0, len = queue.length; i < len; i++ ) {
32693 var obj = queue[i];
32694 obj.item.position('absolute');
32695 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32701 * Any logic you want to do after each layout,
32702 * i.e. size the container
32704 _postLayout : function()
32706 this.resizeContainer();
32709 resizeContainer : function()
32711 if ( !this.isResizingContainer ) {
32714 var size = this._getContainerSize();
32716 this.el.setSize(size.width,size.height);
32717 this.boxesEl.setSize(size.width,size.height);
32723 _resetLayout : function()
32725 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32726 this.colWidth = this.el.getWidth();
32727 //this.gutter = this.el.getWidth();
32729 this.measureColumns();
32735 this.colYs.push( 0 );
32741 measureColumns : function()
32743 this.getContainerWidth();
32744 // if columnWidth is 0, default to outerWidth of first item
32745 if ( !this.columnWidth ) {
32746 var firstItem = this.bricks.first();
32747 Roo.log(firstItem);
32748 this.columnWidth = this.containerWidth;
32749 if (firstItem && firstItem.attr('originalwidth') ) {
32750 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32752 // columnWidth fall back to item of first element
32753 Roo.log("set column width?");
32754 this.initialColumnWidth = this.columnWidth ;
32756 // if first elem has no width, default to size of container
32761 if (this.initialColumnWidth) {
32762 this.columnWidth = this.initialColumnWidth;
32767 // column width is fixed at the top - however if container width get's smaller we should
32770 // this bit calcs how man columns..
32772 var columnWidth = this.columnWidth += this.gutter;
32774 // calculate columns
32775 var containerWidth = this.containerWidth + this.gutter;
32777 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32778 // fix rounding errors, typically with gutters
32779 var excess = columnWidth - containerWidth % columnWidth;
32782 // if overshoot is less than a pixel, round up, otherwise floor it
32783 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32784 cols = Math[ mathMethod ]( cols );
32785 this.cols = Math.max( cols, 1 );
32786 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32788 // padding positioning..
32789 var totalColWidth = this.cols * this.columnWidth;
32790 var padavail = this.containerWidth - totalColWidth;
32791 // so for 2 columns - we need 3 'pads'
32793 var padNeeded = (1+this.cols) * this.padWidth;
32795 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32797 this.columnWidth += padExtra
32798 //this.padWidth = Math.floor(padavail / ( this.cols));
32800 // adjust colum width so that padding is fixed??
32802 // we have 3 columns ... total = width * 3
32803 // we have X left over... that should be used by
32805 //if (this.expandC) {
32813 getContainerWidth : function()
32815 /* // container is parent if fit width
32816 var container = this.isFitWidth ? this.element.parentNode : this.element;
32817 // check that this.size and size are there
32818 // IE8 triggers resize on body size change, so they might not be
32820 var size = getSize( container ); //FIXME
32821 this.containerWidth = size && size.innerWidth; //FIXME
32824 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32828 _getItemLayoutPosition : function( item ) // what is item?
32830 // we resize the item to our columnWidth..
32832 item.setWidth(this.columnWidth);
32833 item.autoBoxAdjust = false;
32835 var sz = item.getSize();
32837 // how many columns does this brick span
32838 var remainder = this.containerWidth % this.columnWidth;
32840 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32841 // round if off by 1 pixel, otherwise use ceil
32842 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32843 colSpan = Math.min( colSpan, this.cols );
32845 // normally this should be '1' as we dont' currently allow multi width columns..
32847 var colGroup = this._getColGroup( colSpan );
32848 // get the minimum Y value from the columns
32849 var minimumY = Math.min.apply( Math, colGroup );
32850 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32852 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32854 // position the brick
32856 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32857 y: this.currentSize.y + minimumY + this.padHeight
32861 // apply setHeight to necessary columns
32862 var setHeight = minimumY + sz.height + this.padHeight;
32863 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32865 var setSpan = this.cols + 1 - colGroup.length;
32866 for ( var i = 0; i < setSpan; i++ ) {
32867 this.colYs[ shortColIndex + i ] = setHeight ;
32874 * @param {Number} colSpan - number of columns the element spans
32875 * @returns {Array} colGroup
32877 _getColGroup : function( colSpan )
32879 if ( colSpan < 2 ) {
32880 // if brick spans only one column, use all the column Ys
32885 // how many different places could this brick fit horizontally
32886 var groupCount = this.cols + 1 - colSpan;
32887 // for each group potential horizontal position
32888 for ( var i = 0; i < groupCount; i++ ) {
32889 // make an array of colY values for that one group
32890 var groupColYs = this.colYs.slice( i, i + colSpan );
32891 // and get the max value of the array
32892 colGroup[i] = Math.max.apply( Math, groupColYs );
32897 _manageStamp : function( stamp )
32899 var stampSize = stamp.getSize();
32900 var offset = stamp.getBox();
32901 // get the columns that this stamp affects
32902 var firstX = this.isOriginLeft ? offset.x : offset.right;
32903 var lastX = firstX + stampSize.width;
32904 var firstCol = Math.floor( firstX / this.columnWidth );
32905 firstCol = Math.max( 0, firstCol );
32907 var lastCol = Math.floor( lastX / this.columnWidth );
32908 // lastCol should not go over if multiple of columnWidth #425
32909 lastCol -= lastX % this.columnWidth ? 0 : 1;
32910 lastCol = Math.min( this.cols - 1, lastCol );
32912 // set colYs to bottom of the stamp
32913 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32916 for ( var i = firstCol; i <= lastCol; i++ ) {
32917 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32922 _getContainerSize : function()
32924 this.maxY = Math.max.apply( Math, this.colYs );
32929 if ( this.isFitWidth ) {
32930 size.width = this._getContainerFitWidth();
32936 _getContainerFitWidth : function()
32938 var unusedCols = 0;
32939 // count unused columns
32942 if ( this.colYs[i] !== 0 ) {
32947 // fit container to columns that have been used
32948 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32951 needsResizeLayout : function()
32953 var previousWidth = this.containerWidth;
32954 this.getContainerWidth();
32955 return previousWidth !== this.containerWidth;
32970 * @class Roo.bootstrap.MasonryBrick
32971 * @extends Roo.bootstrap.Component
32972 * Bootstrap MasonryBrick class
32975 * Create a new MasonryBrick
32976 * @param {Object} config The config object
32979 Roo.bootstrap.MasonryBrick = function(config){
32981 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32983 Roo.bootstrap.MasonryBrick.register(this);
32989 * When a MasonryBrick is clcik
32990 * @param {Roo.bootstrap.MasonryBrick} this
32991 * @param {Roo.EventObject} e
32997 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33000 * @cfg {String} title
33004 * @cfg {String} html
33008 * @cfg {String} bgimage
33012 * @cfg {String} videourl
33016 * @cfg {String} cls
33020 * @cfg {String} href
33024 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33029 * @cfg {String} placetitle (center|bottom)
33034 * @cfg {Boolean} isFitContainer defalut true
33036 isFitContainer : true,
33039 * @cfg {Boolean} preventDefault defalut false
33041 preventDefault : false,
33044 * @cfg {Boolean} inverse defalut false
33046 maskInverse : false,
33048 getAutoCreate : function()
33050 if(!this.isFitContainer){
33051 return this.getSplitAutoCreate();
33054 var cls = 'masonry-brick masonry-brick-full';
33056 if(this.href.length){
33057 cls += ' masonry-brick-link';
33060 if(this.bgimage.length){
33061 cls += ' masonry-brick-image';
33064 if(this.maskInverse){
33065 cls += ' mask-inverse';
33068 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33069 cls += ' enable-mask';
33073 cls += ' masonry-' + this.size + '-brick';
33076 if(this.placetitle.length){
33078 switch (this.placetitle) {
33080 cls += ' masonry-center-title';
33083 cls += ' masonry-bottom-title';
33090 if(!this.html.length && !this.bgimage.length){
33091 cls += ' masonry-center-title';
33094 if(!this.html.length && this.bgimage.length){
33095 cls += ' masonry-bottom-title';
33100 cls += ' ' + this.cls;
33104 tag: (this.href.length) ? 'a' : 'div',
33109 cls: 'masonry-brick-mask'
33113 cls: 'masonry-brick-paragraph',
33119 if(this.href.length){
33120 cfg.href = this.href;
33123 var cn = cfg.cn[1].cn;
33125 if(this.title.length){
33128 cls: 'masonry-brick-title',
33133 if(this.html.length){
33136 cls: 'masonry-brick-text',
33141 if (!this.title.length && !this.html.length) {
33142 cfg.cn[1].cls += ' hide';
33145 if(this.bgimage.length){
33148 cls: 'masonry-brick-image-view',
33153 if(this.videourl.length){
33154 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33155 // youtube support only?
33158 cls: 'masonry-brick-image-view',
33161 allowfullscreen : true
33169 getSplitAutoCreate : function()
33171 var cls = 'masonry-brick masonry-brick-split';
33173 if(this.href.length){
33174 cls += ' masonry-brick-link';
33177 if(this.bgimage.length){
33178 cls += ' masonry-brick-image';
33182 cls += ' masonry-' + this.size + '-brick';
33185 switch (this.placetitle) {
33187 cls += ' masonry-center-title';
33190 cls += ' masonry-bottom-title';
33193 if(!this.bgimage.length){
33194 cls += ' masonry-center-title';
33197 if(this.bgimage.length){
33198 cls += ' masonry-bottom-title';
33204 cls += ' ' + this.cls;
33208 tag: (this.href.length) ? 'a' : 'div',
33213 cls: 'masonry-brick-split-head',
33217 cls: 'masonry-brick-paragraph',
33224 cls: 'masonry-brick-split-body',
33230 if(this.href.length){
33231 cfg.href = this.href;
33234 if(this.title.length){
33235 cfg.cn[0].cn[0].cn.push({
33237 cls: 'masonry-brick-title',
33242 if(this.html.length){
33243 cfg.cn[1].cn.push({
33245 cls: 'masonry-brick-text',
33250 if(this.bgimage.length){
33251 cfg.cn[0].cn.push({
33253 cls: 'masonry-brick-image-view',
33258 if(this.videourl.length){
33259 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33260 // youtube support only?
33261 cfg.cn[0].cn.cn.push({
33263 cls: 'masonry-brick-image-view',
33266 allowfullscreen : true
33273 initEvents: function()
33275 switch (this.size) {
33308 this.el.on('touchstart', this.onTouchStart, this);
33309 this.el.on('touchmove', this.onTouchMove, this);
33310 this.el.on('touchend', this.onTouchEnd, this);
33311 this.el.on('contextmenu', this.onContextMenu, this);
33313 this.el.on('mouseenter' ,this.enter, this);
33314 this.el.on('mouseleave', this.leave, this);
33315 this.el.on('click', this.onClick, this);
33318 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33319 this.parent().bricks.push(this);
33324 onClick: function(e, el)
33326 var time = this.endTimer - this.startTimer;
33327 // Roo.log(e.preventDefault());
33330 e.preventDefault();
33335 if(!this.preventDefault){
33339 e.preventDefault();
33341 if (this.activeClass != '') {
33342 this.selectBrick();
33345 this.fireEvent('click', this, e);
33348 enter: function(e, el)
33350 e.preventDefault();
33352 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33356 if(this.bgimage.length && this.html.length){
33357 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33361 leave: function(e, el)
33363 e.preventDefault();
33365 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33369 if(this.bgimage.length && this.html.length){
33370 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33374 onTouchStart: function(e, el)
33376 // e.preventDefault();
33378 this.touchmoved = false;
33380 if(!this.isFitContainer){
33384 if(!this.bgimage.length || !this.html.length){
33388 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33390 this.timer = new Date().getTime();
33394 onTouchMove: function(e, el)
33396 this.touchmoved = true;
33399 onContextMenu : function(e,el)
33401 e.preventDefault();
33402 e.stopPropagation();
33406 onTouchEnd: function(e, el)
33408 // e.preventDefault();
33410 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33417 if(!this.bgimage.length || !this.html.length){
33419 if(this.href.length){
33420 window.location.href = this.href;
33426 if(!this.isFitContainer){
33430 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33432 window.location.href = this.href;
33435 //selection on single brick only
33436 selectBrick : function() {
33438 if (!this.parentId) {
33442 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33443 var index = m.selectedBrick.indexOf(this.id);
33446 m.selectedBrick.splice(index,1);
33447 this.el.removeClass(this.activeClass);
33451 for(var i = 0; i < m.selectedBrick.length; i++) {
33452 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33453 b.el.removeClass(b.activeClass);
33456 m.selectedBrick = [];
33458 m.selectedBrick.push(this.id);
33459 this.el.addClass(this.activeClass);
33463 isSelected : function(){
33464 return this.el.hasClass(this.activeClass);
33469 Roo.apply(Roo.bootstrap.MasonryBrick, {
33472 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33474 * register a Masonry Brick
33475 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33478 register : function(brick)
33480 //this.groups[brick.id] = brick;
33481 this.groups.add(brick.id, brick);
33484 * fetch a masonry brick based on the masonry brick ID
33485 * @param {string} the masonry brick to add
33486 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33489 get: function(brick_id)
33491 // if (typeof(this.groups[brick_id]) == 'undefined') {
33494 // return this.groups[brick_id] ;
33496 if(this.groups.key(brick_id)) {
33497 return this.groups.key(brick_id);
33515 * @class Roo.bootstrap.Brick
33516 * @extends Roo.bootstrap.Component
33517 * Bootstrap Brick class
33520 * Create a new Brick
33521 * @param {Object} config The config object
33524 Roo.bootstrap.Brick = function(config){
33525 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33531 * When a Brick is click
33532 * @param {Roo.bootstrap.Brick} this
33533 * @param {Roo.EventObject} e
33539 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33542 * @cfg {String} title
33546 * @cfg {String} html
33550 * @cfg {String} bgimage
33554 * @cfg {String} cls
33558 * @cfg {String} href
33562 * @cfg {String} video
33566 * @cfg {Boolean} square
33570 getAutoCreate : function()
33572 var cls = 'roo-brick';
33574 if(this.href.length){
33575 cls += ' roo-brick-link';
33578 if(this.bgimage.length){
33579 cls += ' roo-brick-image';
33582 if(!this.html.length && !this.bgimage.length){
33583 cls += ' roo-brick-center-title';
33586 if(!this.html.length && this.bgimage.length){
33587 cls += ' roo-brick-bottom-title';
33591 cls += ' ' + this.cls;
33595 tag: (this.href.length) ? 'a' : 'div',
33600 cls: 'roo-brick-paragraph',
33606 if(this.href.length){
33607 cfg.href = this.href;
33610 var cn = cfg.cn[0].cn;
33612 if(this.title.length){
33615 cls: 'roo-brick-title',
33620 if(this.html.length){
33623 cls: 'roo-brick-text',
33630 if(this.bgimage.length){
33633 cls: 'roo-brick-image-view',
33641 initEvents: function()
33643 if(this.title.length || this.html.length){
33644 this.el.on('mouseenter' ,this.enter, this);
33645 this.el.on('mouseleave', this.leave, this);
33648 Roo.EventManager.onWindowResize(this.resize, this);
33650 if(this.bgimage.length){
33651 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33652 this.imageEl.on('load', this.onImageLoad, this);
33659 onImageLoad : function()
33664 resize : function()
33666 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33668 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33670 if(this.bgimage.length){
33671 var image = this.el.select('.roo-brick-image-view', true).first();
33673 image.setWidth(paragraph.getWidth());
33676 image.setHeight(paragraph.getWidth());
33679 this.el.setHeight(image.getHeight());
33680 paragraph.setHeight(image.getHeight());
33686 enter: function(e, el)
33688 e.preventDefault();
33690 if(this.bgimage.length){
33691 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33692 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33696 leave: function(e, el)
33698 e.preventDefault();
33700 if(this.bgimage.length){
33701 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33702 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33717 * @class Roo.bootstrap.NumberField
33718 * @extends Roo.bootstrap.Input
33719 * Bootstrap NumberField class
33725 * Create a new NumberField
33726 * @param {Object} config The config object
33729 Roo.bootstrap.NumberField = function(config){
33730 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33733 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33736 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33738 allowDecimals : true,
33740 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33742 decimalSeparator : ".",
33744 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33746 decimalPrecision : 2,
33748 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33750 allowNegative : true,
33753 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33757 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33759 minValue : Number.NEGATIVE_INFINITY,
33761 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33763 maxValue : Number.MAX_VALUE,
33765 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33767 minText : "The minimum value for this field is {0}",
33769 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33771 maxText : "The maximum value for this field is {0}",
33773 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33774 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33776 nanText : "{0} is not a valid number",
33778 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33780 thousandsDelimiter : false,
33782 * @cfg {String} valueAlign alignment of value
33784 valueAlign : "left",
33786 getAutoCreate : function()
33788 var hiddenInput = {
33792 cls: 'hidden-number-input'
33796 hiddenInput.name = this.name;
33801 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33803 this.name = hiddenInput.name;
33805 if(cfg.cn.length > 0) {
33806 cfg.cn.push(hiddenInput);
33813 initEvents : function()
33815 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33817 var allowed = "0123456789";
33819 if(this.allowDecimals){
33820 allowed += this.decimalSeparator;
33823 if(this.allowNegative){
33827 if(this.thousandsDelimiter) {
33831 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33833 var keyPress = function(e){
33835 var k = e.getKey();
33837 var c = e.getCharCode();
33840 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33841 allowed.indexOf(String.fromCharCode(c)) === -1
33847 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33851 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33856 this.el.on("keypress", keyPress, this);
33859 validateValue : function(value)
33862 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33866 var num = this.parseValue(value);
33869 this.markInvalid(String.format(this.nanText, value));
33873 if(num < this.minValue){
33874 this.markInvalid(String.format(this.minText, this.minValue));
33878 if(num > this.maxValue){
33879 this.markInvalid(String.format(this.maxText, this.maxValue));
33886 getValue : function()
33888 var v = this.hiddenEl().getValue();
33890 return this.fixPrecision(this.parseValue(v));
33893 parseValue : function(value)
33895 if(this.thousandsDelimiter) {
33897 r = new RegExp(",", "g");
33898 value = value.replace(r, "");
33901 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33902 return isNaN(value) ? '' : value;
33905 fixPrecision : function(value)
33907 if(this.thousandsDelimiter) {
33909 r = new RegExp(",", "g");
33910 value = value.replace(r, "");
33913 var nan = isNaN(value);
33915 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33916 return nan ? '' : value;
33918 return parseFloat(value).toFixed(this.decimalPrecision);
33921 setValue : function(v)
33923 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33929 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33931 this.inputEl().dom.value = (v == '') ? '' :
33932 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33934 if(!this.allowZero && v === '0') {
33935 this.hiddenEl().dom.value = '';
33936 this.inputEl().dom.value = '';
33943 decimalPrecisionFcn : function(v)
33945 return Math.floor(v);
33948 beforeBlur : function()
33950 var v = this.parseValue(this.getRawValue());
33952 if(v || v === 0 || v === ''){
33957 hiddenEl : function()
33959 return this.el.select('input.hidden-number-input',true).first();
33971 * @class Roo.bootstrap.DocumentSlider
33972 * @extends Roo.bootstrap.Component
33973 * Bootstrap DocumentSlider class
33976 * Create a new DocumentViewer
33977 * @param {Object} config The config object
33980 Roo.bootstrap.DocumentSlider = function(config){
33981 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33988 * Fire after initEvent
33989 * @param {Roo.bootstrap.DocumentSlider} this
33994 * Fire after update
33995 * @param {Roo.bootstrap.DocumentSlider} this
34001 * @param {Roo.bootstrap.DocumentSlider} this
34007 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34013 getAutoCreate : function()
34017 cls : 'roo-document-slider',
34021 cls : 'roo-document-slider-header',
34025 cls : 'roo-document-slider-header-title'
34031 cls : 'roo-document-slider-body',
34035 cls : 'roo-document-slider-prev',
34039 cls : 'fa fa-chevron-left'
34045 cls : 'roo-document-slider-thumb',
34049 cls : 'roo-document-slider-image'
34055 cls : 'roo-document-slider-next',
34059 cls : 'fa fa-chevron-right'
34071 initEvents : function()
34073 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34074 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34076 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34077 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34079 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34080 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34082 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34083 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34085 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34086 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34088 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34089 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34091 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34092 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34094 this.thumbEl.on('click', this.onClick, this);
34096 this.prevIndicator.on('click', this.prev, this);
34098 this.nextIndicator.on('click', this.next, this);
34102 initial : function()
34104 if(this.files.length){
34105 this.indicator = 1;
34109 this.fireEvent('initial', this);
34112 update : function()
34114 this.imageEl.attr('src', this.files[this.indicator - 1]);
34116 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34118 this.prevIndicator.show();
34120 if(this.indicator == 1){
34121 this.prevIndicator.hide();
34124 this.nextIndicator.show();
34126 if(this.indicator == this.files.length){
34127 this.nextIndicator.hide();
34130 this.thumbEl.scrollTo('top');
34132 this.fireEvent('update', this);
34135 onClick : function(e)
34137 e.preventDefault();
34139 this.fireEvent('click', this);
34144 e.preventDefault();
34146 this.indicator = Math.max(1, this.indicator - 1);
34153 e.preventDefault();
34155 this.indicator = Math.min(this.files.length, this.indicator + 1);
34169 * @class Roo.bootstrap.RadioSet
34170 * @extends Roo.bootstrap.Input
34171 * Bootstrap RadioSet class
34172 * @cfg {String} indicatorpos (left|right) default left
34173 * @cfg {Boolean} inline (true|false) inline the element (default true)
34174 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34176 * Create a new RadioSet
34177 * @param {Object} config The config object
34180 Roo.bootstrap.RadioSet = function(config){
34182 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34186 Roo.bootstrap.RadioSet.register(this);
34191 * Fires when the element is checked or unchecked.
34192 * @param {Roo.bootstrap.RadioSet} this This radio
34193 * @param {Roo.bootstrap.Radio} item The checked item
34198 * Fires when the element is click.
34199 * @param {Roo.bootstrap.RadioSet} this This radio set
34200 * @param {Roo.bootstrap.Radio} item The checked item
34201 * @param {Roo.EventObject} e The event object
34208 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34216 indicatorpos : 'left',
34218 getAutoCreate : function()
34222 cls : 'roo-radio-set-label',
34226 html : this.fieldLabel
34230 if (Roo.bootstrap.version == 3) {
34233 if(this.indicatorpos == 'left'){
34236 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34237 tooltip : 'This field is required'
34242 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34243 tooltip : 'This field is required'
34249 cls : 'roo-radio-set-items'
34252 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34254 if (align === 'left' && this.fieldLabel.length) {
34257 cls : "roo-radio-set-right",
34263 if(this.labelWidth > 12){
34264 label.style = "width: " + this.labelWidth + 'px';
34267 if(this.labelWidth < 13 && this.labelmd == 0){
34268 this.labelmd = this.labelWidth;
34271 if(this.labellg > 0){
34272 label.cls += ' col-lg-' + this.labellg;
34273 items.cls += ' col-lg-' + (12 - this.labellg);
34276 if(this.labelmd > 0){
34277 label.cls += ' col-md-' + this.labelmd;
34278 items.cls += ' col-md-' + (12 - this.labelmd);
34281 if(this.labelsm > 0){
34282 label.cls += ' col-sm-' + this.labelsm;
34283 items.cls += ' col-sm-' + (12 - this.labelsm);
34286 if(this.labelxs > 0){
34287 label.cls += ' col-xs-' + this.labelxs;
34288 items.cls += ' col-xs-' + (12 - this.labelxs);
34294 cls : 'roo-radio-set',
34298 cls : 'roo-radio-set-input',
34301 value : this.value ? this.value : ''
34308 if(this.weight.length){
34309 cfg.cls += ' roo-radio-' + this.weight;
34313 cfg.cls += ' roo-radio-set-inline';
34317 ['xs','sm','md','lg'].map(function(size){
34318 if (settings[size]) {
34319 cfg.cls += ' col-' + size + '-' + settings[size];
34327 initEvents : function()
34329 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34330 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34332 if(!this.fieldLabel.length){
34333 this.labelEl.hide();
34336 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34337 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34339 this.indicator = this.indicatorEl();
34341 if(this.indicator){
34342 this.indicator.addClass('invisible');
34345 this.originalValue = this.getValue();
34349 inputEl: function ()
34351 return this.el.select('.roo-radio-set-input', true).first();
34354 getChildContainer : function()
34356 return this.itemsEl;
34359 register : function(item)
34361 this.radioes.push(item);
34365 validate : function()
34367 if(this.getVisibilityEl().hasClass('hidden')){
34373 Roo.each(this.radioes, function(i){
34382 if(this.allowBlank) {
34386 if(this.disabled || valid){
34391 this.markInvalid();
34396 markValid : function()
34398 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34399 this.indicatorEl().removeClass('visible');
34400 this.indicatorEl().addClass('invisible');
34404 if (Roo.bootstrap.version == 3) {
34405 this.el.removeClass([this.invalidClass, this.validClass]);
34406 this.el.addClass(this.validClass);
34408 this.el.removeClass(['is-invalid','is-valid']);
34409 this.el.addClass(['is-valid']);
34411 this.fireEvent('valid', this);
34414 markInvalid : function(msg)
34416 if(this.allowBlank || this.disabled){
34420 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34421 this.indicatorEl().removeClass('invisible');
34422 this.indicatorEl().addClass('visible');
34424 if (Roo.bootstrap.version == 3) {
34425 this.el.removeClass([this.invalidClass, this.validClass]);
34426 this.el.addClass(this.invalidClass);
34428 this.el.removeClass(['is-invalid','is-valid']);
34429 this.el.addClass(['is-invalid']);
34432 this.fireEvent('invalid', this, msg);
34436 setValue : function(v, suppressEvent)
34438 if(this.value === v){
34445 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34448 Roo.each(this.radioes, function(i){
34450 i.el.removeClass('checked');
34453 Roo.each(this.radioes, function(i){
34455 if(i.value === v || i.value.toString() === v.toString()){
34457 i.el.addClass('checked');
34459 if(suppressEvent !== true){
34460 this.fireEvent('check', this, i);
34471 clearInvalid : function(){
34473 if(!this.el || this.preventMark){
34477 this.el.removeClass([this.invalidClass]);
34479 this.fireEvent('valid', this);
34484 Roo.apply(Roo.bootstrap.RadioSet, {
34488 register : function(set)
34490 this.groups[set.name] = set;
34493 get: function(name)
34495 if (typeof(this.groups[name]) == 'undefined') {
34499 return this.groups[name] ;
34505 * Ext JS Library 1.1.1
34506 * Copyright(c) 2006-2007, Ext JS, LLC.
34508 * Originally Released Under LGPL - original licence link has changed is not relivant.
34511 * <script type="text/javascript">
34516 * @class Roo.bootstrap.SplitBar
34517 * @extends Roo.util.Observable
34518 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34522 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34523 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34524 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34525 split.minSize = 100;
34526 split.maxSize = 600;
34527 split.animate = true;
34528 split.on('moved', splitterMoved);
34531 * Create a new SplitBar
34532 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34533 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34534 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34535 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34536 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34537 position of the SplitBar).
34539 Roo.bootstrap.SplitBar = function(cfg){
34544 // dragElement : elm
34545 // resizingElement: el,
34547 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34548 // placement : Roo.bootstrap.SplitBar.LEFT ,
34549 // existingProxy ???
34552 this.el = Roo.get(cfg.dragElement, true);
34553 this.el.dom.unselectable = "on";
34555 this.resizingEl = Roo.get(cfg.resizingElement, true);
34559 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34560 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34563 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34566 * The minimum size of the resizing element. (Defaults to 0)
34572 * The maximum size of the resizing element. (Defaults to 2000)
34575 this.maxSize = 2000;
34578 * Whether to animate the transition to the new size
34581 this.animate = false;
34584 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34587 this.useShim = false;
34592 if(!cfg.existingProxy){
34594 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34596 this.proxy = Roo.get(cfg.existingProxy).dom;
34599 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34602 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34605 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34608 this.dragSpecs = {};
34611 * @private The adapter to use to positon and resize elements
34613 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34614 this.adapter.init(this);
34616 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34618 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34619 this.el.addClass("roo-splitbar-h");
34622 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34623 this.el.addClass("roo-splitbar-v");
34629 * Fires when the splitter is moved (alias for {@link #event-moved})
34630 * @param {Roo.bootstrap.SplitBar} this
34631 * @param {Number} newSize the new width or height
34636 * Fires when the splitter is moved
34637 * @param {Roo.bootstrap.SplitBar} this
34638 * @param {Number} newSize the new width or height
34642 * @event beforeresize
34643 * Fires before the splitter is dragged
34644 * @param {Roo.bootstrap.SplitBar} this
34646 "beforeresize" : true,
34648 "beforeapply" : true
34651 Roo.util.Observable.call(this);
34654 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34655 onStartProxyDrag : function(x, y){
34656 this.fireEvent("beforeresize", this);
34658 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34660 o.enableDisplayMode("block");
34661 // all splitbars share the same overlay
34662 Roo.bootstrap.SplitBar.prototype.overlay = o;
34664 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34665 this.overlay.show();
34666 Roo.get(this.proxy).setDisplayed("block");
34667 var size = this.adapter.getElementSize(this);
34668 this.activeMinSize = this.getMinimumSize();;
34669 this.activeMaxSize = this.getMaximumSize();;
34670 var c1 = size - this.activeMinSize;
34671 var c2 = Math.max(this.activeMaxSize - size, 0);
34672 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34673 this.dd.resetConstraints();
34674 this.dd.setXConstraint(
34675 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34676 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34678 this.dd.setYConstraint(0, 0);
34680 this.dd.resetConstraints();
34681 this.dd.setXConstraint(0, 0);
34682 this.dd.setYConstraint(
34683 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34684 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34687 this.dragSpecs.startSize = size;
34688 this.dragSpecs.startPoint = [x, y];
34689 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34693 * @private Called after the drag operation by the DDProxy
34695 onEndProxyDrag : function(e){
34696 Roo.get(this.proxy).setDisplayed(false);
34697 var endPoint = Roo.lib.Event.getXY(e);
34699 this.overlay.hide();
34702 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34703 newSize = this.dragSpecs.startSize +
34704 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34705 endPoint[0] - this.dragSpecs.startPoint[0] :
34706 this.dragSpecs.startPoint[0] - endPoint[0]
34709 newSize = this.dragSpecs.startSize +
34710 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34711 endPoint[1] - this.dragSpecs.startPoint[1] :
34712 this.dragSpecs.startPoint[1] - endPoint[1]
34715 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34716 if(newSize != this.dragSpecs.startSize){
34717 if(this.fireEvent('beforeapply', this, newSize) !== false){
34718 this.adapter.setElementSize(this, newSize);
34719 this.fireEvent("moved", this, newSize);
34720 this.fireEvent("resize", this, newSize);
34726 * Get the adapter this SplitBar uses
34727 * @return The adapter object
34729 getAdapter : function(){
34730 return this.adapter;
34734 * Set the adapter this SplitBar uses
34735 * @param {Object} adapter A SplitBar adapter object
34737 setAdapter : function(adapter){
34738 this.adapter = adapter;
34739 this.adapter.init(this);
34743 * Gets the minimum size for the resizing element
34744 * @return {Number} The minimum size
34746 getMinimumSize : function(){
34747 return this.minSize;
34751 * Sets the minimum size for the resizing element
34752 * @param {Number} minSize The minimum size
34754 setMinimumSize : function(minSize){
34755 this.minSize = minSize;
34759 * Gets the maximum size for the resizing element
34760 * @return {Number} The maximum size
34762 getMaximumSize : function(){
34763 return this.maxSize;
34767 * Sets the maximum size for the resizing element
34768 * @param {Number} maxSize The maximum size
34770 setMaximumSize : function(maxSize){
34771 this.maxSize = maxSize;
34775 * Sets the initialize size for the resizing element
34776 * @param {Number} size The initial size
34778 setCurrentSize : function(size){
34779 var oldAnimate = this.animate;
34780 this.animate = false;
34781 this.adapter.setElementSize(this, size);
34782 this.animate = oldAnimate;
34786 * Destroy this splitbar.
34787 * @param {Boolean} removeEl True to remove the element
34789 destroy : function(removeEl){
34791 this.shim.remove();
34794 this.proxy.parentNode.removeChild(this.proxy);
34802 * @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.
34804 Roo.bootstrap.SplitBar.createProxy = function(dir){
34805 var proxy = new Roo.Element(document.createElement("div"));
34806 proxy.unselectable();
34807 var cls = 'roo-splitbar-proxy';
34808 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34809 document.body.appendChild(proxy.dom);
34814 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34815 * Default Adapter. It assumes the splitter and resizing element are not positioned
34816 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34818 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34821 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34822 // do nothing for now
34823 init : function(s){
34827 * Called before drag operations to get the current size of the resizing element.
34828 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34830 getElementSize : function(s){
34831 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34832 return s.resizingEl.getWidth();
34834 return s.resizingEl.getHeight();
34839 * Called after drag operations to set the size of the resizing element.
34840 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34841 * @param {Number} newSize The new size to set
34842 * @param {Function} onComplete A function to be invoked when resizing is complete
34844 setElementSize : function(s, newSize, onComplete){
34845 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34847 s.resizingEl.setWidth(newSize);
34849 onComplete(s, newSize);
34852 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34857 s.resizingEl.setHeight(newSize);
34859 onComplete(s, newSize);
34862 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34869 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34870 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34871 * Adapter that moves the splitter element to align with the resized sizing element.
34872 * Used with an absolute positioned SplitBar.
34873 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34874 * document.body, make sure you assign an id to the body element.
34876 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34877 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34878 this.container = Roo.get(container);
34881 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34882 init : function(s){
34883 this.basic.init(s);
34886 getElementSize : function(s){
34887 return this.basic.getElementSize(s);
34890 setElementSize : function(s, newSize, onComplete){
34891 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34894 moveSplitter : function(s){
34895 var yes = Roo.bootstrap.SplitBar;
34896 switch(s.placement){
34898 s.el.setX(s.resizingEl.getRight());
34901 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34904 s.el.setY(s.resizingEl.getBottom());
34907 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34914 * Orientation constant - Create a vertical SplitBar
34918 Roo.bootstrap.SplitBar.VERTICAL = 1;
34921 * Orientation constant - Create a horizontal SplitBar
34925 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34928 * Placement constant - The resizing element is to the left of the splitter element
34932 Roo.bootstrap.SplitBar.LEFT = 1;
34935 * Placement constant - The resizing element is to the right of the splitter element
34939 Roo.bootstrap.SplitBar.RIGHT = 2;
34942 * Placement constant - The resizing element is positioned above the splitter element
34946 Roo.bootstrap.SplitBar.TOP = 3;
34949 * Placement constant - The resizing element is positioned under splitter element
34953 Roo.bootstrap.SplitBar.BOTTOM = 4;
34954 Roo.namespace("Roo.bootstrap.layout");/*
34956 * Ext JS Library 1.1.1
34957 * Copyright(c) 2006-2007, Ext JS, LLC.
34959 * Originally Released Under LGPL - original licence link has changed is not relivant.
34962 * <script type="text/javascript">
34966 * @class Roo.bootstrap.layout.Manager
34967 * @extends Roo.bootstrap.Component
34968 * Base class for layout managers.
34970 Roo.bootstrap.layout.Manager = function(config)
34972 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34978 /** false to disable window resize monitoring @type Boolean */
34979 this.monitorWindowResize = true;
34984 * Fires when a layout is performed.
34985 * @param {Roo.LayoutManager} this
34989 * @event regionresized
34990 * Fires when the user resizes a region.
34991 * @param {Roo.LayoutRegion} region The resized region
34992 * @param {Number} newSize The new size (width for east/west, height for north/south)
34994 "regionresized" : true,
34996 * @event regioncollapsed
34997 * Fires when a region is collapsed.
34998 * @param {Roo.LayoutRegion} region The collapsed region
35000 "regioncollapsed" : true,
35002 * @event regionexpanded
35003 * Fires when a region is expanded.
35004 * @param {Roo.LayoutRegion} region The expanded region
35006 "regionexpanded" : true
35008 this.updating = false;
35011 this.el = Roo.get(config.el);
35017 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35022 monitorWindowResize : true,
35028 onRender : function(ct, position)
35031 this.el = Roo.get(ct);
35034 //this.fireEvent('render',this);
35038 initEvents: function()
35042 // ie scrollbar fix
35043 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35044 document.body.scroll = "no";
35045 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35046 this.el.position('relative');
35048 this.id = this.el.id;
35049 this.el.addClass("roo-layout-container");
35050 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35051 if(this.el.dom != document.body ) {
35052 this.el.on('resize', this.layout,this);
35053 this.el.on('show', this.layout,this);
35059 * Returns true if this layout is currently being updated
35060 * @return {Boolean}
35062 isUpdating : function(){
35063 return this.updating;
35067 * Suspend the LayoutManager from doing auto-layouts while
35068 * making multiple add or remove calls
35070 beginUpdate : function(){
35071 this.updating = true;
35075 * Restore auto-layouts and optionally disable the manager from performing a layout
35076 * @param {Boolean} noLayout true to disable a layout update
35078 endUpdate : function(noLayout){
35079 this.updating = false;
35085 layout: function(){
35089 onRegionResized : function(region, newSize){
35090 this.fireEvent("regionresized", region, newSize);
35094 onRegionCollapsed : function(region){
35095 this.fireEvent("regioncollapsed", region);
35098 onRegionExpanded : function(region){
35099 this.fireEvent("regionexpanded", region);
35103 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35104 * performs box-model adjustments.
35105 * @return {Object} The size as an object {width: (the width), height: (the height)}
35107 getViewSize : function()
35110 if(this.el.dom != document.body){
35111 size = this.el.getSize();
35113 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35115 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35116 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35121 * Returns the Element this layout is bound to.
35122 * @return {Roo.Element}
35124 getEl : function(){
35129 * Returns the specified region.
35130 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35131 * @return {Roo.LayoutRegion}
35133 getRegion : function(target){
35134 return this.regions[target.toLowerCase()];
35137 onWindowResize : function(){
35138 if(this.monitorWindowResize){
35145 * Ext JS Library 1.1.1
35146 * Copyright(c) 2006-2007, Ext JS, LLC.
35148 * Originally Released Under LGPL - original licence link has changed is not relivant.
35151 * <script type="text/javascript">
35154 * @class Roo.bootstrap.layout.Border
35155 * @extends Roo.bootstrap.layout.Manager
35156 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35157 * please see: examples/bootstrap/nested.html<br><br>
35159 <b>The container the layout is rendered into can be either the body element or any other element.
35160 If it is not the body element, the container needs to either be an absolute positioned element,
35161 or you will need to add "position:relative" to the css of the container. You will also need to specify
35162 the container size if it is not the body element.</b>
35165 * Create a new Border
35166 * @param {Object} config Configuration options
35168 Roo.bootstrap.layout.Border = function(config){
35169 config = config || {};
35170 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35174 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35175 if(config[region]){
35176 config[region].region = region;
35177 this.addRegion(config[region]);
35183 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35185 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35187 parent : false, // this might point to a 'nest' or a ???
35190 * Creates and adds a new region if it doesn't already exist.
35191 * @param {String} target The target region key (north, south, east, west or center).
35192 * @param {Object} config The regions config object
35193 * @return {BorderLayoutRegion} The new region
35195 addRegion : function(config)
35197 if(!this.regions[config.region]){
35198 var r = this.factory(config);
35199 this.bindRegion(r);
35201 return this.regions[config.region];
35205 bindRegion : function(r){
35206 this.regions[r.config.region] = r;
35208 r.on("visibilitychange", this.layout, this);
35209 r.on("paneladded", this.layout, this);
35210 r.on("panelremoved", this.layout, this);
35211 r.on("invalidated", this.layout, this);
35212 r.on("resized", this.onRegionResized, this);
35213 r.on("collapsed", this.onRegionCollapsed, this);
35214 r.on("expanded", this.onRegionExpanded, this);
35218 * Performs a layout update.
35220 layout : function()
35222 if(this.updating) {
35226 // render all the rebions if they have not been done alreayd?
35227 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35228 if(this.regions[region] && !this.regions[region].bodyEl){
35229 this.regions[region].onRender(this.el)
35233 var size = this.getViewSize();
35234 var w = size.width;
35235 var h = size.height;
35240 //var x = 0, y = 0;
35242 var rs = this.regions;
35243 var north = rs["north"];
35244 var south = rs["south"];
35245 var west = rs["west"];
35246 var east = rs["east"];
35247 var center = rs["center"];
35248 //if(this.hideOnLayout){ // not supported anymore
35249 //c.el.setStyle("display", "none");
35251 if(north && north.isVisible()){
35252 var b = north.getBox();
35253 var m = north.getMargins();
35254 b.width = w - (m.left+m.right);
35257 centerY = b.height + b.y + m.bottom;
35258 centerH -= centerY;
35259 north.updateBox(this.safeBox(b));
35261 if(south && south.isVisible()){
35262 var b = south.getBox();
35263 var m = south.getMargins();
35264 b.width = w - (m.left+m.right);
35266 var totalHeight = (b.height + m.top + m.bottom);
35267 b.y = h - totalHeight + m.top;
35268 centerH -= totalHeight;
35269 south.updateBox(this.safeBox(b));
35271 if(west && west.isVisible()){
35272 var b = west.getBox();
35273 var m = west.getMargins();
35274 b.height = centerH - (m.top+m.bottom);
35276 b.y = centerY + m.top;
35277 var totalWidth = (b.width + m.left + m.right);
35278 centerX += totalWidth;
35279 centerW -= totalWidth;
35280 west.updateBox(this.safeBox(b));
35282 if(east && east.isVisible()){
35283 var b = east.getBox();
35284 var m = east.getMargins();
35285 b.height = centerH - (m.top+m.bottom);
35286 var totalWidth = (b.width + m.left + m.right);
35287 b.x = w - totalWidth + m.left;
35288 b.y = centerY + m.top;
35289 centerW -= totalWidth;
35290 east.updateBox(this.safeBox(b));
35293 var m = center.getMargins();
35295 x: centerX + m.left,
35296 y: centerY + m.top,
35297 width: centerW - (m.left+m.right),
35298 height: centerH - (m.top+m.bottom)
35300 //if(this.hideOnLayout){
35301 //center.el.setStyle("display", "block");
35303 center.updateBox(this.safeBox(centerBox));
35306 this.fireEvent("layout", this);
35310 safeBox : function(box){
35311 box.width = Math.max(0, box.width);
35312 box.height = Math.max(0, box.height);
35317 * Adds a ContentPanel (or subclass) to this layout.
35318 * @param {String} target The target region key (north, south, east, west or center).
35319 * @param {Roo.ContentPanel} panel The panel to add
35320 * @return {Roo.ContentPanel} The added panel
35322 add : function(target, panel){
35324 target = target.toLowerCase();
35325 return this.regions[target].add(panel);
35329 * Remove a ContentPanel (or subclass) to this layout.
35330 * @param {String} target The target region key (north, south, east, west or center).
35331 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35332 * @return {Roo.ContentPanel} The removed panel
35334 remove : function(target, panel){
35335 target = target.toLowerCase();
35336 return this.regions[target].remove(panel);
35340 * Searches all regions for a panel with the specified id
35341 * @param {String} panelId
35342 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35344 findPanel : function(panelId){
35345 var rs = this.regions;
35346 for(var target in rs){
35347 if(typeof rs[target] != "function"){
35348 var p = rs[target].getPanel(panelId);
35358 * Searches all regions for a panel with the specified id and activates (shows) it.
35359 * @param {String/ContentPanel} panelId The panels id or the panel itself
35360 * @return {Roo.ContentPanel} The shown panel or null
35362 showPanel : function(panelId) {
35363 var rs = this.regions;
35364 for(var target in rs){
35365 var r = rs[target];
35366 if(typeof r != "function"){
35367 if(r.hasPanel(panelId)){
35368 return r.showPanel(panelId);
35376 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35377 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35380 restoreState : function(provider){
35382 provider = Roo.state.Manager;
35384 var sm = new Roo.LayoutStateManager();
35385 sm.init(this, provider);
35391 * Adds a xtype elements to the layout.
35395 xtype : 'ContentPanel',
35402 xtype : 'NestedLayoutPanel',
35408 items : [ ... list of content panels or nested layout panels.. ]
35412 * @param {Object} cfg Xtype definition of item to add.
35414 addxtype : function(cfg)
35416 // basically accepts a pannel...
35417 // can accept a layout region..!?!?
35418 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35421 // theory? children can only be panels??
35423 //if (!cfg.xtype.match(/Panel$/)) {
35428 if (typeof(cfg.region) == 'undefined') {
35429 Roo.log("Failed to add Panel, region was not set");
35433 var region = cfg.region;
35439 xitems = cfg.items;
35444 if ( region == 'center') {
35445 Roo.log("Center: " + cfg.title);
35451 case 'Content': // ContentPanel (el, cfg)
35452 case 'Scroll': // ContentPanel (el, cfg)
35454 cfg.autoCreate = cfg.autoCreate || true;
35455 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35457 // var el = this.el.createChild();
35458 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35461 this.add(region, ret);
35465 case 'TreePanel': // our new panel!
35466 cfg.el = this.el.createChild();
35467 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35468 this.add(region, ret);
35473 // create a new Layout (which is a Border Layout...
35475 var clayout = cfg.layout;
35476 clayout.el = this.el.createChild();
35477 clayout.items = clayout.items || [];
35481 // replace this exitems with the clayout ones..
35482 xitems = clayout.items;
35484 // force background off if it's in center...
35485 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35486 cfg.background = false;
35488 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35491 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35492 //console.log('adding nested layout panel ' + cfg.toSource());
35493 this.add(region, ret);
35494 nb = {}; /// find first...
35499 // needs grid and region
35501 //var el = this.getRegion(region).el.createChild();
35503 *var el = this.el.createChild();
35504 // create the grid first...
35505 cfg.grid.container = el;
35506 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35509 if (region == 'center' && this.active ) {
35510 cfg.background = false;
35513 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35515 this.add(region, ret);
35517 if (cfg.background) {
35518 // render grid on panel activation (if panel background)
35519 ret.on('activate', function(gp) {
35520 if (!gp.grid.rendered) {
35521 // gp.grid.render(el);
35525 // cfg.grid.render(el);
35531 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35532 // it was the old xcomponent building that caused this before.
35533 // espeically if border is the top element in the tree.
35543 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35545 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35546 this.add(region, ret);
35550 throw "Can not add '" + cfg.xtype + "' to Border";
35556 this.beginUpdate();
35560 Roo.each(xitems, function(i) {
35561 region = nb && i.region ? i.region : false;
35563 var add = ret.addxtype(i);
35566 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35567 if (!i.background) {
35568 abn[region] = nb[region] ;
35575 // make the last non-background panel active..
35576 //if (nb) { Roo.log(abn); }
35579 for(var r in abn) {
35580 region = this.getRegion(r);
35582 // tried using nb[r], but it does not work..
35584 region.showPanel(abn[r]);
35595 factory : function(cfg)
35598 var validRegions = Roo.bootstrap.layout.Border.regions;
35600 var target = cfg.region;
35603 var r = Roo.bootstrap.layout;
35607 return new r.North(cfg);
35609 return new r.South(cfg);
35611 return new r.East(cfg);
35613 return new r.West(cfg);
35615 return new r.Center(cfg);
35617 throw 'Layout region "'+target+'" not supported.';
35624 * Ext JS Library 1.1.1
35625 * Copyright(c) 2006-2007, Ext JS, LLC.
35627 * Originally Released Under LGPL - original licence link has changed is not relivant.
35630 * <script type="text/javascript">
35634 * @class Roo.bootstrap.layout.Basic
35635 * @extends Roo.util.Observable
35636 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35637 * and does not have a titlebar, tabs or any other features. All it does is size and position
35638 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35639 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35640 * @cfg {string} region the region that it inhabits..
35641 * @cfg {bool} skipConfig skip config?
35645 Roo.bootstrap.layout.Basic = function(config){
35647 this.mgr = config.mgr;
35649 this.position = config.region;
35651 var skipConfig = config.skipConfig;
35655 * @scope Roo.BasicLayoutRegion
35659 * @event beforeremove
35660 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35661 * @param {Roo.LayoutRegion} this
35662 * @param {Roo.ContentPanel} panel The panel
35663 * @param {Object} e The cancel event object
35665 "beforeremove" : true,
35667 * @event invalidated
35668 * Fires when the layout for this region is changed.
35669 * @param {Roo.LayoutRegion} this
35671 "invalidated" : true,
35673 * @event visibilitychange
35674 * Fires when this region is shown or hidden
35675 * @param {Roo.LayoutRegion} this
35676 * @param {Boolean} visibility true or false
35678 "visibilitychange" : true,
35680 * @event paneladded
35681 * Fires when a panel is added.
35682 * @param {Roo.LayoutRegion} this
35683 * @param {Roo.ContentPanel} panel The panel
35685 "paneladded" : true,
35687 * @event panelremoved
35688 * Fires when a panel is removed.
35689 * @param {Roo.LayoutRegion} this
35690 * @param {Roo.ContentPanel} panel The panel
35692 "panelremoved" : true,
35694 * @event beforecollapse
35695 * Fires when this region before collapse.
35696 * @param {Roo.LayoutRegion} this
35698 "beforecollapse" : true,
35701 * Fires when this region is collapsed.
35702 * @param {Roo.LayoutRegion} this
35704 "collapsed" : true,
35707 * Fires when this region is expanded.
35708 * @param {Roo.LayoutRegion} this
35713 * Fires when this region is slid into view.
35714 * @param {Roo.LayoutRegion} this
35716 "slideshow" : true,
35719 * Fires when this region slides out of view.
35720 * @param {Roo.LayoutRegion} this
35722 "slidehide" : true,
35724 * @event panelactivated
35725 * Fires when a panel is activated.
35726 * @param {Roo.LayoutRegion} this
35727 * @param {Roo.ContentPanel} panel The activated panel
35729 "panelactivated" : true,
35732 * Fires when the user resizes this region.
35733 * @param {Roo.LayoutRegion} this
35734 * @param {Number} newSize The new size (width for east/west, height for north/south)
35738 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35739 this.panels = new Roo.util.MixedCollection();
35740 this.panels.getKey = this.getPanelId.createDelegate(this);
35742 this.activePanel = null;
35743 // ensure listeners are added...
35745 if (config.listeners || config.events) {
35746 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35747 listeners : config.listeners || {},
35748 events : config.events || {}
35752 if(skipConfig !== true){
35753 this.applyConfig(config);
35757 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35759 getPanelId : function(p){
35763 applyConfig : function(config){
35764 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35765 this.config = config;
35770 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35771 * the width, for horizontal (north, south) the height.
35772 * @param {Number} newSize The new width or height
35774 resizeTo : function(newSize){
35775 var el = this.el ? this.el :
35776 (this.activePanel ? this.activePanel.getEl() : null);
35778 switch(this.position){
35781 el.setWidth(newSize);
35782 this.fireEvent("resized", this, newSize);
35786 el.setHeight(newSize);
35787 this.fireEvent("resized", this, newSize);
35793 getBox : function(){
35794 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35797 getMargins : function(){
35798 return this.margins;
35801 updateBox : function(box){
35803 var el = this.activePanel.getEl();
35804 el.dom.style.left = box.x + "px";
35805 el.dom.style.top = box.y + "px";
35806 this.activePanel.setSize(box.width, box.height);
35810 * Returns the container element for this region.
35811 * @return {Roo.Element}
35813 getEl : function(){
35814 return this.activePanel;
35818 * Returns true if this region is currently visible.
35819 * @return {Boolean}
35821 isVisible : function(){
35822 return this.activePanel ? true : false;
35825 setActivePanel : function(panel){
35826 panel = this.getPanel(panel);
35827 if(this.activePanel && this.activePanel != panel){
35828 this.activePanel.setActiveState(false);
35829 this.activePanel.getEl().setLeftTop(-10000,-10000);
35831 this.activePanel = panel;
35832 panel.setActiveState(true);
35834 panel.setSize(this.box.width, this.box.height);
35836 this.fireEvent("panelactivated", this, panel);
35837 this.fireEvent("invalidated");
35841 * Show the specified panel.
35842 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35843 * @return {Roo.ContentPanel} The shown panel or null
35845 showPanel : function(panel){
35846 panel = this.getPanel(panel);
35848 this.setActivePanel(panel);
35854 * Get the active panel for this region.
35855 * @return {Roo.ContentPanel} The active panel or null
35857 getActivePanel : function(){
35858 return this.activePanel;
35862 * Add the passed ContentPanel(s)
35863 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35864 * @return {Roo.ContentPanel} The panel added (if only one was added)
35866 add : function(panel){
35867 if(arguments.length > 1){
35868 for(var i = 0, len = arguments.length; i < len; i++) {
35869 this.add(arguments[i]);
35873 if(this.hasPanel(panel)){
35874 this.showPanel(panel);
35877 var el = panel.getEl();
35878 if(el.dom.parentNode != this.mgr.el.dom){
35879 this.mgr.el.dom.appendChild(el.dom);
35881 if(panel.setRegion){
35882 panel.setRegion(this);
35884 this.panels.add(panel);
35885 el.setStyle("position", "absolute");
35886 if(!panel.background){
35887 this.setActivePanel(panel);
35888 if(this.config.initialSize && this.panels.getCount()==1){
35889 this.resizeTo(this.config.initialSize);
35892 this.fireEvent("paneladded", this, panel);
35897 * Returns true if the panel is in this region.
35898 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35899 * @return {Boolean}
35901 hasPanel : function(panel){
35902 if(typeof panel == "object"){ // must be panel obj
35903 panel = panel.getId();
35905 return this.getPanel(panel) ? true : false;
35909 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35910 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35911 * @param {Boolean} preservePanel Overrides the config preservePanel option
35912 * @return {Roo.ContentPanel} The panel that was removed
35914 remove : function(panel, preservePanel){
35915 panel = this.getPanel(panel);
35920 this.fireEvent("beforeremove", this, panel, e);
35921 if(e.cancel === true){
35924 var panelId = panel.getId();
35925 this.panels.removeKey(panelId);
35930 * Returns the panel specified or null if it's not in this region.
35931 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35932 * @return {Roo.ContentPanel}
35934 getPanel : function(id){
35935 if(typeof id == "object"){ // must be panel obj
35938 return this.panels.get(id);
35942 * Returns this regions position (north/south/east/west/center).
35945 getPosition: function(){
35946 return this.position;
35950 * Ext JS Library 1.1.1
35951 * Copyright(c) 2006-2007, Ext JS, LLC.
35953 * Originally Released Under LGPL - original licence link has changed is not relivant.
35956 * <script type="text/javascript">
35960 * @class Roo.bootstrap.layout.Region
35961 * @extends Roo.bootstrap.layout.Basic
35962 * This class represents a region in a layout manager.
35964 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35965 * @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})
35966 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35967 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35968 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35969 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35970 * @cfg {String} title The title for the region (overrides panel titles)
35971 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35972 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35973 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35974 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35975 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35976 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35977 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35978 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35979 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35980 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35982 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35983 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35984 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35985 * @cfg {Number} width For East/West panels
35986 * @cfg {Number} height For North/South panels
35987 * @cfg {Boolean} split To show the splitter
35988 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35990 * @cfg {string} cls Extra CSS classes to add to region
35992 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35993 * @cfg {string} region the region that it inhabits..
35996 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35997 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35999 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36000 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36001 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36003 Roo.bootstrap.layout.Region = function(config)
36005 this.applyConfig(config);
36007 var mgr = config.mgr;
36008 var pos = config.region;
36009 config.skipConfig = true;
36010 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36013 this.onRender(mgr.el);
36016 this.visible = true;
36017 this.collapsed = false;
36018 this.unrendered_panels = [];
36021 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36023 position: '', // set by wrapper (eg. north/south etc..)
36024 unrendered_panels : null, // unrendered panels.
36026 tabPosition : false,
36028 mgr: false, // points to 'Border'
36031 createBody : function(){
36032 /** This region's body element
36033 * @type Roo.Element */
36034 this.bodyEl = this.el.createChild({
36036 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36040 onRender: function(ctr, pos)
36042 var dh = Roo.DomHelper;
36043 /** This region's container element
36044 * @type Roo.Element */
36045 this.el = dh.append(ctr.dom, {
36047 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36049 /** This region's title element
36050 * @type Roo.Element */
36052 this.titleEl = dh.append(this.el.dom, {
36054 unselectable: "on",
36055 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36057 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36058 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36062 this.titleEl.enableDisplayMode();
36063 /** This region's title text element
36064 * @type HTMLElement */
36065 this.titleTextEl = this.titleEl.dom.firstChild;
36066 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36068 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36069 this.closeBtn.enableDisplayMode();
36070 this.closeBtn.on("click", this.closeClicked, this);
36071 this.closeBtn.hide();
36073 this.createBody(this.config);
36074 if(this.config.hideWhenEmpty){
36076 this.on("paneladded", this.validateVisibility, this);
36077 this.on("panelremoved", this.validateVisibility, this);
36079 if(this.autoScroll){
36080 this.bodyEl.setStyle("overflow", "auto");
36082 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36084 //if(c.titlebar !== false){
36085 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36086 this.titleEl.hide();
36088 this.titleEl.show();
36089 if(this.config.title){
36090 this.titleTextEl.innerHTML = this.config.title;
36094 if(this.config.collapsed){
36095 this.collapse(true);
36097 if(this.config.hidden){
36101 if (this.unrendered_panels && this.unrendered_panels.length) {
36102 for (var i =0;i< this.unrendered_panels.length; i++) {
36103 this.add(this.unrendered_panels[i]);
36105 this.unrendered_panels = null;
36111 applyConfig : function(c)
36114 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36115 var dh = Roo.DomHelper;
36116 if(c.titlebar !== false){
36117 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36118 this.collapseBtn.on("click", this.collapse, this);
36119 this.collapseBtn.enableDisplayMode();
36121 if(c.showPin === true || this.showPin){
36122 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36123 this.stickBtn.enableDisplayMode();
36124 this.stickBtn.on("click", this.expand, this);
36125 this.stickBtn.hide();
36130 /** This region's collapsed element
36131 * @type Roo.Element */
36134 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36135 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36138 if(c.floatable !== false){
36139 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36140 this.collapsedEl.on("click", this.collapseClick, this);
36143 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36144 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36145 id: "message", unselectable: "on", style:{"float":"left"}});
36146 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36148 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36149 this.expandBtn.on("click", this.expand, this);
36153 if(this.collapseBtn){
36154 this.collapseBtn.setVisible(c.collapsible == true);
36157 this.cmargins = c.cmargins || this.cmargins ||
36158 (this.position == "west" || this.position == "east" ?
36159 {top: 0, left: 2, right:2, bottom: 0} :
36160 {top: 2, left: 0, right:0, bottom: 2});
36162 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36165 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36167 this.autoScroll = c.autoScroll || false;
36172 this.duration = c.duration || .30;
36173 this.slideDuration = c.slideDuration || .45;
36178 * Returns true if this region is currently visible.
36179 * @return {Boolean}
36181 isVisible : function(){
36182 return this.visible;
36186 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36187 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36189 //setCollapsedTitle : function(title){
36190 // title = title || " ";
36191 // if(this.collapsedTitleTextEl){
36192 // this.collapsedTitleTextEl.innerHTML = title;
36196 getBox : function(){
36198 // if(!this.collapsed){
36199 b = this.el.getBox(false, true);
36201 // b = this.collapsedEl.getBox(false, true);
36206 getMargins : function(){
36207 return this.margins;
36208 //return this.collapsed ? this.cmargins : this.margins;
36211 highlight : function(){
36212 this.el.addClass("x-layout-panel-dragover");
36215 unhighlight : function(){
36216 this.el.removeClass("x-layout-panel-dragover");
36219 updateBox : function(box)
36221 if (!this.bodyEl) {
36222 return; // not rendered yet..
36226 if(!this.collapsed){
36227 this.el.dom.style.left = box.x + "px";
36228 this.el.dom.style.top = box.y + "px";
36229 this.updateBody(box.width, box.height);
36231 this.collapsedEl.dom.style.left = box.x + "px";
36232 this.collapsedEl.dom.style.top = box.y + "px";
36233 this.collapsedEl.setSize(box.width, box.height);
36236 this.tabs.autoSizeTabs();
36240 updateBody : function(w, h)
36243 this.el.setWidth(w);
36244 w -= this.el.getBorderWidth("rl");
36245 if(this.config.adjustments){
36246 w += this.config.adjustments[0];
36249 if(h !== null && h > 0){
36250 this.el.setHeight(h);
36251 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36252 h -= this.el.getBorderWidth("tb");
36253 if(this.config.adjustments){
36254 h += this.config.adjustments[1];
36256 this.bodyEl.setHeight(h);
36258 h = this.tabs.syncHeight(h);
36261 if(this.panelSize){
36262 w = w !== null ? w : this.panelSize.width;
36263 h = h !== null ? h : this.panelSize.height;
36265 if(this.activePanel){
36266 var el = this.activePanel.getEl();
36267 w = w !== null ? w : el.getWidth();
36268 h = h !== null ? h : el.getHeight();
36269 this.panelSize = {width: w, height: h};
36270 this.activePanel.setSize(w, h);
36272 if(Roo.isIE && this.tabs){
36273 this.tabs.el.repaint();
36278 * Returns the container element for this region.
36279 * @return {Roo.Element}
36281 getEl : function(){
36286 * Hides this region.
36289 //if(!this.collapsed){
36290 this.el.dom.style.left = "-2000px";
36293 // this.collapsedEl.dom.style.left = "-2000px";
36294 // this.collapsedEl.hide();
36296 this.visible = false;
36297 this.fireEvent("visibilitychange", this, false);
36301 * Shows this region if it was previously hidden.
36304 //if(!this.collapsed){
36307 // this.collapsedEl.show();
36309 this.visible = true;
36310 this.fireEvent("visibilitychange", this, true);
36313 closeClicked : function(){
36314 if(this.activePanel){
36315 this.remove(this.activePanel);
36319 collapseClick : function(e){
36321 e.stopPropagation();
36324 e.stopPropagation();
36330 * Collapses this region.
36331 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36334 collapse : function(skipAnim, skipCheck = false){
36335 if(this.collapsed) {
36339 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36341 this.collapsed = true;
36343 this.split.el.hide();
36345 if(this.config.animate && skipAnim !== true){
36346 this.fireEvent("invalidated", this);
36347 this.animateCollapse();
36349 this.el.setLocation(-20000,-20000);
36351 this.collapsedEl.show();
36352 this.fireEvent("collapsed", this);
36353 this.fireEvent("invalidated", this);
36359 animateCollapse : function(){
36364 * Expands this region if it was previously collapsed.
36365 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36366 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36369 expand : function(e, skipAnim){
36371 e.stopPropagation();
36373 if(!this.collapsed || this.el.hasActiveFx()) {
36377 this.afterSlideIn();
36380 this.collapsed = false;
36381 if(this.config.animate && skipAnim !== true){
36382 this.animateExpand();
36386 this.split.el.show();
36388 this.collapsedEl.setLocation(-2000,-2000);
36389 this.collapsedEl.hide();
36390 this.fireEvent("invalidated", this);
36391 this.fireEvent("expanded", this);
36395 animateExpand : function(){
36399 initTabs : function()
36401 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36403 var ts = new Roo.bootstrap.panel.Tabs({
36404 el: this.bodyEl.dom,
36406 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36407 disableTooltips: this.config.disableTabTips,
36408 toolbar : this.config.toolbar
36411 if(this.config.hideTabs){
36412 ts.stripWrap.setDisplayed(false);
36415 ts.resizeTabs = this.config.resizeTabs === true;
36416 ts.minTabWidth = this.config.minTabWidth || 40;
36417 ts.maxTabWidth = this.config.maxTabWidth || 250;
36418 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36419 ts.monitorResize = false;
36420 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36421 ts.bodyEl.addClass('roo-layout-tabs-body');
36422 this.panels.each(this.initPanelAsTab, this);
36425 initPanelAsTab : function(panel){
36426 var ti = this.tabs.addTab(
36430 this.config.closeOnTab && panel.isClosable(),
36433 if(panel.tabTip !== undefined){
36434 ti.setTooltip(panel.tabTip);
36436 ti.on("activate", function(){
36437 this.setActivePanel(panel);
36440 if(this.config.closeOnTab){
36441 ti.on("beforeclose", function(t, e){
36443 this.remove(panel);
36447 panel.tabItem = ti;
36452 updatePanelTitle : function(panel, title)
36454 if(this.activePanel == panel){
36455 this.updateTitle(title);
36458 var ti = this.tabs.getTab(panel.getEl().id);
36460 if(panel.tabTip !== undefined){
36461 ti.setTooltip(panel.tabTip);
36466 updateTitle : function(title){
36467 if(this.titleTextEl && !this.config.title){
36468 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36472 setActivePanel : function(panel)
36474 panel = this.getPanel(panel);
36475 if(this.activePanel && this.activePanel != panel){
36476 if(this.activePanel.setActiveState(false) === false){
36480 this.activePanel = panel;
36481 panel.setActiveState(true);
36482 if(this.panelSize){
36483 panel.setSize(this.panelSize.width, this.panelSize.height);
36486 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36488 this.updateTitle(panel.getTitle());
36490 this.fireEvent("invalidated", this);
36492 this.fireEvent("panelactivated", this, panel);
36496 * Shows the specified panel.
36497 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36498 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36500 showPanel : function(panel)
36502 panel = this.getPanel(panel);
36505 var tab = this.tabs.getTab(panel.getEl().id);
36506 if(tab.isHidden()){
36507 this.tabs.unhideTab(tab.id);
36511 this.setActivePanel(panel);
36518 * Get the active panel for this region.
36519 * @return {Roo.ContentPanel} The active panel or null
36521 getActivePanel : function(){
36522 return this.activePanel;
36525 validateVisibility : function(){
36526 if(this.panels.getCount() < 1){
36527 this.updateTitle(" ");
36528 this.closeBtn.hide();
36531 if(!this.isVisible()){
36538 * Adds the passed ContentPanel(s) to this region.
36539 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36540 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36542 add : function(panel)
36544 if(arguments.length > 1){
36545 for(var i = 0, len = arguments.length; i < len; i++) {
36546 this.add(arguments[i]);
36551 // if we have not been rendered yet, then we can not really do much of this..
36552 if (!this.bodyEl) {
36553 this.unrendered_panels.push(panel);
36560 if(this.hasPanel(panel)){
36561 this.showPanel(panel);
36564 panel.setRegion(this);
36565 this.panels.add(panel);
36566 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36567 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36568 // and hide them... ???
36569 this.bodyEl.dom.appendChild(panel.getEl().dom);
36570 if(panel.background !== true){
36571 this.setActivePanel(panel);
36573 this.fireEvent("paneladded", this, panel);
36580 this.initPanelAsTab(panel);
36584 if(panel.background !== true){
36585 this.tabs.activate(panel.getEl().id);
36587 this.fireEvent("paneladded", this, panel);
36592 * Hides the tab for the specified panel.
36593 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36595 hidePanel : function(panel){
36596 if(this.tabs && (panel = this.getPanel(panel))){
36597 this.tabs.hideTab(panel.getEl().id);
36602 * Unhides the tab for a previously hidden panel.
36603 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36605 unhidePanel : function(panel){
36606 if(this.tabs && (panel = this.getPanel(panel))){
36607 this.tabs.unhideTab(panel.getEl().id);
36611 clearPanels : function(){
36612 while(this.panels.getCount() > 0){
36613 this.remove(this.panels.first());
36618 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36619 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36620 * @param {Boolean} preservePanel Overrides the config preservePanel option
36621 * @return {Roo.ContentPanel} The panel that was removed
36623 remove : function(panel, preservePanel)
36625 panel = this.getPanel(panel);
36630 this.fireEvent("beforeremove", this, panel, e);
36631 if(e.cancel === true){
36634 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36635 var panelId = panel.getId();
36636 this.panels.removeKey(panelId);
36638 document.body.appendChild(panel.getEl().dom);
36641 this.tabs.removeTab(panel.getEl().id);
36642 }else if (!preservePanel){
36643 this.bodyEl.dom.removeChild(panel.getEl().dom);
36645 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36646 var p = this.panels.first();
36647 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36648 tempEl.appendChild(p.getEl().dom);
36649 this.bodyEl.update("");
36650 this.bodyEl.dom.appendChild(p.getEl().dom);
36652 this.updateTitle(p.getTitle());
36654 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36655 this.setActivePanel(p);
36657 panel.setRegion(null);
36658 if(this.activePanel == panel){
36659 this.activePanel = null;
36661 if(this.config.autoDestroy !== false && preservePanel !== true){
36662 try{panel.destroy();}catch(e){}
36664 this.fireEvent("panelremoved", this, panel);
36669 * Returns the TabPanel component used by this region
36670 * @return {Roo.TabPanel}
36672 getTabs : function(){
36676 createTool : function(parentEl, className){
36677 var btn = Roo.DomHelper.append(parentEl, {
36679 cls: "x-layout-tools-button",
36682 cls: "roo-layout-tools-button-inner " + className,
36686 btn.addClassOnOver("roo-layout-tools-button-over");
36691 * Ext JS Library 1.1.1
36692 * Copyright(c) 2006-2007, Ext JS, LLC.
36694 * Originally Released Under LGPL - original licence link has changed is not relivant.
36697 * <script type="text/javascript">
36703 * @class Roo.SplitLayoutRegion
36704 * @extends Roo.LayoutRegion
36705 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36707 Roo.bootstrap.layout.Split = function(config){
36708 this.cursor = config.cursor;
36709 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36712 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36714 splitTip : "Drag to resize.",
36715 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36716 useSplitTips : false,
36718 applyConfig : function(config){
36719 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36722 onRender : function(ctr,pos) {
36724 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36725 if(!this.config.split){
36730 var splitEl = Roo.DomHelper.append(ctr.dom, {
36732 id: this.el.id + "-split",
36733 cls: "roo-layout-split roo-layout-split-"+this.position,
36736 /** The SplitBar for this region
36737 * @type Roo.SplitBar */
36738 // does not exist yet...
36739 Roo.log([this.position, this.orientation]);
36741 this.split = new Roo.bootstrap.SplitBar({
36742 dragElement : splitEl,
36743 resizingElement: this.el,
36744 orientation : this.orientation
36747 this.split.on("moved", this.onSplitMove, this);
36748 this.split.useShim = this.config.useShim === true;
36749 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36750 if(this.useSplitTips){
36751 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36753 //if(config.collapsible){
36754 // this.split.el.on("dblclick", this.collapse, this);
36757 if(typeof this.config.minSize != "undefined"){
36758 this.split.minSize = this.config.minSize;
36760 if(typeof this.config.maxSize != "undefined"){
36761 this.split.maxSize = this.config.maxSize;
36763 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36764 this.hideSplitter();
36769 getHMaxSize : function(){
36770 var cmax = this.config.maxSize || 10000;
36771 var center = this.mgr.getRegion("center");
36772 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36775 getVMaxSize : function(){
36776 var cmax = this.config.maxSize || 10000;
36777 var center = this.mgr.getRegion("center");
36778 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36781 onSplitMove : function(split, newSize){
36782 this.fireEvent("resized", this, newSize);
36786 * Returns the {@link Roo.SplitBar} for this region.
36787 * @return {Roo.SplitBar}
36789 getSplitBar : function(){
36794 this.hideSplitter();
36795 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36798 hideSplitter : function(){
36800 this.split.el.setLocation(-2000,-2000);
36801 this.split.el.hide();
36807 this.split.el.show();
36809 Roo.bootstrap.layout.Split.superclass.show.call(this);
36812 beforeSlide: function(){
36813 if(Roo.isGecko){// firefox overflow auto bug workaround
36814 this.bodyEl.clip();
36816 this.tabs.bodyEl.clip();
36818 if(this.activePanel){
36819 this.activePanel.getEl().clip();
36821 if(this.activePanel.beforeSlide){
36822 this.activePanel.beforeSlide();
36828 afterSlide : function(){
36829 if(Roo.isGecko){// firefox overflow auto bug workaround
36830 this.bodyEl.unclip();
36832 this.tabs.bodyEl.unclip();
36834 if(this.activePanel){
36835 this.activePanel.getEl().unclip();
36836 if(this.activePanel.afterSlide){
36837 this.activePanel.afterSlide();
36843 initAutoHide : function(){
36844 if(this.autoHide !== false){
36845 if(!this.autoHideHd){
36846 var st = new Roo.util.DelayedTask(this.slideIn, this);
36847 this.autoHideHd = {
36848 "mouseout": function(e){
36849 if(!e.within(this.el, true)){
36853 "mouseover" : function(e){
36859 this.el.on(this.autoHideHd);
36863 clearAutoHide : function(){
36864 if(this.autoHide !== false){
36865 this.el.un("mouseout", this.autoHideHd.mouseout);
36866 this.el.un("mouseover", this.autoHideHd.mouseover);
36870 clearMonitor : function(){
36871 Roo.get(document).un("click", this.slideInIf, this);
36874 // these names are backwards but not changed for compat
36875 slideOut : function(){
36876 if(this.isSlid || this.el.hasActiveFx()){
36879 this.isSlid = true;
36880 if(this.collapseBtn){
36881 this.collapseBtn.hide();
36883 this.closeBtnState = this.closeBtn.getStyle('display');
36884 this.closeBtn.hide();
36886 this.stickBtn.show();
36889 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36890 this.beforeSlide();
36891 this.el.setStyle("z-index", 10001);
36892 this.el.slideIn(this.getSlideAnchor(), {
36893 callback: function(){
36895 this.initAutoHide();
36896 Roo.get(document).on("click", this.slideInIf, this);
36897 this.fireEvent("slideshow", this);
36904 afterSlideIn : function(){
36905 this.clearAutoHide();
36906 this.isSlid = false;
36907 this.clearMonitor();
36908 this.el.setStyle("z-index", "");
36909 if(this.collapseBtn){
36910 this.collapseBtn.show();
36912 this.closeBtn.setStyle('display', this.closeBtnState);
36914 this.stickBtn.hide();
36916 this.fireEvent("slidehide", this);
36919 slideIn : function(cb){
36920 if(!this.isSlid || this.el.hasActiveFx()){
36924 this.isSlid = false;
36925 this.beforeSlide();
36926 this.el.slideOut(this.getSlideAnchor(), {
36927 callback: function(){
36928 this.el.setLeftTop(-10000, -10000);
36930 this.afterSlideIn();
36938 slideInIf : function(e){
36939 if(!e.within(this.el)){
36944 animateCollapse : function(){
36945 this.beforeSlide();
36946 this.el.setStyle("z-index", 20000);
36947 var anchor = this.getSlideAnchor();
36948 this.el.slideOut(anchor, {
36949 callback : function(){
36950 this.el.setStyle("z-index", "");
36951 this.collapsedEl.slideIn(anchor, {duration:.3});
36953 this.el.setLocation(-10000,-10000);
36955 this.fireEvent("collapsed", this);
36962 animateExpand : function(){
36963 this.beforeSlide();
36964 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36965 this.el.setStyle("z-index", 20000);
36966 this.collapsedEl.hide({
36969 this.el.slideIn(this.getSlideAnchor(), {
36970 callback : function(){
36971 this.el.setStyle("z-index", "");
36974 this.split.el.show();
36976 this.fireEvent("invalidated", this);
36977 this.fireEvent("expanded", this);
37005 getAnchor : function(){
37006 return this.anchors[this.position];
37009 getCollapseAnchor : function(){
37010 return this.canchors[this.position];
37013 getSlideAnchor : function(){
37014 return this.sanchors[this.position];
37017 getAlignAdj : function(){
37018 var cm = this.cmargins;
37019 switch(this.position){
37035 getExpandAdj : function(){
37036 var c = this.collapsedEl, cm = this.cmargins;
37037 switch(this.position){
37039 return [-(cm.right+c.getWidth()+cm.left), 0];
37042 return [cm.right+c.getWidth()+cm.left, 0];
37045 return [0, -(cm.top+cm.bottom+c.getHeight())];
37048 return [0, cm.top+cm.bottom+c.getHeight()];
37054 * Ext JS Library 1.1.1
37055 * Copyright(c) 2006-2007, Ext JS, LLC.
37057 * Originally Released Under LGPL - original licence link has changed is not relivant.
37060 * <script type="text/javascript">
37063 * These classes are private internal classes
37065 Roo.bootstrap.layout.Center = function(config){
37066 config.region = "center";
37067 Roo.bootstrap.layout.Region.call(this, config);
37068 this.visible = true;
37069 this.minWidth = config.minWidth || 20;
37070 this.minHeight = config.minHeight || 20;
37073 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37075 // center panel can't be hidden
37079 // center panel can't be hidden
37082 getMinWidth: function(){
37083 return this.minWidth;
37086 getMinHeight: function(){
37087 return this.minHeight;
37101 Roo.bootstrap.layout.North = function(config)
37103 config.region = 'north';
37104 config.cursor = 'n-resize';
37106 Roo.bootstrap.layout.Split.call(this, config);
37110 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37111 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37112 this.split.el.addClass("roo-layout-split-v");
37114 var size = config.initialSize || config.height;
37115 if(typeof size != "undefined"){
37116 this.el.setHeight(size);
37119 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37121 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37125 getBox : function(){
37126 if(this.collapsed){
37127 return this.collapsedEl.getBox();
37129 var box = this.el.getBox();
37131 box.height += this.split.el.getHeight();
37136 updateBox : function(box){
37137 if(this.split && !this.collapsed){
37138 box.height -= this.split.el.getHeight();
37139 this.split.el.setLeft(box.x);
37140 this.split.el.setTop(box.y+box.height);
37141 this.split.el.setWidth(box.width);
37143 if(this.collapsed){
37144 this.updateBody(box.width, null);
37146 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37154 Roo.bootstrap.layout.South = function(config){
37155 config.region = 'south';
37156 config.cursor = 's-resize';
37157 Roo.bootstrap.layout.Split.call(this, config);
37159 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37160 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37161 this.split.el.addClass("roo-layout-split-v");
37163 var size = config.initialSize || config.height;
37164 if(typeof size != "undefined"){
37165 this.el.setHeight(size);
37169 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37170 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37171 getBox : function(){
37172 if(this.collapsed){
37173 return this.collapsedEl.getBox();
37175 var box = this.el.getBox();
37177 var sh = this.split.el.getHeight();
37184 updateBox : function(box){
37185 if(this.split && !this.collapsed){
37186 var sh = this.split.el.getHeight();
37189 this.split.el.setLeft(box.x);
37190 this.split.el.setTop(box.y-sh);
37191 this.split.el.setWidth(box.width);
37193 if(this.collapsed){
37194 this.updateBody(box.width, null);
37196 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37200 Roo.bootstrap.layout.East = function(config){
37201 config.region = "east";
37202 config.cursor = "e-resize";
37203 Roo.bootstrap.layout.Split.call(this, config);
37205 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37206 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37207 this.split.el.addClass("roo-layout-split-h");
37209 var size = config.initialSize || config.width;
37210 if(typeof size != "undefined"){
37211 this.el.setWidth(size);
37214 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37215 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37216 getBox : function(){
37217 if(this.collapsed){
37218 return this.collapsedEl.getBox();
37220 var box = this.el.getBox();
37222 var sw = this.split.el.getWidth();
37229 updateBox : function(box){
37230 if(this.split && !this.collapsed){
37231 var sw = this.split.el.getWidth();
37233 this.split.el.setLeft(box.x);
37234 this.split.el.setTop(box.y);
37235 this.split.el.setHeight(box.height);
37238 if(this.collapsed){
37239 this.updateBody(null, box.height);
37241 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37245 Roo.bootstrap.layout.West = function(config){
37246 config.region = "west";
37247 config.cursor = "w-resize";
37249 Roo.bootstrap.layout.Split.call(this, config);
37251 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37252 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37253 this.split.el.addClass("roo-layout-split-h");
37257 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37258 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37260 onRender: function(ctr, pos)
37262 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37263 var size = this.config.initialSize || this.config.width;
37264 if(typeof size != "undefined"){
37265 this.el.setWidth(size);
37269 getBox : function(){
37270 if(this.collapsed){
37271 return this.collapsedEl.getBox();
37273 var box = this.el.getBox();
37275 box.width += this.split.el.getWidth();
37280 updateBox : function(box){
37281 if(this.split && !this.collapsed){
37282 var sw = this.split.el.getWidth();
37284 this.split.el.setLeft(box.x+box.width);
37285 this.split.el.setTop(box.y);
37286 this.split.el.setHeight(box.height);
37288 if(this.collapsed){
37289 this.updateBody(null, box.height);
37291 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37293 });Roo.namespace("Roo.bootstrap.panel");/*
37295 * Ext JS Library 1.1.1
37296 * Copyright(c) 2006-2007, Ext JS, LLC.
37298 * Originally Released Under LGPL - original licence link has changed is not relivant.
37301 * <script type="text/javascript">
37304 * @class Roo.ContentPanel
37305 * @extends Roo.util.Observable
37306 * A basic ContentPanel element.
37307 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37308 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37309 * @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
37310 * @cfg {Boolean} closable True if the panel can be closed/removed
37311 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37312 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37313 * @cfg {Toolbar} toolbar A toolbar for this panel
37314 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37315 * @cfg {String} title The title for this panel
37316 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37317 * @cfg {String} url Calls {@link #setUrl} with this value
37318 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37319 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37320 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37321 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37322 * @cfg {Boolean} badges render the badges
37325 * Create a new ContentPanel.
37326 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37327 * @param {String/Object} config A string to set only the title or a config object
37328 * @param {String} content (optional) Set the HTML content for this panel
37329 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37331 Roo.bootstrap.panel.Content = function( config){
37333 this.tpl = config.tpl || false;
37335 var el = config.el;
37336 var content = config.content;
37338 if(config.autoCreate){ // xtype is available if this is called from factory
37341 this.el = Roo.get(el);
37342 if(!this.el && config && config.autoCreate){
37343 if(typeof config.autoCreate == "object"){
37344 if(!config.autoCreate.id){
37345 config.autoCreate.id = config.id||el;
37347 this.el = Roo.DomHelper.append(document.body,
37348 config.autoCreate, true);
37350 var elcfg = { tag: "div",
37351 cls: "roo-layout-inactive-content",
37355 elcfg.html = config.html;
37359 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37362 this.closable = false;
37363 this.loaded = false;
37364 this.active = false;
37367 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37369 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37371 this.wrapEl = this.el; //this.el.wrap();
37373 if (config.toolbar.items) {
37374 ti = config.toolbar.items ;
37375 delete config.toolbar.items ;
37379 this.toolbar.render(this.wrapEl, 'before');
37380 for(var i =0;i < ti.length;i++) {
37381 // Roo.log(['add child', items[i]]);
37382 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37384 this.toolbar.items = nitems;
37385 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37386 delete config.toolbar;
37390 // xtype created footer. - not sure if will work as we normally have to render first..
37391 if (this.footer && !this.footer.el && this.footer.xtype) {
37392 if (!this.wrapEl) {
37393 this.wrapEl = this.el.wrap();
37396 this.footer.container = this.wrapEl.createChild();
37398 this.footer = Roo.factory(this.footer, Roo);
37403 if(typeof config == "string"){
37404 this.title = config;
37406 Roo.apply(this, config);
37410 this.resizeEl = Roo.get(this.resizeEl, true);
37412 this.resizeEl = this.el;
37414 // handle view.xtype
37422 * Fires when this panel is activated.
37423 * @param {Roo.ContentPanel} this
37427 * @event deactivate
37428 * Fires when this panel is activated.
37429 * @param {Roo.ContentPanel} this
37431 "deactivate" : true,
37435 * Fires when this panel is resized if fitToFrame is true.
37436 * @param {Roo.ContentPanel} this
37437 * @param {Number} width The width after any component adjustments
37438 * @param {Number} height The height after any component adjustments
37444 * Fires when this tab is created
37445 * @param {Roo.ContentPanel} this
37456 if(this.autoScroll){
37457 this.resizeEl.setStyle("overflow", "auto");
37459 // fix randome scrolling
37460 //this.el.on('scroll', function() {
37461 // Roo.log('fix random scolling');
37462 // this.scrollTo('top',0);
37465 content = content || this.content;
37467 this.setContent(content);
37469 if(config && config.url){
37470 this.setUrl(this.url, this.params, this.loadOnce);
37475 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37477 if (this.view && typeof(this.view.xtype) != 'undefined') {
37478 this.view.el = this.el.appendChild(document.createElement("div"));
37479 this.view = Roo.factory(this.view);
37480 this.view.render && this.view.render(false, '');
37484 this.fireEvent('render', this);
37487 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37491 setRegion : function(region){
37492 this.region = region;
37493 this.setActiveClass(region && !this.background);
37497 setActiveClass: function(state)
37500 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37501 this.el.setStyle('position','relative');
37503 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37504 this.el.setStyle('position', 'absolute');
37509 * Returns the toolbar for this Panel if one was configured.
37510 * @return {Roo.Toolbar}
37512 getToolbar : function(){
37513 return this.toolbar;
37516 setActiveState : function(active)
37518 this.active = active;
37519 this.setActiveClass(active);
37521 if(this.fireEvent("deactivate", this) === false){
37526 this.fireEvent("activate", this);
37530 * Updates this panel's element
37531 * @param {String} content The new content
37532 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37534 setContent : function(content, loadScripts){
37535 this.el.update(content, loadScripts);
37538 ignoreResize : function(w, h){
37539 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37542 this.lastSize = {width: w, height: h};
37547 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37548 * @return {Roo.UpdateManager} The UpdateManager
37550 getUpdateManager : function(){
37551 return this.el.getUpdateManager();
37554 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37555 * @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:
37558 url: "your-url.php",
37559 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37560 callback: yourFunction,
37561 scope: yourObject, //(optional scope)
37564 text: "Loading...",
37569 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37570 * 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.
37571 * @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}
37572 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37573 * @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.
37574 * @return {Roo.ContentPanel} this
37577 var um = this.el.getUpdateManager();
37578 um.update.apply(um, arguments);
37584 * 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.
37585 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37586 * @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)
37587 * @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)
37588 * @return {Roo.UpdateManager} The UpdateManager
37590 setUrl : function(url, params, loadOnce){
37591 if(this.refreshDelegate){
37592 this.removeListener("activate", this.refreshDelegate);
37594 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37595 this.on("activate", this.refreshDelegate);
37596 return this.el.getUpdateManager();
37599 _handleRefresh : function(url, params, loadOnce){
37600 if(!loadOnce || !this.loaded){
37601 var updater = this.el.getUpdateManager();
37602 updater.update(url, params, this._setLoaded.createDelegate(this));
37606 _setLoaded : function(){
37607 this.loaded = true;
37611 * Returns this panel's id
37614 getId : function(){
37619 * Returns this panel's element - used by regiosn to add.
37620 * @return {Roo.Element}
37622 getEl : function(){
37623 return this.wrapEl || this.el;
37628 adjustForComponents : function(width, height)
37630 //Roo.log('adjustForComponents ');
37631 if(this.resizeEl != this.el){
37632 width -= this.el.getFrameWidth('lr');
37633 height -= this.el.getFrameWidth('tb');
37636 var te = this.toolbar.getEl();
37637 te.setWidth(width);
37638 height -= te.getHeight();
37641 var te = this.footer.getEl();
37642 te.setWidth(width);
37643 height -= te.getHeight();
37647 if(this.adjustments){
37648 width += this.adjustments[0];
37649 height += this.adjustments[1];
37651 return {"width": width, "height": height};
37654 setSize : function(width, height){
37655 if(this.fitToFrame && !this.ignoreResize(width, height)){
37656 if(this.fitContainer && this.resizeEl != this.el){
37657 this.el.setSize(width, height);
37659 var size = this.adjustForComponents(width, height);
37660 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37661 this.fireEvent('resize', this, size.width, size.height);
37666 * Returns this panel's title
37669 getTitle : function(){
37671 if (typeof(this.title) != 'object') {
37676 for (var k in this.title) {
37677 if (!this.title.hasOwnProperty(k)) {
37681 if (k.indexOf('-') >= 0) {
37682 var s = k.split('-');
37683 for (var i = 0; i<s.length; i++) {
37684 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37687 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37694 * Set this panel's title
37695 * @param {String} title
37697 setTitle : function(title){
37698 this.title = title;
37700 this.region.updatePanelTitle(this, title);
37705 * Returns true is this panel was configured to be closable
37706 * @return {Boolean}
37708 isClosable : function(){
37709 return this.closable;
37712 beforeSlide : function(){
37714 this.resizeEl.clip();
37717 afterSlide : function(){
37719 this.resizeEl.unclip();
37723 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37724 * Will fail silently if the {@link #setUrl} method has not been called.
37725 * This does not activate the panel, just updates its content.
37727 refresh : function(){
37728 if(this.refreshDelegate){
37729 this.loaded = false;
37730 this.refreshDelegate();
37735 * Destroys this panel
37737 destroy : function(){
37738 this.el.removeAllListeners();
37739 var tempEl = document.createElement("span");
37740 tempEl.appendChild(this.el.dom);
37741 tempEl.innerHTML = "";
37747 * form - if the content panel contains a form - this is a reference to it.
37748 * @type {Roo.form.Form}
37752 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37753 * This contains a reference to it.
37759 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37769 * @param {Object} cfg Xtype definition of item to add.
37773 getChildContainer: function () {
37774 return this.getEl();
37779 var ret = new Roo.factory(cfg);
37784 if (cfg.xtype.match(/^Form$/)) {
37787 //if (this.footer) {
37788 // el = this.footer.container.insertSibling(false, 'before');
37790 el = this.el.createChild();
37793 this.form = new Roo.form.Form(cfg);
37796 if ( this.form.allItems.length) {
37797 this.form.render(el.dom);
37801 // should only have one of theses..
37802 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37803 // views.. should not be just added - used named prop 'view''
37805 cfg.el = this.el.appendChild(document.createElement("div"));
37808 var ret = new Roo.factory(cfg);
37810 ret.render && ret.render(false, ''); // render blank..
37820 * @class Roo.bootstrap.panel.Grid
37821 * @extends Roo.bootstrap.panel.Content
37823 * Create a new GridPanel.
37824 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37825 * @param {Object} config A the config object
37831 Roo.bootstrap.panel.Grid = function(config)
37835 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37836 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37838 config.el = this.wrapper;
37839 //this.el = this.wrapper;
37841 if (config.container) {
37842 // ctor'ed from a Border/panel.grid
37845 this.wrapper.setStyle("overflow", "hidden");
37846 this.wrapper.addClass('roo-grid-container');
37851 if(config.toolbar){
37852 var tool_el = this.wrapper.createChild();
37853 this.toolbar = Roo.factory(config.toolbar);
37855 if (config.toolbar.items) {
37856 ti = config.toolbar.items ;
37857 delete config.toolbar.items ;
37861 this.toolbar.render(tool_el);
37862 for(var i =0;i < ti.length;i++) {
37863 // Roo.log(['add child', items[i]]);
37864 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37866 this.toolbar.items = nitems;
37868 delete config.toolbar;
37871 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37872 config.grid.scrollBody = true;;
37873 config.grid.monitorWindowResize = false; // turn off autosizing
37874 config.grid.autoHeight = false;
37875 config.grid.autoWidth = false;
37877 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37879 if (config.background) {
37880 // render grid on panel activation (if panel background)
37881 this.on('activate', function(gp) {
37882 if (!gp.grid.rendered) {
37883 gp.grid.render(this.wrapper);
37884 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37889 this.grid.render(this.wrapper);
37890 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37893 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37894 // ??? needed ??? config.el = this.wrapper;
37899 // xtype created footer. - not sure if will work as we normally have to render first..
37900 if (this.footer && !this.footer.el && this.footer.xtype) {
37902 var ctr = this.grid.getView().getFooterPanel(true);
37903 this.footer.dataSource = this.grid.dataSource;
37904 this.footer = Roo.factory(this.footer, Roo);
37905 this.footer.render(ctr);
37915 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37916 getId : function(){
37917 return this.grid.id;
37921 * Returns the grid for this panel
37922 * @return {Roo.bootstrap.Table}
37924 getGrid : function(){
37928 setSize : function(width, height){
37929 if(!this.ignoreResize(width, height)){
37930 var grid = this.grid;
37931 var size = this.adjustForComponents(width, height);
37932 var gridel = grid.getGridEl();
37933 gridel.setSize(size.width, size.height);
37935 var thd = grid.getGridEl().select('thead',true).first();
37936 var tbd = grid.getGridEl().select('tbody', true).first();
37938 tbd.setSize(width, height - thd.getHeight());
37947 beforeSlide : function(){
37948 this.grid.getView().scroller.clip();
37951 afterSlide : function(){
37952 this.grid.getView().scroller.unclip();
37955 destroy : function(){
37956 this.grid.destroy();
37958 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37963 * @class Roo.bootstrap.panel.Nest
37964 * @extends Roo.bootstrap.panel.Content
37966 * Create a new Panel, that can contain a layout.Border.
37969 * @param {Roo.BorderLayout} layout The layout for this panel
37970 * @param {String/Object} config A string to set only the title or a config object
37972 Roo.bootstrap.panel.Nest = function(config)
37974 // construct with only one argument..
37975 /* FIXME - implement nicer consturctors
37976 if (layout.layout) {
37978 layout = config.layout;
37979 delete config.layout;
37981 if (layout.xtype && !layout.getEl) {
37982 // then layout needs constructing..
37983 layout = Roo.factory(layout, Roo);
37987 config.el = config.layout.getEl();
37989 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37991 config.layout.monitorWindowResize = false; // turn off autosizing
37992 this.layout = config.layout;
37993 this.layout.getEl().addClass("roo-layout-nested-layout");
37994 this.layout.parent = this;
38001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38003 setSize : function(width, height){
38004 if(!this.ignoreResize(width, height)){
38005 var size = this.adjustForComponents(width, height);
38006 var el = this.layout.getEl();
38007 if (size.height < 1) {
38008 el.setWidth(size.width);
38010 el.setSize(size.width, size.height);
38012 var touch = el.dom.offsetWidth;
38013 this.layout.layout();
38014 // ie requires a double layout on the first pass
38015 if(Roo.isIE && !this.initialized){
38016 this.initialized = true;
38017 this.layout.layout();
38022 // activate all subpanels if not currently active..
38024 setActiveState : function(active){
38025 this.active = active;
38026 this.setActiveClass(active);
38029 this.fireEvent("deactivate", this);
38033 this.fireEvent("activate", this);
38034 // not sure if this should happen before or after..
38035 if (!this.layout) {
38036 return; // should not happen..
38039 for (var r in this.layout.regions) {
38040 reg = this.layout.getRegion(r);
38041 if (reg.getActivePanel()) {
38042 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38043 reg.setActivePanel(reg.getActivePanel());
38046 if (!reg.panels.length) {
38049 reg.showPanel(reg.getPanel(0));
38058 * Returns the nested BorderLayout for this panel
38059 * @return {Roo.BorderLayout}
38061 getLayout : function(){
38062 return this.layout;
38066 * Adds a xtype elements to the layout of the nested panel
38070 xtype : 'ContentPanel',
38077 xtype : 'NestedLayoutPanel',
38083 items : [ ... list of content panels or nested layout panels.. ]
38087 * @param {Object} cfg Xtype definition of item to add.
38089 addxtype : function(cfg) {
38090 return this.layout.addxtype(cfg);
38095 * Ext JS Library 1.1.1
38096 * Copyright(c) 2006-2007, Ext JS, LLC.
38098 * Originally Released Under LGPL - original licence link has changed is not relivant.
38101 * <script type="text/javascript">
38104 * @class Roo.TabPanel
38105 * @extends Roo.util.Observable
38106 * A lightweight tab container.
38110 // basic tabs 1, built from existing content
38111 var tabs = new Roo.TabPanel("tabs1");
38112 tabs.addTab("script", "View Script");
38113 tabs.addTab("markup", "View Markup");
38114 tabs.activate("script");
38116 // more advanced tabs, built from javascript
38117 var jtabs = new Roo.TabPanel("jtabs");
38118 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38120 // set up the UpdateManager
38121 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38122 var updater = tab2.getUpdateManager();
38123 updater.setDefaultUrl("ajax1.htm");
38124 tab2.on('activate', updater.refresh, updater, true);
38126 // Use setUrl for Ajax loading
38127 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38128 tab3.setUrl("ajax2.htm", null, true);
38131 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38134 jtabs.activate("jtabs-1");
38137 * Create a new TabPanel.
38138 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38139 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38141 Roo.bootstrap.panel.Tabs = function(config){
38143 * The container element for this TabPanel.
38144 * @type Roo.Element
38146 this.el = Roo.get(config.el);
38149 if(typeof config == "boolean"){
38150 this.tabPosition = config ? "bottom" : "top";
38152 Roo.apply(this, config);
38156 if(this.tabPosition == "bottom"){
38157 // if tabs are at the bottom = create the body first.
38158 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38159 this.el.addClass("roo-tabs-bottom");
38161 // next create the tabs holders
38163 if (this.tabPosition == "west"){
38165 var reg = this.region; // fake it..
38167 if (!reg.mgr.parent) {
38170 reg = reg.mgr.parent.region;
38172 Roo.log("got nest?");
38174 if (reg.mgr.getRegion('west')) {
38175 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38176 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38177 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38178 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38179 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38187 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38188 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38189 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38190 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38195 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38198 // finally - if tabs are at the top, then create the body last..
38199 if(this.tabPosition != "bottom"){
38200 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38201 * @type Roo.Element
38203 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38204 this.el.addClass("roo-tabs-top");
38208 this.bodyEl.setStyle("position", "relative");
38210 this.active = null;
38211 this.activateDelegate = this.activate.createDelegate(this);
38216 * Fires when the active tab changes
38217 * @param {Roo.TabPanel} this
38218 * @param {Roo.TabPanelItem} activePanel The new active tab
38222 * @event beforetabchange
38223 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38224 * @param {Roo.TabPanel} this
38225 * @param {Object} e Set cancel to true on this object to cancel the tab change
38226 * @param {Roo.TabPanelItem} tab The tab being changed to
38228 "beforetabchange" : true
38231 Roo.EventManager.onWindowResize(this.onResize, this);
38232 this.cpad = this.el.getPadding("lr");
38233 this.hiddenCount = 0;
38236 // toolbar on the tabbar support...
38237 if (this.toolbar) {
38238 alert("no toolbar support yet");
38239 this.toolbar = false;
38241 var tcfg = this.toolbar;
38242 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38243 this.toolbar = new Roo.Toolbar(tcfg);
38244 if (Roo.isSafari) {
38245 var tbl = tcfg.container.child('table', true);
38246 tbl.setAttribute('width', '100%');
38254 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38257 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38259 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38261 tabPosition : "top",
38263 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38265 currentTabWidth : 0,
38267 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38271 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38275 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38277 preferredTabWidth : 175,
38279 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38281 resizeTabs : false,
38283 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38285 monitorResize : true,
38287 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38289 toolbar : false, // set by caller..
38291 region : false, /// set by caller
38293 disableTooltips : true, // not used yet...
38296 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38297 * @param {String} id The id of the div to use <b>or create</b>
38298 * @param {String} text The text for the tab
38299 * @param {String} content (optional) Content to put in the TabPanelItem body
38300 * @param {Boolean} closable (optional) True to create a close icon on the tab
38301 * @return {Roo.TabPanelItem} The created TabPanelItem
38303 addTab : function(id, text, content, closable, tpl)
38305 var item = new Roo.bootstrap.panel.TabItem({
38309 closable : closable,
38312 this.addTabItem(item);
38314 item.setContent(content);
38320 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38321 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38322 * @return {Roo.TabPanelItem}
38324 getTab : function(id){
38325 return this.items[id];
38329 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38330 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38332 hideTab : function(id){
38333 var t = this.items[id];
38336 this.hiddenCount++;
38337 this.autoSizeTabs();
38342 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38343 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38345 unhideTab : function(id){
38346 var t = this.items[id];
38348 t.setHidden(false);
38349 this.hiddenCount--;
38350 this.autoSizeTabs();
38355 * Adds an existing {@link Roo.TabPanelItem}.
38356 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38358 addTabItem : function(item)
38360 this.items[item.id] = item;
38361 this.items.push(item);
38362 this.autoSizeTabs();
38363 // if(this.resizeTabs){
38364 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38365 // this.autoSizeTabs();
38367 // item.autoSize();
38372 * Removes a {@link Roo.TabPanelItem}.
38373 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38375 removeTab : function(id){
38376 var items = this.items;
38377 var tab = items[id];
38378 if(!tab) { return; }
38379 var index = items.indexOf(tab);
38380 if(this.active == tab && items.length > 1){
38381 var newTab = this.getNextAvailable(index);
38386 this.stripEl.dom.removeChild(tab.pnode.dom);
38387 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38388 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38390 items.splice(index, 1);
38391 delete this.items[tab.id];
38392 tab.fireEvent("close", tab);
38393 tab.purgeListeners();
38394 this.autoSizeTabs();
38397 getNextAvailable : function(start){
38398 var items = this.items;
38400 // look for a next tab that will slide over to
38401 // replace the one being removed
38402 while(index < items.length){
38403 var item = items[++index];
38404 if(item && !item.isHidden()){
38408 // if one isn't found select the previous tab (on the left)
38411 var item = items[--index];
38412 if(item && !item.isHidden()){
38420 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38421 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38423 disableTab : function(id){
38424 var tab = this.items[id];
38425 if(tab && this.active != tab){
38431 * Enables a {@link Roo.TabPanelItem} that is disabled.
38432 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38434 enableTab : function(id){
38435 var tab = this.items[id];
38440 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38441 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38442 * @return {Roo.TabPanelItem} The TabPanelItem.
38444 activate : function(id)
38446 //Roo.log('activite:' + id);
38448 var tab = this.items[id];
38452 if(tab == this.active || tab.disabled){
38456 this.fireEvent("beforetabchange", this, e, tab);
38457 if(e.cancel !== true && !tab.disabled){
38459 this.active.hide();
38461 this.active = this.items[id];
38462 this.active.show();
38463 this.fireEvent("tabchange", this, this.active);
38469 * Gets the active {@link Roo.TabPanelItem}.
38470 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38472 getActiveTab : function(){
38473 return this.active;
38477 * Updates the tab body element to fit the height of the container element
38478 * for overflow scrolling
38479 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38481 syncHeight : function(targetHeight){
38482 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38483 var bm = this.bodyEl.getMargins();
38484 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38485 this.bodyEl.setHeight(newHeight);
38489 onResize : function(){
38490 if(this.monitorResize){
38491 this.autoSizeTabs();
38496 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38498 beginUpdate : function(){
38499 this.updating = true;
38503 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38505 endUpdate : function(){
38506 this.updating = false;
38507 this.autoSizeTabs();
38511 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38513 autoSizeTabs : function()
38515 var count = this.items.length;
38516 var vcount = count - this.hiddenCount;
38519 this.stripEl.hide();
38521 this.stripEl.show();
38524 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38529 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38530 var availWidth = Math.floor(w / vcount);
38531 var b = this.stripBody;
38532 if(b.getWidth() > w){
38533 var tabs = this.items;
38534 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38535 if(availWidth < this.minTabWidth){
38536 /*if(!this.sleft){ // incomplete scrolling code
38537 this.createScrollButtons();
38540 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38543 if(this.currentTabWidth < this.preferredTabWidth){
38544 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38550 * Returns the number of tabs in this TabPanel.
38553 getCount : function(){
38554 return this.items.length;
38558 * Resizes all the tabs to the passed width
38559 * @param {Number} The new width
38561 setTabWidth : function(width){
38562 this.currentTabWidth = width;
38563 for(var i = 0, len = this.items.length; i < len; i++) {
38564 if(!this.items[i].isHidden()) {
38565 this.items[i].setWidth(width);
38571 * Destroys this TabPanel
38572 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38574 destroy : function(removeEl){
38575 Roo.EventManager.removeResizeListener(this.onResize, this);
38576 for(var i = 0, len = this.items.length; i < len; i++){
38577 this.items[i].purgeListeners();
38579 if(removeEl === true){
38580 this.el.update("");
38585 createStrip : function(container)
38587 var strip = document.createElement("nav");
38588 strip.className = Roo.bootstrap.version == 4 ?
38589 "navbar-light bg-light" :
38590 "navbar navbar-default"; //"x-tabs-wrap";
38591 container.appendChild(strip);
38595 createStripList : function(strip)
38597 // div wrapper for retard IE
38598 // returns the "tr" element.
38599 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38600 //'<div class="x-tabs-strip-wrap">'+
38601 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38602 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38603 return strip.firstChild; //.firstChild.firstChild.firstChild;
38605 createBody : function(container)
38607 var body = document.createElement("div");
38608 Roo.id(body, "tab-body");
38609 //Roo.fly(body).addClass("x-tabs-body");
38610 Roo.fly(body).addClass("tab-content");
38611 container.appendChild(body);
38614 createItemBody :function(bodyEl, id){
38615 var body = Roo.getDom(id);
38617 body = document.createElement("div");
38620 //Roo.fly(body).addClass("x-tabs-item-body");
38621 Roo.fly(body).addClass("tab-pane");
38622 bodyEl.insertBefore(body, bodyEl.firstChild);
38626 createStripElements : function(stripEl, text, closable, tpl)
38628 var td = document.createElement("li"); // was td..
38629 td.className = 'nav-item';
38631 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38634 stripEl.appendChild(td);
38636 td.className = "x-tabs-closable";
38637 if(!this.closeTpl){
38638 this.closeTpl = new Roo.Template(
38639 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38640 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38641 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38644 var el = this.closeTpl.overwrite(td, {"text": text});
38645 var close = el.getElementsByTagName("div")[0];
38646 var inner = el.getElementsByTagName("em")[0];
38647 return {"el": el, "close": close, "inner": inner};
38650 // not sure what this is..
38651 // if(!this.tabTpl){
38652 //this.tabTpl = new Roo.Template(
38653 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38654 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38656 // this.tabTpl = new Roo.Template(
38657 // '<a href="#">' +
38658 // '<span unselectable="on"' +
38659 // (this.disableTooltips ? '' : ' title="{text}"') +
38660 // ' >{text}</span></a>'
38666 var template = tpl || this.tabTpl || false;
38669 template = new Roo.Template(
38670 Roo.bootstrap.version == 4 ?
38672 '<a class="nav-link" href="#" unselectable="on"' +
38673 (this.disableTooltips ? '' : ' title="{text}"') +
38676 '<a class="nav-link" href="#">' +
38677 '<span unselectable="on"' +
38678 (this.disableTooltips ? '' : ' title="{text}"') +
38679 ' >{text}</span></a>'
38684 switch (typeof(template)) {
38688 template = new Roo.Template(template);
38694 var el = template.overwrite(td, {"text": text});
38696 var inner = el.getElementsByTagName("span")[0];
38698 return {"el": el, "inner": inner};
38706 * @class Roo.TabPanelItem
38707 * @extends Roo.util.Observable
38708 * Represents an individual item (tab plus body) in a TabPanel.
38709 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38710 * @param {String} id The id of this TabPanelItem
38711 * @param {String} text The text for the tab of this TabPanelItem
38712 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38714 Roo.bootstrap.panel.TabItem = function(config){
38716 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38717 * @type Roo.TabPanel
38719 this.tabPanel = config.panel;
38721 * The id for this TabPanelItem
38724 this.id = config.id;
38726 this.disabled = false;
38728 this.text = config.text;
38730 this.loaded = false;
38731 this.closable = config.closable;
38734 * The body element for this TabPanelItem.
38735 * @type Roo.Element
38737 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38738 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38739 this.bodyEl.setStyle("display", "block");
38740 this.bodyEl.setStyle("zoom", "1");
38741 //this.hideAction();
38743 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38745 this.el = Roo.get(els.el);
38746 this.inner = Roo.get(els.inner, true);
38747 this.textEl = Roo.bootstrap.version == 4 ?
38748 this.el : Roo.get(this.el.dom.firstChild, true);
38750 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38751 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38754 // this.el.on("mousedown", this.onTabMouseDown, this);
38755 this.el.on("click", this.onTabClick, this);
38757 if(config.closable){
38758 var c = Roo.get(els.close, true);
38759 c.dom.title = this.closeText;
38760 c.addClassOnOver("close-over");
38761 c.on("click", this.closeClick, this);
38767 * Fires when this tab becomes the active tab.
38768 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38769 * @param {Roo.TabPanelItem} this
38773 * @event beforeclose
38774 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38775 * @param {Roo.TabPanelItem} this
38776 * @param {Object} e Set cancel to true on this object to cancel the close.
38778 "beforeclose": true,
38781 * Fires when this tab is closed.
38782 * @param {Roo.TabPanelItem} this
38786 * @event deactivate
38787 * Fires when this tab is no longer the active tab.
38788 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38789 * @param {Roo.TabPanelItem} this
38791 "deactivate" : true
38793 this.hidden = false;
38795 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38798 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38800 purgeListeners : function(){
38801 Roo.util.Observable.prototype.purgeListeners.call(this);
38802 this.el.removeAllListeners();
38805 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38808 this.status_node.addClass("active");
38811 this.tabPanel.stripWrap.repaint();
38813 this.fireEvent("activate", this.tabPanel, this);
38817 * Returns true if this tab is the active tab.
38818 * @return {Boolean}
38820 isActive : function(){
38821 return this.tabPanel.getActiveTab() == this;
38825 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38828 this.status_node.removeClass("active");
38830 this.fireEvent("deactivate", this.tabPanel, this);
38833 hideAction : function(){
38834 this.bodyEl.hide();
38835 this.bodyEl.setStyle("position", "absolute");
38836 this.bodyEl.setLeft("-20000px");
38837 this.bodyEl.setTop("-20000px");
38840 showAction : function(){
38841 this.bodyEl.setStyle("position", "relative");
38842 this.bodyEl.setTop("");
38843 this.bodyEl.setLeft("");
38844 this.bodyEl.show();
38848 * Set the tooltip for the tab.
38849 * @param {String} tooltip The tab's tooltip
38851 setTooltip : function(text){
38852 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38853 this.textEl.dom.qtip = text;
38854 this.textEl.dom.removeAttribute('title');
38856 this.textEl.dom.title = text;
38860 onTabClick : function(e){
38861 e.preventDefault();
38862 this.tabPanel.activate(this.id);
38865 onTabMouseDown : function(e){
38866 e.preventDefault();
38867 this.tabPanel.activate(this.id);
38870 getWidth : function(){
38871 return this.inner.getWidth();
38874 setWidth : function(width){
38875 var iwidth = width - this.linode.getPadding("lr");
38876 this.inner.setWidth(iwidth);
38877 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38878 this.linode.setWidth(width);
38882 * Show or hide the tab
38883 * @param {Boolean} hidden True to hide or false to show.
38885 setHidden : function(hidden){
38886 this.hidden = hidden;
38887 this.linode.setStyle("display", hidden ? "none" : "");
38891 * Returns true if this tab is "hidden"
38892 * @return {Boolean}
38894 isHidden : function(){
38895 return this.hidden;
38899 * Returns the text for this tab
38902 getText : function(){
38906 autoSize : function(){
38907 //this.el.beginMeasure();
38908 this.textEl.setWidth(1);
38910 * #2804 [new] Tabs in Roojs
38911 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38913 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38914 //this.el.endMeasure();
38918 * Sets the text for the tab (Note: this also sets the tooltip text)
38919 * @param {String} text The tab's text and tooltip
38921 setText : function(text){
38923 this.textEl.update(text);
38924 this.setTooltip(text);
38925 //if(!this.tabPanel.resizeTabs){
38926 // this.autoSize();
38930 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38932 activate : function(){
38933 this.tabPanel.activate(this.id);
38937 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38939 disable : function(){
38940 if(this.tabPanel.active != this){
38941 this.disabled = true;
38942 this.status_node.addClass("disabled");
38947 * Enables this TabPanelItem if it was previously disabled.
38949 enable : function(){
38950 this.disabled = false;
38951 this.status_node.removeClass("disabled");
38955 * Sets the content for this TabPanelItem.
38956 * @param {String} content The content
38957 * @param {Boolean} loadScripts true to look for and load scripts
38959 setContent : function(content, loadScripts){
38960 this.bodyEl.update(content, loadScripts);
38964 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38965 * @return {Roo.UpdateManager} The UpdateManager
38967 getUpdateManager : function(){
38968 return this.bodyEl.getUpdateManager();
38972 * Set a URL to be used to load the content for this TabPanelItem.
38973 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38974 * @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)
38975 * @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)
38976 * @return {Roo.UpdateManager} The UpdateManager
38978 setUrl : function(url, params, loadOnce){
38979 if(this.refreshDelegate){
38980 this.un('activate', this.refreshDelegate);
38982 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38983 this.on("activate", this.refreshDelegate);
38984 return this.bodyEl.getUpdateManager();
38988 _handleRefresh : function(url, params, loadOnce){
38989 if(!loadOnce || !this.loaded){
38990 var updater = this.bodyEl.getUpdateManager();
38991 updater.update(url, params, this._setLoaded.createDelegate(this));
38996 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38997 * Will fail silently if the setUrl method has not been called.
38998 * This does not activate the panel, just updates its content.
39000 refresh : function(){
39001 if(this.refreshDelegate){
39002 this.loaded = false;
39003 this.refreshDelegate();
39008 _setLoaded : function(){
39009 this.loaded = true;
39013 closeClick : function(e){
39016 this.fireEvent("beforeclose", this, o);
39017 if(o.cancel !== true){
39018 this.tabPanel.removeTab(this.id);
39022 * The text displayed in the tooltip for the close icon.
39025 closeText : "Close this tab"
39028 * This script refer to:
39029 * Title: International Telephone Input
39030 * Author: Jack O'Connor
39031 * Code version: v12.1.12
39032 * Availability: https://github.com/jackocnr/intl-tel-input.git
39035 Roo.bootstrap.PhoneInputData = function() {
39038 "Afghanistan (افغانستان)",
39043 "Albania (Shqipëri)",
39048 "Algeria (الجزائر)",
39073 "Antigua and Barbuda",
39083 "Armenia (Հայաստան)",
39099 "Austria (Österreich)",
39104 "Azerbaijan (Azərbaycan)",
39114 "Bahrain (البحرين)",
39119 "Bangladesh (বাংলাদেশ)",
39129 "Belarus (Беларусь)",
39134 "Belgium (België)",
39164 "Bosnia and Herzegovina (Босна и Херцеговина)",
39179 "British Indian Ocean Territory",
39184 "British Virgin Islands",
39194 "Bulgaria (България)",
39204 "Burundi (Uburundi)",
39209 "Cambodia (កម្ពុជា)",
39214 "Cameroon (Cameroun)",
39223 ["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"]
39226 "Cape Verde (Kabu Verdi)",
39231 "Caribbean Netherlands",
39242 "Central African Republic (République centrafricaine)",
39262 "Christmas Island",
39268 "Cocos (Keeling) Islands",
39279 "Comoros (جزر القمر)",
39284 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39289 "Congo (Republic) (Congo-Brazzaville)",
39309 "Croatia (Hrvatska)",
39330 "Czech Republic (Česká republika)",
39335 "Denmark (Danmark)",
39350 "Dominican Republic (República Dominicana)",
39354 ["809", "829", "849"]
39372 "Equatorial Guinea (Guinea Ecuatorial)",
39392 "Falkland Islands (Islas Malvinas)",
39397 "Faroe Islands (Føroyar)",
39418 "French Guiana (Guyane française)",
39423 "French Polynesia (Polynésie française)",
39438 "Georgia (საქართველო)",
39443 "Germany (Deutschland)",
39463 "Greenland (Kalaallit Nunaat)",
39500 "Guinea-Bissau (Guiné Bissau)",
39525 "Hungary (Magyarország)",
39530 "Iceland (Ísland)",
39550 "Iraq (العراق)",
39566 "Israel (ישראל)",
39593 "Jordan (الأردن)",
39598 "Kazakhstan (Казахстан)",
39619 "Kuwait (الكويت)",
39624 "Kyrgyzstan (Кыргызстан)",
39634 "Latvia (Latvija)",
39639 "Lebanon (لبنان)",
39654 "Libya (ليبيا)",
39664 "Lithuania (Lietuva)",
39679 "Macedonia (FYROM) (Македонија)",
39684 "Madagascar (Madagasikara)",
39714 "Marshall Islands",
39724 "Mauritania (موريتانيا)",
39729 "Mauritius (Moris)",
39750 "Moldova (Republica Moldova)",
39760 "Mongolia (Монгол)",
39765 "Montenegro (Crna Gora)",
39775 "Morocco (المغرب)",
39781 "Mozambique (Moçambique)",
39786 "Myanmar (Burma) (မြန်မာ)",
39791 "Namibia (Namibië)",
39806 "Netherlands (Nederland)",
39811 "New Caledonia (Nouvelle-Calédonie)",
39846 "North Korea (조선 민주주의 인민 공화국)",
39851 "Northern Mariana Islands",
39867 "Pakistan (پاکستان)",
39877 "Palestine (فلسطين)",
39887 "Papua New Guinea",
39929 "Réunion (La Réunion)",
39935 "Romania (România)",
39951 "Saint Barthélemy",
39962 "Saint Kitts and Nevis",
39972 "Saint Martin (Saint-Martin (partie française))",
39978 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39983 "Saint Vincent and the Grenadines",
39998 "São Tomé and Príncipe (São Tomé e Príncipe)",
40003 "Saudi Arabia (المملكة العربية السعودية)",
40008 "Senegal (Sénégal)",
40038 "Slovakia (Slovensko)",
40043 "Slovenia (Slovenija)",
40053 "Somalia (Soomaaliya)",
40063 "South Korea (대한민국)",
40068 "South Sudan (جنوب السودان)",
40078 "Sri Lanka (ශ්රී ලංකාව)",
40083 "Sudan (السودان)",
40093 "Svalbard and Jan Mayen",
40104 "Sweden (Sverige)",
40109 "Switzerland (Schweiz)",
40114 "Syria (سوريا)",
40159 "Trinidad and Tobago",
40164 "Tunisia (تونس)",
40169 "Turkey (Türkiye)",
40179 "Turks and Caicos Islands",
40189 "U.S. Virgin Islands",
40199 "Ukraine (Україна)",
40204 "United Arab Emirates (الإمارات العربية المتحدة)",
40226 "Uzbekistan (Oʻzbekiston)",
40236 "Vatican City (Città del Vaticano)",
40247 "Vietnam (Việt Nam)",
40252 "Wallis and Futuna (Wallis-et-Futuna)",
40257 "Western Sahara (الصحراء الغربية)",
40263 "Yemen (اليمن)",
40287 * This script refer to:
40288 * Title: International Telephone Input
40289 * Author: Jack O'Connor
40290 * Code version: v12.1.12
40291 * Availability: https://github.com/jackocnr/intl-tel-input.git
40295 * @class Roo.bootstrap.PhoneInput
40296 * @extends Roo.bootstrap.TriggerField
40297 * An input with International dial-code selection
40299 * @cfg {String} defaultDialCode default '+852'
40300 * @cfg {Array} preferedCountries default []
40303 * Create a new PhoneInput.
40304 * @param {Object} config Configuration options
40307 Roo.bootstrap.PhoneInput = function(config) {
40308 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40311 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40313 listWidth: undefined,
40315 selectedClass: 'active',
40317 invalidClass : "has-warning",
40319 validClass: 'has-success',
40321 allowed: '0123456789',
40326 * @cfg {String} defaultDialCode The default dial code when initializing the input
40328 defaultDialCode: '+852',
40331 * @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
40333 preferedCountries: false,
40335 getAutoCreate : function()
40337 var data = Roo.bootstrap.PhoneInputData();
40338 var align = this.labelAlign || this.parentLabelAlign();
40341 this.allCountries = [];
40342 this.dialCodeMapping = [];
40344 for (var i = 0; i < data.length; i++) {
40346 this.allCountries[i] = {
40350 priority: c[3] || 0,
40351 areaCodes: c[4] || null
40353 this.dialCodeMapping[c[2]] = {
40356 priority: c[3] || 0,
40357 areaCodes: c[4] || null
40369 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40370 maxlength: this.max_length,
40371 cls : 'form-control tel-input',
40372 autocomplete: 'new-password'
40375 var hiddenInput = {
40378 cls: 'hidden-tel-input'
40382 hiddenInput.name = this.name;
40385 if (this.disabled) {
40386 input.disabled = true;
40389 var flag_container = {
40406 cls: this.hasFeedback ? 'has-feedback' : '',
40412 cls: 'dial-code-holder',
40419 cls: 'roo-select2-container input-group',
40426 if (this.fieldLabel.length) {
40429 tooltip: 'This field is required'
40435 cls: 'control-label',
40441 html: this.fieldLabel
40444 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40450 if(this.indicatorpos == 'right') {
40451 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40458 if(align == 'left') {
40466 if(this.labelWidth > 12){
40467 label.style = "width: " + this.labelWidth + 'px';
40469 if(this.labelWidth < 13 && this.labelmd == 0){
40470 this.labelmd = this.labelWidth;
40472 if(this.labellg > 0){
40473 label.cls += ' col-lg-' + this.labellg;
40474 input.cls += ' col-lg-' + (12 - this.labellg);
40476 if(this.labelmd > 0){
40477 label.cls += ' col-md-' + this.labelmd;
40478 container.cls += ' col-md-' + (12 - this.labelmd);
40480 if(this.labelsm > 0){
40481 label.cls += ' col-sm-' + this.labelsm;
40482 container.cls += ' col-sm-' + (12 - this.labelsm);
40484 if(this.labelxs > 0){
40485 label.cls += ' col-xs-' + this.labelxs;
40486 container.cls += ' col-xs-' + (12 - this.labelxs);
40496 var settings = this;
40498 ['xs','sm','md','lg'].map(function(size){
40499 if (settings[size]) {
40500 cfg.cls += ' col-' + size + '-' + settings[size];
40504 this.store = new Roo.data.Store({
40505 proxy : new Roo.data.MemoryProxy({}),
40506 reader : new Roo.data.JsonReader({
40517 'name' : 'dialCode',
40521 'name' : 'priority',
40525 'name' : 'areaCodes',
40532 if(!this.preferedCountries) {
40533 this.preferedCountries = [
40540 var p = this.preferedCountries.reverse();
40543 for (var i = 0; i < p.length; i++) {
40544 for (var j = 0; j < this.allCountries.length; j++) {
40545 if(this.allCountries[j].iso2 == p[i]) {
40546 var t = this.allCountries[j];
40547 this.allCountries.splice(j,1);
40548 this.allCountries.unshift(t);
40554 this.store.proxy.data = {
40556 data: this.allCountries
40562 initEvents : function()
40565 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40567 this.indicator = this.indicatorEl();
40568 this.flag = this.flagEl();
40569 this.dialCodeHolder = this.dialCodeHolderEl();
40571 this.trigger = this.el.select('div.flag-box',true).first();
40572 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40577 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40578 _this.list.setWidth(lw);
40581 this.list.on('mouseover', this.onViewOver, this);
40582 this.list.on('mousemove', this.onViewMove, this);
40583 this.inputEl().on("keyup", this.onKeyUp, this);
40584 this.inputEl().on("keypress", this.onKeyPress, this);
40586 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40588 this.view = new Roo.View(this.list, this.tpl, {
40589 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40592 this.view.on('click', this.onViewClick, this);
40593 this.setValue(this.defaultDialCode);
40596 onTriggerClick : function(e)
40598 Roo.log('trigger click');
40603 if(this.isExpanded()){
40605 this.hasFocus = false;
40607 this.store.load({});
40608 this.hasFocus = true;
40613 isExpanded : function()
40615 return this.list.isVisible();
40618 collapse : function()
40620 if(!this.isExpanded()){
40624 Roo.get(document).un('mousedown', this.collapseIf, this);
40625 Roo.get(document).un('mousewheel', this.collapseIf, this);
40626 this.fireEvent('collapse', this);
40630 expand : function()
40634 if(this.isExpanded() || !this.hasFocus){
40638 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40639 this.list.setWidth(lw);
40642 this.restrictHeight();
40644 Roo.get(document).on('mousedown', this.collapseIf, this);
40645 Roo.get(document).on('mousewheel', this.collapseIf, this);
40647 this.fireEvent('expand', this);
40650 restrictHeight : function()
40652 this.list.alignTo(this.inputEl(), this.listAlign);
40653 this.list.alignTo(this.inputEl(), this.listAlign);
40656 onViewOver : function(e, t)
40658 if(this.inKeyMode){
40661 var item = this.view.findItemFromChild(t);
40664 var index = this.view.indexOf(item);
40665 this.select(index, false);
40670 onViewClick : function(view, doFocus, el, e)
40672 var index = this.view.getSelectedIndexes()[0];
40674 var r = this.store.getAt(index);
40677 this.onSelect(r, index);
40679 if(doFocus !== false && !this.blockFocus){
40680 this.inputEl().focus();
40684 onViewMove : function(e, t)
40686 this.inKeyMode = false;
40689 select : function(index, scrollIntoView)
40691 this.selectedIndex = index;
40692 this.view.select(index);
40693 if(scrollIntoView !== false){
40694 var el = this.view.getNode(index);
40696 this.list.scrollChildIntoView(el, false);
40701 createList : function()
40703 this.list = Roo.get(document.body).createChild({
40705 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40706 style: 'display:none'
40709 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40712 collapseIf : function(e)
40714 var in_combo = e.within(this.el);
40715 var in_list = e.within(this.list);
40716 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40718 if (in_combo || in_list || is_list) {
40724 onSelect : function(record, index)
40726 if(this.fireEvent('beforeselect', this, record, index) !== false){
40728 this.setFlagClass(record.data.iso2);
40729 this.setDialCode(record.data.dialCode);
40730 this.hasFocus = false;
40732 this.fireEvent('select', this, record, index);
40736 flagEl : function()
40738 var flag = this.el.select('div.flag',true).first();
40745 dialCodeHolderEl : function()
40747 var d = this.el.select('input.dial-code-holder',true).first();
40754 setDialCode : function(v)
40756 this.dialCodeHolder.dom.value = '+'+v;
40759 setFlagClass : function(n)
40761 this.flag.dom.className = 'flag '+n;
40764 getValue : function()
40766 var v = this.inputEl().getValue();
40767 if(this.dialCodeHolder) {
40768 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40773 setValue : function(v)
40775 var d = this.getDialCode(v);
40777 //invalid dial code
40778 if(v.length == 0 || !d || d.length == 0) {
40780 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40781 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40787 this.setFlagClass(this.dialCodeMapping[d].iso2);
40788 this.setDialCode(d);
40789 this.inputEl().dom.value = v.replace('+'+d,'');
40790 this.hiddenEl().dom.value = this.getValue();
40795 getDialCode : function(v)
40799 if (v.length == 0) {
40800 return this.dialCodeHolder.dom.value;
40804 if (v.charAt(0) != "+") {
40807 var numericChars = "";
40808 for (var i = 1; i < v.length; i++) {
40809 var c = v.charAt(i);
40812 if (this.dialCodeMapping[numericChars]) {
40813 dialCode = v.substr(1, i);
40815 if (numericChars.length == 4) {
40825 this.setValue(this.defaultDialCode);
40829 hiddenEl : function()
40831 return this.el.select('input.hidden-tel-input',true).first();
40834 // after setting val
40835 onKeyUp : function(e){
40836 this.setValue(this.getValue());
40839 onKeyPress : function(e){
40840 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40847 * @class Roo.bootstrap.MoneyField
40848 * @extends Roo.bootstrap.ComboBox
40849 * Bootstrap MoneyField class
40852 * Create a new MoneyField.
40853 * @param {Object} config Configuration options
40856 Roo.bootstrap.MoneyField = function(config) {
40858 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40862 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40865 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40867 allowDecimals : true,
40869 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40871 decimalSeparator : ".",
40873 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40875 decimalPrecision : 0,
40877 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40879 allowNegative : true,
40881 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40885 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40887 minValue : Number.NEGATIVE_INFINITY,
40889 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40891 maxValue : Number.MAX_VALUE,
40893 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40895 minText : "The minimum value for this field is {0}",
40897 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40899 maxText : "The maximum value for this field is {0}",
40901 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40902 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40904 nanText : "{0} is not a valid number",
40906 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40910 * @cfg {String} defaults currency of the MoneyField
40911 * value should be in lkey
40913 defaultCurrency : false,
40915 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40917 thousandsDelimiter : false,
40919 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40930 getAutoCreate : function()
40932 var align = this.labelAlign || this.parentLabelAlign();
40944 cls : 'form-control roo-money-amount-input',
40945 autocomplete: 'new-password'
40948 var hiddenInput = {
40952 cls: 'hidden-number-input'
40955 if(this.max_length) {
40956 input.maxlength = this.max_length;
40960 hiddenInput.name = this.name;
40963 if (this.disabled) {
40964 input.disabled = true;
40967 var clg = 12 - this.inputlg;
40968 var cmd = 12 - this.inputmd;
40969 var csm = 12 - this.inputsm;
40970 var cxs = 12 - this.inputxs;
40974 cls : 'row roo-money-field',
40978 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40982 cls: 'roo-select2-container input-group',
40986 cls : 'form-control roo-money-currency-input',
40987 autocomplete: 'new-password',
40989 name : this.currencyName
40993 cls : 'input-group-addon',
41007 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41011 cls: this.hasFeedback ? 'has-feedback' : '',
41022 if (this.fieldLabel.length) {
41025 tooltip: 'This field is required'
41031 cls: 'control-label',
41037 html: this.fieldLabel
41040 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41046 if(this.indicatorpos == 'right') {
41047 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41054 if(align == 'left') {
41062 if(this.labelWidth > 12){
41063 label.style = "width: " + this.labelWidth + 'px';
41065 if(this.labelWidth < 13 && this.labelmd == 0){
41066 this.labelmd = this.labelWidth;
41068 if(this.labellg > 0){
41069 label.cls += ' col-lg-' + this.labellg;
41070 input.cls += ' col-lg-' + (12 - this.labellg);
41072 if(this.labelmd > 0){
41073 label.cls += ' col-md-' + this.labelmd;
41074 container.cls += ' col-md-' + (12 - this.labelmd);
41076 if(this.labelsm > 0){
41077 label.cls += ' col-sm-' + this.labelsm;
41078 container.cls += ' col-sm-' + (12 - this.labelsm);
41080 if(this.labelxs > 0){
41081 label.cls += ' col-xs-' + this.labelxs;
41082 container.cls += ' col-xs-' + (12 - this.labelxs);
41093 var settings = this;
41095 ['xs','sm','md','lg'].map(function(size){
41096 if (settings[size]) {
41097 cfg.cls += ' col-' + size + '-' + settings[size];
41104 initEvents : function()
41106 this.indicator = this.indicatorEl();
41108 this.initCurrencyEvent();
41110 this.initNumberEvent();
41113 initCurrencyEvent : function()
41116 throw "can not find store for combo";
41119 this.store = Roo.factory(this.store, Roo.data);
41120 this.store.parent = this;
41124 this.triggerEl = this.el.select('.input-group-addon', true).first();
41126 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41131 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41132 _this.list.setWidth(lw);
41135 this.list.on('mouseover', this.onViewOver, this);
41136 this.list.on('mousemove', this.onViewMove, this);
41137 this.list.on('scroll', this.onViewScroll, this);
41140 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41143 this.view = new Roo.View(this.list, this.tpl, {
41144 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41147 this.view.on('click', this.onViewClick, this);
41149 this.store.on('beforeload', this.onBeforeLoad, this);
41150 this.store.on('load', this.onLoad, this);
41151 this.store.on('loadexception', this.onLoadException, this);
41153 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41154 "up" : function(e){
41155 this.inKeyMode = true;
41159 "down" : function(e){
41160 if(!this.isExpanded()){
41161 this.onTriggerClick();
41163 this.inKeyMode = true;
41168 "enter" : function(e){
41171 if(this.fireEvent("specialkey", this, e)){
41172 this.onViewClick(false);
41178 "esc" : function(e){
41182 "tab" : function(e){
41185 if(this.fireEvent("specialkey", this, e)){
41186 this.onViewClick(false);
41194 doRelay : function(foo, bar, hname){
41195 if(hname == 'down' || this.scope.isExpanded()){
41196 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41204 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41208 initNumberEvent : function(e)
41210 this.inputEl().on("keydown" , this.fireKey, this);
41211 this.inputEl().on("focus", this.onFocus, this);
41212 this.inputEl().on("blur", this.onBlur, this);
41214 this.inputEl().relayEvent('keyup', this);
41216 if(this.indicator){
41217 this.indicator.addClass('invisible');
41220 this.originalValue = this.getValue();
41222 if(this.validationEvent == 'keyup'){
41223 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41224 this.inputEl().on('keyup', this.filterValidation, this);
41226 else if(this.validationEvent !== false){
41227 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41230 if(this.selectOnFocus){
41231 this.on("focus", this.preFocus, this);
41234 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41235 this.inputEl().on("keypress", this.filterKeys, this);
41237 this.inputEl().relayEvent('keypress', this);
41240 var allowed = "0123456789";
41242 if(this.allowDecimals){
41243 allowed += this.decimalSeparator;
41246 if(this.allowNegative){
41250 if(this.thousandsDelimiter) {
41254 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41256 var keyPress = function(e){
41258 var k = e.getKey();
41260 var c = e.getCharCode();
41263 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41264 allowed.indexOf(String.fromCharCode(c)) === -1
41270 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41274 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41279 this.inputEl().on("keypress", keyPress, this);
41283 onTriggerClick : function(e)
41290 this.loadNext = false;
41292 if(this.isExpanded()){
41297 this.hasFocus = true;
41299 if(this.triggerAction == 'all') {
41300 this.doQuery(this.allQuery, true);
41304 this.doQuery(this.getRawValue());
41307 getCurrency : function()
41309 var v = this.currencyEl().getValue();
41314 restrictHeight : function()
41316 this.list.alignTo(this.currencyEl(), this.listAlign);
41317 this.list.alignTo(this.currencyEl(), this.listAlign);
41320 onViewClick : function(view, doFocus, el, e)
41322 var index = this.view.getSelectedIndexes()[0];
41324 var r = this.store.getAt(index);
41327 this.onSelect(r, index);
41331 onSelect : function(record, index){
41333 if(this.fireEvent('beforeselect', this, record, index) !== false){
41335 this.setFromCurrencyData(index > -1 ? record.data : false);
41339 this.fireEvent('select', this, record, index);
41343 setFromCurrencyData : function(o)
41347 this.lastCurrency = o;
41349 if (this.currencyField) {
41350 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41352 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41355 this.lastSelectionText = currency;
41357 //setting default currency
41358 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41359 this.setCurrency(this.defaultCurrency);
41363 this.setCurrency(currency);
41366 setFromData : function(o)
41370 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41372 this.setFromCurrencyData(c);
41377 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41379 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41382 this.setValue(value);
41386 setCurrency : function(v)
41388 this.currencyValue = v;
41391 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41396 setValue : function(v)
41398 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41404 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41406 this.inputEl().dom.value = (v == '') ? '' :
41407 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41409 if(!this.allowZero && v === '0') {
41410 this.hiddenEl().dom.value = '';
41411 this.inputEl().dom.value = '';
41418 getRawValue : function()
41420 var v = this.inputEl().getValue();
41425 getValue : function()
41427 return this.fixPrecision(this.parseValue(this.getRawValue()));
41430 parseValue : function(value)
41432 if(this.thousandsDelimiter) {
41434 r = new RegExp(",", "g");
41435 value = value.replace(r, "");
41438 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41439 return isNaN(value) ? '' : value;
41443 fixPrecision : function(value)
41445 if(this.thousandsDelimiter) {
41447 r = new RegExp(",", "g");
41448 value = value.replace(r, "");
41451 var nan = isNaN(value);
41453 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41454 return nan ? '' : value;
41456 return parseFloat(value).toFixed(this.decimalPrecision);
41459 decimalPrecisionFcn : function(v)
41461 return Math.floor(v);
41464 validateValue : function(value)
41466 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41470 var num = this.parseValue(value);
41473 this.markInvalid(String.format(this.nanText, value));
41477 if(num < this.minValue){
41478 this.markInvalid(String.format(this.minText, this.minValue));
41482 if(num > this.maxValue){
41483 this.markInvalid(String.format(this.maxText, this.maxValue));
41490 validate : function()
41492 if(this.disabled || this.allowBlank){
41497 var currency = this.getCurrency();
41499 if(this.validateValue(this.getRawValue()) && currency.length){
41504 this.markInvalid();
41508 getName: function()
41513 beforeBlur : function()
41519 var v = this.parseValue(this.getRawValue());
41526 onBlur : function()
41530 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41531 //this.el.removeClass(this.focusClass);
41534 this.hasFocus = false;
41536 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41540 var v = this.getValue();
41542 if(String(v) !== String(this.startValue)){
41543 this.fireEvent('change', this, v, this.startValue);
41546 this.fireEvent("blur", this);
41549 inputEl : function()
41551 return this.el.select('.roo-money-amount-input', true).first();
41554 currencyEl : function()
41556 return this.el.select('.roo-money-currency-input', true).first();
41559 hiddenEl : function()
41561 return this.el.select('input.hidden-number-input',true).first();
41565 * @class Roo.bootstrap.BezierSignature
41566 * @extends Roo.bootstrap.Component
41567 * Bootstrap BezierSignature class
41568 * This script refer to:
41569 * Title: Signature Pad
41571 * Availability: https://github.com/szimek/signature_pad
41574 * Create a new BezierSignature
41575 * @param {Object} config The config object
41578 Roo.bootstrap.BezierSignature = function(config){
41579 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41585 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41592 mouse_btn_down: true,
41595 * @cfg {int} canvas height
41597 canvas_height: '200px',
41600 * @cfg {float|function} Radius of a single dot.
41605 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41610 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41615 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41620 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41625 * @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.
41627 bg_color: 'rgba(0, 0, 0, 0)',
41630 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41632 dot_color: 'black',
41635 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41637 velocity_filter_weight: 0.7,
41640 * @cfg {function} Callback when stroke begin.
41645 * @cfg {function} Callback when stroke end.
41649 getAutoCreate : function()
41651 var cls = 'roo-signature column';
41654 cls += ' ' + this.cls;
41664 for(var i = 0; i < col_sizes.length; i++) {
41665 if(this[col_sizes[i]]) {
41666 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41676 cls: 'roo-signature-body',
41680 cls: 'roo-signature-body-canvas',
41681 height: this.canvas_height,
41682 width: this.canvas_width
41689 style: 'display: none'
41697 initEvents: function()
41699 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41701 var canvas = this.canvasEl();
41703 // mouse && touch event swapping...
41704 canvas.dom.style.touchAction = 'none';
41705 canvas.dom.style.msTouchAction = 'none';
41707 this.mouse_btn_down = false;
41708 canvas.on('mousedown', this._handleMouseDown, this);
41709 canvas.on('mousemove', this._handleMouseMove, this);
41710 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41712 if (window.PointerEvent) {
41713 canvas.on('pointerdown', this._handleMouseDown, this);
41714 canvas.on('pointermove', this._handleMouseMove, this);
41715 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41718 if ('ontouchstart' in window) {
41719 canvas.on('touchstart', this._handleTouchStart, this);
41720 canvas.on('touchmove', this._handleTouchMove, this);
41721 canvas.on('touchend', this._handleTouchEnd, this);
41724 Roo.EventManager.onWindowResize(this.resize, this, true);
41726 // file input event
41727 this.fileEl().on('change', this.uploadImage, this);
41734 resize: function(){
41736 var canvas = this.canvasEl().dom;
41737 var ctx = this.canvasElCtx();
41738 var img_data = false;
41740 if(canvas.width > 0) {
41741 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41743 // setting canvas width will clean img data
41746 var style = window.getComputedStyle ?
41747 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41749 var padding_left = parseInt(style.paddingLeft) || 0;
41750 var padding_right = parseInt(style.paddingRight) || 0;
41752 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41755 ctx.putImageData(img_data, 0, 0);
41759 _handleMouseDown: function(e)
41761 if (e.browserEvent.which === 1) {
41762 this.mouse_btn_down = true;
41763 this.strokeBegin(e);
41767 _handleMouseMove: function (e)
41769 if (this.mouse_btn_down) {
41770 this.strokeMoveUpdate(e);
41774 _handleMouseUp: function (e)
41776 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41777 this.mouse_btn_down = false;
41782 _handleTouchStart: function (e) {
41784 e.preventDefault();
41785 if (e.browserEvent.targetTouches.length === 1) {
41786 // var touch = e.browserEvent.changedTouches[0];
41787 // this.strokeBegin(touch);
41789 this.strokeBegin(e); // assume e catching the correct xy...
41793 _handleTouchMove: function (e) {
41794 e.preventDefault();
41795 // var touch = event.targetTouches[0];
41796 // _this._strokeMoveUpdate(touch);
41797 this.strokeMoveUpdate(e);
41800 _handleTouchEnd: function (e) {
41801 var wasCanvasTouched = e.target === this.canvasEl().dom;
41802 if (wasCanvasTouched) {
41803 e.preventDefault();
41804 // var touch = event.changedTouches[0];
41805 // _this._strokeEnd(touch);
41810 reset: function () {
41811 this._lastPoints = [];
41812 this._lastVelocity = 0;
41813 this._lastWidth = (this.min_width + this.max_width) / 2;
41814 this.canvasElCtx().fillStyle = this.dot_color;
41817 strokeMoveUpdate: function(e)
41819 this.strokeUpdate(e);
41821 if (this.throttle) {
41822 this.throttleStroke(this.strokeUpdate, this.throttle);
41825 this.strokeUpdate(e);
41829 strokeBegin: function(e)
41831 var newPointGroup = {
41832 color: this.dot_color,
41836 if (typeof this.onBegin === 'function') {
41840 this.curve_data.push(newPointGroup);
41842 this.strokeUpdate(e);
41845 strokeUpdate: function(e)
41847 var rect = this.canvasEl().dom.getBoundingClientRect();
41848 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41849 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41850 var lastPoints = lastPointGroup.points;
41851 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41852 var isLastPointTooClose = lastPoint
41853 ? point.distanceTo(lastPoint) <= this.min_distance
41855 var color = lastPointGroup.color;
41856 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41857 var curve = this.addPoint(point);
41859 this.drawDot({color: color, point: point});
41862 this.drawCurve({color: color, curve: curve});
41872 strokeEnd: function(e)
41874 this.strokeUpdate(e);
41875 if (typeof this.onEnd === 'function') {
41880 addPoint: function (point) {
41881 var _lastPoints = this._lastPoints;
41882 _lastPoints.push(point);
41883 if (_lastPoints.length > 2) {
41884 if (_lastPoints.length === 3) {
41885 _lastPoints.unshift(_lastPoints[0]);
41887 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41888 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41889 _lastPoints.shift();
41895 calculateCurveWidths: function (startPoint, endPoint) {
41896 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41897 (1 - this.velocity_filter_weight) * this._lastVelocity;
41899 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41902 start: this._lastWidth
41905 this._lastVelocity = velocity;
41906 this._lastWidth = newWidth;
41910 drawDot: function (_a) {
41911 var color = _a.color, point = _a.point;
41912 var ctx = this.canvasElCtx();
41913 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41915 this.drawCurveSegment(point.x, point.y, width);
41917 ctx.fillStyle = color;
41921 drawCurve: function (_a) {
41922 var color = _a.color, curve = _a.curve;
41923 var ctx = this.canvasElCtx();
41924 var widthDelta = curve.endWidth - curve.startWidth;
41925 var drawSteps = Math.floor(curve.length()) * 2;
41927 ctx.fillStyle = color;
41928 for (var i = 0; i < drawSteps; i += 1) {
41929 var t = i / drawSteps;
41935 var x = uuu * curve.startPoint.x;
41936 x += 3 * uu * t * curve.control1.x;
41937 x += 3 * u * tt * curve.control2.x;
41938 x += ttt * curve.endPoint.x;
41939 var y = uuu * curve.startPoint.y;
41940 y += 3 * uu * t * curve.control1.y;
41941 y += 3 * u * tt * curve.control2.y;
41942 y += ttt * curve.endPoint.y;
41943 var width = curve.startWidth + ttt * widthDelta;
41944 this.drawCurveSegment(x, y, width);
41950 drawCurveSegment: function (x, y, width) {
41951 var ctx = this.canvasElCtx();
41953 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41954 this.is_empty = false;
41959 var ctx = this.canvasElCtx();
41960 var canvas = this.canvasEl().dom;
41961 ctx.fillStyle = this.bg_color;
41962 ctx.clearRect(0, 0, canvas.width, canvas.height);
41963 ctx.fillRect(0, 0, canvas.width, canvas.height);
41964 this.curve_data = [];
41966 this.is_empty = true;
41971 return this.el.select('input',true).first();
41974 canvasEl: function()
41976 return this.el.select('canvas',true).first();
41979 canvasElCtx: function()
41981 return this.el.select('canvas',true).first().dom.getContext('2d');
41984 getImage: function(type)
41986 if(this.is_empty) {
41991 return this.canvasEl().dom.toDataURL('image/'+type, 1);
41994 drawFromImage: function(img_src)
41996 var img = new Image();
41998 img.onload = function(){
41999 this.canvasElCtx().drawImage(img, 0, 0);
42004 this.is_empty = false;
42007 selectImage: function()
42009 this.fileEl().dom.click();
42012 uploadImage: function(e)
42014 var reader = new FileReader();
42016 reader.onload = function(e){
42017 var img = new Image();
42018 img.onload = function(){
42020 this.canvasElCtx().drawImage(img, 0, 0);
42022 img.src = e.target.result;
42025 reader.readAsDataURL(e.target.files[0]);
42028 // Bezier Point Constructor
42029 Point: (function () {
42030 function Point(x, y, time) {
42033 this.time = time || Date.now();
42035 Point.prototype.distanceTo = function (start) {
42036 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42038 Point.prototype.equals = function (other) {
42039 return this.x === other.x && this.y === other.y && this.time === other.time;
42041 Point.prototype.velocityFrom = function (start) {
42042 return this.time !== start.time
42043 ? this.distanceTo(start) / (this.time - start.time)
42050 // Bezier Constructor
42051 Bezier: (function () {
42052 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42053 this.startPoint = startPoint;
42054 this.control2 = control2;
42055 this.control1 = control1;
42056 this.endPoint = endPoint;
42057 this.startWidth = startWidth;
42058 this.endWidth = endWidth;
42060 Bezier.fromPoints = function (points, widths, scope) {
42061 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42062 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42063 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42065 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42066 var dx1 = s1.x - s2.x;
42067 var dy1 = s1.y - s2.y;
42068 var dx2 = s2.x - s3.x;
42069 var dy2 = s2.y - s3.y;
42070 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42071 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42072 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42073 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42074 var dxm = m1.x - m2.x;
42075 var dym = m1.y - m2.y;
42076 var k = l2 / (l1 + l2);
42077 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42078 var tx = s2.x - cm.x;
42079 var ty = s2.y - cm.y;
42081 c1: new scope.Point(m1.x + tx, m1.y + ty),
42082 c2: new scope.Point(m2.x + tx, m2.y + ty)
42085 Bezier.prototype.length = function () {
42090 for (var i = 0; i <= steps; i += 1) {
42092 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42093 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42095 var xdiff = cx - px;
42096 var ydiff = cy - py;
42097 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42104 Bezier.prototype.point = function (t, start, c1, c2, end) {
42105 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42106 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42107 + (3.0 * c2 * (1.0 - t) * t * t)
42108 + (end * t * t * t);
42113 throttleStroke: function(fn, wait) {
42114 if (wait === void 0) { wait = 250; }
42116 var timeout = null;
42120 var later = function () {
42121 previous = Date.now();
42123 result = fn.apply(storedContext, storedArgs);
42125 storedContext = null;
42129 return function wrapper() {
42131 for (var _i = 0; _i < arguments.length; _i++) {
42132 args[_i] = arguments[_i];
42134 var now = Date.now();
42135 var remaining = wait - (now - previous);
42136 storedContext = this;
42138 if (remaining <= 0 || remaining > wait) {
42140 clearTimeout(timeout);
42144 result = fn.apply(storedContext, storedArgs);
42146 storedContext = null;
42150 else if (!timeout) {
42151 timeout = window.setTimeout(later, remaining);