2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets[0], 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');
418 this.fireEvent('show', this);
423 * Hide a component - adds 'hidden' class
427 if(!this.getVisibilityEl()){
431 this.getVisibilityEl().addClass('hidden');
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 fs
593 * @cfg {String} badge text for badge
594 * @cfg {String} theme (default|glow)
595 * @cfg {Boolean} inverse dark themed version
596 * @cfg {Boolean} toggle is it a slidy toggle button
597 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598 * @cfg {String} ontext text for on slidy toggle state
599 * @cfg {String} offtext text for off slidy toggle state
600 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
601 * @cfg {Boolean} removeClass remove the standard class..
602 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
605 * Create a new button
606 * @param {Object} config The config object
610 Roo.bootstrap.Button = function(config){
611 Roo.bootstrap.Button.superclass.constructor.call(this, config);
612 this.weightClass = ["btn-default btn-outline-secondary",
624 * When a butotn is pressed
625 * @param {Roo.bootstrap.Button} btn
626 * @param {Roo.EventObject} e
631 * After the button has been toggles
632 * @param {Roo.bootstrap.Button} btn
633 * @param {Roo.EventObject} e
634 * @param {boolean} pressed (also available as button.pressed)
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
661 preventDefault: true,
669 getAutoCreate : function(){
677 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
683 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685 if (this.toggle == true) {
688 cls: 'slider-frame roo-button',
693 'data-off-text':'OFF',
694 cls: 'slider-button',
700 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701 cfg.cls += ' '+this.weight;
710 cfg["aria-hidden"] = true;
712 cfg.html = "×";
718 if (this.theme==='default') {
719 cfg.cls = 'btn roo-button';
721 //if (this.parentType != 'Navbar') {
722 this.weight = this.weight.length ? this.weight : 'default';
724 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728 cfg.cls += ' btn-' + outline + weight;
729 if (this.weight == 'default') {
731 cfg.cls += ' btn-' + this.weight;
734 } else if (this.theme==='glow') {
737 cfg.cls = 'btn-glow roo-button';
739 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741 cfg.cls += ' ' + this.weight;
747 this.cls += ' inverse';
751 if (this.active || this.pressed === true) {
752 cfg.cls += ' active';
756 cfg.disabled = 'disabled';
760 Roo.log('changing to ul' );
762 this.glyphicon = 'caret';
763 if (Roo.bootstrap.version == 4) {
764 this.fa = 'caret-down';
769 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771 //gsRoo.log(this.parentType);
772 if (this.parentType === 'Navbar' && !this.parent().bar) {
773 Roo.log('changing to li?');
782 href : this.href || '#'
785 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
786 cfg.cls += ' dropdown';
793 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
795 if (this.glyphicon) {
796 cfg.html = ' ' + cfg.html;
801 cls: 'glyphicon glyphicon-' + this.glyphicon
806 cfg.html = ' ' + cfg.html;
811 cls: 'fa fas fa-' + this.fa
821 // cfg.cls='btn roo-button';
825 var value = cfg.html;
830 cls: 'glyphicon glyphicon-' + this.glyphicon,
837 cls: 'fa fas fa-' + this.fa,
842 var bw = this.badge_weight.length ? this.badge_weight :
843 (this.weight.length ? this.weight : 'secondary');
844 bw = bw == 'default' ? 'secondary' : bw;
850 cls: 'badge badge-' + bw,
859 cfg.cls += ' dropdown';
860 cfg.html = typeof(cfg.html) != 'undefined' ?
861 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
864 if (cfg.tag !== 'a' && this.href !== '') {
865 throw "Tag must be a to set href.";
866 } else if (this.href.length > 0) {
867 cfg.href = this.href;
870 if(this.removeClass){
875 cfg.target = this.target;
880 initEvents: function() {
881 // Roo.log('init events?');
882 // Roo.log(this.el.dom);
885 if (typeof (this.menu) != 'undefined') {
886 this.menu.parentType = this.xtype;
887 this.menu.triggerEl = this.el;
888 this.addxtype(Roo.apply({}, this.menu));
892 if (this.el.hasClass('roo-button')) {
893 this.el.on('click', this.onClick, this);
895 this.el.select('.roo-button').on('click', this.onClick, this);
898 if(this.removeClass){
899 this.el.on('click', this.onClick, this);
902 this.el.enableDisplayMode();
905 onClick : function(e)
911 Roo.log('button on click ');
912 if(this.preventDefault){
916 if (this.pressed === true || this.pressed === false) {
917 this.toggleActive(e);
921 this.fireEvent('click', this, e);
925 * Enables this button
929 this.disabled = false;
930 this.el.removeClass('disabled');
934 * Disable this button
938 this.disabled = true;
939 this.el.addClass('disabled');
942 * sets the active state on/off,
943 * @param {Boolean} state (optional) Force a particular state
945 setActive : function(v) {
947 this.el[v ? 'addClass' : 'removeClass']('active');
951 * toggles the current active state
953 toggleActive : function(e)
955 this.setActive(!this.pressed);
956 this.fireEvent('toggle', this, e, !this.pressed);
959 * get the current active state
960 * @return {boolean} true if it's active
962 isActive : function()
964 return this.el.hasClass('active');
967 * set the text of the first selected button
969 setText : function(str)
971 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
974 * get the text of the first selected button
978 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
981 setWeight : function(str)
983 this.el.removeClass(this.weightClass);
985 var outline = this.outline ? 'outline-' : '';
986 if (str == 'default') {
987 this.el.addClass('btn-default btn-outline-secondary');
990 this.el.addClass('btn-' + outline + str);
1004 * @class Roo.bootstrap.Column
1005 * @extends Roo.bootstrap.Component
1006 * Bootstrap Column class
1007 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1017 * @cfg {Boolean} hidden (true|false) hide the element
1018 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019 * @cfg {String} fa (ban|check|...) font awesome icon
1020 * @cfg {Number} fasize (1|2|....) font awsome size
1022 * @cfg {String} icon (info-sign|check|...) glyphicon name
1024 * @cfg {String} html content of column.
1027 * Create a new Column
1028 * @param {Object} config The config object
1031 Roo.bootstrap.Column = function(config){
1032 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1054 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1062 ['xs','sm','md','lg'].map(function(size){
1063 //Roo.log( size + ':' + settings[size]);
1065 if (settings[size+'off'] !== false) {
1066 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1069 if (settings[size] === false) {
1073 if (!settings[size]) { // 0 = hidden
1074 cfg.cls += ' hidden-' + size;
1077 cfg.cls += ' col-' + size + '-' + settings[size];
1082 cfg.cls += ' hidden';
1085 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086 cfg.cls +=' alert alert-' + this.alert;
1090 if (this.html.length) {
1091 cfg.html = this.html;
1095 if (this.fasize > 1) {
1096 fasize = ' fa-' + this.fasize + 'x';
1098 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1103 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1122 * @class Roo.bootstrap.Container
1123 * @extends Roo.bootstrap.Component
1124 * Bootstrap Container class
1125 * @cfg {Boolean} jumbotron is it a jumbotron element
1126 * @cfg {String} html content of element
1127 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1129 * @cfg {String} header content of header (for panel)
1130 * @cfg {String} footer content of footer (for panel)
1131 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132 * @cfg {String} tag (header|aside|section) type of HTML tag.
1133 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134 * @cfg {String} fa font awesome icon
1135 * @cfg {String} icon (info-sign|check|...) glyphicon name
1136 * @cfg {Boolean} hidden (true|false) hide the element
1137 * @cfg {Boolean} expandable (true|false) default false
1138 * @cfg {Boolean} expanded (true|false) default true
1139 * @cfg {String} rheader contet on the right of header
1140 * @cfg {Boolean} clickable (true|false) default false
1144 * Create a new Container
1145 * @param {Object} config The config object
1148 Roo.bootstrap.Container = function(config){
1149 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1155 * After the panel has been expand
1157 * @param {Roo.bootstrap.Container} this
1162 * After the panel has been collapsed
1164 * @param {Roo.bootstrap.Container} this
1169 * When a element is chick
1170 * @param {Roo.bootstrap.Container} this
1171 * @param {Roo.EventObject} e
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1195 getChildContainer : function() {
1201 if (this.panel.length) {
1202 return this.el.select('.panel-body',true).first();
1209 getAutoCreate : function(){
1212 tag : this.tag || 'div',
1216 if (this.jumbotron) {
1217 cfg.cls = 'jumbotron';
1222 // - this is applied by the parent..
1224 // cfg.cls = this.cls + '';
1227 if (this.sticky.length) {
1229 var bd = Roo.get(document.body);
1230 if (!bd.hasClass('bootstrap-sticky')) {
1231 bd.addClass('bootstrap-sticky');
1232 Roo.select('html',true).setStyle('height', '100%');
1235 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239 if (this.well.length) {
1240 switch (this.well) {
1243 cfg.cls +=' well well-' +this.well;
1252 cfg.cls += ' hidden';
1256 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257 cfg.cls +=' alert alert-' + this.alert;
1262 if (this.panel.length) {
1263 cfg.cls += ' panel panel-' + this.panel;
1265 if (this.header.length) {
1269 if(this.expandable){
1271 cfg.cls = cfg.cls + ' expandable';
1275 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1283 cls : 'panel-title',
1284 html : (this.expandable ? ' ' : '') + this.header
1288 cls: 'panel-header-right',
1294 cls : 'panel-heading',
1295 style : this.expandable ? 'cursor: pointer' : '',
1303 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1308 if (this.footer.length) {
1310 cls : 'panel-footer',
1319 body.html = this.html || cfg.html;
1320 // prefix with the icons..
1322 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1325 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1330 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331 cfg.cls = 'container';
1337 initEvents: function()
1339 if(this.expandable){
1340 var headerEl = this.headerEl();
1343 headerEl.on('click', this.onToggleClick, this);
1348 this.el.on('click', this.onClick, this);
1353 onToggleClick : function()
1355 var headerEl = this.headerEl();
1371 if(this.fireEvent('expand', this)) {
1373 this.expanded = true;
1375 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377 this.el.select('.panel-body',true).first().removeClass('hide');
1379 var toggleEl = this.toggleEl();
1385 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1390 collapse : function()
1392 if(this.fireEvent('collapse', this)) {
1394 this.expanded = false;
1396 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397 this.el.select('.panel-body',true).first().addClass('hide');
1399 var toggleEl = this.toggleEl();
1405 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409 toggleEl : function()
1411 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415 return this.el.select('.panel-heading .fa',true).first();
1418 headerEl : function()
1420 if(!this.el || !this.panel.length || !this.header.length){
1424 return this.el.select('.panel-heading',true).first()
1429 if(!this.el || !this.panel.length){
1433 return this.el.select('.panel-body',true).first()
1436 titleEl : function()
1438 if(!this.el || !this.panel.length || !this.header.length){
1442 return this.el.select('.panel-title',true).first();
1445 setTitle : function(v)
1447 var titleEl = this.titleEl();
1453 titleEl.dom.innerHTML = v;
1456 getTitle : function()
1459 var titleEl = this.titleEl();
1465 return titleEl.dom.innerHTML;
1468 setRightTitle : function(v)
1470 var t = this.el.select('.panel-header-right',true).first();
1476 t.dom.innerHTML = v;
1479 onClick : function(e)
1483 this.fireEvent('click', this, e);
1496 * @class Roo.bootstrap.Img
1497 * @extends Roo.bootstrap.Component
1498 * Bootstrap Img class
1499 * @cfg {Boolean} imgResponsive false | true
1500 * @cfg {String} border rounded | circle | thumbnail
1501 * @cfg {String} src image source
1502 * @cfg {String} alt image alternative text
1503 * @cfg {String} href a tag href
1504 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505 * @cfg {String} xsUrl xs image source
1506 * @cfg {String} smUrl sm image source
1507 * @cfg {String} mdUrl md image source
1508 * @cfg {String} lgUrl lg image source
1511 * Create a new Input
1512 * @param {Object} config The config object
1515 Roo.bootstrap.Img = function(config){
1516 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1522 * The img click event for the img.
1523 * @param {Roo.EventObject} e
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1531 imgResponsive: true,
1541 getAutoCreate : function()
1543 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544 return this.createSingleImg();
1549 cls: 'roo-image-responsive-group',
1554 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556 if(!_this[size + 'Url']){
1562 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563 html: _this.html || cfg.html,
1564 src: _this[size + 'Url']
1567 img.cls += ' roo-image-responsive-' + size;
1569 var s = ['xs', 'sm', 'md', 'lg'];
1571 s.splice(s.indexOf(size), 1);
1573 Roo.each(s, function(ss){
1574 img.cls += ' hidden-' + ss;
1577 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578 cfg.cls += ' img-' + _this.border;
1582 cfg.alt = _this.alt;
1595 a.target = _this.target;
1599 cfg.cn.push((_this.href) ? a : img);
1606 createSingleImg : function()
1610 cls: (this.imgResponsive) ? 'img-responsive' : '',
1612 src : 'about:blank' // just incase src get's set to undefined?!?
1615 cfg.html = this.html || cfg.html;
1617 cfg.src = this.src || cfg.src;
1619 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620 cfg.cls += ' img-' + this.border;
1637 a.target = this.target;
1642 return (this.href) ? a : cfg;
1645 initEvents: function()
1648 this.el.on('click', this.onClick, this);
1653 onClick : function(e)
1655 Roo.log('img onclick');
1656 this.fireEvent('click', this, e);
1659 * Sets the url of the image - used to update it
1660 * @param {String} url the url of the image
1663 setSrc : function(url)
1667 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668 this.el.dom.src = url;
1672 this.el.select('img', true).first().dom.src = url;
1688 * @class Roo.bootstrap.Link
1689 * @extends Roo.bootstrap.Component
1690 * Bootstrap Link Class
1691 * @cfg {String} alt image alternative text
1692 * @cfg {String} href a tag href
1693 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694 * @cfg {String} html the content of the link.
1695 * @cfg {String} anchor name for the anchor link
1696 * @cfg {String} fa - favicon
1698 * @cfg {Boolean} preventDefault (true | false) default false
1702 * Create a new Input
1703 * @param {Object} config The config object
1706 Roo.bootstrap.Link = function(config){
1707 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1713 * The img click event for the img.
1714 * @param {Roo.EventObject} e
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1724 preventDefault: false,
1730 getAutoCreate : function()
1732 var html = this.html || '';
1734 if (this.fa !== false) {
1735 html = '<i class="fa fa-' + this.fa + '"></i>';
1740 // anchor's do not require html/href...
1741 if (this.anchor === false) {
1743 cfg.href = this.href || '#';
1745 cfg.name = this.anchor;
1746 if (this.html !== false || this.fa !== false) {
1749 if (this.href !== false) {
1750 cfg.href = this.href;
1754 if(this.alt !== false){
1759 if(this.target !== false) {
1760 cfg.target = this.target;
1766 initEvents: function() {
1768 if(!this.href || this.preventDefault){
1769 this.el.on('click', this.onClick, this);
1773 onClick : function(e)
1775 if(this.preventDefault){
1778 //Roo.log('img onclick');
1779 this.fireEvent('click', this, e);
1792 * @class Roo.bootstrap.Header
1793 * @extends Roo.bootstrap.Component
1794 * Bootstrap Header class
1795 * @cfg {String} html content of header
1796 * @cfg {Number} level (1|2|3|4|5|6) default 1
1799 * Create a new Header
1800 * @param {Object} config The config object
1804 Roo.bootstrap.Header = function(config){
1805 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1816 getAutoCreate : function(){
1821 tag: 'h' + (1 *this.level),
1822 html: this.html || ''
1834 * Ext JS Library 1.1.1
1835 * Copyright(c) 2006-2007, Ext JS, LLC.
1837 * Originally Released Under LGPL - original licence link has changed is not relivant.
1840 * <script type="text/javascript">
1844 * @class Roo.bootstrap.MenuMgr
1845 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1848 Roo.bootstrap.MenuMgr = function(){
1849 var menus, active, groups = {}, attached = false, lastShow = new Date();
1851 // private - called when first menu is created
1854 active = new Roo.util.MixedCollection();
1855 Roo.get(document).addKeyListener(27, function(){
1856 if(active.length > 0){
1864 if(active && active.length > 0){
1865 var c = active.clone();
1875 if(active.length < 1){
1876 Roo.get(document).un("mouseup", onMouseDown);
1884 var last = active.last();
1885 lastShow = new Date();
1888 Roo.get(document).on("mouseup", onMouseDown);
1893 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894 m.parentMenu.activeChild = m;
1895 }else if(last && last.isVisible()){
1896 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1901 function onBeforeHide(m){
1903 m.activeChild.hide();
1905 if(m.autoHideTimer){
1906 clearTimeout(m.autoHideTimer);
1907 delete m.autoHideTimer;
1912 function onBeforeShow(m){
1913 var pm = m.parentMenu;
1914 if(!pm && !m.allowOtherMenus){
1916 }else if(pm && pm.activeChild && active != m){
1917 pm.activeChild.hide();
1921 // private this should really trigger on mouseup..
1922 function onMouseDown(e){
1923 Roo.log("on Mouse Up");
1925 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926 Roo.log("MenuManager hideAll");
1935 function onBeforeCheck(mi, state){
1937 var g = groups[mi.group];
1938 for(var i = 0, l = g.length; i < l; i++){
1940 g[i].setChecked(false);
1949 * Hides all menus that are currently visible
1951 hideAll : function(){
1956 register : function(menu){
1960 menus[menu.id] = menu;
1961 menu.on("beforehide", onBeforeHide);
1962 menu.on("hide", onHide);
1963 menu.on("beforeshow", onBeforeShow);
1964 menu.on("show", onShow);
1966 if(g && menu.events["checkchange"]){
1970 groups[g].push(menu);
1971 menu.on("checkchange", onCheck);
1976 * Returns a {@link Roo.menu.Menu} object
1977 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978 * be used to generate and return a new Menu instance.
1980 get : function(menu){
1981 if(typeof menu == "string"){ // menu id
1983 }else if(menu.events){ // menu instance
1986 /*else if(typeof menu.length == 'number'){ // array of menu items?
1987 return new Roo.bootstrap.Menu({items:menu});
1988 }else{ // otherwise, must be a config
1989 return new Roo.bootstrap.Menu(menu);
1996 unregister : function(menu){
1997 delete menus[menu.id];
1998 menu.un("beforehide", onBeforeHide);
1999 menu.un("hide", onHide);
2000 menu.un("beforeshow", onBeforeShow);
2001 menu.un("show", onShow);
2003 if(g && menu.events["checkchange"]){
2004 groups[g].remove(menu);
2005 menu.un("checkchange", onCheck);
2010 registerCheckable : function(menuItem){
2011 var g = menuItem.group;
2016 groups[g].push(menuItem);
2017 menuItem.on("beforecheckchange", onBeforeCheck);
2022 unregisterCheckable : function(menuItem){
2023 var g = menuItem.group;
2025 groups[g].remove(menuItem);
2026 menuItem.un("beforecheckchange", onBeforeCheck);
2038 * @class Roo.bootstrap.Menu
2039 * @extends Roo.bootstrap.Component
2040 * Bootstrap Menu class - container for MenuItems
2041 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042 * @cfg {bool} hidden if the menu should be hidden when rendered.
2043 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2044 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2048 * @param {Object} config The config object
2052 Roo.bootstrap.Menu = function(config){
2053 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054 if (this.registerMenu && this.type != 'treeview') {
2055 Roo.bootstrap.MenuMgr.register(this);
2062 * Fires before this menu is displayed
2063 * @param {Roo.menu.Menu} this
2068 * Fires before this menu is hidden
2069 * @param {Roo.menu.Menu} this
2074 * Fires after this menu is displayed
2075 * @param {Roo.menu.Menu} this
2080 * Fires after this menu is hidden
2081 * @param {Roo.menu.Menu} this
2086 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087 * @param {Roo.menu.Menu} this
2088 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089 * @param {Roo.EventObject} e
2094 * Fires when the mouse is hovering over this menu
2095 * @param {Roo.menu.Menu} this
2096 * @param {Roo.EventObject} e
2097 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2102 * Fires when the mouse exits this menu
2103 * @param {Roo.menu.Menu} this
2104 * @param {Roo.EventObject} e
2105 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2110 * Fires when a menu item contained in this menu is clicked
2111 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112 * @param {Roo.EventObject} e
2116 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2123 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2126 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128 registerMenu : true,
2130 menuItems :false, // stores the menu items..
2140 getChildContainer : function() {
2144 getAutoCreate : function(){
2146 //if (['right'].indexOf(this.align)!==-1) {
2147 // cfg.cn[1].cls += ' pull-right'
2153 cls : 'dropdown-menu' ,
2154 style : 'z-index:1000'
2158 if (this.type === 'submenu') {
2159 cfg.cls = 'submenu active';
2161 if (this.type === 'treeview') {
2162 cfg.cls = 'treeview-menu';
2167 initEvents : function() {
2169 // Roo.log("ADD event");
2170 // Roo.log(this.triggerEl.dom);
2172 this.triggerEl.on('click', this.onTriggerClick, this);
2174 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2177 if (this.triggerEl.hasClass('nav-item')) {
2178 // dropdown toggle on the 'a' in BS4?
2179 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181 this.triggerEl.addClass('dropdown-toggle');
2184 this.el.on('touchstart' , this.onTouch, this);
2186 this.el.on('click' , this.onClick, this);
2188 this.el.on("mouseover", this.onMouseOver, this);
2189 this.el.on("mouseout", this.onMouseOut, this);
2193 findTargetItem : function(e)
2195 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2199 //Roo.log(t); Roo.log(t.id);
2201 //Roo.log(this.menuitems);
2202 return this.menuitems.get(t.id);
2204 //return this.items.get(t.menuItemId);
2210 onTouch : function(e)
2212 Roo.log("menu.onTouch");
2213 //e.stopEvent(); this make the user popdown broken
2217 onClick : function(e)
2219 Roo.log("menu.onClick");
2221 var t = this.findTargetItem(e);
2222 if(!t || t.isContainer){
2227 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2228 if(t == this.activeItem && t.shouldDeactivate(e)){
2229 this.activeItem.deactivate();
2230 delete this.activeItem;
2234 this.setActiveItem(t, true);
2242 Roo.log('pass click event');
2246 this.fireEvent("click", this, t, e);
2250 if(!t.href.length || t.href == '#'){
2251 (function() { _this.hide(); }).defer(100);
2256 onMouseOver : function(e){
2257 var t = this.findTargetItem(e);
2260 // if(t.canActivate && !t.disabled){
2261 // this.setActiveItem(t, true);
2265 this.fireEvent("mouseover", this, e, t);
2267 isVisible : function(){
2268 return !this.hidden;
2270 onMouseOut : function(e){
2271 var t = this.findTargetItem(e);
2274 // if(t == this.activeItem && t.shouldDeactivate(e)){
2275 // this.activeItem.deactivate();
2276 // delete this.activeItem;
2279 this.fireEvent("mouseout", this, e, t);
2284 * Displays this menu relative to another element
2285 * @param {String/HTMLElement/Roo.Element} element The element to align to
2286 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287 * the element (defaults to this.defaultAlign)
2288 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290 show : function(el, pos, parentMenu){
2291 this.parentMenu = parentMenu;
2295 this.fireEvent("beforeshow", this);
2296 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2299 * Displays this menu at a specific xy position
2300 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303 showAt : function(xy, parentMenu, /* private: */_e){
2304 this.parentMenu = parentMenu;
2309 this.fireEvent("beforeshow", this);
2310 //xy = this.el.adjustForConstraints(xy);
2314 this.hideMenuItems();
2315 this.hidden = false;
2316 this.triggerEl.addClass('open');
2317 this.el.addClass('show');
2319 // reassign x when hitting right
2320 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2324 // reassign y when hitting bottom
2325 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2329 // but the list may align on trigger left or trigger top... should it be a properity?
2331 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2336 this.fireEvent("show", this);
2342 this.doFocus.defer(50, this);
2346 doFocus : function(){
2348 this.focusEl.focus();
2353 * Hides this menu and optionally all parent menus
2354 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356 hide : function(deep)
2359 this.hideMenuItems();
2360 if(this.el && this.isVisible()){
2361 this.fireEvent("beforehide", this);
2362 if(this.activeItem){
2363 this.activeItem.deactivate();
2364 this.activeItem = null;
2366 this.triggerEl.removeClass('open');;
2367 this.el.removeClass('show');
2369 this.fireEvent("hide", this);
2371 if(deep === true && this.parentMenu){
2372 this.parentMenu.hide(true);
2376 onTriggerClick : function(e)
2378 Roo.log('trigger click');
2380 var target = e.getTarget();
2382 Roo.log(target.nodeName.toLowerCase());
2384 if(target.nodeName.toLowerCase() === 'i'){
2390 onTriggerPress : function(e)
2392 Roo.log('trigger press');
2393 //Roo.log(e.getTarget());
2394 // Roo.log(this.triggerEl.dom);
2396 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397 var pel = Roo.get(e.getTarget());
2398 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399 Roo.log('is treeview or dropdown?');
2403 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2407 if (this.isVisible()) {
2412 this.show(this.triggerEl, false, false);
2415 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2422 hideMenuItems : function()
2424 Roo.log("hide Menu Items");
2428 //$(backdrop).remove()
2429 this.el.select('.open',true).each(function(aa) {
2431 aa.removeClass('open');
2432 //var parent = getParent($(this))
2433 //var relatedTarget = { relatedTarget: this }
2435 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436 //if (e.isDefaultPrevented()) return
2437 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2440 addxtypeChild : function (tree, cntr) {
2441 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443 this.menuitems.add(comp);
2455 this.getEl().dom.innerHTML = '';
2456 this.menuitems.clear();
2470 * @class Roo.bootstrap.MenuItem
2471 * @extends Roo.bootstrap.Component
2472 * Bootstrap MenuItem class
2473 * @cfg {String} html the menu label
2474 * @cfg {String} href the link
2475 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477 * @cfg {Boolean} active used on sidebars to highlight active itesm
2478 * @cfg {String} fa favicon to show on left of menu item.
2479 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2483 * Create a new MenuItem
2484 * @param {Object} config The config object
2488 Roo.bootstrap.MenuItem = function(config){
2489 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494 * The raw click event for the entire grid.
2495 * @param {Roo.bootstrap.MenuItem} this
2496 * @param {Roo.EventObject} e
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2506 preventDefault: false,
2507 isContainer : false,
2511 getAutoCreate : function(){
2513 if(this.isContainer){
2516 cls: 'dropdown-menu-item dropdown-item'
2530 if (this.fa !== false) {
2533 cls : 'fa fa-' + this.fa
2542 cls: 'dropdown-menu-item dropdown-item',
2545 if (this.parent().type == 'treeview') {
2546 cfg.cls = 'treeview-menu';
2549 cfg.cls += ' active';
2554 anc.href = this.href || cfg.cn[0].href ;
2555 ctag.html = this.html || cfg.cn[0].html ;
2559 initEvents: function()
2561 if (this.parent().type == 'treeview') {
2562 this.el.select('a').on('click', this.onClick, this);
2566 this.menu.parentType = this.xtype;
2567 this.menu.triggerEl = this.el;
2568 this.menu = this.addxtype(Roo.apply({}, this.menu));
2572 onClick : function(e)
2574 Roo.log('item on click ');
2576 if(this.preventDefault){
2579 //this.parent().hideMenuItems();
2581 this.fireEvent('click', this, e);
2600 * @class Roo.bootstrap.MenuSeparator
2601 * @extends Roo.bootstrap.Component
2602 * Bootstrap MenuSeparator class
2605 * Create a new MenuItem
2606 * @param {Object} config The config object
2610 Roo.bootstrap.MenuSeparator = function(config){
2611 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2616 getAutoCreate : function(){
2635 * @class Roo.bootstrap.Modal
2636 * @extends Roo.bootstrap.Component
2637 * Bootstrap Modal class
2638 * @cfg {String} title Title of dialog
2639 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2641 * @cfg {Boolean} specificTitle default false
2642 * @cfg {Array} buttons Array of buttons or standard button set..
2643 * @cfg {String} buttonPosition (left|right|center) default right
2644 * @cfg {Boolean} animate default true
2645 * @cfg {Boolean} allow_close default true
2646 * @cfg {Boolean} fitwindow default false
2647 * @cfg {String} size (sm|lg) default empty
2648 * @cfg {Number} max_width set the max width of modal
2652 * Create a new Modal Dialog
2653 * @param {Object} config The config object
2656 Roo.bootstrap.Modal = function(config){
2657 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2662 * The raw btnclick event for the button
2663 * @param {Roo.EventObject} e
2668 * Fire when dialog resize
2669 * @param {Roo.bootstrap.Modal} this
2670 * @param {Roo.EventObject} e
2674 this.buttons = this.buttons || [];
2677 this.tmpl = Roo.factory(this.tmpl);
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2684 title : 'test dialog',
2694 specificTitle: false,
2696 buttonPosition: 'right',
2719 onRender : function(ct, position)
2721 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724 var cfg = Roo.apply({}, this.getAutoCreate());
2727 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2729 //if (!cfg.name.length) {
2733 cfg.cls += ' ' + this.cls;
2736 cfg.style = this.style;
2738 this.el = Roo.get(document.body).createChild(cfg, position);
2740 //var type = this.el.dom.type;
2743 if(this.tabIndex !== undefined){
2744 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747 this.dialogEl = this.el.select('.modal-dialog',true).first();
2748 this.bodyEl = this.el.select('.modal-body',true).first();
2749 this.closeEl = this.el.select('.modal-header .close', true).first();
2750 this.headerEl = this.el.select('.modal-header',true).first();
2751 this.titleEl = this.el.select('.modal-title',true).first();
2752 this.footerEl = this.el.select('.modal-footer',true).first();
2754 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2756 //this.el.addClass("x-dlg-modal");
2758 if (this.buttons.length) {
2759 Roo.each(this.buttons, function(bb) {
2760 var b = Roo.apply({}, bb);
2761 b.xns = b.xns || Roo.bootstrap;
2762 b.xtype = b.xtype || 'Button';
2763 if (typeof(b.listeners) == 'undefined') {
2764 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2767 var btn = Roo.factory(b);
2769 btn.render(this.el.select('.modal-footer div').first());
2773 // render the children.
2776 if(typeof(this.items) != 'undefined'){
2777 var items = this.items;
2780 for(var i =0;i < items.length;i++) {
2781 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2785 this.items = nitems;
2787 // where are these used - they used to be body/close/footer
2791 //this.el.addClass([this.fieldClass, this.cls]);
2795 getAutoCreate : function()
2799 html : this.html || ''
2804 cls : 'modal-title',
2808 if(this.specificTitle){
2814 if (this.allow_close && Roo.bootstrap.version == 3) {
2824 if (this.allow_close && Roo.bootstrap.version == 4) {
2834 if(this.size.length){
2835 size = 'modal-' + this.size;
2842 cls: "modal-dialog " + size,
2845 cls : "modal-content",
2848 cls : 'modal-header',
2853 cls : 'modal-footer',
2857 cls: 'btn-' + this.buttonPosition
2874 modal.cls += ' fade';
2880 getChildContainer : function() {
2885 getButtonContainer : function() {
2886 return this.el.select('.modal-footer div',true).first();
2889 initEvents : function()
2891 if (this.allow_close) {
2892 this.closeEl.on('click', this.hide, this);
2894 Roo.EventManager.onWindowResize(this.resize, this, true);
2901 this.maskEl.setSize(
2902 Roo.lib.Dom.getViewWidth(true),
2903 Roo.lib.Dom.getViewHeight(true)
2906 if (this.fitwindow) {
2908 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2914 if(this.max_width !== 0) {
2916 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2919 this.setSize(w, this.height);
2923 if(this.max_height) {
2924 this.setSize(w,Math.min(
2926 Roo.lib.Dom.getViewportHeight(true) - 60
2932 if(!this.fit_content) {
2933 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2937 this.setSize(w, Math.min(
2939 this.headerEl.getHeight() +
2940 this.footerEl.getHeight() +
2941 this.getChildHeight(this.bodyEl.dom.childNodes),
2942 Roo.lib.Dom.getViewportHeight(true) - 60)
2948 setSize : function(w,h)
2959 if (!this.rendered) {
2963 //this.el.setStyle('display', 'block');
2964 this.el.removeClass('hideing');
2965 this.el.dom.style.display='block';
2967 Roo.get(document.body).addClass('modal-open');
2969 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2972 this.el.addClass('show');
2973 this.el.addClass('in');
2976 this.el.addClass('show');
2977 this.el.addClass('in');
2980 // not sure how we can show data in here..
2982 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2985 Roo.get(document.body).addClass("x-body-masked");
2987 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2988 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989 this.maskEl.dom.style.display = 'block';
2990 this.maskEl.addClass('show');
2995 this.fireEvent('show', this);
2997 // set zindex here - otherwise it appears to be ignored...
2998 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3001 this.items.forEach( function(e) {
3002 e.layout ? e.layout() : false;
3010 if(this.fireEvent("beforehide", this) !== false){
3012 this.maskEl.removeClass('show');
3014 this.maskEl.dom.style.display = '';
3015 Roo.get(document.body).removeClass("x-body-masked");
3016 this.el.removeClass('in');
3017 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3019 if(this.animate){ // why
3020 this.el.addClass('hideing');
3021 this.el.removeClass('show');
3023 if (!this.el.hasClass('hideing')) {
3024 return; // it's been shown again...
3027 this.el.dom.style.display='';
3029 Roo.get(document.body).removeClass('modal-open');
3030 this.el.removeClass('hideing');
3034 this.el.removeClass('show');
3035 this.el.dom.style.display='';
3036 Roo.get(document.body).removeClass('modal-open');
3039 this.fireEvent('hide', this);
3042 isVisible : function()
3045 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3049 addButton : function(str, cb)
3053 var b = Roo.apply({}, { html : str } );
3054 b.xns = b.xns || Roo.bootstrap;
3055 b.xtype = b.xtype || 'Button';
3056 if (typeof(b.listeners) == 'undefined') {
3057 b.listeners = { click : cb.createDelegate(this) };
3060 var btn = Roo.factory(b);
3062 btn.render(this.el.select('.modal-footer div').first());
3068 setDefaultButton : function(btn)
3070 //this.el.select('.modal-footer').()
3074 resizeTo: function(w,h)
3078 this.dialogEl.setWidth(w);
3079 if (this.diff === false) {
3080 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3083 this.bodyEl.setHeight(h - this.diff);
3085 this.fireEvent('resize', this);
3088 setContentSize : function(w, h)
3092 onButtonClick: function(btn,e)
3095 this.fireEvent('btnclick', btn.name, e);
3098 * Set the title of the Dialog
3099 * @param {String} str new Title
3101 setTitle: function(str) {
3102 this.titleEl.dom.innerHTML = str;
3105 * Set the body of the Dialog
3106 * @param {String} str new Title
3108 setBody: function(str) {
3109 this.bodyEl.dom.innerHTML = str;
3112 * Set the body of the Dialog using the template
3113 * @param {Obj} data - apply this data to the template and replace the body contents.
3115 applyBody: function(obj)
3118 Roo.log("Error - using apply Body without a template");
3121 this.tmpl.overwrite(this.bodyEl, obj);
3124 getChildHeight : function(child_nodes)
3128 child_nodes.length == 0
3133 var child_height = 0;
3135 for(var i = 0; i < child_nodes.length; i++) {
3138 * for modal with tabs...
3139 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3141 var layout_childs = child_nodes[i].childNodes;
3143 for(var j = 0; j < layout_childs.length; j++) {
3145 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3147 var layout_body_childs = layout_childs[j].childNodes;
3149 for(var k = 0; k < layout_body_childs.length; k++) {
3151 if(layout_body_childs[k].classList.contains('navbar')) {
3152 child_height += layout_body_childs[k].offsetHeight;
3156 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3158 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3160 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3162 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3178 child_height += child_nodes[i].offsetHeight;
3179 // Roo.log(child_nodes[i].offsetHeight);
3182 return child_height;
3188 Roo.apply(Roo.bootstrap.Modal, {
3190 * Button config that displays a single OK button
3199 * Button config that displays Yes and No buttons
3215 * Button config that displays OK and Cancel buttons
3230 * Button config that displays Yes, No and Cancel buttons
3254 * messagebox - can be used as a replace
3258 * @class Roo.MessageBox
3259 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3268 // process text value...
3272 // Show a dialog using config options:
3274 title:'Save Changes?',
3275 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276 buttons: Roo.Msg.YESNOCANCEL,
3283 Roo.bootstrap.MessageBox = function(){
3284 var dlg, opt, mask, waitTimer;
3285 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286 var buttons, activeTextEl, bwidth;
3290 var handleButton = function(button){
3292 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3296 var handleHide = function(){
3298 dlg.el.removeClass(opt.cls);
3301 // Roo.TaskMgr.stop(waitTimer);
3302 // waitTimer = null;
3307 var updateButtons = function(b){
3310 buttons["ok"].hide();
3311 buttons["cancel"].hide();
3312 buttons["yes"].hide();
3313 buttons["no"].hide();
3314 //dlg.footer.dom.style.display = 'none';
3317 dlg.footerEl.dom.style.display = '';
3318 for(var k in buttons){
3319 if(typeof buttons[k] != "function"){
3322 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323 width += buttons[k].el.getWidth()+15;
3333 var handleEsc = function(d, k, e){
3334 if(opt && opt.closable !== false){
3344 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345 * @return {Roo.BasicDialog} The BasicDialog element
3347 getDialog : function(){
3349 dlg = new Roo.bootstrap.Modal( {
3352 //constraintoviewport:false,
3354 //collapsible : false,
3359 //buttonAlign:"center",
3360 closeClick : function(){
3361 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3364 handleButton("cancel");
3369 dlg.on("hide", handleHide);
3371 //dlg.addKeyListener(27, handleEsc);
3373 this.buttons = buttons;
3374 var bt = this.buttonText;
3375 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3380 bodyEl = dlg.bodyEl.createChild({
3382 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383 '<textarea class="roo-mb-textarea"></textarea>' +
3384 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3386 msgEl = bodyEl.dom.firstChild;
3387 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388 textboxEl.enableDisplayMode();
3389 textboxEl.addKeyListener([10,13], function(){
3390 if(dlg.isVisible() && opt && opt.buttons){
3393 }else if(opt.buttons.yes){
3394 handleButton("yes");
3398 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399 textareaEl.enableDisplayMode();
3400 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401 progressEl.enableDisplayMode();
3403 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404 var pf = progressEl.dom.firstChild;
3406 pp = Roo.get(pf.firstChild);
3407 pp.setHeight(pf.offsetHeight);
3415 * Updates the message box body text
3416 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417 * the XHTML-compliant non-breaking space character '&#160;')
3418 * @return {Roo.MessageBox} This message box
3420 updateText : function(text)
3422 if(!dlg.isVisible() && !opt.width){
3423 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3426 msgEl.innerHTML = text || ' ';
3428 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3431 Math.min(opt.width || cw , this.maxWidth),
3432 Math.max(opt.minWidth || this.minWidth, bwidth)
3435 activeTextEl.setWidth(w);
3437 if(dlg.isVisible()){
3438 dlg.fixedcenter = false;
3440 // to big, make it scroll. = But as usual stupid IE does not support
3443 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3447 bodyEl.dom.style.height = '';
3448 bodyEl.dom.style.overflowY = '';
3451 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3453 bodyEl.dom.style.overflowX = '';
3456 dlg.setContentSize(w, bodyEl.getHeight());
3457 if(dlg.isVisible()){
3458 dlg.fixedcenter = true;
3464 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3465 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468 * @return {Roo.MessageBox} This message box
3470 updateProgress : function(value, text){
3472 this.updateText(text);
3475 if (pp) { // weird bug on my firefox - for some reason this is not defined
3476 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3483 * Returns true if the message box is currently displayed
3484 * @return {Boolean} True if the message box is visible, else false
3486 isVisible : function(){
3487 return dlg && dlg.isVisible();
3491 * Hides the message box if it is displayed
3494 if(this.isVisible()){
3500 * Displays a new message box, or reinitializes an existing message box, based on the config options
3501 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502 * The following config object properties are supported:
3504 Property Type Description
3505 ---------- --------------- ------------------------------------------------------------------------------------
3506 animEl String/Element An id or Element from which the message box should animate as it opens and
3507 closes (defaults to undefined)
3508 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable Boolean False to hide the top-right close button (defaults to true). Note that
3511 progress and wait dialogs will ignore this property and always hide the
3512 close button as they can only be closed programmatically.
3513 cls String A custom CSS class to apply to the message box element
3514 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3515 displayed (defaults to 75)
3516 fn Function A callback function to execute after closing the dialog. The arguments to the
3517 function will be btn (the name of the button that was clicked, if applicable,
3518 e.g. "ok"), and text (the value of the active text field, if applicable).
3519 Progress and wait dialogs will ignore this option since they do not respond to
3520 user actions and can only be closed programmatically, so any required function
3521 should be called by the same code after it closes the dialog.
3522 icon String A CSS class that provides a background image to be used as an icon for
3523 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3525 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3526 modal Boolean False to allow user interaction with the page while the message box is
3527 displayed (defaults to true)
3528 msg String A string that will replace the existing message box body text (defaults
3529 to the XHTML-compliant non-breaking space character ' ')
3530 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3531 progress Boolean True to display a progress bar (defaults to false)
3532 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3535 title String The title text
3536 value String The string value to set into the active textbox element if displayed
3537 wait Boolean True to display a progress bar (defaults to false)
3538 width Number The width of the dialog in pixels
3545 msg: 'Please enter your address:',
3547 buttons: Roo.MessageBox.OKCANCEL,
3550 animEl: 'addAddressBtn'
3553 * @param {Object} config Configuration options
3554 * @return {Roo.MessageBox} This message box
3556 show : function(options)
3559 // this causes nightmares if you show one dialog after another
3560 // especially on callbacks..
3562 if(this.isVisible()){
3565 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3567 Roo.log("New Dialog Message:" + options.msg )
3568 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3572 var d = this.getDialog();
3574 d.setTitle(opt.title || " ");
3575 d.closeEl.setDisplayed(opt.closable !== false);
3576 activeTextEl = textboxEl;
3577 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3582 textareaEl.setHeight(typeof opt.multiline == "number" ?
3583 opt.multiline : this.defaultTextHeight);
3584 activeTextEl = textareaEl;
3593 progressEl.setDisplayed(opt.progress === true);
3594 this.updateProgress(0);
3595 activeTextEl.dom.value = opt.value || "";
3597 dlg.setDefaultButton(activeTextEl);
3599 var bs = opt.buttons;
3603 }else if(bs && bs.yes){
3604 db = buttons["yes"];
3606 dlg.setDefaultButton(db);
3608 bwidth = updateButtons(opt.buttons);
3609 this.updateText(opt.msg);
3611 d.el.addClass(opt.cls);
3613 d.proxyDrag = opt.proxyDrag === true;
3614 d.modal = opt.modal !== false;
3615 d.mask = opt.modal !== false ? mask : false;
3617 // force it to the end of the z-index stack so it gets a cursor in FF
3618 document.body.appendChild(dlg.el.dom);
3619 d.animateTarget = null;
3620 d.show(options.animEl);
3626 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3627 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628 * and closing the message box when the process is complete.
3629 * @param {String} title The title bar text
3630 * @param {String} msg The message box body text
3631 * @return {Roo.MessageBox} This message box
3633 progress : function(title, msg){
3640 minWidth: this.minProgressWidth,
3647 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648 * If a callback function is passed it will be called after the user clicks the button, and the
3649 * id of the button that was clicked will be passed as the only parameter to the callback
3650 * (could also be the top-right close button).
3651 * @param {String} title The title bar text
3652 * @param {String} msg The message box body text
3653 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654 * @param {Object} scope (optional) The scope of the callback function
3655 * @return {Roo.MessageBox} This message box
3657 alert : function(title, msg, fn, scope)
3672 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3673 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674 * You are responsible for closing the message box when the process is complete.
3675 * @param {String} msg The message box body text
3676 * @param {String} title (optional) The title bar text
3677 * @return {Roo.MessageBox} This message box
3679 wait : function(msg, title){
3690 waitTimer = Roo.TaskMgr.start({
3692 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3700 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703 * @param {String} title The title bar text
3704 * @param {String} msg The message box body text
3705 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706 * @param {Object} scope (optional) The scope of the callback function
3707 * @return {Roo.MessageBox} This message box
3709 confirm : function(title, msg, fn, scope){
3713 buttons: this.YESNO,
3722 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3724 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725 * (could also be the top-right close button) and the text that was entered will be passed as the two
3726 * parameters to the callback.
3727 * @param {String} title The title bar text
3728 * @param {String} msg The message box body text
3729 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730 * @param {Object} scope (optional) The scope of the callback function
3731 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733 * @return {Roo.MessageBox} This message box
3735 prompt : function(title, msg, fn, scope, multiline){
3739 buttons: this.OKCANCEL,
3744 multiline: multiline,
3751 * Button config that displays a single OK button
3756 * Button config that displays Yes and No buttons
3759 YESNO : {yes:true, no:true},
3761 * Button config that displays OK and Cancel buttons
3764 OKCANCEL : {ok:true, cancel:true},
3766 * Button config that displays Yes, No and Cancel buttons
3769 YESNOCANCEL : {yes:true, no:true, cancel:true},
3772 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3775 defaultTextHeight : 75,
3777 * The maximum width in pixels of the message box (defaults to 600)
3782 * The minimum width in pixels of the message box (defaults to 100)
3787 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3788 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3791 minProgressWidth : 250,
3793 * An object containing the default button text strings that can be overriden for localized language support.
3794 * Supported properties are: ok, cancel, yes and no.
3795 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3808 * Shorthand for {@link Roo.MessageBox}
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3820 * @class Roo.bootstrap.Navbar
3821 * @extends Roo.bootstrap.Component
3822 * Bootstrap Navbar class
3825 * Create a new Navbar
3826 * @param {Object} config The config object
3830 Roo.bootstrap.Navbar = function(config){
3831 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3835 * @event beforetoggle
3836 * Fire before toggle the menu
3837 * @param {Roo.EventObject} e
3839 "beforetoggle" : true
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3852 getAutoCreate : function(){
3855 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3859 initEvents :function ()
3861 //Roo.log(this.el.select('.navbar-toggle',true));
3862 this.el.select('.navbar-toggle',true).on('click', function() {
3863 if(this.fireEvent('beforetoggle', this) !== false){
3864 this.el.select('.navbar-collapse',true).toggleClass('in');
3874 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3876 var size = this.el.getSize();
3877 this.maskEl.setSize(size.width, size.height);
3878 this.maskEl.enableDisplayMode("block");
3887 getChildContainer : function()
3889 if (this.el.select('.collapse').getCount()) {
3890 return this.el.select('.collapse',true).first();
3923 * @class Roo.bootstrap.NavSimplebar
3924 * @extends Roo.bootstrap.Navbar
3925 * Bootstrap Sidebar class
3927 * @cfg {Boolean} inverse is inverted color
3929 * @cfg {String} type (nav | pills | tabs)
3930 * @cfg {Boolean} arrangement stacked | justified
3931 * @cfg {String} align (left | right) alignment
3933 * @cfg {Boolean} main (true|false) main nav bar? default false
3934 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3936 * @cfg {String} tag (header|footer|nav|div) default is nav
3938 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3942 * Create a new Sidebar
3943 * @param {Object} config The config object
3947 Roo.bootstrap.NavSimplebar = function(config){
3948 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3951 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3967 getAutoCreate : function(){
3971 tag : this.tag || 'div',
3972 cls : 'navbar navbar-expand-lg'
3974 if (['light','white'].indexOf(this.weight) > -1) {
3975 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3977 cfg.cls += ' bg-' + this.weight;
3989 this.type = this.type || 'nav';
3990 if (['tabs','pills'].indexOf(this.type)!==-1) {
3991 cfg.cn[0].cls += ' nav-' + this.type
3995 if (this.type!=='nav') {
3996 Roo.log('nav type must be nav/tabs/pills')
3998 cfg.cn[0].cls += ' navbar-nav'
4004 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4005 cfg.cn[0].cls += ' nav-' + this.arrangement;
4009 if (this.align === 'right') {
4010 cfg.cn[0].cls += ' navbar-right';
4014 cfg.cls += ' navbar-inverse';
4038 * navbar-expand-md fixed-top
4042 * @class Roo.bootstrap.NavHeaderbar
4043 * @extends Roo.bootstrap.NavSimplebar
4044 * Bootstrap Sidebar class
4046 * @cfg {String} brand what is brand
4047 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4048 * @cfg {String} brand_href href of the brand
4049 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4050 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4051 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4052 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4055 * Create a new Sidebar
4056 * @param {Object} config The config object
4060 Roo.bootstrap.NavHeaderbar = function(config){
4061 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4065 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4072 desktopCenter : false,
4075 getAutoCreate : function(){
4078 tag: this.nav || 'nav',
4079 cls: 'navbar navbar-expand-md',
4085 if (this.desktopCenter) {
4086 cn.push({cls : 'container', cn : []});
4093 cls: 'navbar-header',
4098 cls: 'navbar-toggle navbar-toggler',
4099 'data-toggle': 'collapse',
4104 html: 'Toggle navigation'
4108 cls: 'icon-bar navbar-toggler-icon'
4126 cls: 'collapse navbar-collapse',
4130 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4132 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4133 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4135 // tag can override this..
4137 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4140 if (this.brand !== '') {
4143 href: this.brand_href ? this.brand_href : '#',
4144 cls: 'navbar-brand',
4152 cfg.cls += ' main-nav';
4160 getHeaderChildContainer : function()
4162 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4163 return this.el.select('.navbar-header',true).first();
4166 return this.getChildContainer();
4170 initEvents : function()
4172 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4174 if (this.autohide) {
4179 Roo.get(document).on('scroll',function(e) {
4180 var ns = Roo.get(document).getScroll().top;
4181 var os = prevScroll;
4185 ft.removeClass('slideDown');
4186 ft.addClass('slideUp');
4189 ft.removeClass('slideUp');
4190 ft.addClass('slideDown');
4211 * @class Roo.bootstrap.NavSidebar
4212 * @extends Roo.bootstrap.Navbar
4213 * Bootstrap Sidebar class
4216 * Create a new Sidebar
4217 * @param {Object} config The config object
4221 Roo.bootstrap.NavSidebar = function(config){
4222 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4225 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4227 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4229 getAutoCreate : function(){
4234 cls: 'sidebar sidebar-nav'
4256 * @class Roo.bootstrap.NavGroup
4257 * @extends Roo.bootstrap.Component
4258 * Bootstrap NavGroup class
4259 * @cfg {String} align (left|right)
4260 * @cfg {Boolean} inverse
4261 * @cfg {String} type (nav|pills|tab) default nav
4262 * @cfg {String} navId - reference Id for navbar.
4266 * Create a new nav group
4267 * @param {Object} config The config object
4270 Roo.bootstrap.NavGroup = function(config){
4271 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4274 Roo.bootstrap.NavGroup.register(this);
4278 * Fires when the active item changes
4279 * @param {Roo.bootstrap.NavGroup} this
4280 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4281 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4288 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4299 getAutoCreate : function()
4301 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4308 if (['tabs','pills'].indexOf(this.type)!==-1) {
4309 cfg.cls += ' nav-' + this.type
4311 if (this.type!=='nav') {
4312 Roo.log('nav type must be nav/tabs/pills')
4314 cfg.cls += ' navbar-nav'
4317 if (this.parent() && this.parent().sidebar) {
4320 cls: 'dashboard-menu sidebar-menu'
4326 if (this.form === true) {
4332 if (this.align === 'right') {
4333 cfg.cls += ' navbar-right ml-md-auto';
4335 cfg.cls += ' navbar-left';
4339 if (this.align === 'right') {
4340 cfg.cls += ' navbar-right ml-md-auto';
4342 cfg.cls += ' mr-auto';
4346 cfg.cls += ' navbar-inverse';
4354 * sets the active Navigation item
4355 * @param {Roo.bootstrap.NavItem} the new current navitem
4357 setActiveItem : function(item)
4360 Roo.each(this.navItems, function(v){
4365 v.setActive(false, true);
4372 item.setActive(true, true);
4373 this.fireEvent('changed', this, item, prev);
4378 * gets the active Navigation item
4379 * @return {Roo.bootstrap.NavItem} the current navitem
4381 getActive : function()
4385 Roo.each(this.navItems, function(v){
4396 indexOfNav : function()
4400 Roo.each(this.navItems, function(v,i){
4411 * adds a Navigation item
4412 * @param {Roo.bootstrap.NavItem} the navitem to add
4414 addItem : function(cfg)
4416 var cn = new Roo.bootstrap.NavItem(cfg);
4418 cn.parentId = this.id;
4419 cn.onRender(this.el, null);
4423 * register a Navigation item
4424 * @param {Roo.bootstrap.NavItem} the navitem to add
4426 register : function(item)
4428 this.navItems.push( item);
4429 item.navId = this.navId;
4434 * clear all the Navigation item
4437 clearAll : function()
4440 this.el.dom.innerHTML = '';
4443 getNavItem: function(tabId)
4446 Roo.each(this.navItems, function(e) {
4447 if (e.tabId == tabId) {
4457 setActiveNext : function()
4459 var i = this.indexOfNav(this.getActive());
4460 if (i > this.navItems.length) {
4463 this.setActiveItem(this.navItems[i+1]);
4465 setActivePrev : function()
4467 var i = this.indexOfNav(this.getActive());
4471 this.setActiveItem(this.navItems[i-1]);
4473 clearWasActive : function(except) {
4474 Roo.each(this.navItems, function(e) {
4475 if (e.tabId != except.tabId && e.was_active) {
4476 e.was_active = false;
4483 getWasActive : function ()
4486 Roo.each(this.navItems, function(e) {
4501 Roo.apply(Roo.bootstrap.NavGroup, {
4505 * register a Navigation Group
4506 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4508 register : function(navgrp)
4510 this.groups[navgrp.navId] = navgrp;
4514 * fetch a Navigation Group based on the navigation ID
4515 * @param {string} the navgroup to add
4516 * @returns {Roo.bootstrap.NavGroup} the navgroup
4518 get: function(navId) {
4519 if (typeof(this.groups[navId]) == 'undefined') {
4521 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4523 return this.groups[navId] ;
4538 * @class Roo.bootstrap.NavItem
4539 * @extends Roo.bootstrap.Component
4540 * Bootstrap Navbar.NavItem class
4541 * @cfg {String} href link to
4542 * @cfg {String} html content of button
4543 * @cfg {String} badge text inside badge
4544 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4545 * @cfg {String} glyphicon name of glyphicon
4546 * @cfg {String} icon name of font awesome icon
4547 * @cfg {Boolean} active Is item active
4548 * @cfg {Boolean} disabled Is item disabled
4550 * @cfg {Boolean} preventDefault (true | false) default false
4551 * @cfg {String} tabId the tab that this item activates.
4552 * @cfg {String} tagtype (a|span) render as a href or span?
4553 * @cfg {Boolean} animateRef (true|false) link to element default false
4556 * Create a new Navbar Item
4557 * @param {Object} config The config object
4559 Roo.bootstrap.NavItem = function(config){
4560 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4565 * The raw click event for the entire grid.
4566 * @param {Roo.EventObject} e
4571 * Fires when the active item active state changes
4572 * @param {Roo.bootstrap.NavItem} this
4573 * @param {boolean} state the new state
4579 * Fires when scroll to element
4580 * @param {Roo.bootstrap.NavItem} this
4581 * @param {Object} options
4582 * @param {Roo.EventObject} e
4590 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4598 preventDefault : false,
4605 getAutoCreate : function(){
4614 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4616 if (this.disabled) {
4617 cfg.cls += ' disabled';
4620 if (this.href || this.html || this.glyphicon || this.icon) {
4624 href : this.href || "#",
4625 html: this.html || ''
4628 if (this.tagtype == 'a') {
4629 cfg.cn[0].cls = 'nav-link';
4632 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4635 if(this.glyphicon) {
4636 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4641 cfg.cn[0].html += " <span class='caret'></span>";
4645 if (this.badge !== '') {
4647 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4655 initEvents: function()
4657 if (typeof (this.menu) != 'undefined') {
4658 this.menu.parentType = this.xtype;
4659 this.menu.triggerEl = this.el;
4660 this.menu = this.addxtype(Roo.apply({}, this.menu));
4663 this.el.select('a',true).on('click', this.onClick, this);
4665 if(this.tagtype == 'span'){
4666 this.el.select('span',true).on('click', this.onClick, this);
4669 // at this point parent should be available..
4670 this.parent().register(this);
4673 onClick : function(e)
4675 if (e.getTarget('.dropdown-menu-item')) {
4676 // did you click on a menu itemm.... - then don't trigger onclick..
4681 this.preventDefault ||
4684 Roo.log("NavItem - prevent Default?");
4688 if (this.disabled) {
4692 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4693 if (tg && tg.transition) {
4694 Roo.log("waiting for the transitionend");
4700 //Roo.log("fire event clicked");
4701 if(this.fireEvent('click', this, e) === false){
4705 if(this.tagtype == 'span'){
4709 //Roo.log(this.href);
4710 var ael = this.el.select('a',true).first();
4713 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4714 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4715 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4716 return; // ignore... - it's a 'hash' to another page.
4718 Roo.log("NavItem - prevent Default?");
4720 this.scrollToElement(e);
4724 var p = this.parent();
4726 if (['tabs','pills'].indexOf(p.type)!==-1) {
4727 if (typeof(p.setActiveItem) !== 'undefined') {
4728 p.setActiveItem(this);
4732 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4733 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4734 // remove the collapsed menu expand...
4735 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4739 isActive: function () {
4742 setActive : function(state, fire, is_was_active)
4744 if (this.active && !state && this.navId) {
4745 this.was_active = true;
4746 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4748 nv.clearWasActive(this);
4752 this.active = state;
4755 this.el.removeClass('active');
4756 } else if (!this.el.hasClass('active')) {
4757 this.el.addClass('active');
4760 this.fireEvent('changed', this, state);
4763 // show a panel if it's registered and related..
4765 if (!this.navId || !this.tabId || !state || is_was_active) {
4769 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4773 var pan = tg.getPanelByName(this.tabId);
4777 // if we can not flip to new panel - go back to old nav highlight..
4778 if (false == tg.showPanel(pan)) {
4779 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4781 var onav = nv.getWasActive();
4783 onav.setActive(true, false, true);
4792 // this should not be here...
4793 setDisabled : function(state)
4795 this.disabled = state;
4797 this.el.removeClass('disabled');
4798 } else if (!this.el.hasClass('disabled')) {
4799 this.el.addClass('disabled');
4805 * Fetch the element to display the tooltip on.
4806 * @return {Roo.Element} defaults to this.el
4808 tooltipEl : function()
4810 return this.el.select('' + this.tagtype + '', true).first();
4813 scrollToElement : function(e)
4815 var c = document.body;
4818 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4820 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4821 c = document.documentElement;
4824 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4830 var o = target.calcOffsetsTo(c);
4837 this.fireEvent('scrollto', this, options, e);
4839 Roo.get(c).scrollTo('top', options.value, true);
4852 * <span> icon </span>
4853 * <span> text </span>
4854 * <span>badge </span>
4858 * @class Roo.bootstrap.NavSidebarItem
4859 * @extends Roo.bootstrap.NavItem
4860 * Bootstrap Navbar.NavSidebarItem class
4861 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4862 * {Boolean} open is the menu open
4863 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4864 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4865 * {String} buttonSize (sm|md|lg)the extra classes for the button
4866 * {Boolean} showArrow show arrow next to the text (default true)
4868 * Create a new Navbar Button
4869 * @param {Object} config The config object
4871 Roo.bootstrap.NavSidebarItem = function(config){
4872 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4877 * The raw click event for the entire grid.
4878 * @param {Roo.EventObject} e
4883 * Fires when the active item active state changes
4884 * @param {Roo.bootstrap.NavSidebarItem} this
4885 * @param {boolean} state the new state
4893 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4895 badgeWeight : 'default',
4901 buttonWeight : 'default',
4907 getAutoCreate : function(){
4912 href : this.href || '#',
4918 if(this.buttonView){
4921 href : this.href || '#',
4922 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4935 cfg.cls += ' active';
4938 if (this.disabled) {
4939 cfg.cls += ' disabled';
4942 cfg.cls += ' open x-open';
4945 if (this.glyphicon || this.icon) {
4946 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4947 a.cn.push({ tag : 'i', cls : c }) ;
4950 if(!this.buttonView){
4953 html : this.html || ''
4960 if (this.badge !== '') {
4961 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4967 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4970 a.cls += ' dropdown-toggle treeview' ;
4976 initEvents : function()
4978 if (typeof (this.menu) != 'undefined') {
4979 this.menu.parentType = this.xtype;
4980 this.menu.triggerEl = this.el;
4981 this.menu = this.addxtype(Roo.apply({}, this.menu));
4984 this.el.on('click', this.onClick, this);
4986 if(this.badge !== ''){
4987 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4992 onClick : function(e)
4999 if(this.preventDefault){
5003 this.fireEvent('click', this);
5006 disable : function()
5008 this.setDisabled(true);
5013 this.setDisabled(false);
5016 setDisabled : function(state)
5018 if(this.disabled == state){
5022 this.disabled = state;
5025 this.el.addClass('disabled');
5029 this.el.removeClass('disabled');
5034 setActive : function(state)
5036 if(this.active == state){
5040 this.active = state;
5043 this.el.addClass('active');
5047 this.el.removeClass('active');
5052 isActive: function ()
5057 setBadge : function(str)
5063 this.badgeEl.dom.innerHTML = str;
5080 * @class Roo.bootstrap.Row
5081 * @extends Roo.bootstrap.Component
5082 * Bootstrap Row class (contains columns...)
5086 * @param {Object} config The config object
5089 Roo.bootstrap.Row = function(config){
5090 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5093 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5095 getAutoCreate : function(){
5114 * @class Roo.bootstrap.Element
5115 * @extends Roo.bootstrap.Component
5116 * Bootstrap Element class
5117 * @cfg {String} html contents of the element
5118 * @cfg {String} tag tag of the element
5119 * @cfg {String} cls class of the element
5120 * @cfg {Boolean} preventDefault (true|false) default false
5121 * @cfg {Boolean} clickable (true|false) default false
5124 * Create a new Element
5125 * @param {Object} config The config object
5128 Roo.bootstrap.Element = function(config){
5129 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5135 * When a element is chick
5136 * @param {Roo.bootstrap.Element} this
5137 * @param {Roo.EventObject} e
5143 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5148 preventDefault: false,
5151 getAutoCreate : function(){
5155 // cls: this.cls, double assign in parent class Component.js :: onRender
5162 initEvents: function()
5164 Roo.bootstrap.Element.superclass.initEvents.call(this);
5167 this.el.on('click', this.onClick, this);
5172 onClick : function(e)
5174 if(this.preventDefault){
5178 this.fireEvent('click', this, e);
5181 getValue : function()
5183 return this.el.dom.innerHTML;
5186 setValue : function(value)
5188 this.el.dom.innerHTML = value;
5203 * @class Roo.bootstrap.Pagination
5204 * @extends Roo.bootstrap.Component
5205 * Bootstrap Pagination class
5206 * @cfg {String} size xs | sm | md | lg
5207 * @cfg {Boolean} inverse false | true
5210 * Create a new Pagination
5211 * @param {Object} config The config object
5214 Roo.bootstrap.Pagination = function(config){
5215 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5218 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5224 getAutoCreate : function(){
5230 cfg.cls += ' inverse';
5236 cfg.cls += " " + this.cls;
5254 * @class Roo.bootstrap.PaginationItem
5255 * @extends Roo.bootstrap.Component
5256 * Bootstrap PaginationItem class
5257 * @cfg {String} html text
5258 * @cfg {String} href the link
5259 * @cfg {Boolean} preventDefault (true | false) default true
5260 * @cfg {Boolean} active (true | false) default false
5261 * @cfg {Boolean} disabled default false
5265 * Create a new PaginationItem
5266 * @param {Object} config The config object
5270 Roo.bootstrap.PaginationItem = function(config){
5271 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5276 * The raw click event for the entire grid.
5277 * @param {Roo.EventObject} e
5283 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5287 preventDefault: true,
5292 getAutoCreate : function(){
5298 href : this.href ? this.href : '#',
5299 html : this.html ? this.html : ''
5309 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5313 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5319 initEvents: function() {
5321 this.el.on('click', this.onClick, this);
5324 onClick : function(e)
5326 Roo.log('PaginationItem on click ');
5327 if(this.preventDefault){
5335 this.fireEvent('click', this, e);
5351 * @class Roo.bootstrap.Slider
5352 * @extends Roo.bootstrap.Component
5353 * Bootstrap Slider class
5356 * Create a new Slider
5357 * @param {Object} config The config object
5360 Roo.bootstrap.Slider = function(config){
5361 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5364 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5366 getAutoCreate : function(){
5370 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5374 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5386 * Ext JS Library 1.1.1
5387 * Copyright(c) 2006-2007, Ext JS, LLC.
5389 * Originally Released Under LGPL - original licence link has changed is not relivant.
5392 * <script type="text/javascript">
5397 * @class Roo.grid.ColumnModel
5398 * @extends Roo.util.Observable
5399 * This is the default implementation of a ColumnModel used by the Grid. It defines
5400 * the columns in the grid.
5403 var colModel = new Roo.grid.ColumnModel([
5404 {header: "Ticker", width: 60, sortable: true, locked: true},
5405 {header: "Company Name", width: 150, sortable: true},
5406 {header: "Market Cap.", width: 100, sortable: true},
5407 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5408 {header: "Employees", width: 100, sortable: true, resizable: false}
5413 * The config options listed for this class are options which may appear in each
5414 * individual column definition.
5415 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5417 * @param {Object} config An Array of column config objects. See this class's
5418 * config objects for details.
5420 Roo.grid.ColumnModel = function(config){
5422 * The config passed into the constructor
5424 this.config = config;
5427 // if no id, create one
5428 // if the column does not have a dataIndex mapping,
5429 // map it to the order it is in the config
5430 for(var i = 0, len = config.length; i < len; i++){
5432 if(typeof c.dataIndex == "undefined"){
5435 if(typeof c.renderer == "string"){
5436 c.renderer = Roo.util.Format[c.renderer];
5438 if(typeof c.id == "undefined"){
5441 if(c.editor && c.editor.xtype){
5442 c.editor = Roo.factory(c.editor, Roo.grid);
5444 if(c.editor && c.editor.isFormField){
5445 c.editor = new Roo.grid.GridEditor(c.editor);
5447 this.lookup[c.id] = c;
5451 * The width of columns which have no width specified (defaults to 100)
5454 this.defaultWidth = 100;
5457 * Default sortable of columns which have no sortable specified (defaults to false)
5460 this.defaultSortable = false;
5464 * @event widthchange
5465 * Fires when the width of a column changes.
5466 * @param {ColumnModel} this
5467 * @param {Number} columnIndex The column index
5468 * @param {Number} newWidth The new width
5470 "widthchange": true,
5472 * @event headerchange
5473 * Fires when the text of a header changes.
5474 * @param {ColumnModel} this
5475 * @param {Number} columnIndex The column index
5476 * @param {Number} newText The new header text
5478 "headerchange": true,
5480 * @event hiddenchange
5481 * Fires when a column is hidden or "unhidden".
5482 * @param {ColumnModel} this
5483 * @param {Number} columnIndex The column index
5484 * @param {Boolean} hidden true if hidden, false otherwise
5486 "hiddenchange": true,
5488 * @event columnmoved
5489 * Fires when a column is moved.
5490 * @param {ColumnModel} this
5491 * @param {Number} oldIndex
5492 * @param {Number} newIndex
5494 "columnmoved" : true,
5496 * @event columlockchange
5497 * Fires when a column's locked state is changed
5498 * @param {ColumnModel} this
5499 * @param {Number} colIndex
5500 * @param {Boolean} locked true if locked
5502 "columnlockchange" : true
5504 Roo.grid.ColumnModel.superclass.constructor.call(this);
5506 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5508 * @cfg {String} header The header text to display in the Grid view.
5511 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5512 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5513 * specified, the column's index is used as an index into the Record's data Array.
5516 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5517 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5520 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5521 * Defaults to the value of the {@link #defaultSortable} property.
5522 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5525 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5528 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5531 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5534 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5537 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5538 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5539 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5540 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5543 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5546 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5549 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5552 * @cfg {String} cursor (Optional)
5555 * @cfg {String} tooltip (Optional)
5558 * @cfg {Number} xs (Optional)
5561 * @cfg {Number} sm (Optional)
5564 * @cfg {Number} md (Optional)
5567 * @cfg {Number} lg (Optional)
5570 * Returns the id of the column at the specified index.
5571 * @param {Number} index The column index
5572 * @return {String} the id
5574 getColumnId : function(index){
5575 return this.config[index].id;
5579 * Returns the column for a specified id.
5580 * @param {String} id The column id
5581 * @return {Object} the column
5583 getColumnById : function(id){
5584 return this.lookup[id];
5589 * Returns the column for a specified dataIndex.
5590 * @param {String} dataIndex The column dataIndex
5591 * @return {Object|Boolean} the column or false if not found
5593 getColumnByDataIndex: function(dataIndex){
5594 var index = this.findColumnIndex(dataIndex);
5595 return index > -1 ? this.config[index] : false;
5599 * Returns the index for a specified column id.
5600 * @param {String} id The column id
5601 * @return {Number} the index, or -1 if not found
5603 getIndexById : function(id){
5604 for(var i = 0, len = this.config.length; i < len; i++){
5605 if(this.config[i].id == id){
5613 * Returns the index for a specified column dataIndex.
5614 * @param {String} dataIndex The column dataIndex
5615 * @return {Number} the index, or -1 if not found
5618 findColumnIndex : function(dataIndex){
5619 for(var i = 0, len = this.config.length; i < len; i++){
5620 if(this.config[i].dataIndex == dataIndex){
5628 moveColumn : function(oldIndex, newIndex){
5629 var c = this.config[oldIndex];
5630 this.config.splice(oldIndex, 1);
5631 this.config.splice(newIndex, 0, c);
5632 this.dataMap = null;
5633 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5636 isLocked : function(colIndex){
5637 return this.config[colIndex].locked === true;
5640 setLocked : function(colIndex, value, suppressEvent){
5641 if(this.isLocked(colIndex) == value){
5644 this.config[colIndex].locked = value;
5646 this.fireEvent("columnlockchange", this, colIndex, value);
5650 getTotalLockedWidth : function(){
5652 for(var i = 0; i < this.config.length; i++){
5653 if(this.isLocked(i) && !this.isHidden(i)){
5654 this.totalWidth += this.getColumnWidth(i);
5660 getLockedCount : function(){
5661 for(var i = 0, len = this.config.length; i < len; i++){
5662 if(!this.isLocked(i)){
5667 return this.config.length;
5671 * Returns the number of columns.
5674 getColumnCount : function(visibleOnly){
5675 if(visibleOnly === true){
5677 for(var i = 0, len = this.config.length; i < len; i++){
5678 if(!this.isHidden(i)){
5684 return this.config.length;
5688 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5689 * @param {Function} fn
5690 * @param {Object} scope (optional)
5691 * @return {Array} result
5693 getColumnsBy : function(fn, scope){
5695 for(var i = 0, len = this.config.length; i < len; i++){
5696 var c = this.config[i];
5697 if(fn.call(scope||this, c, i) === true){
5705 * Returns true if the specified column is sortable.
5706 * @param {Number} col The column index
5709 isSortable : function(col){
5710 if(typeof this.config[col].sortable == "undefined"){
5711 return this.defaultSortable;
5713 return this.config[col].sortable;
5717 * Returns the rendering (formatting) function defined for the column.
5718 * @param {Number} col The column index.
5719 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5721 getRenderer : function(col){
5722 if(!this.config[col].renderer){
5723 return Roo.grid.ColumnModel.defaultRenderer;
5725 return this.config[col].renderer;
5729 * Sets the rendering (formatting) function for a column.
5730 * @param {Number} col The column index
5731 * @param {Function} fn The function to use to process the cell's raw data
5732 * to return HTML markup for the grid view. The render function is called with
5733 * the following parameters:<ul>
5734 * <li>Data value.</li>
5735 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5736 * <li>css A CSS style string to apply to the table cell.</li>
5737 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5738 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5739 * <li>Row index</li>
5740 * <li>Column index</li>
5741 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5743 setRenderer : function(col, fn){
5744 this.config[col].renderer = fn;
5748 * Returns the width for the specified column.
5749 * @param {Number} col The column index
5752 getColumnWidth : function(col){
5753 return this.config[col].width * 1 || this.defaultWidth;
5757 * Sets the width for a column.
5758 * @param {Number} col The column index
5759 * @param {Number} width The new width
5761 setColumnWidth : function(col, width, suppressEvent){
5762 this.config[col].width = width;
5763 this.totalWidth = null;
5765 this.fireEvent("widthchange", this, col, width);
5770 * Returns the total width of all columns.
5771 * @param {Boolean} includeHidden True to include hidden column widths
5774 getTotalWidth : function(includeHidden){
5775 if(!this.totalWidth){
5776 this.totalWidth = 0;
5777 for(var i = 0, len = this.config.length; i < len; i++){
5778 if(includeHidden || !this.isHidden(i)){
5779 this.totalWidth += this.getColumnWidth(i);
5783 return this.totalWidth;
5787 * Returns the header for the specified column.
5788 * @param {Number} col The column index
5791 getColumnHeader : function(col){
5792 return this.config[col].header;
5796 * Sets the header for a column.
5797 * @param {Number} col The column index
5798 * @param {String} header The new header
5800 setColumnHeader : function(col, header){
5801 this.config[col].header = header;
5802 this.fireEvent("headerchange", this, col, header);
5806 * Returns the tooltip for the specified column.
5807 * @param {Number} col The column index
5810 getColumnTooltip : function(col){
5811 return this.config[col].tooltip;
5814 * Sets the tooltip for a column.
5815 * @param {Number} col The column index
5816 * @param {String} tooltip The new tooltip
5818 setColumnTooltip : function(col, tooltip){
5819 this.config[col].tooltip = tooltip;
5823 * Returns the dataIndex for the specified column.
5824 * @param {Number} col The column index
5827 getDataIndex : function(col){
5828 return this.config[col].dataIndex;
5832 * Sets the dataIndex for a column.
5833 * @param {Number} col The column index
5834 * @param {Number} dataIndex The new dataIndex
5836 setDataIndex : function(col, dataIndex){
5837 this.config[col].dataIndex = dataIndex;
5843 * Returns true if the cell is editable.
5844 * @param {Number} colIndex The column index
5845 * @param {Number} rowIndex The row index - this is nto actually used..?
5848 isCellEditable : function(colIndex, rowIndex){
5849 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5853 * Returns the editor defined for the cell/column.
5854 * return false or null to disable editing.
5855 * @param {Number} colIndex The column index
5856 * @param {Number} rowIndex The row index
5859 getCellEditor : function(colIndex, rowIndex){
5860 return this.config[colIndex].editor;
5864 * Sets if a column is editable.
5865 * @param {Number} col The column index
5866 * @param {Boolean} editable True if the column is editable
5868 setEditable : function(col, editable){
5869 this.config[col].editable = editable;
5874 * Returns true if the column is hidden.
5875 * @param {Number} colIndex The column index
5878 isHidden : function(colIndex){
5879 return this.config[colIndex].hidden;
5884 * Returns true if the column width cannot be changed
5886 isFixed : function(colIndex){
5887 return this.config[colIndex].fixed;
5891 * Returns true if the column can be resized
5894 isResizable : function(colIndex){
5895 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5898 * Sets if a column is hidden.
5899 * @param {Number} colIndex The column index
5900 * @param {Boolean} hidden True if the column is hidden
5902 setHidden : function(colIndex, hidden){
5903 this.config[colIndex].hidden = hidden;
5904 this.totalWidth = null;
5905 this.fireEvent("hiddenchange", this, colIndex, hidden);
5909 * Sets the editor for a column.
5910 * @param {Number} col The column index
5911 * @param {Object} editor The editor object
5913 setEditor : function(col, editor){
5914 this.config[col].editor = editor;
5918 Roo.grid.ColumnModel.defaultRenderer = function(value)
5920 if(typeof value == "object") {
5923 if(typeof value == "string" && value.length < 1){
5927 return String.format("{0}", value);
5930 // Alias for backwards compatibility
5931 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5934 * Ext JS Library 1.1.1
5935 * Copyright(c) 2006-2007, Ext JS, LLC.
5937 * Originally Released Under LGPL - original licence link has changed is not relivant.
5940 * <script type="text/javascript">
5944 * @class Roo.LoadMask
5945 * A simple utility class for generically masking elements while loading data. If the element being masked has
5946 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5947 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5948 * element's UpdateManager load indicator and will be destroyed after the initial load.
5950 * Create a new LoadMask
5951 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5952 * @param {Object} config The config object
5954 Roo.LoadMask = function(el, config){
5955 this.el = Roo.get(el);
5956 Roo.apply(this, config);
5958 this.store.on('beforeload', this.onBeforeLoad, this);
5959 this.store.on('load', this.onLoad, this);
5960 this.store.on('loadexception', this.onLoadException, this);
5961 this.removeMask = false;
5963 var um = this.el.getUpdateManager();
5964 um.showLoadIndicator = false; // disable the default indicator
5965 um.on('beforeupdate', this.onBeforeLoad, this);
5966 um.on('update', this.onLoad, this);
5967 um.on('failure', this.onLoad, this);
5968 this.removeMask = true;
5972 Roo.LoadMask.prototype = {
5974 * @cfg {Boolean} removeMask
5975 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5976 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5980 * The text to display in a centered loading message box (defaults to 'Loading...')
5984 * @cfg {String} msgCls
5985 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5987 msgCls : 'x-mask-loading',
5990 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5996 * Disables the mask to prevent it from being displayed
5998 disable : function(){
5999 this.disabled = true;
6003 * Enables the mask so that it can be displayed
6005 enable : function(){
6006 this.disabled = false;
6009 onLoadException : function()
6013 if (typeof(arguments[3]) != 'undefined') {
6014 Roo.MessageBox.alert("Error loading",arguments[3]);
6018 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6019 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6026 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6031 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6035 onBeforeLoad : function(){
6037 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6042 destroy : function(){
6044 this.store.un('beforeload', this.onBeforeLoad, this);
6045 this.store.un('load', this.onLoad, this);
6046 this.store.un('loadexception', this.onLoadException, this);
6048 var um = this.el.getUpdateManager();
6049 um.un('beforeupdate', this.onBeforeLoad, this);
6050 um.un('update', this.onLoad, this);
6051 um.un('failure', this.onLoad, this);
6062 * @class Roo.bootstrap.Table
6063 * @extends Roo.bootstrap.Component
6064 * Bootstrap Table class
6065 * @cfg {String} cls table class
6066 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6067 * @cfg {String} bgcolor Specifies the background color for a table
6068 * @cfg {Number} border Specifies whether the table cells should have borders or not
6069 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6070 * @cfg {Number} cellspacing Specifies the space between cells
6071 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6072 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6073 * @cfg {String} sortable Specifies that the table should be sortable
6074 * @cfg {String} summary Specifies a summary of the content of a table
6075 * @cfg {Number} width Specifies the width of a table
6076 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6078 * @cfg {boolean} striped Should the rows be alternative striped
6079 * @cfg {boolean} bordered Add borders to the table
6080 * @cfg {boolean} hover Add hover highlighting
6081 * @cfg {boolean} condensed Format condensed
6082 * @cfg {boolean} responsive Format condensed
6083 * @cfg {Boolean} loadMask (true|false) default false
6084 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6085 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6086 * @cfg {Boolean} rowSelection (true|false) default false
6087 * @cfg {Boolean} cellSelection (true|false) default false
6088 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6089 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6090 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6091 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6095 * Create a new Table
6096 * @param {Object} config The config object
6099 Roo.bootstrap.Table = function(config){
6100 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6105 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6106 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6107 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6108 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6110 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6112 this.sm.grid = this;
6113 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6114 this.sm = this.selModel;
6115 this.sm.xmodule = this.xmodule || false;
6118 if (this.cm && typeof(this.cm.config) == 'undefined') {
6119 this.colModel = new Roo.grid.ColumnModel(this.cm);
6120 this.cm = this.colModel;
6121 this.cm.xmodule = this.xmodule || false;
6124 this.store= Roo.factory(this.store, Roo.data);
6125 this.ds = this.store;
6126 this.ds.xmodule = this.xmodule || false;
6129 if (this.footer && this.store) {
6130 this.footer.dataSource = this.ds;
6131 this.footer = Roo.factory(this.footer);
6138 * Fires when a cell is clicked
6139 * @param {Roo.bootstrap.Table} this
6140 * @param {Roo.Element} el
6141 * @param {Number} rowIndex
6142 * @param {Number} columnIndex
6143 * @param {Roo.EventObject} e
6147 * @event celldblclick
6148 * Fires when a cell is double clicked
6149 * @param {Roo.bootstrap.Table} this
6150 * @param {Roo.Element} el
6151 * @param {Number} rowIndex
6152 * @param {Number} columnIndex
6153 * @param {Roo.EventObject} e
6155 "celldblclick" : true,
6158 * Fires when a row is clicked
6159 * @param {Roo.bootstrap.Table} this
6160 * @param {Roo.Element} el
6161 * @param {Number} rowIndex
6162 * @param {Roo.EventObject} e
6166 * @event rowdblclick
6167 * Fires when a row is double clicked
6168 * @param {Roo.bootstrap.Table} this
6169 * @param {Roo.Element} el
6170 * @param {Number} rowIndex
6171 * @param {Roo.EventObject} e
6173 "rowdblclick" : true,
6176 * Fires when a mouseover occur
6177 * @param {Roo.bootstrap.Table} this
6178 * @param {Roo.Element} el
6179 * @param {Number} rowIndex
6180 * @param {Number} columnIndex
6181 * @param {Roo.EventObject} e
6186 * Fires when a mouseout occur
6187 * @param {Roo.bootstrap.Table} this
6188 * @param {Roo.Element} el
6189 * @param {Number} rowIndex
6190 * @param {Number} columnIndex
6191 * @param {Roo.EventObject} e
6196 * Fires when a row is rendered, so you can change add a style to it.
6197 * @param {Roo.bootstrap.Table} this
6198 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6202 * @event rowsrendered
6203 * Fires when all the rows have been rendered
6204 * @param {Roo.bootstrap.Table} this
6206 'rowsrendered' : true,
6208 * @event contextmenu
6209 * The raw contextmenu event for the entire grid.
6210 * @param {Roo.EventObject} e
6212 "contextmenu" : true,
6214 * @event rowcontextmenu
6215 * Fires when a row is right clicked
6216 * @param {Roo.bootstrap.Table} this
6217 * @param {Number} rowIndex
6218 * @param {Roo.EventObject} e
6220 "rowcontextmenu" : true,
6222 * @event cellcontextmenu
6223 * Fires when a cell is right clicked
6224 * @param {Roo.bootstrap.Table} this
6225 * @param {Number} rowIndex
6226 * @param {Number} cellIndex
6227 * @param {Roo.EventObject} e
6229 "cellcontextmenu" : true,
6231 * @event headercontextmenu
6232 * Fires when a header is right clicked
6233 * @param {Roo.bootstrap.Table} this
6234 * @param {Number} columnIndex
6235 * @param {Roo.EventObject} e
6237 "headercontextmenu" : true
6241 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6267 rowSelection : false,
6268 cellSelection : false,
6271 // Roo.Element - the tbody
6273 // Roo.Element - thead element
6276 container: false, // used by gridpanel...
6282 auto_hide_footer : false,
6284 getAutoCreate : function()
6286 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6293 if (this.scrollBody) {
6294 cfg.cls += ' table-body-fixed';
6297 cfg.cls += ' table-striped';
6301 cfg.cls += ' table-hover';
6303 if (this.bordered) {
6304 cfg.cls += ' table-bordered';
6306 if (this.condensed) {
6307 cfg.cls += ' table-condensed';
6309 if (this.responsive) {
6310 cfg.cls += ' table-responsive';
6314 cfg.cls+= ' ' +this.cls;
6317 // this lot should be simplifed...
6330 ].forEach(function(k) {
6338 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6341 if(this.store || this.cm){
6342 if(this.headerShow){
6343 cfg.cn.push(this.renderHeader());
6346 cfg.cn.push(this.renderBody());
6348 if(this.footerShow){
6349 cfg.cn.push(this.renderFooter());
6351 // where does this come from?
6352 //cfg.cls+= ' TableGrid';
6355 return { cn : [ cfg ] };
6358 initEvents : function()
6360 if(!this.store || !this.cm){
6363 if (this.selModel) {
6364 this.selModel.initEvents();
6368 //Roo.log('initEvents with ds!!!!');
6370 this.mainBody = this.el.select('tbody', true).first();
6371 this.mainHead = this.el.select('thead', true).first();
6372 this.mainFoot = this.el.select('tfoot', true).first();
6378 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6379 e.on('click', _this.sort, _this);
6382 this.mainBody.on("click", this.onClick, this);
6383 this.mainBody.on("dblclick", this.onDblClick, this);
6385 // why is this done????? = it breaks dialogs??
6386 //this.parent().el.setStyle('position', 'relative');
6390 this.footer.parentId = this.id;
6391 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6394 this.el.select('tfoot tr td').first().addClass('hide');
6399 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6402 this.store.on('load', this.onLoad, this);
6403 this.store.on('beforeload', this.onBeforeLoad, this);
6404 this.store.on('update', this.onUpdate, this);
6405 this.store.on('add', this.onAdd, this);
6406 this.store.on("clear", this.clear, this);
6408 this.el.on("contextmenu", this.onContextMenu, this);
6410 this.mainBody.on('scroll', this.onBodyScroll, this);
6412 this.cm.on("headerchange", this.onHeaderChange, this);
6414 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6418 onContextMenu : function(e, t)
6420 this.processEvent("contextmenu", e);
6423 processEvent : function(name, e)
6425 if (name != 'touchstart' ) {
6426 this.fireEvent(name, e);
6429 var t = e.getTarget();
6431 var cell = Roo.get(t);
6437 if(cell.findParent('tfoot', false, true)){
6441 if(cell.findParent('thead', false, true)){
6443 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6444 cell = Roo.get(t).findParent('th', false, true);
6446 Roo.log("failed to find th in thead?");
6447 Roo.log(e.getTarget());
6452 var cellIndex = cell.dom.cellIndex;
6454 var ename = name == 'touchstart' ? 'click' : name;
6455 this.fireEvent("header" + ename, this, cellIndex, e);
6460 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6461 cell = Roo.get(t).findParent('td', false, true);
6463 Roo.log("failed to find th in tbody?");
6464 Roo.log(e.getTarget());
6469 var row = cell.findParent('tr', false, true);
6470 var cellIndex = cell.dom.cellIndex;
6471 var rowIndex = row.dom.rowIndex - 1;
6475 this.fireEvent("row" + name, this, rowIndex, e);
6479 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6485 onMouseover : function(e, el)
6487 var cell = Roo.get(el);
6493 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6494 cell = cell.findParent('td', false, true);
6497 var row = cell.findParent('tr', false, true);
6498 var cellIndex = cell.dom.cellIndex;
6499 var rowIndex = row.dom.rowIndex - 1; // start from 0
6501 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6505 onMouseout : function(e, el)
6507 var cell = Roo.get(el);
6513 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6514 cell = cell.findParent('td', false, true);
6517 var row = cell.findParent('tr', false, true);
6518 var cellIndex = cell.dom.cellIndex;
6519 var rowIndex = row.dom.rowIndex - 1; // start from 0
6521 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6525 onClick : function(e, el)
6527 var cell = Roo.get(el);
6529 if(!cell || (!this.cellSelection && !this.rowSelection)){
6533 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6534 cell = cell.findParent('td', false, true);
6537 if(!cell || typeof(cell) == 'undefined'){
6541 var row = cell.findParent('tr', false, true);
6543 if(!row || typeof(row) == 'undefined'){
6547 var cellIndex = cell.dom.cellIndex;
6548 var rowIndex = this.getRowIndex(row);
6550 // why??? - should these not be based on SelectionModel?
6551 if(this.cellSelection){
6552 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6555 if(this.rowSelection){
6556 this.fireEvent('rowclick', this, row, rowIndex, e);
6562 onDblClick : function(e,el)
6564 var cell = Roo.get(el);
6566 if(!cell || (!this.cellSelection && !this.rowSelection)){
6570 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6571 cell = cell.findParent('td', false, true);
6574 if(!cell || typeof(cell) == 'undefined'){
6578 var row = cell.findParent('tr', false, true);
6580 if(!row || typeof(row) == 'undefined'){
6584 var cellIndex = cell.dom.cellIndex;
6585 var rowIndex = this.getRowIndex(row);
6587 if(this.cellSelection){
6588 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6591 if(this.rowSelection){
6592 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6596 sort : function(e,el)
6598 var col = Roo.get(el);
6600 if(!col.hasClass('sortable')){
6604 var sort = col.attr('sort');
6607 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6611 this.store.sortInfo = {field : sort, direction : dir};
6614 Roo.log("calling footer first");
6615 this.footer.onClick('first');
6618 this.store.load({ params : { start : 0 } });
6622 renderHeader : function()
6630 this.totalWidth = 0;
6632 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6634 var config = cm.config[i];
6638 cls : 'x-hcol-' + i,
6640 html: cm.getColumnHeader(i)
6645 if(typeof(config.sortable) != 'undefined' && config.sortable){
6647 c.html = '<i class="glyphicon"></i>' + c.html;
6650 if(typeof(config.lgHeader) != 'undefined'){
6651 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6654 if(typeof(config.mdHeader) != 'undefined'){
6655 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6658 if(typeof(config.smHeader) != 'undefined'){
6659 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6662 if(typeof(config.xsHeader) != 'undefined'){
6663 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6670 if(typeof(config.tooltip) != 'undefined'){
6671 c.tooltip = config.tooltip;
6674 if(typeof(config.colspan) != 'undefined'){
6675 c.colspan = config.colspan;
6678 if(typeof(config.hidden) != 'undefined' && config.hidden){
6679 c.style += ' display:none;';
6682 if(typeof(config.dataIndex) != 'undefined'){
6683 c.sort = config.dataIndex;
6688 if(typeof(config.align) != 'undefined' && config.align.length){
6689 c.style += ' text-align:' + config.align + ';';
6692 if(typeof(config.width) != 'undefined'){
6693 c.style += ' width:' + config.width + 'px;';
6694 this.totalWidth += config.width;
6696 this.totalWidth += 100; // assume minimum of 100 per column?
6699 if(typeof(config.cls) != 'undefined'){
6700 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6703 ['xs','sm','md','lg'].map(function(size){
6705 if(typeof(config[size]) == 'undefined'){
6709 if (!config[size]) { // 0 = hidden
6710 c.cls += ' hidden-' + size;
6714 c.cls += ' col-' + size + '-' + config[size];
6724 renderBody : function()
6734 colspan : this.cm.getColumnCount()
6744 renderFooter : function()
6754 colspan : this.cm.getColumnCount()
6768 // Roo.log('ds onload');
6773 var ds = this.store;
6775 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6776 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6777 if (_this.store.sortInfo) {
6779 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6780 e.select('i', true).addClass(['glyphicon-arrow-up']);
6783 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6784 e.select('i', true).addClass(['glyphicon-arrow-down']);
6789 var tbody = this.mainBody;
6791 if(ds.getCount() > 0){
6792 ds.data.each(function(d,rowIndex){
6793 var row = this.renderRow(cm, ds, rowIndex);
6795 tbody.createChild(row);
6799 if(row.cellObjects.length){
6800 Roo.each(row.cellObjects, function(r){
6801 _this.renderCellObject(r);
6808 var tfoot = this.el.select('tfoot', true).first();
6810 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6812 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6814 var total = this.ds.getTotalCount();
6816 if(this.footer.pageSize < total){
6817 this.mainFoot.show();
6821 Roo.each(this.el.select('tbody td', true).elements, function(e){
6822 e.on('mouseover', _this.onMouseover, _this);
6825 Roo.each(this.el.select('tbody td', true).elements, function(e){
6826 e.on('mouseout', _this.onMouseout, _this);
6828 this.fireEvent('rowsrendered', this);
6834 onUpdate : function(ds,record)
6836 this.refreshRow(record);
6840 onRemove : function(ds, record, index, isUpdate){
6841 if(isUpdate !== true){
6842 this.fireEvent("beforerowremoved", this, index, record);
6844 var bt = this.mainBody.dom;
6846 var rows = this.el.select('tbody > tr', true).elements;
6848 if(typeof(rows[index]) != 'undefined'){
6849 bt.removeChild(rows[index].dom);
6852 // if(bt.rows[index]){
6853 // bt.removeChild(bt.rows[index]);
6856 if(isUpdate !== true){
6857 //this.stripeRows(index);
6858 //this.syncRowHeights(index, index);
6860 this.fireEvent("rowremoved", this, index, record);
6864 onAdd : function(ds, records, rowIndex)
6866 //Roo.log('on Add called');
6867 // - note this does not handle multiple adding very well..
6868 var bt = this.mainBody.dom;
6869 for (var i =0 ; i < records.length;i++) {
6870 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6871 //Roo.log(records[i]);
6872 //Roo.log(this.store.getAt(rowIndex+i));
6873 this.insertRow(this.store, rowIndex + i, false);
6880 refreshRow : function(record){
6881 var ds = this.store, index;
6882 if(typeof record == 'number'){
6884 record = ds.getAt(index);
6886 index = ds.indexOf(record);
6888 this.insertRow(ds, index, true);
6890 this.onRemove(ds, record, index+1, true);
6892 //this.syncRowHeights(index, index);
6894 this.fireEvent("rowupdated", this, index, record);
6897 insertRow : function(dm, rowIndex, isUpdate){
6900 this.fireEvent("beforerowsinserted", this, rowIndex);
6902 //var s = this.getScrollState();
6903 var row = this.renderRow(this.cm, this.store, rowIndex);
6904 // insert before rowIndex..
6905 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6909 if(row.cellObjects.length){
6910 Roo.each(row.cellObjects, function(r){
6911 _this.renderCellObject(r);
6916 this.fireEvent("rowsinserted", this, rowIndex);
6917 //this.syncRowHeights(firstRow, lastRow);
6918 //this.stripeRows(firstRow);
6925 getRowDom : function(rowIndex)
6927 var rows = this.el.select('tbody > tr', true).elements;
6929 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6932 // returns the object tree for a tr..
6935 renderRow : function(cm, ds, rowIndex)
6937 var d = ds.getAt(rowIndex);
6941 cls : 'x-row-' + rowIndex,
6945 var cellObjects = [];
6947 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6948 var config = cm.config[i];
6950 var renderer = cm.getRenderer(i);
6954 if(typeof(renderer) !== 'undefined'){
6955 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6957 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6958 // and are rendered into the cells after the row is rendered - using the id for the element.
6960 if(typeof(value) === 'object'){
6970 rowIndex : rowIndex,
6975 this.fireEvent('rowclass', this, rowcfg);
6979 cls : rowcfg.rowClass + ' x-col-' + i,
6981 html: (typeof(value) === 'object') ? '' : value
6988 if(typeof(config.colspan) != 'undefined'){
6989 td.colspan = config.colspan;
6992 if(typeof(config.hidden) != 'undefined' && config.hidden){
6993 td.style += ' display:none;';
6996 if(typeof(config.align) != 'undefined' && config.align.length){
6997 td.style += ' text-align:' + config.align + ';';
6999 if(typeof(config.valign) != 'undefined' && config.valign.length){
7000 td.style += ' vertical-align:' + config.valign + ';';
7003 if(typeof(config.width) != 'undefined'){
7004 td.style += ' width:' + config.width + 'px;';
7007 if(typeof(config.cursor) != 'undefined'){
7008 td.style += ' cursor:' + config.cursor + ';';
7011 if(typeof(config.cls) != 'undefined'){
7012 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7015 ['xs','sm','md','lg'].map(function(size){
7017 if(typeof(config[size]) == 'undefined'){
7021 if (!config[size]) { // 0 = hidden
7022 td.cls += ' hidden-' + size;
7026 td.cls += ' col-' + size + '-' + config[size];
7034 row.cellObjects = cellObjects;
7042 onBeforeLoad : function()
7051 this.el.select('tbody', true).first().dom.innerHTML = '';
7054 * Show or hide a row.
7055 * @param {Number} rowIndex to show or hide
7056 * @param {Boolean} state hide
7058 setRowVisibility : function(rowIndex, state)
7060 var bt = this.mainBody.dom;
7062 var rows = this.el.select('tbody > tr', true).elements;
7064 if(typeof(rows[rowIndex]) == 'undefined'){
7067 rows[rowIndex].dom.style.display = state ? '' : 'none';
7071 getSelectionModel : function(){
7073 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7075 return this.selModel;
7078 * Render the Roo.bootstrap object from renderder
7080 renderCellObject : function(r)
7084 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7086 var t = r.cfg.render(r.container);
7089 Roo.each(r.cfg.cn, function(c){
7091 container: t.getChildContainer(),
7094 _this.renderCellObject(child);
7099 getRowIndex : function(row)
7103 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7114 * Returns the grid's underlying element = used by panel.Grid
7115 * @return {Element} The element
7117 getGridEl : function(){
7121 * Forces a resize - used by panel.Grid
7122 * @return {Element} The element
7124 autoSize : function()
7126 //var ctr = Roo.get(this.container.dom.parentElement);
7127 var ctr = Roo.get(this.el.dom);
7129 var thd = this.getGridEl().select('thead',true).first();
7130 var tbd = this.getGridEl().select('tbody', true).first();
7131 var tfd = this.getGridEl().select('tfoot', true).first();
7133 var cw = ctr.getWidth();
7137 tbd.setSize(ctr.getWidth(),
7138 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7140 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7143 cw = Math.max(cw, this.totalWidth);
7144 this.getGridEl().select('tr',true).setWidth(cw);
7145 // resize 'expandable coloumn?
7147 return; // we doe not have a view in this design..
7150 onBodyScroll: function()
7152 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7154 this.mainHead.setStyle({
7155 'position' : 'relative',
7156 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7162 var scrollHeight = this.mainBody.dom.scrollHeight;
7164 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7166 var height = this.mainBody.getHeight();
7168 if(scrollHeight - height == scrollTop) {
7170 var total = this.ds.getTotalCount();
7172 if(this.footer.cursor + this.footer.pageSize < total){
7174 this.footer.ds.load({
7176 start : this.footer.cursor + this.footer.pageSize,
7177 limit : this.footer.pageSize
7187 onHeaderChange : function()
7189 var header = this.renderHeader();
7190 var table = this.el.select('table', true).first();
7192 this.mainHead.remove();
7193 this.mainHead = table.createChild(header, this.mainBody, false);
7196 onHiddenChange : function(colModel, colIndex, hidden)
7198 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7199 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7201 this.CSS.updateRule(thSelector, "display", "");
7202 this.CSS.updateRule(tdSelector, "display", "");
7205 this.CSS.updateRule(thSelector, "display", "none");
7206 this.CSS.updateRule(tdSelector, "display", "none");
7209 this.onHeaderChange();
7213 setColumnWidth: function(col_index, width)
7215 // width = "md-2 xs-2..."
7216 if(!this.colModel.config[col_index]) {
7220 var w = width.split(" ");
7222 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7224 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7227 for(var j = 0; j < w.length; j++) {
7233 var size_cls = w[j].split("-");
7235 if(!Number.isInteger(size_cls[1] * 1)) {
7239 if(!this.colModel.config[col_index][size_cls[0]]) {
7243 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7247 h_row[0].classList.replace(
7248 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7249 "col-"+size_cls[0]+"-"+size_cls[1]
7252 for(var i = 0; i < rows.length; i++) {
7254 var size_cls = w[j].split("-");
7256 if(!Number.isInteger(size_cls[1] * 1)) {
7260 if(!this.colModel.config[col_index][size_cls[0]]) {
7264 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7268 rows[i].classList.replace(
7269 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7270 "col-"+size_cls[0]+"-"+size_cls[1]
7274 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7289 * @class Roo.bootstrap.TableCell
7290 * @extends Roo.bootstrap.Component
7291 * Bootstrap TableCell class
7292 * @cfg {String} html cell contain text
7293 * @cfg {String} cls cell class
7294 * @cfg {String} tag cell tag (td|th) default td
7295 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7296 * @cfg {String} align Aligns the content in a cell
7297 * @cfg {String} axis Categorizes cells
7298 * @cfg {String} bgcolor Specifies the background color of a cell
7299 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7300 * @cfg {Number} colspan Specifies the number of columns a cell should span
7301 * @cfg {String} headers Specifies one or more header cells a cell is related to
7302 * @cfg {Number} height Sets the height of a cell
7303 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7304 * @cfg {Number} rowspan Sets the number of rows a cell should span
7305 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7306 * @cfg {String} valign Vertical aligns the content in a cell
7307 * @cfg {Number} width Specifies the width of a cell
7310 * Create a new TableCell
7311 * @param {Object} config The config object
7314 Roo.bootstrap.TableCell = function(config){
7315 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7318 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7338 getAutoCreate : function(){
7339 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7359 cfg.align=this.align
7365 cfg.bgcolor=this.bgcolor
7368 cfg.charoff=this.charoff
7371 cfg.colspan=this.colspan
7374 cfg.headers=this.headers
7377 cfg.height=this.height
7380 cfg.nowrap=this.nowrap
7383 cfg.rowspan=this.rowspan
7386 cfg.scope=this.scope
7389 cfg.valign=this.valign
7392 cfg.width=this.width
7411 * @class Roo.bootstrap.TableRow
7412 * @extends Roo.bootstrap.Component
7413 * Bootstrap TableRow class
7414 * @cfg {String} cls row class
7415 * @cfg {String} align Aligns the content in a table row
7416 * @cfg {String} bgcolor Specifies a background color for a table row
7417 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7418 * @cfg {String} valign Vertical aligns the content in a table row
7421 * Create a new TableRow
7422 * @param {Object} config The config object
7425 Roo.bootstrap.TableRow = function(config){
7426 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7429 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7437 getAutoCreate : function(){
7438 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7448 cfg.align = this.align;
7451 cfg.bgcolor = this.bgcolor;
7454 cfg.charoff = this.charoff;
7457 cfg.valign = this.valign;
7475 * @class Roo.bootstrap.TableBody
7476 * @extends Roo.bootstrap.Component
7477 * Bootstrap TableBody class
7478 * @cfg {String} cls element class
7479 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7480 * @cfg {String} align Aligns the content inside the element
7481 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7482 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7485 * Create a new TableBody
7486 * @param {Object} config The config object
7489 Roo.bootstrap.TableBody = function(config){
7490 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7493 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7501 getAutoCreate : function(){
7502 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7516 cfg.align = this.align;
7519 cfg.charoff = this.charoff;
7522 cfg.valign = this.valign;
7529 // initEvents : function()
7536 // this.store = Roo.factory(this.store, Roo.data);
7537 // this.store.on('load', this.onLoad, this);
7539 // this.store.load();
7543 // onLoad: function ()
7545 // this.fireEvent('load', this);
7555 * Ext JS Library 1.1.1
7556 * Copyright(c) 2006-2007, Ext JS, LLC.
7558 * Originally Released Under LGPL - original licence link has changed is not relivant.
7561 * <script type="text/javascript">
7564 // as we use this in bootstrap.
7565 Roo.namespace('Roo.form');
7567 * @class Roo.form.Action
7568 * Internal Class used to handle form actions
7570 * @param {Roo.form.BasicForm} el The form element or its id
7571 * @param {Object} config Configuration options
7576 // define the action interface
7577 Roo.form.Action = function(form, options){
7579 this.options = options || {};
7582 * Client Validation Failed
7585 Roo.form.Action.CLIENT_INVALID = 'client';
7587 * Server Validation Failed
7590 Roo.form.Action.SERVER_INVALID = 'server';
7592 * Connect to Server Failed
7595 Roo.form.Action.CONNECT_FAILURE = 'connect';
7597 * Reading Data from Server Failed
7600 Roo.form.Action.LOAD_FAILURE = 'load';
7602 Roo.form.Action.prototype = {
7604 failureType : undefined,
7605 response : undefined,
7609 run : function(options){
7614 success : function(response){
7619 handleResponse : function(response){
7623 // default connection failure
7624 failure : function(response){
7626 this.response = response;
7627 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7628 this.form.afterAction(this, false);
7631 processResponse : function(response){
7632 this.response = response;
7633 if(!response.responseText){
7636 this.result = this.handleResponse(response);
7640 // utility functions used internally
7641 getUrl : function(appendParams){
7642 var url = this.options.url || this.form.url || this.form.el.dom.action;
7644 var p = this.getParams();
7646 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7652 getMethod : function(){
7653 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7656 getParams : function(){
7657 var bp = this.form.baseParams;
7658 var p = this.options.params;
7660 if(typeof p == "object"){
7661 p = Roo.urlEncode(Roo.applyIf(p, bp));
7662 }else if(typeof p == 'string' && bp){
7663 p += '&' + Roo.urlEncode(bp);
7666 p = Roo.urlEncode(bp);
7671 createCallback : function(){
7673 success: this.success,
7674 failure: this.failure,
7676 timeout: (this.form.timeout*1000),
7677 upload: this.form.fileUpload ? this.success : undefined
7682 Roo.form.Action.Submit = function(form, options){
7683 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7686 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7689 haveProgress : false,
7690 uploadComplete : false,
7692 // uploadProgress indicator.
7693 uploadProgress : function()
7695 if (!this.form.progressUrl) {
7699 if (!this.haveProgress) {
7700 Roo.MessageBox.progress("Uploading", "Uploading");
7702 if (this.uploadComplete) {
7703 Roo.MessageBox.hide();
7707 this.haveProgress = true;
7709 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7711 var c = new Roo.data.Connection();
7713 url : this.form.progressUrl,
7718 success : function(req){
7719 //console.log(data);
7723 rdata = Roo.decode(req.responseText)
7725 Roo.log("Invalid data from server..");
7729 if (!rdata || !rdata.success) {
7731 Roo.MessageBox.alert(Roo.encode(rdata));
7734 var data = rdata.data;
7736 if (this.uploadComplete) {
7737 Roo.MessageBox.hide();
7742 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7743 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7746 this.uploadProgress.defer(2000,this);
7749 failure: function(data) {
7750 Roo.log('progress url failed ');
7761 // run get Values on the form, so it syncs any secondary forms.
7762 this.form.getValues();
7764 var o = this.options;
7765 var method = this.getMethod();
7766 var isPost = method == 'POST';
7767 if(o.clientValidation === false || this.form.isValid()){
7769 if (this.form.progressUrl) {
7770 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7771 (new Date() * 1) + '' + Math.random());
7776 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7777 form:this.form.el.dom,
7778 url:this.getUrl(!isPost),
7780 params:isPost ? this.getParams() : null,
7781 isUpload: this.form.fileUpload
7784 this.uploadProgress();
7786 }else if (o.clientValidation !== false){ // client validation failed
7787 this.failureType = Roo.form.Action.CLIENT_INVALID;
7788 this.form.afterAction(this, false);
7792 success : function(response)
7794 this.uploadComplete= true;
7795 if (this.haveProgress) {
7796 Roo.MessageBox.hide();
7800 var result = this.processResponse(response);
7801 if(result === true || result.success){
7802 this.form.afterAction(this, true);
7806 this.form.markInvalid(result.errors);
7807 this.failureType = Roo.form.Action.SERVER_INVALID;
7809 this.form.afterAction(this, false);
7811 failure : function(response)
7813 this.uploadComplete= true;
7814 if (this.haveProgress) {
7815 Roo.MessageBox.hide();
7818 this.response = response;
7819 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7820 this.form.afterAction(this, false);
7823 handleResponse : function(response){
7824 if(this.form.errorReader){
7825 var rs = this.form.errorReader.read(response);
7828 for(var i = 0, len = rs.records.length; i < len; i++) {
7829 var r = rs.records[i];
7833 if(errors.length < 1){
7837 success : rs.success,
7843 ret = Roo.decode(response.responseText);
7847 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7857 Roo.form.Action.Load = function(form, options){
7858 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7859 this.reader = this.form.reader;
7862 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7867 Roo.Ajax.request(Roo.apply(
7868 this.createCallback(), {
7869 method:this.getMethod(),
7870 url:this.getUrl(false),
7871 params:this.getParams()
7875 success : function(response){
7877 var result = this.processResponse(response);
7878 if(result === true || !result.success || !result.data){
7879 this.failureType = Roo.form.Action.LOAD_FAILURE;
7880 this.form.afterAction(this, false);
7883 this.form.clearInvalid();
7884 this.form.setValues(result.data);
7885 this.form.afterAction(this, true);
7888 handleResponse : function(response){
7889 if(this.form.reader){
7890 var rs = this.form.reader.read(response);
7891 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7893 success : rs.success,
7897 return Roo.decode(response.responseText);
7901 Roo.form.Action.ACTION_TYPES = {
7902 'load' : Roo.form.Action.Load,
7903 'submit' : Roo.form.Action.Submit
7912 * @class Roo.bootstrap.Form
7913 * @extends Roo.bootstrap.Component
7914 * Bootstrap Form class
7915 * @cfg {String} method GET | POST (default POST)
7916 * @cfg {String} labelAlign top | left (default top)
7917 * @cfg {String} align left | right - for navbars
7918 * @cfg {Boolean} loadMask load mask when submit (default true)
7923 * @param {Object} config The config object
7927 Roo.bootstrap.Form = function(config){
7929 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7931 Roo.bootstrap.Form.popover.apply();
7935 * @event clientvalidation
7936 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7937 * @param {Form} this
7938 * @param {Boolean} valid true if the form has passed client-side validation
7940 clientvalidation: true,
7942 * @event beforeaction
7943 * Fires before any action is performed. Return false to cancel the action.
7944 * @param {Form} this
7945 * @param {Action} action The action to be performed
7949 * @event actionfailed
7950 * Fires when an action fails.
7951 * @param {Form} this
7952 * @param {Action} action The action that failed
7954 actionfailed : true,
7956 * @event actioncomplete
7957 * Fires when an action is completed.
7958 * @param {Form} this
7959 * @param {Action} action The action that completed
7961 actioncomplete : true
7965 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7968 * @cfg {String} method
7969 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7974 * The URL to use for form actions if one isn't supplied in the action options.
7977 * @cfg {Boolean} fileUpload
7978 * Set to true if this form is a file upload.
7982 * @cfg {Object} baseParams
7983 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7987 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7991 * @cfg {Sting} align (left|right) for navbar forms
7996 activeAction : null,
7999 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8000 * element by passing it or its id or mask the form itself by passing in true.
8003 waitMsgTarget : false,
8008 * @cfg {Boolean} errorMask (true|false) default false
8013 * @cfg {Number} maskOffset Default 100
8018 * @cfg {Boolean} maskBody
8022 getAutoCreate : function(){
8026 method : this.method || 'POST',
8027 id : this.id || Roo.id(),
8030 if (this.parent().xtype.match(/^Nav/)) {
8031 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8035 if (this.labelAlign == 'left' ) {
8036 cfg.cls += ' form-horizontal';
8042 initEvents : function()
8044 this.el.on('submit', this.onSubmit, this);
8045 // this was added as random key presses on the form where triggering form submit.
8046 this.el.on('keypress', function(e) {
8047 if (e.getCharCode() != 13) {
8050 // we might need to allow it for textareas.. and some other items.
8051 // check e.getTarget().
8053 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8057 Roo.log("keypress blocked");
8065 onSubmit : function(e){
8070 * Returns true if client-side validation on the form is successful.
8073 isValid : function(){
8074 var items = this.getItems();
8078 items.each(function(f){
8084 Roo.log('invalid field: ' + f.name);
8088 if(!target && f.el.isVisible(true)){
8094 if(this.errorMask && !valid){
8095 Roo.bootstrap.Form.popover.mask(this, target);
8102 * Returns true if any fields in this form have changed since their original load.
8105 isDirty : function(){
8107 var items = this.getItems();
8108 items.each(function(f){
8118 * Performs a predefined action (submit or load) or custom actions you define on this form.
8119 * @param {String} actionName The name of the action type
8120 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8121 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8122 * accept other config options):
8124 Property Type Description
8125 ---------------- --------------- ----------------------------------------------------------------------------------
8126 url String The url for the action (defaults to the form's url)
8127 method String The form method to use (defaults to the form's method, or POST if not defined)
8128 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8129 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8130 validate the form on the client (defaults to false)
8132 * @return {BasicForm} this
8134 doAction : function(action, options){
8135 if(typeof action == 'string'){
8136 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8138 if(this.fireEvent('beforeaction', this, action) !== false){
8139 this.beforeAction(action);
8140 action.run.defer(100, action);
8146 beforeAction : function(action){
8147 var o = action.options;
8152 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8154 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8157 // not really supported yet.. ??
8159 //if(this.waitMsgTarget === true){
8160 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8161 //}else if(this.waitMsgTarget){
8162 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8163 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8165 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8171 afterAction : function(action, success){
8172 this.activeAction = null;
8173 var o = action.options;
8178 Roo.get(document.body).unmask();
8184 //if(this.waitMsgTarget === true){
8185 // this.el.unmask();
8186 //}else if(this.waitMsgTarget){
8187 // this.waitMsgTarget.unmask();
8189 // Roo.MessageBox.updateProgress(1);
8190 // Roo.MessageBox.hide();
8197 Roo.callback(o.success, o.scope, [this, action]);
8198 this.fireEvent('actioncomplete', this, action);
8202 // failure condition..
8203 // we have a scenario where updates need confirming.
8204 // eg. if a locking scenario exists..
8205 // we look for { errors : { needs_confirm : true }} in the response.
8207 (typeof(action.result) != 'undefined') &&
8208 (typeof(action.result.errors) != 'undefined') &&
8209 (typeof(action.result.errors.needs_confirm) != 'undefined')
8212 Roo.log("not supported yet");
8215 Roo.MessageBox.confirm(
8216 "Change requires confirmation",
8217 action.result.errorMsg,
8222 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8232 Roo.callback(o.failure, o.scope, [this, action]);
8233 // show an error message if no failed handler is set..
8234 if (!this.hasListener('actionfailed')) {
8235 Roo.log("need to add dialog support");
8237 Roo.MessageBox.alert("Error",
8238 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8239 action.result.errorMsg :
8240 "Saving Failed, please check your entries or try again"
8245 this.fireEvent('actionfailed', this, action);
8250 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8251 * @param {String} id The value to search for
8254 findField : function(id){
8255 var items = this.getItems();
8256 var field = items.get(id);
8258 items.each(function(f){
8259 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8266 return field || null;
8269 * Mark fields in this form invalid in bulk.
8270 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8271 * @return {BasicForm} this
8273 markInvalid : function(errors){
8274 if(errors instanceof Array){
8275 for(var i = 0, len = errors.length; i < len; i++){
8276 var fieldError = errors[i];
8277 var f = this.findField(fieldError.id);
8279 f.markInvalid(fieldError.msg);
8285 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8286 field.markInvalid(errors[id]);
8290 //Roo.each(this.childForms || [], function (f) {
8291 // f.markInvalid(errors);
8298 * Set values for fields in this form in bulk.
8299 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8300 * @return {BasicForm} this
8302 setValues : function(values){
8303 if(values instanceof Array){ // array of objects
8304 for(var i = 0, len = values.length; i < len; i++){
8306 var f = this.findField(v.id);
8308 f.setValue(v.value);
8309 if(this.trackResetOnLoad){
8310 f.originalValue = f.getValue();
8314 }else{ // object hash
8317 if(typeof values[id] != 'function' && (field = this.findField(id))){
8319 if (field.setFromData &&
8321 field.displayField &&
8322 // combos' with local stores can
8323 // be queried via setValue()
8324 // to set their value..
8325 (field.store && !field.store.isLocal)
8329 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8330 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8331 field.setFromData(sd);
8333 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8335 field.setFromData(values);
8338 field.setValue(values[id]);
8342 if(this.trackResetOnLoad){
8343 field.originalValue = field.getValue();
8349 //Roo.each(this.childForms || [], function (f) {
8350 // f.setValues(values);
8357 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8358 * they are returned as an array.
8359 * @param {Boolean} asString
8362 getValues : function(asString){
8363 //if (this.childForms) {
8364 // copy values from the child forms
8365 // Roo.each(this.childForms, function (f) {
8366 // this.setValues(f.getValues());
8372 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8373 if(asString === true){
8376 return Roo.urlDecode(fs);
8380 * Returns the fields in this form as an object with key/value pairs.
8381 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8384 getFieldValues : function(with_hidden)
8386 var items = this.getItems();
8388 items.each(function(f){
8394 var v = f.getValue();
8396 if (f.inputType =='radio') {
8397 if (typeof(ret[f.getName()]) == 'undefined') {
8398 ret[f.getName()] = ''; // empty..
8401 if (!f.el.dom.checked) {
8409 if(f.xtype == 'MoneyField'){
8410 ret[f.currencyName] = f.getCurrency();
8413 // not sure if this supported any more..
8414 if ((typeof(v) == 'object') && f.getRawValue) {
8415 v = f.getRawValue() ; // dates..
8417 // combo boxes where name != hiddenName...
8418 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8419 ret[f.name] = f.getRawValue();
8421 ret[f.getName()] = v;
8428 * Clears all invalid messages in this form.
8429 * @return {BasicForm} this
8431 clearInvalid : function(){
8432 var items = this.getItems();
8434 items.each(function(f){
8443 * @return {BasicForm} this
8446 var items = this.getItems();
8447 items.each(function(f){
8451 Roo.each(this.childForms || [], function (f) {
8459 getItems : function()
8461 var r=new Roo.util.MixedCollection(false, function(o){
8462 return o.id || (o.id = Roo.id());
8464 var iter = function(el) {
8471 Roo.each(el.items,function(e) {
8480 hideFields : function(items)
8482 Roo.each(items, function(i){
8484 var f = this.findField(i);
8495 showFields : function(items)
8497 Roo.each(items, function(i){
8499 var f = this.findField(i);
8512 Roo.apply(Roo.bootstrap.Form, {
8539 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8540 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8541 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8542 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8545 this.maskEl.top.enableDisplayMode("block");
8546 this.maskEl.left.enableDisplayMode("block");
8547 this.maskEl.bottom.enableDisplayMode("block");
8548 this.maskEl.right.enableDisplayMode("block");
8550 this.toolTip = new Roo.bootstrap.Tooltip({
8551 cls : 'roo-form-error-popover',
8553 'left' : ['r-l', [-2,0], 'right'],
8554 'right' : ['l-r', [2,0], 'left'],
8555 'bottom' : ['tl-bl', [0,2], 'top'],
8556 'top' : [ 'bl-tl', [0,-2], 'bottom']
8560 this.toolTip.render(Roo.get(document.body));
8562 this.toolTip.el.enableDisplayMode("block");
8564 Roo.get(document.body).on('click', function(){
8568 Roo.get(document.body).on('touchstart', function(){
8572 this.isApplied = true
8575 mask : function(form, target)
8579 this.target = target;
8581 if(!this.form.errorMask || !target.el){
8585 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8587 Roo.log(scrollable);
8589 var ot = this.target.el.calcOffsetsTo(scrollable);
8591 var scrollTo = ot[1] - this.form.maskOffset;
8593 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8595 scrollable.scrollTo('top', scrollTo);
8597 var box = this.target.el.getBox();
8599 var zIndex = Roo.bootstrap.Modal.zIndex++;
8602 this.maskEl.top.setStyle('position', 'absolute');
8603 this.maskEl.top.setStyle('z-index', zIndex);
8604 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8605 this.maskEl.top.setLeft(0);
8606 this.maskEl.top.setTop(0);
8607 this.maskEl.top.show();
8609 this.maskEl.left.setStyle('position', 'absolute');
8610 this.maskEl.left.setStyle('z-index', zIndex);
8611 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8612 this.maskEl.left.setLeft(0);
8613 this.maskEl.left.setTop(box.y - this.padding);
8614 this.maskEl.left.show();
8616 this.maskEl.bottom.setStyle('position', 'absolute');
8617 this.maskEl.bottom.setStyle('z-index', zIndex);
8618 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8619 this.maskEl.bottom.setLeft(0);
8620 this.maskEl.bottom.setTop(box.bottom + this.padding);
8621 this.maskEl.bottom.show();
8623 this.maskEl.right.setStyle('position', 'absolute');
8624 this.maskEl.right.setStyle('z-index', zIndex);
8625 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8626 this.maskEl.right.setLeft(box.right + this.padding);
8627 this.maskEl.right.setTop(box.y - this.padding);
8628 this.maskEl.right.show();
8630 this.toolTip.bindEl = this.target.el;
8632 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8634 var tip = this.target.blankText;
8636 if(this.target.getValue() !== '' ) {
8638 if (this.target.invalidText.length) {
8639 tip = this.target.invalidText;
8640 } else if (this.target.regexText.length){
8641 tip = this.target.regexText;
8645 this.toolTip.show(tip);
8647 this.intervalID = window.setInterval(function() {
8648 Roo.bootstrap.Form.popover.unmask();
8651 window.onwheel = function(){ return false;};
8653 (function(){ this.isMasked = true; }).defer(500, this);
8659 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8663 this.maskEl.top.setStyle('position', 'absolute');
8664 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8665 this.maskEl.top.hide();
8667 this.maskEl.left.setStyle('position', 'absolute');
8668 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8669 this.maskEl.left.hide();
8671 this.maskEl.bottom.setStyle('position', 'absolute');
8672 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8673 this.maskEl.bottom.hide();
8675 this.maskEl.right.setStyle('position', 'absolute');
8676 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8677 this.maskEl.right.hide();
8679 this.toolTip.hide();
8681 this.toolTip.el.hide();
8683 window.onwheel = function(){ return true;};
8685 if(this.intervalID){
8686 window.clearInterval(this.intervalID);
8687 this.intervalID = false;
8690 this.isMasked = false;
8700 * Ext JS Library 1.1.1
8701 * Copyright(c) 2006-2007, Ext JS, LLC.
8703 * Originally Released Under LGPL - original licence link has changed is not relivant.
8706 * <script type="text/javascript">
8709 * @class Roo.form.VTypes
8710 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8713 Roo.form.VTypes = function(){
8714 // closure these in so they are only created once.
8715 var alpha = /^[a-zA-Z_]+$/;
8716 var alphanum = /^[a-zA-Z0-9_]+$/;
8717 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8718 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8720 // All these messages and functions are configurable
8723 * The function used to validate email addresses
8724 * @param {String} value The email address
8726 'email' : function(v){
8727 return email.test(v);
8730 * The error text to display when the email validation function returns false
8733 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8735 * The keystroke filter mask to be applied on email input
8738 'emailMask' : /[a-z0-9_\.\-@]/i,
8741 * The function used to validate URLs
8742 * @param {String} value The URL
8744 'url' : function(v){
8748 * The error text to display when the url validation function returns false
8751 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8754 * The function used to validate alpha values
8755 * @param {String} value The value
8757 'alpha' : function(v){
8758 return alpha.test(v);
8761 * The error text to display when the alpha validation function returns false
8764 'alphaText' : 'This field should only contain letters and _',
8766 * The keystroke filter mask to be applied on alpha input
8769 'alphaMask' : /[a-z_]/i,
8772 * The function used to validate alphanumeric values
8773 * @param {String} value The value
8775 'alphanum' : function(v){
8776 return alphanum.test(v);
8779 * The error text to display when the alphanumeric validation function returns false
8782 'alphanumText' : 'This field should only contain letters, numbers and _',
8784 * The keystroke filter mask to be applied on alphanumeric input
8787 'alphanumMask' : /[a-z0-9_]/i
8797 * @class Roo.bootstrap.Input
8798 * @extends Roo.bootstrap.Component
8799 * Bootstrap Input class
8800 * @cfg {Boolean} disabled is it disabled
8801 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8802 * @cfg {String} name name of the input
8803 * @cfg {string} fieldLabel - the label associated
8804 * @cfg {string} placeholder - placeholder to put in text.
8805 * @cfg {string} before - input group add on before
8806 * @cfg {string} after - input group add on after
8807 * @cfg {string} size - (lg|sm) or leave empty..
8808 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8809 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8810 * @cfg {Number} md colspan out of 12 for computer-sized screens
8811 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8812 * @cfg {string} value default value of the input
8813 * @cfg {Number} labelWidth set the width of label
8814 * @cfg {Number} labellg set the width of label (1-12)
8815 * @cfg {Number} labelmd set the width of label (1-12)
8816 * @cfg {Number} labelsm set the width of label (1-12)
8817 * @cfg {Number} labelxs set the width of label (1-12)
8818 * @cfg {String} labelAlign (top|left)
8819 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8820 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8821 * @cfg {String} indicatorpos (left|right) default left
8822 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8823 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8825 * @cfg {String} align (left|center|right) Default left
8826 * @cfg {Boolean} forceFeedback (true|false) Default false
8829 * Create a new Input
8830 * @param {Object} config The config object
8833 Roo.bootstrap.Input = function(config){
8835 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8840 * Fires when this field receives input focus.
8841 * @param {Roo.form.Field} this
8846 * Fires when this field loses input focus.
8847 * @param {Roo.form.Field} this
8852 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8853 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8854 * @param {Roo.form.Field} this
8855 * @param {Roo.EventObject} e The event object
8860 * Fires just before the field blurs if the field value has changed.
8861 * @param {Roo.form.Field} this
8862 * @param {Mixed} newValue The new value
8863 * @param {Mixed} oldValue The original value
8868 * Fires after the field has been marked as invalid.
8869 * @param {Roo.form.Field} this
8870 * @param {String} msg The validation message
8875 * Fires after the field has been validated with no errors.
8876 * @param {Roo.form.Field} this
8881 * Fires after the key up
8882 * @param {Roo.form.Field} this
8883 * @param {Roo.EventObject} e The event Object
8889 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8891 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8892 automatic validation (defaults to "keyup").
8894 validationEvent : "keyup",
8896 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8898 validateOnBlur : true,
8900 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8902 validationDelay : 250,
8904 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8906 focusClass : "x-form-focus", // not needed???
8910 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8912 invalidClass : "has-warning",
8915 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8917 validClass : "has-success",
8920 * @cfg {Boolean} hasFeedback (true|false) default true
8925 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8927 invalidFeedbackClass : "glyphicon-warning-sign",
8930 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8932 validFeedbackClass : "glyphicon-ok",
8935 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8937 selectOnFocus : false,
8940 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8944 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8949 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8951 disableKeyFilter : false,
8954 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8958 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8962 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8964 blankText : "Please complete this mandatory field",
8967 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8971 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8973 maxLength : Number.MAX_VALUE,
8975 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8977 minLengthText : "The minimum length for this field is {0}",
8979 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8981 maxLengthText : "The maximum length for this field is {0}",
8985 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8986 * If available, this function will be called only after the basic validators all return true, and will be passed the
8987 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8991 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8992 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8993 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8997 * @cfg {String} regexText -- Depricated - use Invalid Text
9002 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9008 autocomplete: false,
9027 formatedValue : false,
9028 forceFeedback : false,
9030 indicatorpos : 'left',
9040 parentLabelAlign : function()
9043 while (parent.parent()) {
9044 parent = parent.parent();
9045 if (typeof(parent.labelAlign) !='undefined') {
9046 return parent.labelAlign;
9053 getAutoCreate : function()
9055 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9061 if(this.inputType != 'hidden'){
9062 cfg.cls = 'form-group' //input-group
9068 type : this.inputType,
9070 cls : 'form-control',
9071 placeholder : this.placeholder || '',
9072 autocomplete : this.autocomplete || 'new-password'
9075 if(this.capture.length){
9076 input.capture = this.capture;
9079 if(this.accept.length){
9080 input.accept = this.accept + "/*";
9084 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9087 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9088 input.maxLength = this.maxLength;
9091 if (this.disabled) {
9092 input.disabled=true;
9095 if (this.readOnly) {
9096 input.readonly=true;
9100 input.name = this.name;
9104 input.cls += ' input-' + this.size;
9108 ['xs','sm','md','lg'].map(function(size){
9109 if (settings[size]) {
9110 cfg.cls += ' col-' + size + '-' + settings[size];
9114 var inputblock = input;
9118 cls: 'glyphicon form-control-feedback'
9121 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9124 cls : 'has-feedback',
9132 if (this.before || this.after) {
9135 cls : 'input-group',
9139 if (this.before && typeof(this.before) == 'string') {
9141 inputblock.cn.push({
9143 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9147 if (this.before && typeof(this.before) == 'object') {
9148 this.before = Roo.factory(this.before);
9150 inputblock.cn.push({
9152 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9153 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9157 inputblock.cn.push(input);
9159 if (this.after && typeof(this.after) == 'string') {
9160 inputblock.cn.push({
9162 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9166 if (this.after && typeof(this.after) == 'object') {
9167 this.after = Roo.factory(this.after);
9169 inputblock.cn.push({
9171 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9172 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9176 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9177 inputblock.cls += ' has-feedback';
9178 inputblock.cn.push(feedback);
9183 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9184 tooltip : 'This field is required'
9186 if (Roo.bootstrap.version == 4) {
9189 style : 'display-none'
9192 if (align ==='left' && this.fieldLabel.length) {
9194 cfg.cls += ' roo-form-group-label-left row';
9201 cls : 'control-label col-form-label',
9202 html : this.fieldLabel
9213 var labelCfg = cfg.cn[1];
9214 var contentCfg = cfg.cn[2];
9216 if(this.indicatorpos == 'right'){
9221 cls : 'control-label col-form-label',
9225 html : this.fieldLabel
9239 labelCfg = cfg.cn[0];
9240 contentCfg = cfg.cn[1];
9244 if(this.labelWidth > 12){
9245 labelCfg.style = "width: " + this.labelWidth + 'px';
9248 if(this.labelWidth < 13 && this.labelmd == 0){
9249 this.labelmd = this.labelWidth;
9252 if(this.labellg > 0){
9253 labelCfg.cls += ' col-lg-' + this.labellg;
9254 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9257 if(this.labelmd > 0){
9258 labelCfg.cls += ' col-md-' + this.labelmd;
9259 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9262 if(this.labelsm > 0){
9263 labelCfg.cls += ' col-sm-' + this.labelsm;
9264 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9267 if(this.labelxs > 0){
9268 labelCfg.cls += ' col-xs-' + this.labelxs;
9269 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9273 } else if ( this.fieldLabel.length) {
9278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9279 tooltip : 'This field is required'
9283 //cls : 'input-group-addon',
9284 html : this.fieldLabel
9292 if(this.indicatorpos == 'right'){
9297 //cls : 'input-group-addon',
9298 html : this.fieldLabel
9303 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9304 tooltip : 'This field is required'
9324 if (this.parentType === 'Navbar' && this.parent().bar) {
9325 cfg.cls += ' navbar-form';
9328 if (this.parentType === 'NavGroup') {
9329 cfg.cls += ' navbar-form';
9337 * return the real input element.
9339 inputEl: function ()
9341 return this.el.select('input.form-control',true).first();
9344 tooltipEl : function()
9346 return this.inputEl();
9349 indicatorEl : function()
9351 if (Roo.bootstrap.version == 4) {
9352 return false; // not enabled in v4 yet.
9355 var indicator = this.el.select('i.roo-required-indicator',true).first();
9365 setDisabled : function(v)
9367 var i = this.inputEl().dom;
9369 i.removeAttribute('disabled');
9373 i.setAttribute('disabled','true');
9375 initEvents : function()
9378 this.inputEl().on("keydown" , this.fireKey, this);
9379 this.inputEl().on("focus", this.onFocus, this);
9380 this.inputEl().on("blur", this.onBlur, this);
9382 this.inputEl().relayEvent('keyup', this);
9384 this.indicator = this.indicatorEl();
9387 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9390 // reference to original value for reset
9391 this.originalValue = this.getValue();
9392 //Roo.form.TextField.superclass.initEvents.call(this);
9393 if(this.validationEvent == 'keyup'){
9394 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9395 this.inputEl().on('keyup', this.filterValidation, this);
9397 else if(this.validationEvent !== false){
9398 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9401 if(this.selectOnFocus){
9402 this.on("focus", this.preFocus, this);
9405 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9406 this.inputEl().on("keypress", this.filterKeys, this);
9408 this.inputEl().relayEvent('keypress', this);
9411 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9412 this.el.on("click", this.autoSize, this);
9415 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9416 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9419 if (typeof(this.before) == 'object') {
9420 this.before.render(this.el.select('.roo-input-before',true).first());
9422 if (typeof(this.after) == 'object') {
9423 this.after.render(this.el.select('.roo-input-after',true).first());
9426 this.inputEl().on('change', this.onChange, this);
9429 filterValidation : function(e){
9430 if(!e.isNavKeyPress()){
9431 this.validationTask.delay(this.validationDelay);
9435 * Validates the field value
9436 * @return {Boolean} True if the value is valid, else false
9438 validate : function(){
9439 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9440 if(this.disabled || this.validateValue(this.getRawValue())){
9451 * Validates a value according to the field's validation rules and marks the field as invalid
9452 * if the validation fails
9453 * @param {Mixed} value The value to validate
9454 * @return {Boolean} True if the value is valid, else false
9456 validateValue : function(value)
9458 if(this.getVisibilityEl().hasClass('hidden')){
9462 if(value.length < 1) { // if it's blank
9463 if(this.allowBlank){
9469 if(value.length < this.minLength){
9472 if(value.length > this.maxLength){
9476 var vt = Roo.form.VTypes;
9477 if(!vt[this.vtype](value, this)){
9481 if(typeof this.validator == "function"){
9482 var msg = this.validator(value);
9486 if (typeof(msg) == 'string') {
9487 this.invalidText = msg;
9491 if(this.regex && !this.regex.test(value)){
9499 fireKey : function(e){
9500 //Roo.log('field ' + e.getKey());
9501 if(e.isNavKeyPress()){
9502 this.fireEvent("specialkey", this, e);
9505 focus : function (selectText){
9507 this.inputEl().focus();
9508 if(selectText === true){
9509 this.inputEl().dom.select();
9515 onFocus : function(){
9516 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9517 // this.el.addClass(this.focusClass);
9520 this.hasFocus = true;
9521 this.startValue = this.getValue();
9522 this.fireEvent("focus", this);
9526 beforeBlur : Roo.emptyFn,
9530 onBlur : function(){
9532 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9533 //this.el.removeClass(this.focusClass);
9535 this.hasFocus = false;
9536 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9539 var v = this.getValue();
9540 if(String(v) !== String(this.startValue)){
9541 this.fireEvent('change', this, v, this.startValue);
9543 this.fireEvent("blur", this);
9546 onChange : function(e)
9548 var v = this.getValue();
9549 if(String(v) !== String(this.startValue)){
9550 this.fireEvent('change', this, v, this.startValue);
9556 * Resets the current field value to the originally loaded value and clears any validation messages
9559 this.setValue(this.originalValue);
9563 * Returns the name of the field
9564 * @return {Mixed} name The name field
9566 getName: function(){
9570 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9571 * @return {Mixed} value The field value
9573 getValue : function(){
9575 var v = this.inputEl().getValue();
9580 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9581 * @return {Mixed} value The field value
9583 getRawValue : function(){
9584 var v = this.inputEl().getValue();
9590 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9591 * @param {Mixed} value The value to set
9593 setRawValue : function(v){
9594 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9597 selectText : function(start, end){
9598 var v = this.getRawValue();
9600 start = start === undefined ? 0 : start;
9601 end = end === undefined ? v.length : end;
9602 var d = this.inputEl().dom;
9603 if(d.setSelectionRange){
9604 d.setSelectionRange(start, end);
9605 }else if(d.createTextRange){
9606 var range = d.createTextRange();
9607 range.moveStart("character", start);
9608 range.moveEnd("character", v.length-end);
9615 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9616 * @param {Mixed} value The value to set
9618 setValue : function(v){
9621 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9627 processValue : function(value){
9628 if(this.stripCharsRe){
9629 var newValue = value.replace(this.stripCharsRe, '');
9630 if(newValue !== value){
9631 this.setRawValue(newValue);
9638 preFocus : function(){
9640 if(this.selectOnFocus){
9641 this.inputEl().dom.select();
9644 filterKeys : function(e){
9646 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9649 var c = e.getCharCode(), cc = String.fromCharCode(c);
9650 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9653 if(!this.maskRe.test(cc)){
9658 * Clear any invalid styles/messages for this field
9660 clearInvalid : function(){
9662 if(!this.el || this.preventMark){ // not rendered
9667 this.el.removeClass(this.invalidClass);
9669 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9671 var feedback = this.el.select('.form-control-feedback', true).first();
9674 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9680 this.indicator.removeClass('visible');
9681 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9684 this.fireEvent('valid', this);
9688 * Mark this field as valid
9690 markValid : function()
9692 if(!this.el || this.preventMark){ // not rendered...
9696 this.el.removeClass([this.invalidClass, this.validClass]);
9698 var feedback = this.el.select('.form-control-feedback', true).first();
9701 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9705 this.indicator.removeClass('visible');
9706 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9713 if(this.allowBlank && !this.getRawValue().length){
9717 this.el.addClass(this.validClass);
9719 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9721 var feedback = this.el.select('.form-control-feedback', true).first();
9724 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9730 this.fireEvent('valid', this);
9734 * Mark this field as invalid
9735 * @param {String} msg The validation message
9737 markInvalid : function(msg)
9739 if(!this.el || this.preventMark){ // not rendered
9743 this.el.removeClass([this.invalidClass, this.validClass]);
9745 var feedback = this.el.select('.form-control-feedback', true).first();
9748 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9755 if(this.allowBlank && !this.getRawValue().length){
9760 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9761 this.indicator.addClass('visible');
9764 this.el.addClass(this.invalidClass);
9766 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9768 var feedback = this.el.select('.form-control-feedback', true).first();
9771 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9773 if(this.getValue().length || this.forceFeedback){
9774 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9781 this.fireEvent('invalid', this, msg);
9784 SafariOnKeyDown : function(event)
9786 // this is a workaround for a password hang bug on chrome/ webkit.
9787 if (this.inputEl().dom.type != 'password') {
9791 var isSelectAll = false;
9793 if(this.inputEl().dom.selectionEnd > 0){
9794 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9796 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9797 event.preventDefault();
9802 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9804 event.preventDefault();
9805 // this is very hacky as keydown always get's upper case.
9807 var cc = String.fromCharCode(event.getCharCode());
9808 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9812 adjustWidth : function(tag, w){
9813 tag = tag.toLowerCase();
9814 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9815 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9819 if(tag == 'textarea'){
9822 }else if(Roo.isOpera){
9826 if(tag == 'textarea'){
9834 setFieldLabel : function(v)
9840 if(this.indicatorEl()){
9841 var ar = this.el.select('label > span',true);
9843 if (ar.elements.length) {
9844 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9845 this.fieldLabel = v;
9849 var br = this.el.select('label',true);
9851 if(br.elements.length) {
9852 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9853 this.fieldLabel = v;
9857 Roo.log('Cannot Found any of label > span || label in input');
9861 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9862 this.fieldLabel = v;
9877 * @class Roo.bootstrap.TextArea
9878 * @extends Roo.bootstrap.Input
9879 * Bootstrap TextArea class
9880 * @cfg {Number} cols Specifies the visible width of a text area
9881 * @cfg {Number} rows Specifies the visible number of lines in a text area
9882 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9883 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9884 * @cfg {string} html text
9887 * Create a new TextArea
9888 * @param {Object} config The config object
9891 Roo.bootstrap.TextArea = function(config){
9892 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9896 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9906 getAutoCreate : function(){
9908 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9914 if(this.inputType != 'hidden'){
9915 cfg.cls = 'form-group' //input-group
9923 value : this.value || '',
9924 html: this.html || '',
9925 cls : 'form-control',
9926 placeholder : this.placeholder || ''
9930 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9931 input.maxLength = this.maxLength;
9935 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9939 input.cols = this.cols;
9942 if (this.readOnly) {
9943 input.readonly = true;
9947 input.name = this.name;
9951 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9955 ['xs','sm','md','lg'].map(function(size){
9956 if (settings[size]) {
9957 cfg.cls += ' col-' + size + '-' + settings[size];
9961 var inputblock = input;
9963 if(this.hasFeedback && !this.allowBlank){
9967 cls: 'glyphicon form-control-feedback'
9971 cls : 'has-feedback',
9980 if (this.before || this.after) {
9983 cls : 'input-group',
9987 inputblock.cn.push({
9989 cls : 'input-group-addon',
9994 inputblock.cn.push(input);
9996 if(this.hasFeedback && !this.allowBlank){
9997 inputblock.cls += ' has-feedback';
9998 inputblock.cn.push(feedback);
10002 inputblock.cn.push({
10004 cls : 'input-group-addon',
10011 if (align ==='left' && this.fieldLabel.length) {
10016 cls : 'control-label',
10017 html : this.fieldLabel
10028 if(this.labelWidth > 12){
10029 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10032 if(this.labelWidth < 13 && this.labelmd == 0){
10033 this.labelmd = this.labelWidth;
10036 if(this.labellg > 0){
10037 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10038 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10041 if(this.labelmd > 0){
10042 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10043 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10046 if(this.labelsm > 0){
10047 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10048 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10051 if(this.labelxs > 0){
10052 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10053 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10056 } else if ( this.fieldLabel.length) {
10061 //cls : 'input-group-addon',
10062 html : this.fieldLabel
10080 if (this.disabled) {
10081 input.disabled=true;
10088 * return the real textarea element.
10090 inputEl: function ()
10092 return this.el.select('textarea.form-control',true).first();
10096 * Clear any invalid styles/messages for this field
10098 clearInvalid : function()
10101 if(!this.el || this.preventMark){ // not rendered
10105 var label = this.el.select('label', true).first();
10106 var icon = this.el.select('i.fa-star', true).first();
10112 this.el.removeClass(this.invalidClass);
10114 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10116 var feedback = this.el.select('.form-control-feedback', true).first();
10119 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10124 this.fireEvent('valid', this);
10128 * Mark this field as valid
10130 markValid : function()
10132 if(!this.el || this.preventMark){ // not rendered
10136 this.el.removeClass([this.invalidClass, this.validClass]);
10138 var feedback = this.el.select('.form-control-feedback', true).first();
10141 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10144 if(this.disabled || this.allowBlank){
10148 var label = this.el.select('label', true).first();
10149 var icon = this.el.select('i.fa-star', true).first();
10155 this.el.addClass(this.validClass);
10157 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10159 var feedback = this.el.select('.form-control-feedback', true).first();
10162 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10163 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10168 this.fireEvent('valid', this);
10172 * Mark this field as invalid
10173 * @param {String} msg The validation message
10175 markInvalid : function(msg)
10177 if(!this.el || this.preventMark){ // not rendered
10181 this.el.removeClass([this.invalidClass, this.validClass]);
10183 var feedback = this.el.select('.form-control-feedback', true).first();
10186 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10189 if(this.disabled || this.allowBlank){
10193 var label = this.el.select('label', true).first();
10194 var icon = this.el.select('i.fa-star', true).first();
10196 if(!this.getValue().length && label && !icon){
10197 this.el.createChild({
10199 cls : 'text-danger fa fa-lg fa-star',
10200 tooltip : 'This field is required',
10201 style : 'margin-right:5px;'
10205 this.el.addClass(this.invalidClass);
10207 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10209 var feedback = this.el.select('.form-control-feedback', true).first();
10212 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10214 if(this.getValue().length || this.forceFeedback){
10215 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10222 this.fireEvent('invalid', this, msg);
10230 * trigger field - base class for combo..
10235 * @class Roo.bootstrap.TriggerField
10236 * @extends Roo.bootstrap.Input
10237 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10238 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10239 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10240 * for which you can provide a custom implementation. For example:
10242 var trigger = new Roo.bootstrap.TriggerField();
10243 trigger.onTriggerClick = myTriggerFn;
10244 trigger.applyTo('my-field');
10247 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10248 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10249 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10250 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10251 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10254 * Create a new TriggerField.
10255 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10256 * to the base TextField)
10258 Roo.bootstrap.TriggerField = function(config){
10259 this.mimicing = false;
10260 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10263 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10265 * @cfg {String} triggerClass A CSS class to apply to the trigger
10268 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10273 * @cfg {Boolean} removable (true|false) special filter default false
10277 /** @cfg {Boolean} grow @hide */
10278 /** @cfg {Number} growMin @hide */
10279 /** @cfg {Number} growMax @hide */
10285 autoSize: Roo.emptyFn,
10289 deferHeight : true,
10292 actionMode : 'wrap',
10297 getAutoCreate : function(){
10299 var align = this.labelAlign || this.parentLabelAlign();
10304 cls: 'form-group' //input-group
10311 type : this.inputType,
10312 cls : 'form-control',
10313 autocomplete: 'new-password',
10314 placeholder : this.placeholder || ''
10318 input.name = this.name;
10321 input.cls += ' input-' + this.size;
10324 if (this.disabled) {
10325 input.disabled=true;
10328 var inputblock = input;
10330 if(this.hasFeedback && !this.allowBlank){
10334 cls: 'glyphicon form-control-feedback'
10337 if(this.removable && !this.editable && !this.tickable){
10339 cls : 'has-feedback',
10345 cls : 'roo-combo-removable-btn close'
10352 cls : 'has-feedback',
10361 if(this.removable && !this.editable && !this.tickable){
10363 cls : 'roo-removable',
10369 cls : 'roo-combo-removable-btn close'
10376 if (this.before || this.after) {
10379 cls : 'input-group',
10383 inputblock.cn.push({
10385 cls : 'input-group-addon input-group-prepend input-group-text',
10390 inputblock.cn.push(input);
10392 if(this.hasFeedback && !this.allowBlank){
10393 inputblock.cls += ' has-feedback';
10394 inputblock.cn.push(feedback);
10398 inputblock.cn.push({
10400 cls : 'input-group-addon input-group-append input-group-text',
10409 var ibwrap = inputblock;
10414 cls: 'roo-select2-choices',
10418 cls: 'roo-select2-search-field',
10430 cls: 'roo-select2-container input-group',
10435 cls: 'form-hidden-field'
10441 if(!this.multiple && this.showToggleBtn){
10447 if (this.caret != false) {
10450 cls: 'fa fa-' + this.caret
10457 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10462 cls: 'combobox-clear',
10476 combobox.cls += ' roo-select2-container-multi';
10480 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10481 tooltip : 'This field is required'
10483 if (Roo.bootstrap.version == 4) {
10486 style : 'display:none'
10491 if (align ==='left' && this.fieldLabel.length) {
10493 cfg.cls += ' roo-form-group-label-left row';
10500 cls : 'control-label',
10501 html : this.fieldLabel
10513 var labelCfg = cfg.cn[1];
10514 var contentCfg = cfg.cn[2];
10516 if(this.indicatorpos == 'right'){
10521 cls : 'control-label',
10525 html : this.fieldLabel
10539 labelCfg = cfg.cn[0];
10540 contentCfg = cfg.cn[1];
10543 if(this.labelWidth > 12){
10544 labelCfg.style = "width: " + this.labelWidth + 'px';
10547 if(this.labelWidth < 13 && this.labelmd == 0){
10548 this.labelmd = this.labelWidth;
10551 if(this.labellg > 0){
10552 labelCfg.cls += ' col-lg-' + this.labellg;
10553 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10556 if(this.labelmd > 0){
10557 labelCfg.cls += ' col-md-' + this.labelmd;
10558 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10561 if(this.labelsm > 0){
10562 labelCfg.cls += ' col-sm-' + this.labelsm;
10563 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10566 if(this.labelxs > 0){
10567 labelCfg.cls += ' col-xs-' + this.labelxs;
10568 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10571 } else if ( this.fieldLabel.length) {
10572 // Roo.log(" label");
10577 //cls : 'input-group-addon',
10578 html : this.fieldLabel
10586 if(this.indicatorpos == 'right'){
10594 html : this.fieldLabel
10608 // Roo.log(" no label && no align");
10615 ['xs','sm','md','lg'].map(function(size){
10616 if (settings[size]) {
10617 cfg.cls += ' col-' + size + '-' + settings[size];
10628 onResize : function(w, h){
10629 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10630 // if(typeof w == 'number'){
10631 // var x = w - this.trigger.getWidth();
10632 // this.inputEl().setWidth(this.adjustWidth('input', x));
10633 // this.trigger.setStyle('left', x+'px');
10638 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10641 getResizeEl : function(){
10642 return this.inputEl();
10646 getPositionEl : function(){
10647 return this.inputEl();
10651 alignErrorIcon : function(){
10652 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10656 initEvents : function(){
10660 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10661 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10662 if(!this.multiple && this.showToggleBtn){
10663 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10664 if(this.hideTrigger){
10665 this.trigger.setDisplayed(false);
10667 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10671 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10674 if(this.removable && !this.editable && !this.tickable){
10675 var close = this.closeTriggerEl();
10678 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10679 close.on('click', this.removeBtnClick, this, close);
10683 //this.trigger.addClassOnOver('x-form-trigger-over');
10684 //this.trigger.addClassOnClick('x-form-trigger-click');
10687 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10691 closeTriggerEl : function()
10693 var close = this.el.select('.roo-combo-removable-btn', true).first();
10694 return close ? close : false;
10697 removeBtnClick : function(e, h, el)
10699 e.preventDefault();
10701 if(this.fireEvent("remove", this) !== false){
10703 this.fireEvent("afterremove", this)
10707 createList : function()
10709 this.list = Roo.get(document.body).createChild({
10710 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10711 cls: 'typeahead typeahead-long dropdown-menu',
10712 style: 'display:none'
10715 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10720 initTrigger : function(){
10725 onDestroy : function(){
10727 this.trigger.removeAllListeners();
10728 // this.trigger.remove();
10731 // this.wrap.remove();
10733 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10737 onFocus : function(){
10738 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10740 if(!this.mimicing){
10741 this.wrap.addClass('x-trigger-wrap-focus');
10742 this.mimicing = true;
10743 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10744 if(this.monitorTab){
10745 this.el.on("keydown", this.checkTab, this);
10752 checkTab : function(e){
10753 if(e.getKey() == e.TAB){
10754 this.triggerBlur();
10759 onBlur : function(){
10764 mimicBlur : function(e, t){
10766 if(!this.wrap.contains(t) && this.validateBlur()){
10767 this.triggerBlur();
10773 triggerBlur : function(){
10774 this.mimicing = false;
10775 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10776 if(this.monitorTab){
10777 this.el.un("keydown", this.checkTab, this);
10779 //this.wrap.removeClass('x-trigger-wrap-focus');
10780 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10784 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10785 validateBlur : function(e, t){
10790 onDisable : function(){
10791 this.inputEl().dom.disabled = true;
10792 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10794 // this.wrap.addClass('x-item-disabled');
10799 onEnable : function(){
10800 this.inputEl().dom.disabled = false;
10801 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10803 // this.el.removeClass('x-item-disabled');
10808 onShow : function(){
10809 var ae = this.getActionEl();
10812 ae.dom.style.display = '';
10813 ae.dom.style.visibility = 'visible';
10819 onHide : function(){
10820 var ae = this.getActionEl();
10821 ae.dom.style.display = 'none';
10825 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10826 * by an implementing function.
10828 * @param {EventObject} e
10830 onTriggerClick : Roo.emptyFn
10834 * Ext JS Library 1.1.1
10835 * Copyright(c) 2006-2007, Ext JS, LLC.
10837 * Originally Released Under LGPL - original licence link has changed is not relivant.
10840 * <script type="text/javascript">
10845 * @class Roo.data.SortTypes
10847 * Defines the default sorting (casting?) comparison functions used when sorting data.
10849 Roo.data.SortTypes = {
10851 * Default sort that does nothing
10852 * @param {Mixed} s The value being converted
10853 * @return {Mixed} The comparison value
10855 none : function(s){
10860 * The regular expression used to strip tags
10864 stripTagsRE : /<\/?[^>]+>/gi,
10867 * Strips all HTML tags to sort on text only
10868 * @param {Mixed} s The value being converted
10869 * @return {String} The comparison value
10871 asText : function(s){
10872 return String(s).replace(this.stripTagsRE, "");
10876 * Strips all HTML tags to sort on text only - Case insensitive
10877 * @param {Mixed} s The value being converted
10878 * @return {String} The comparison value
10880 asUCText : function(s){
10881 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10885 * Case insensitive string
10886 * @param {Mixed} s The value being converted
10887 * @return {String} The comparison value
10889 asUCString : function(s) {
10890 return String(s).toUpperCase();
10895 * @param {Mixed} s The value being converted
10896 * @return {Number} The comparison value
10898 asDate : function(s) {
10902 if(s instanceof Date){
10903 return s.getTime();
10905 return Date.parse(String(s));
10910 * @param {Mixed} s The value being converted
10911 * @return {Float} The comparison value
10913 asFloat : function(s) {
10914 var val = parseFloat(String(s).replace(/,/g, ""));
10923 * @param {Mixed} s The value being converted
10924 * @return {Number} The comparison value
10926 asInt : function(s) {
10927 var val = parseInt(String(s).replace(/,/g, ""));
10935 * Ext JS Library 1.1.1
10936 * Copyright(c) 2006-2007, Ext JS, LLC.
10938 * Originally Released Under LGPL - original licence link has changed is not relivant.
10941 * <script type="text/javascript">
10945 * @class Roo.data.Record
10946 * Instances of this class encapsulate both record <em>definition</em> information, and record
10947 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10948 * to access Records cached in an {@link Roo.data.Store} object.<br>
10950 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10951 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10954 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10956 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10957 * {@link #create}. The parameters are the same.
10958 * @param {Array} data An associative Array of data values keyed by the field name.
10959 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10960 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10961 * not specified an integer id is generated.
10963 Roo.data.Record = function(data, id){
10964 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10969 * Generate a constructor for a specific record layout.
10970 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10971 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10972 * Each field definition object may contain the following properties: <ul>
10973 * <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,
10974 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10975 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10976 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10977 * is being used, then this is a string containing the javascript expression to reference the data relative to
10978 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10979 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10980 * this may be omitted.</p></li>
10981 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10982 * <ul><li>auto (Default, implies no conversion)</li>
10987 * <li>date</li></ul></p></li>
10988 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10989 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10990 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10991 * by the Reader into an object that will be stored in the Record. It is passed the
10992 * following parameters:<ul>
10993 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10995 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10997 * <br>usage:<br><pre><code>
10998 var TopicRecord = Roo.data.Record.create(
10999 {name: 'title', mapping: 'topic_title'},
11000 {name: 'author', mapping: 'username'},
11001 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11002 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11003 {name: 'lastPoster', mapping: 'user2'},
11004 {name: 'excerpt', mapping: 'post_text'}
11007 var myNewRecord = new TopicRecord({
11008 title: 'Do my job please',
11011 lastPost: new Date(),
11012 lastPoster: 'Animal',
11013 excerpt: 'No way dude!'
11015 myStore.add(myNewRecord);
11020 Roo.data.Record.create = function(o){
11021 var f = function(){
11022 f.superclass.constructor.apply(this, arguments);
11024 Roo.extend(f, Roo.data.Record);
11025 var p = f.prototype;
11026 p.fields = new Roo.util.MixedCollection(false, function(field){
11029 for(var i = 0, len = o.length; i < len; i++){
11030 p.fields.add(new Roo.data.Field(o[i]));
11032 f.getField = function(name){
11033 return p.fields.get(name);
11038 Roo.data.Record.AUTO_ID = 1000;
11039 Roo.data.Record.EDIT = 'edit';
11040 Roo.data.Record.REJECT = 'reject';
11041 Roo.data.Record.COMMIT = 'commit';
11043 Roo.data.Record.prototype = {
11045 * Readonly flag - true if this record has been modified.
11054 join : function(store){
11055 this.store = store;
11059 * Set the named field to the specified value.
11060 * @param {String} name The name of the field to set.
11061 * @param {Object} value The value to set the field to.
11063 set : function(name, value){
11064 if(this.data[name] == value){
11068 if(!this.modified){
11069 this.modified = {};
11071 if(typeof this.modified[name] == 'undefined'){
11072 this.modified[name] = this.data[name];
11074 this.data[name] = value;
11075 if(!this.editing && this.store){
11076 this.store.afterEdit(this);
11081 * Get the value of the named field.
11082 * @param {String} name The name of the field to get the value of.
11083 * @return {Object} The value of the field.
11085 get : function(name){
11086 return this.data[name];
11090 beginEdit : function(){
11091 this.editing = true;
11092 this.modified = {};
11096 cancelEdit : function(){
11097 this.editing = false;
11098 delete this.modified;
11102 endEdit : function(){
11103 this.editing = false;
11104 if(this.dirty && this.store){
11105 this.store.afterEdit(this);
11110 * Usually called by the {@link Roo.data.Store} which owns the Record.
11111 * Rejects all changes made to the Record since either creation, or the last commit operation.
11112 * Modified fields are reverted to their original values.
11114 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11115 * of reject operations.
11117 reject : function(){
11118 var m = this.modified;
11120 if(typeof m[n] != "function"){
11121 this.data[n] = m[n];
11124 this.dirty = false;
11125 delete this.modified;
11126 this.editing = false;
11128 this.store.afterReject(this);
11133 * Usually called by the {@link Roo.data.Store} which owns the Record.
11134 * Commits all changes made to the Record since either creation, or the last commit operation.
11136 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11137 * of commit operations.
11139 commit : function(){
11140 this.dirty = false;
11141 delete this.modified;
11142 this.editing = false;
11144 this.store.afterCommit(this);
11149 hasError : function(){
11150 return this.error != null;
11154 clearError : function(){
11159 * Creates a copy of this record.
11160 * @param {String} id (optional) A new record id if you don't want to use this record's id
11163 copy : function(newId) {
11164 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11168 * Ext JS Library 1.1.1
11169 * Copyright(c) 2006-2007, Ext JS, LLC.
11171 * Originally Released Under LGPL - original licence link has changed is not relivant.
11174 * <script type="text/javascript">
11180 * @class Roo.data.Store
11181 * @extends Roo.util.Observable
11182 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11183 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11185 * 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
11186 * has no knowledge of the format of the data returned by the Proxy.<br>
11188 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11189 * instances from the data object. These records are cached and made available through accessor functions.
11191 * Creates a new Store.
11192 * @param {Object} config A config object containing the objects needed for the Store to access data,
11193 * and read the data into Records.
11195 Roo.data.Store = function(config){
11196 this.data = new Roo.util.MixedCollection(false);
11197 this.data.getKey = function(o){
11200 this.baseParams = {};
11202 this.paramNames = {
11207 "multisort" : "_multisort"
11210 if(config && config.data){
11211 this.inlineData = config.data;
11212 delete config.data;
11215 Roo.apply(this, config);
11217 if(this.reader){ // reader passed
11218 this.reader = Roo.factory(this.reader, Roo.data);
11219 this.reader.xmodule = this.xmodule || false;
11220 if(!this.recordType){
11221 this.recordType = this.reader.recordType;
11223 if(this.reader.onMetaChange){
11224 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11228 if(this.recordType){
11229 this.fields = this.recordType.prototype.fields;
11231 this.modified = [];
11235 * @event datachanged
11236 * Fires when the data cache has changed, and a widget which is using this Store
11237 * as a Record cache should refresh its view.
11238 * @param {Store} this
11240 datachanged : true,
11242 * @event metachange
11243 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11244 * @param {Store} this
11245 * @param {Object} meta The JSON metadata
11250 * Fires when Records have been added to the Store
11251 * @param {Store} this
11252 * @param {Roo.data.Record[]} records The array of Records added
11253 * @param {Number} index The index at which the record(s) were added
11258 * Fires when a Record has been removed from the Store
11259 * @param {Store} this
11260 * @param {Roo.data.Record} record The Record that was removed
11261 * @param {Number} index The index at which the record was removed
11266 * Fires when a Record has been updated
11267 * @param {Store} this
11268 * @param {Roo.data.Record} record The Record that was updated
11269 * @param {String} operation The update operation being performed. Value may be one of:
11271 Roo.data.Record.EDIT
11272 Roo.data.Record.REJECT
11273 Roo.data.Record.COMMIT
11279 * Fires when the data cache has been cleared.
11280 * @param {Store} this
11284 * @event beforeload
11285 * Fires before a request is made for a new data object. If the beforeload handler returns false
11286 * the load action will be canceled.
11287 * @param {Store} this
11288 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11292 * @event beforeloadadd
11293 * Fires after a new set of Records has been loaded.
11294 * @param {Store} this
11295 * @param {Roo.data.Record[]} records The Records that were loaded
11296 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11298 beforeloadadd : true,
11301 * Fires after a new set of Records has been loaded, before they are added to the store.
11302 * @param {Store} this
11303 * @param {Roo.data.Record[]} records The Records that were loaded
11304 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11305 * @params {Object} return from reader
11309 * @event loadexception
11310 * Fires if an exception occurs in the Proxy during loading.
11311 * Called with the signature of the Proxy's "loadexception" event.
11312 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11315 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11316 * @param {Object} load options
11317 * @param {Object} jsonData from your request (normally this contains the Exception)
11319 loadexception : true
11323 this.proxy = Roo.factory(this.proxy, Roo.data);
11324 this.proxy.xmodule = this.xmodule || false;
11325 this.relayEvents(this.proxy, ["loadexception"]);
11327 this.sortToggle = {};
11328 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11330 Roo.data.Store.superclass.constructor.call(this);
11332 if(this.inlineData){
11333 this.loadData(this.inlineData);
11334 delete this.inlineData;
11338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11340 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11341 * without a remote query - used by combo/forms at present.
11345 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11348 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11351 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11352 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11355 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11356 * on any HTTP request
11359 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11362 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11366 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11367 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11369 remoteSort : false,
11372 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11373 * loaded or when a record is removed. (defaults to false).
11375 pruneModifiedRecords : false,
11378 lastOptions : null,
11381 * Add Records to the Store and fires the add event.
11382 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11384 add : function(records){
11385 records = [].concat(records);
11386 for(var i = 0, len = records.length; i < len; i++){
11387 records[i].join(this);
11389 var index = this.data.length;
11390 this.data.addAll(records);
11391 this.fireEvent("add", this, records, index);
11395 * Remove a Record from the Store and fires the remove event.
11396 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11398 remove : function(record){
11399 var index = this.data.indexOf(record);
11400 this.data.removeAt(index);
11402 if(this.pruneModifiedRecords){
11403 this.modified.remove(record);
11405 this.fireEvent("remove", this, record, index);
11409 * Remove all Records from the Store and fires the clear event.
11411 removeAll : function(){
11413 if(this.pruneModifiedRecords){
11414 this.modified = [];
11416 this.fireEvent("clear", this);
11420 * Inserts Records to the Store at the given index and fires the add event.
11421 * @param {Number} index The start index at which to insert the passed Records.
11422 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11424 insert : function(index, records){
11425 records = [].concat(records);
11426 for(var i = 0, len = records.length; i < len; i++){
11427 this.data.insert(index, records[i]);
11428 records[i].join(this);
11430 this.fireEvent("add", this, records, index);
11434 * Get the index within the cache of the passed Record.
11435 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11436 * @return {Number} The index of the passed Record. Returns -1 if not found.
11438 indexOf : function(record){
11439 return this.data.indexOf(record);
11443 * Get the index within the cache of the Record with the passed id.
11444 * @param {String} id The id of the Record to find.
11445 * @return {Number} The index of the Record. Returns -1 if not found.
11447 indexOfId : function(id){
11448 return this.data.indexOfKey(id);
11452 * Get the Record with the specified id.
11453 * @param {String} id The id of the Record to find.
11454 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11456 getById : function(id){
11457 return this.data.key(id);
11461 * Get the Record at the specified index.
11462 * @param {Number} index The index of the Record to find.
11463 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11465 getAt : function(index){
11466 return this.data.itemAt(index);
11470 * Returns a range of Records between specified indices.
11471 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11472 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11473 * @return {Roo.data.Record[]} An array of Records
11475 getRange : function(start, end){
11476 return this.data.getRange(start, end);
11480 storeOptions : function(o){
11481 o = Roo.apply({}, o);
11484 this.lastOptions = o;
11488 * Loads the Record cache from the configured Proxy using the configured Reader.
11490 * If using remote paging, then the first load call must specify the <em>start</em>
11491 * and <em>limit</em> properties in the options.params property to establish the initial
11492 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11494 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11495 * and this call will return before the new data has been loaded. Perform any post-processing
11496 * in a callback function, or in a "load" event handler.</strong>
11498 * @param {Object} options An object containing properties which control loading options:<ul>
11499 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11500 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11501 * passed the following arguments:<ul>
11502 * <li>r : Roo.data.Record[]</li>
11503 * <li>options: Options object from the load call</li>
11504 * <li>success: Boolean success indicator</li></ul></li>
11505 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11506 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11509 load : function(options){
11510 options = options || {};
11511 if(this.fireEvent("beforeload", this, options) !== false){
11512 this.storeOptions(options);
11513 var p = Roo.apply(options.params || {}, this.baseParams);
11514 // if meta was not loaded from remote source.. try requesting it.
11515 if (!this.reader.metaFromRemote) {
11516 p._requestMeta = 1;
11518 if(this.sortInfo && this.remoteSort){
11519 var pn = this.paramNames;
11520 p[pn["sort"]] = this.sortInfo.field;
11521 p[pn["dir"]] = this.sortInfo.direction;
11523 if (this.multiSort) {
11524 var pn = this.paramNames;
11525 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11528 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11533 * Reloads the Record cache from the configured Proxy using the configured Reader and
11534 * the options from the last load operation performed.
11535 * @param {Object} options (optional) An object containing properties which may override the options
11536 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11537 * the most recently used options are reused).
11539 reload : function(options){
11540 this.load(Roo.applyIf(options||{}, this.lastOptions));
11544 // Called as a callback by the Reader during a load operation.
11545 loadRecords : function(o, options, success){
11546 if(!o || success === false){
11547 if(success !== false){
11548 this.fireEvent("load", this, [], options, o);
11550 if(options.callback){
11551 options.callback.call(options.scope || this, [], options, false);
11555 // if data returned failure - throw an exception.
11556 if (o.success === false) {
11557 // show a message if no listener is registered.
11558 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11559 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11561 // loadmask wil be hooked into this..
11562 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11565 var r = o.records, t = o.totalRecords || r.length;
11567 this.fireEvent("beforeloadadd", this, r, options, o);
11569 if(!options || options.add !== true){
11570 if(this.pruneModifiedRecords){
11571 this.modified = [];
11573 for(var i = 0, len = r.length; i < len; i++){
11577 this.data = this.snapshot;
11578 delete this.snapshot;
11581 this.data.addAll(r);
11582 this.totalLength = t;
11584 this.fireEvent("datachanged", this);
11586 this.totalLength = Math.max(t, this.data.length+r.length);
11590 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11592 var e = new Roo.data.Record({});
11594 e.set(this.parent.displayField, this.parent.emptyTitle);
11595 e.set(this.parent.valueField, '');
11600 this.fireEvent("load", this, r, options, o);
11601 if(options.callback){
11602 options.callback.call(options.scope || this, r, options, true);
11608 * Loads data from a passed data block. A Reader which understands the format of the data
11609 * must have been configured in the constructor.
11610 * @param {Object} data The data block from which to read the Records. The format of the data expected
11611 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11612 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11614 loadData : function(o, append){
11615 var r = this.reader.readRecords(o);
11616 this.loadRecords(r, {add: append}, true);
11620 * Gets the number of cached records.
11622 * <em>If using paging, this may not be the total size of the dataset. If the data object
11623 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11624 * the data set size</em>
11626 getCount : function(){
11627 return this.data.length || 0;
11631 * Gets the total number of records in the dataset as returned by the server.
11633 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11634 * the dataset size</em>
11636 getTotalCount : function(){
11637 return this.totalLength || 0;
11641 * Returns the sort state of the Store as an object with two properties:
11643 field {String} The name of the field by which the Records are sorted
11644 direction {String} The sort order, "ASC" or "DESC"
11647 getSortState : function(){
11648 return this.sortInfo;
11652 applySort : function(){
11653 if(this.sortInfo && !this.remoteSort){
11654 var s = this.sortInfo, f = s.field;
11655 var st = this.fields.get(f).sortType;
11656 var fn = function(r1, r2){
11657 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11658 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11660 this.data.sort(s.direction, fn);
11661 if(this.snapshot && this.snapshot != this.data){
11662 this.snapshot.sort(s.direction, fn);
11668 * Sets the default sort column and order to be used by the next load operation.
11669 * @param {String} fieldName The name of the field to sort by.
11670 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11672 setDefaultSort : function(field, dir){
11673 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11677 * Sort the Records.
11678 * If remote sorting is used, the sort is performed on the server, and the cache is
11679 * reloaded. If local sorting is used, the cache is sorted internally.
11680 * @param {String} fieldName The name of the field to sort by.
11681 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11683 sort : function(fieldName, dir){
11684 var f = this.fields.get(fieldName);
11686 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11688 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11689 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11694 this.sortToggle[f.name] = dir;
11695 this.sortInfo = {field: f.name, direction: dir};
11696 if(!this.remoteSort){
11698 this.fireEvent("datachanged", this);
11700 this.load(this.lastOptions);
11705 * Calls the specified function for each of the Records in the cache.
11706 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11707 * Returning <em>false</em> aborts and exits the iteration.
11708 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11710 each : function(fn, scope){
11711 this.data.each(fn, scope);
11715 * Gets all records modified since the last commit. Modified records are persisted across load operations
11716 * (e.g., during paging).
11717 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11719 getModifiedRecords : function(){
11720 return this.modified;
11724 createFilterFn : function(property, value, anyMatch){
11725 if(!value.exec){ // not a regex
11726 value = String(value);
11727 if(value.length == 0){
11730 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11732 return function(r){
11733 return value.test(r.data[property]);
11738 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11739 * @param {String} property A field on your records
11740 * @param {Number} start The record index to start at (defaults to 0)
11741 * @param {Number} end The last record index to include (defaults to length - 1)
11742 * @return {Number} The sum
11744 sum : function(property, start, end){
11745 var rs = this.data.items, v = 0;
11746 start = start || 0;
11747 end = (end || end === 0) ? end : rs.length-1;
11749 for(var i = start; i <= end; i++){
11750 v += (rs[i].data[property] || 0);
11756 * Filter the records by a specified property.
11757 * @param {String} field A field on your records
11758 * @param {String/RegExp} value Either a string that the field
11759 * should start with or a RegExp to test against the field
11760 * @param {Boolean} anyMatch True to match any part not just the beginning
11762 filter : function(property, value, anyMatch){
11763 var fn = this.createFilterFn(property, value, anyMatch);
11764 return fn ? this.filterBy(fn) : this.clearFilter();
11768 * Filter by a function. The specified function will be called with each
11769 * record in this data source. If the function returns true the record is included,
11770 * otherwise it is filtered.
11771 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11772 * @param {Object} scope (optional) The scope of the function (defaults to this)
11774 filterBy : function(fn, scope){
11775 this.snapshot = this.snapshot || this.data;
11776 this.data = this.queryBy(fn, scope||this);
11777 this.fireEvent("datachanged", this);
11781 * Query the records by a specified property.
11782 * @param {String} field A field on your records
11783 * @param {String/RegExp} value Either a string that the field
11784 * should start with or a RegExp to test against the field
11785 * @param {Boolean} anyMatch True to match any part not just the beginning
11786 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11788 query : function(property, value, anyMatch){
11789 var fn = this.createFilterFn(property, value, anyMatch);
11790 return fn ? this.queryBy(fn) : this.data.clone();
11794 * Query by a function. The specified function will be called with each
11795 * record in this data source. If the function returns true the record is included
11797 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11798 * @param {Object} scope (optional) The scope of the function (defaults to this)
11799 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11801 queryBy : function(fn, scope){
11802 var data = this.snapshot || this.data;
11803 return data.filterBy(fn, scope||this);
11807 * Collects unique values for a particular dataIndex from this store.
11808 * @param {String} dataIndex The property to collect
11809 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11810 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11811 * @return {Array} An array of the unique values
11813 collect : function(dataIndex, allowNull, bypassFilter){
11814 var d = (bypassFilter === true && this.snapshot) ?
11815 this.snapshot.items : this.data.items;
11816 var v, sv, r = [], l = {};
11817 for(var i = 0, len = d.length; i < len; i++){
11818 v = d[i].data[dataIndex];
11820 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11829 * Revert to a view of the Record cache with no filtering applied.
11830 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11832 clearFilter : function(suppressEvent){
11833 if(this.snapshot && this.snapshot != this.data){
11834 this.data = this.snapshot;
11835 delete this.snapshot;
11836 if(suppressEvent !== true){
11837 this.fireEvent("datachanged", this);
11843 afterEdit : function(record){
11844 if(this.modified.indexOf(record) == -1){
11845 this.modified.push(record);
11847 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11851 afterReject : function(record){
11852 this.modified.remove(record);
11853 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11857 afterCommit : function(record){
11858 this.modified.remove(record);
11859 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11863 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11864 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11866 commitChanges : function(){
11867 var m = this.modified.slice(0);
11868 this.modified = [];
11869 for(var i = 0, len = m.length; i < len; i++){
11875 * Cancel outstanding changes on all changed records.
11877 rejectChanges : function(){
11878 var m = this.modified.slice(0);
11879 this.modified = [];
11880 for(var i = 0, len = m.length; i < len; i++){
11885 onMetaChange : function(meta, rtype, o){
11886 this.recordType = rtype;
11887 this.fields = rtype.prototype.fields;
11888 delete this.snapshot;
11889 this.sortInfo = meta.sortInfo || this.sortInfo;
11890 this.modified = [];
11891 this.fireEvent('metachange', this, this.reader.meta);
11894 moveIndex : function(data, type)
11896 var index = this.indexOf(data);
11898 var newIndex = index + type;
11902 this.insert(newIndex, data);
11907 * Ext JS Library 1.1.1
11908 * Copyright(c) 2006-2007, Ext JS, LLC.
11910 * Originally Released Under LGPL - original licence link has changed is not relivant.
11913 * <script type="text/javascript">
11917 * @class Roo.data.SimpleStore
11918 * @extends Roo.data.Store
11919 * Small helper class to make creating Stores from Array data easier.
11920 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11921 * @cfg {Array} fields An array of field definition objects, or field name strings.
11922 * @cfg {Array} data The multi-dimensional array of data
11924 * @param {Object} config
11926 Roo.data.SimpleStore = function(config){
11927 Roo.data.SimpleStore.superclass.constructor.call(this, {
11929 reader: new Roo.data.ArrayReader({
11932 Roo.data.Record.create(config.fields)
11934 proxy : new Roo.data.MemoryProxy(config.data)
11938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11940 * Ext JS Library 1.1.1
11941 * Copyright(c) 2006-2007, Ext JS, LLC.
11943 * Originally Released Under LGPL - original licence link has changed is not relivant.
11946 * <script type="text/javascript">
11951 * @extends Roo.data.Store
11952 * @class Roo.data.JsonStore
11953 * Small helper class to make creating Stores for JSON data easier. <br/>
11955 var store = new Roo.data.JsonStore({
11956 url: 'get-images.php',
11958 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11961 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11962 * JsonReader and HttpProxy (unless inline data is provided).</b>
11963 * @cfg {Array} fields An array of field definition objects, or field name strings.
11965 * @param {Object} config
11967 Roo.data.JsonStore = function(c){
11968 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11969 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11970 reader: new Roo.data.JsonReader(c, c.fields)
11973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11975 * Ext JS Library 1.1.1
11976 * Copyright(c) 2006-2007, Ext JS, LLC.
11978 * Originally Released Under LGPL - original licence link has changed is not relivant.
11981 * <script type="text/javascript">
11985 Roo.data.Field = function(config){
11986 if(typeof config == "string"){
11987 config = {name: config};
11989 Roo.apply(this, config);
11992 this.type = "auto";
11995 var st = Roo.data.SortTypes;
11996 // named sortTypes are supported, here we look them up
11997 if(typeof this.sortType == "string"){
11998 this.sortType = st[this.sortType];
12001 // set default sortType for strings and dates
12002 if(!this.sortType){
12005 this.sortType = st.asUCString;
12008 this.sortType = st.asDate;
12011 this.sortType = st.none;
12016 var stripRe = /[\$,%]/g;
12018 // prebuilt conversion function for this field, instead of
12019 // switching every time we're reading a value
12021 var cv, dateFormat = this.dateFormat;
12026 cv = function(v){ return v; };
12029 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12033 return v !== undefined && v !== null && v !== '' ?
12034 parseInt(String(v).replace(stripRe, ""), 10) : '';
12039 return v !== undefined && v !== null && v !== '' ?
12040 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12045 cv = function(v){ return v === true || v === "true" || v == 1; };
12052 if(v instanceof Date){
12056 if(dateFormat == "timestamp"){
12057 return new Date(v*1000);
12059 return Date.parseDate(v, dateFormat);
12061 var parsed = Date.parse(v);
12062 return parsed ? new Date(parsed) : null;
12071 Roo.data.Field.prototype = {
12079 * Ext JS Library 1.1.1
12080 * Copyright(c) 2006-2007, Ext JS, LLC.
12082 * Originally Released Under LGPL - original licence link has changed is not relivant.
12085 * <script type="text/javascript">
12088 // Base class for reading structured data from a data source. This class is intended to be
12089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12092 * @class Roo.data.DataReader
12093 * Base class for reading structured data from a data source. This class is intended to be
12094 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12097 Roo.data.DataReader = function(meta, recordType){
12101 this.recordType = recordType instanceof Array ?
12102 Roo.data.Record.create(recordType) : recordType;
12105 Roo.data.DataReader.prototype = {
12107 * Create an empty record
12108 * @param {Object} data (optional) - overlay some values
12109 * @return {Roo.data.Record} record created.
12111 newRow : function(d) {
12113 this.recordType.prototype.fields.each(function(c) {
12115 case 'int' : da[c.name] = 0; break;
12116 case 'date' : da[c.name] = new Date(); break;
12117 case 'float' : da[c.name] = 0.0; break;
12118 case 'boolean' : da[c.name] = false; break;
12119 default : da[c.name] = ""; break;
12123 return new this.recordType(Roo.apply(da, d));
12128 * Ext JS Library 1.1.1
12129 * Copyright(c) 2006-2007, Ext JS, LLC.
12131 * Originally Released Under LGPL - original licence link has changed is not relivant.
12134 * <script type="text/javascript">
12138 * @class Roo.data.DataProxy
12139 * @extends Roo.data.Observable
12140 * This class is an abstract base class for implementations which provide retrieval of
12141 * unformatted data objects.<br>
12143 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12144 * (of the appropriate type which knows how to parse the data object) to provide a block of
12145 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12147 * Custom implementations must implement the load method as described in
12148 * {@link Roo.data.HttpProxy#load}.
12150 Roo.data.DataProxy = function(){
12153 * @event beforeload
12154 * Fires before a network request is made to retrieve a data object.
12155 * @param {Object} This DataProxy object.
12156 * @param {Object} params The params parameter to the load function.
12161 * Fires before the load method's callback is called.
12162 * @param {Object} This DataProxy object.
12163 * @param {Object} o The data object.
12164 * @param {Object} arg The callback argument object passed to the load function.
12168 * @event loadexception
12169 * Fires if an Exception occurs during data retrieval.
12170 * @param {Object} This DataProxy object.
12171 * @param {Object} o The data object.
12172 * @param {Object} arg The callback argument object passed to the load function.
12173 * @param {Object} e The Exception.
12175 loadexception : true
12177 Roo.data.DataProxy.superclass.constructor.call(this);
12180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12183 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12187 * Ext JS Library 1.1.1
12188 * Copyright(c) 2006-2007, Ext JS, LLC.
12190 * Originally Released Under LGPL - original licence link has changed is not relivant.
12193 * <script type="text/javascript">
12196 * @class Roo.data.MemoryProxy
12197 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12198 * to the Reader when its load method is called.
12200 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12202 Roo.data.MemoryProxy = function(data){
12206 Roo.data.MemoryProxy.superclass.constructor.call(this);
12210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12213 * Load data from the requested source (in this case an in-memory
12214 * data object passed to the constructor), read the data object into
12215 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12216 * process that block using the passed callback.
12217 * @param {Object} params This parameter is not used by the MemoryProxy class.
12218 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12219 * object into a block of Roo.data.Records.
12220 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12221 * The function must be passed <ul>
12222 * <li>The Record block object</li>
12223 * <li>The "arg" argument from the load function</li>
12224 * <li>A boolean success indicator</li>
12226 * @param {Object} scope The scope in which to call the callback
12227 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12229 load : function(params, reader, callback, scope, arg){
12230 params = params || {};
12233 result = reader.readRecords(this.data);
12235 this.fireEvent("loadexception", this, arg, null, e);
12236 callback.call(scope, null, arg, false);
12239 callback.call(scope, result, arg, true);
12243 update : function(params, records){
12248 * Ext JS Library 1.1.1
12249 * Copyright(c) 2006-2007, Ext JS, LLC.
12251 * Originally Released Under LGPL - original licence link has changed is not relivant.
12254 * <script type="text/javascript">
12257 * @class Roo.data.HttpProxy
12258 * @extends Roo.data.DataProxy
12259 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12260 * configured to reference a certain URL.<br><br>
12262 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12263 * from which the running page was served.<br><br>
12265 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12267 * Be aware that to enable the browser to parse an XML document, the server must set
12268 * the Content-Type header in the HTTP response to "text/xml".
12270 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12271 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12272 * will be used to make the request.
12274 Roo.data.HttpProxy = function(conn){
12275 Roo.data.HttpProxy.superclass.constructor.call(this);
12276 // is conn a conn config or a real conn?
12278 this.useAjax = !conn || !conn.events;
12282 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12283 // thse are take from connection...
12286 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12289 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12290 * extra parameters to each request made by this object. (defaults to undefined)
12293 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12294 * to each request made by this object. (defaults to undefined)
12297 * @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)
12300 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12303 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12309 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12313 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12314 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12315 * a finer-grained basis than the DataProxy events.
12317 getConnection : function(){
12318 return this.useAjax ? Roo.Ajax : this.conn;
12322 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12323 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12324 * process that block using the passed callback.
12325 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12326 * for the request to the remote server.
12327 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12328 * object into a block of Roo.data.Records.
12329 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12330 * The function must be passed <ul>
12331 * <li>The Record block object</li>
12332 * <li>The "arg" argument from the load function</li>
12333 * <li>A boolean success indicator</li>
12335 * @param {Object} scope The scope in which to call the callback
12336 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12338 load : function(params, reader, callback, scope, arg){
12339 if(this.fireEvent("beforeload", this, params) !== false){
12341 params : params || {},
12343 callback : callback,
12348 callback : this.loadResponse,
12352 Roo.applyIf(o, this.conn);
12353 if(this.activeRequest){
12354 Roo.Ajax.abort(this.activeRequest);
12356 this.activeRequest = Roo.Ajax.request(o);
12358 this.conn.request(o);
12361 callback.call(scope||this, null, arg, false);
12366 loadResponse : function(o, success, response){
12367 delete this.activeRequest;
12369 this.fireEvent("loadexception", this, o, response);
12370 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12375 result = o.reader.read(response);
12377 this.fireEvent("loadexception", this, o, response, e);
12378 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12382 this.fireEvent("load", this, o, o.request.arg);
12383 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12387 update : function(dataSet){
12392 updateResponse : function(dataSet){
12397 * Ext JS Library 1.1.1
12398 * Copyright(c) 2006-2007, Ext JS, LLC.
12400 * Originally Released Under LGPL - original licence link has changed is not relivant.
12403 * <script type="text/javascript">
12407 * @class Roo.data.ScriptTagProxy
12408 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12409 * other than the originating domain of the running page.<br><br>
12411 * <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
12412 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12414 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12415 * source code that is used as the source inside a <script> tag.<br><br>
12417 * In order for the browser to process the returned data, the server must wrap the data object
12418 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12419 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12420 * depending on whether the callback name was passed:
12423 boolean scriptTag = false;
12424 String cb = request.getParameter("callback");
12427 response.setContentType("text/javascript");
12429 response.setContentType("application/x-json");
12431 Writer out = response.getWriter();
12433 out.write(cb + "(");
12435 out.print(dataBlock.toJsonString());
12442 * @param {Object} config A configuration object.
12444 Roo.data.ScriptTagProxy = function(config){
12445 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12446 Roo.apply(this, config);
12447 this.head = document.getElementsByTagName("head")[0];
12450 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12452 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12454 * @cfg {String} url The URL from which to request the data object.
12457 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12461 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12462 * the server the name of the callback function set up by the load call to process the returned data object.
12463 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12464 * javascript output which calls this named function passing the data object as its only parameter.
12466 callbackParam : "callback",
12468 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12469 * name to the request.
12474 * Load data from the configured URL, read the data object into
12475 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12476 * process that block using the passed callback.
12477 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12478 * for the request to the remote server.
12479 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12480 * object into a block of Roo.data.Records.
12481 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12482 * The function must be passed <ul>
12483 * <li>The Record block object</li>
12484 * <li>The "arg" argument from the load function</li>
12485 * <li>A boolean success indicator</li>
12487 * @param {Object} scope The scope in which to call the callback
12488 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12490 load : function(params, reader, callback, scope, arg){
12491 if(this.fireEvent("beforeload", this, params) !== false){
12493 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12495 var url = this.url;
12496 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12498 url += "&_dc=" + (new Date().getTime());
12500 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12503 cb : "stcCallback"+transId,
12504 scriptId : "stcScript"+transId,
12508 callback : callback,
12514 window[trans.cb] = function(o){
12515 conn.handleResponse(o, trans);
12518 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12520 if(this.autoAbort !== false){
12524 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12526 var script = document.createElement("script");
12527 script.setAttribute("src", url);
12528 script.setAttribute("type", "text/javascript");
12529 script.setAttribute("id", trans.scriptId);
12530 this.head.appendChild(script);
12532 this.trans = trans;
12534 callback.call(scope||this, null, arg, false);
12539 isLoading : function(){
12540 return this.trans ? true : false;
12544 * Abort the current server request.
12546 abort : function(){
12547 if(this.isLoading()){
12548 this.destroyTrans(this.trans);
12553 destroyTrans : function(trans, isLoaded){
12554 this.head.removeChild(document.getElementById(trans.scriptId));
12555 clearTimeout(trans.timeoutId);
12557 window[trans.cb] = undefined;
12559 delete window[trans.cb];
12562 // if hasn't been loaded, wait for load to remove it to prevent script error
12563 window[trans.cb] = function(){
12564 window[trans.cb] = undefined;
12566 delete window[trans.cb];
12573 handleResponse : function(o, trans){
12574 this.trans = false;
12575 this.destroyTrans(trans, true);
12578 result = trans.reader.readRecords(o);
12580 this.fireEvent("loadexception", this, o, trans.arg, e);
12581 trans.callback.call(trans.scope||window, null, trans.arg, false);
12584 this.fireEvent("load", this, o, trans.arg);
12585 trans.callback.call(trans.scope||window, result, trans.arg, true);
12589 handleFailure : function(trans){
12590 this.trans = false;
12591 this.destroyTrans(trans, false);
12592 this.fireEvent("loadexception", this, null, trans.arg);
12593 trans.callback.call(trans.scope||window, null, trans.arg, false);
12597 * Ext JS Library 1.1.1
12598 * Copyright(c) 2006-2007, Ext JS, LLC.
12600 * Originally Released Under LGPL - original licence link has changed is not relivant.
12603 * <script type="text/javascript">
12607 * @class Roo.data.JsonReader
12608 * @extends Roo.data.DataReader
12609 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12610 * based on mappings in a provided Roo.data.Record constructor.
12612 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12613 * in the reply previously.
12618 var RecordDef = Roo.data.Record.create([
12619 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12620 {name: 'occupation'} // This field will use "occupation" as the mapping.
12622 var myReader = new Roo.data.JsonReader({
12623 totalProperty: "results", // The property which contains the total dataset size (optional)
12624 root: "rows", // The property which contains an Array of row objects
12625 id: "id" // The property within each row object that provides an ID for the record (optional)
12629 * This would consume a JSON file like this:
12631 { 'results': 2, 'rows': [
12632 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12633 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12636 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12637 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12638 * paged from the remote server.
12639 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12640 * @cfg {String} root name of the property which contains the Array of row objects.
12641 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12642 * @cfg {Array} fields Array of field definition objects
12644 * Create a new JsonReader
12645 * @param {Object} meta Metadata configuration options
12646 * @param {Object} recordType Either an Array of field definition objects,
12647 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12649 Roo.data.JsonReader = function(meta, recordType){
12652 // set some defaults:
12653 Roo.applyIf(meta, {
12654 totalProperty: 'total',
12655 successProperty : 'success',
12660 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12662 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12665 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12666 * Used by Store query builder to append _requestMeta to params.
12669 metaFromRemote : false,
12671 * This method is only used by a DataProxy which has retrieved data from a remote server.
12672 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12673 * @return {Object} data A data block which is used by an Roo.data.Store object as
12674 * a cache of Roo.data.Records.
12676 read : function(response){
12677 var json = response.responseText;
12679 var o = /* eval:var:o */ eval("("+json+")");
12681 throw {message: "JsonReader.read: Json object not found"};
12687 this.metaFromRemote = true;
12688 this.meta = o.metaData;
12689 this.recordType = Roo.data.Record.create(o.metaData.fields);
12690 this.onMetaChange(this.meta, this.recordType, o);
12692 return this.readRecords(o);
12695 // private function a store will implement
12696 onMetaChange : function(meta, recordType, o){
12703 simpleAccess: function(obj, subsc) {
12710 getJsonAccessor: function(){
12712 return function(expr) {
12714 return(re.test(expr))
12715 ? new Function("obj", "return obj." + expr)
12720 return Roo.emptyFn;
12725 * Create a data block containing Roo.data.Records from an XML document.
12726 * @param {Object} o An object which contains an Array of row objects in the property specified
12727 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12728 * which contains the total size of the dataset.
12729 * @return {Object} data A data block which is used by an Roo.data.Store object as
12730 * a cache of Roo.data.Records.
12732 readRecords : function(o){
12734 * After any data loads, the raw JSON data is available for further custom processing.
12738 var s = this.meta, Record = this.recordType,
12739 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12741 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12743 if(s.totalProperty) {
12744 this.getTotal = this.getJsonAccessor(s.totalProperty);
12746 if(s.successProperty) {
12747 this.getSuccess = this.getJsonAccessor(s.successProperty);
12749 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12751 var g = this.getJsonAccessor(s.id);
12752 this.getId = function(rec) {
12754 return (r === undefined || r === "") ? null : r;
12757 this.getId = function(){return null;};
12760 for(var jj = 0; jj < fl; jj++){
12762 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12763 this.ef[jj] = this.getJsonAccessor(map);
12767 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12768 if(s.totalProperty){
12769 var vt = parseInt(this.getTotal(o), 10);
12774 if(s.successProperty){
12775 var vs = this.getSuccess(o);
12776 if(vs === false || vs === 'false'){
12781 for(var i = 0; i < c; i++){
12784 var id = this.getId(n);
12785 for(var j = 0; j < fl; j++){
12787 var v = this.ef[j](n);
12789 Roo.log('missing convert for ' + f.name);
12793 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12795 var record = new Record(values, id);
12797 records[i] = record;
12803 totalRecords : totalRecords
12808 * Ext JS Library 1.1.1
12809 * Copyright(c) 2006-2007, Ext JS, LLC.
12811 * Originally Released Under LGPL - original licence link has changed is not relivant.
12814 * <script type="text/javascript">
12818 * @class Roo.data.ArrayReader
12819 * @extends Roo.data.DataReader
12820 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12821 * Each element of that Array represents a row of data fields. The
12822 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12823 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12827 var RecordDef = Roo.data.Record.create([
12828 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12829 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12831 var myReader = new Roo.data.ArrayReader({
12832 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12836 * This would consume an Array like this:
12838 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12840 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12842 * Create a new JsonReader
12843 * @param {Object} meta Metadata configuration options.
12844 * @param {Object} recordType Either an Array of field definition objects
12845 * as specified to {@link Roo.data.Record#create},
12846 * or an {@link Roo.data.Record} object
12847 * created using {@link Roo.data.Record#create}.
12849 Roo.data.ArrayReader = function(meta, recordType){
12850 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12853 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12855 * Create a data block containing Roo.data.Records from an XML document.
12856 * @param {Object} o An Array of row objects which represents the dataset.
12857 * @return {Object} data A data block which is used by an Roo.data.Store object as
12858 * a cache of Roo.data.Records.
12860 readRecords : function(o){
12861 var sid = this.meta ? this.meta.id : null;
12862 var recordType = this.recordType, fields = recordType.prototype.fields;
12865 for(var i = 0; i < root.length; i++){
12868 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12869 for(var j = 0, jlen = fields.length; j < jlen; j++){
12870 var f = fields.items[j];
12871 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12872 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12874 values[f.name] = v;
12876 var record = new recordType(values, id);
12878 records[records.length] = record;
12882 totalRecords : records.length
12891 * @class Roo.bootstrap.ComboBox
12892 * @extends Roo.bootstrap.TriggerField
12893 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12894 * @cfg {Boolean} append (true|false) default false
12895 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12896 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12897 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12898 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12899 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12900 * @cfg {Boolean} animate default true
12901 * @cfg {Boolean} emptyResultText only for touch device
12902 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12903 * @cfg {String} emptyTitle default ''
12905 * Create a new ComboBox.
12906 * @param {Object} config Configuration options
12908 Roo.bootstrap.ComboBox = function(config){
12909 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12913 * Fires when the dropdown list is expanded
12914 * @param {Roo.bootstrap.ComboBox} combo This combo box
12919 * Fires when the dropdown list is collapsed
12920 * @param {Roo.bootstrap.ComboBox} combo This combo box
12924 * @event beforeselect
12925 * Fires before a list item is selected. Return false to cancel the selection.
12926 * @param {Roo.bootstrap.ComboBox} combo This combo box
12927 * @param {Roo.data.Record} record The data record returned from the underlying store
12928 * @param {Number} index The index of the selected item in the dropdown list
12930 'beforeselect' : true,
12933 * Fires when a list item is selected
12934 * @param {Roo.bootstrap.ComboBox} combo This combo box
12935 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12936 * @param {Number} index The index of the selected item in the dropdown list
12940 * @event beforequery
12941 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12942 * The event object passed has these properties:
12943 * @param {Roo.bootstrap.ComboBox} combo This combo box
12944 * @param {String} query The query
12945 * @param {Boolean} forceAll true to force "all" query
12946 * @param {Boolean} cancel true to cancel the query
12947 * @param {Object} e The query event object
12949 'beforequery': true,
12952 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12953 * @param {Roo.bootstrap.ComboBox} combo This combo box
12958 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12959 * @param {Roo.bootstrap.ComboBox} combo This combo box
12960 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12965 * Fires when the remove value from the combobox array
12966 * @param {Roo.bootstrap.ComboBox} combo This combo box
12970 * @event afterremove
12971 * Fires when the remove value from the combobox array
12972 * @param {Roo.bootstrap.ComboBox} combo This combo box
12974 'afterremove' : true,
12976 * @event specialfilter
12977 * Fires when specialfilter
12978 * @param {Roo.bootstrap.ComboBox} combo This combo box
12980 'specialfilter' : true,
12983 * Fires when tick the element
12984 * @param {Roo.bootstrap.ComboBox} combo This combo box
12988 * @event touchviewdisplay
12989 * Fires when touch view require special display (default is using displayField)
12990 * @param {Roo.bootstrap.ComboBox} combo This combo box
12991 * @param {Object} cfg set html .
12993 'touchviewdisplay' : true
12998 this.tickItems = [];
13000 this.selectedIndex = -1;
13001 if(this.mode == 'local'){
13002 if(config.queryDelay === undefined){
13003 this.queryDelay = 10;
13005 if(config.minChars === undefined){
13011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13014 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13015 * rendering into an Roo.Editor, defaults to false)
13018 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13019 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13022 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13025 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13026 * the dropdown list (defaults to undefined, with no header element)
13030 * @cfg {String/Roo.Template} tpl The template to use to render the output
13034 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13036 listWidth: undefined,
13038 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13039 * mode = 'remote' or 'text' if mode = 'local')
13041 displayField: undefined,
13044 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13045 * mode = 'remote' or 'value' if mode = 'local').
13046 * Note: use of a valueField requires the user make a selection
13047 * in order for a value to be mapped.
13049 valueField: undefined,
13051 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13056 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13057 * field's data value (defaults to the underlying DOM element's name)
13059 hiddenName: undefined,
13061 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13065 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13067 selectedClass: 'active',
13070 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13074 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13075 * anchor positions (defaults to 'tl-bl')
13077 listAlign: 'tl-bl?',
13079 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13083 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13084 * query specified by the allQuery config option (defaults to 'query')
13086 triggerAction: 'query',
13088 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13089 * (defaults to 4, does not apply if editable = false)
13093 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13094 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13098 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13099 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13103 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13104 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13108 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13109 * when editable = true (defaults to false)
13111 selectOnFocus:false,
13113 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13115 queryParam: 'query',
13117 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13118 * when mode = 'remote' (defaults to 'Loading...')
13120 loadingText: 'Loading...',
13122 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13126 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13130 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13131 * traditional select (defaults to true)
13135 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13139 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13143 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13144 * listWidth has a higher value)
13148 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13149 * allow the user to set arbitrary text into the field (defaults to false)
13151 forceSelection:false,
13153 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13154 * if typeAhead = true (defaults to 250)
13156 typeAheadDelay : 250,
13158 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13159 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13161 valueNotFoundText : undefined,
13163 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13165 blockFocus : false,
13168 * @cfg {Boolean} disableClear Disable showing of clear button.
13170 disableClear : false,
13172 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13174 alwaysQuery : false,
13177 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13182 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13184 invalidClass : "has-warning",
13187 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13189 validClass : "has-success",
13192 * @cfg {Boolean} specialFilter (true|false) special filter default false
13194 specialFilter : false,
13197 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13199 mobileTouchView : true,
13202 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13204 useNativeIOS : false,
13207 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13209 mobile_restrict_height : false,
13211 ios_options : false,
13223 btnPosition : 'right',
13224 triggerList : true,
13225 showToggleBtn : true,
13227 emptyResultText: 'Empty',
13228 triggerText : 'Select',
13231 // element that contains real text value.. (when hidden is used..)
13233 getAutoCreate : function()
13238 * Render classic select for iso
13241 if(Roo.isIOS && this.useNativeIOS){
13242 cfg = this.getAutoCreateNativeIOS();
13250 if(Roo.isTouch && this.mobileTouchView){
13251 cfg = this.getAutoCreateTouchView();
13258 if(!this.tickable){
13259 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13264 * ComboBox with tickable selections
13267 var align = this.labelAlign || this.parentLabelAlign();
13270 cls : 'form-group roo-combobox-tickable' //input-group
13273 var btn_text_select = '';
13274 var btn_text_done = '';
13275 var btn_text_cancel = '';
13277 if (this.btn_text_show) {
13278 btn_text_select = 'Select';
13279 btn_text_done = 'Done';
13280 btn_text_cancel = 'Cancel';
13285 cls : 'tickable-buttons',
13290 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13291 //html : this.triggerText
13292 html: btn_text_select
13298 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13300 html: btn_text_done
13306 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13308 html: btn_text_cancel
13314 buttons.cn.unshift({
13316 cls: 'roo-select2-search-field-input'
13322 Roo.each(buttons.cn, function(c){
13324 c.cls += ' btn-' + _this.size;
13327 if (_this.disabled) {
13338 cls: 'form-hidden-field'
13342 cls: 'roo-select2-choices',
13346 cls: 'roo-select2-search-field',
13357 cls: 'roo-select2-container input-group roo-select2-container-multi',
13363 // cls: 'typeahead typeahead-long dropdown-menu',
13364 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13369 if(this.hasFeedback && !this.allowBlank){
13373 cls: 'glyphicon form-control-feedback'
13376 combobox.cn.push(feedback);
13381 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13382 tooltip : 'This field is required'
13384 if (Roo.bootstrap.version == 4) {
13387 style : 'display:none'
13390 if (align ==='left' && this.fieldLabel.length) {
13392 cfg.cls += ' roo-form-group-label-left row';
13399 cls : 'control-label col-form-label',
13400 html : this.fieldLabel
13412 var labelCfg = cfg.cn[1];
13413 var contentCfg = cfg.cn[2];
13416 if(this.indicatorpos == 'right'){
13422 cls : 'control-label col-form-label',
13426 html : this.fieldLabel
13442 labelCfg = cfg.cn[0];
13443 contentCfg = cfg.cn[1];
13447 if(this.labelWidth > 12){
13448 labelCfg.style = "width: " + this.labelWidth + 'px';
13451 if(this.labelWidth < 13 && this.labelmd == 0){
13452 this.labelmd = this.labelWidth;
13455 if(this.labellg > 0){
13456 labelCfg.cls += ' col-lg-' + this.labellg;
13457 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13460 if(this.labelmd > 0){
13461 labelCfg.cls += ' col-md-' + this.labelmd;
13462 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13465 if(this.labelsm > 0){
13466 labelCfg.cls += ' col-sm-' + this.labelsm;
13467 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13470 if(this.labelxs > 0){
13471 labelCfg.cls += ' col-xs-' + this.labelxs;
13472 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13476 } else if ( this.fieldLabel.length) {
13477 // Roo.log(" label");
13482 //cls : 'input-group-addon',
13483 html : this.fieldLabel
13488 if(this.indicatorpos == 'right'){
13492 //cls : 'input-group-addon',
13493 html : this.fieldLabel
13503 // Roo.log(" no label && no align");
13510 ['xs','sm','md','lg'].map(function(size){
13511 if (settings[size]) {
13512 cfg.cls += ' col-' + size + '-' + settings[size];
13520 _initEventsCalled : false,
13523 initEvents: function()
13525 if (this._initEventsCalled) { // as we call render... prevent looping...
13528 this._initEventsCalled = true;
13531 throw "can not find store for combo";
13534 this.indicator = this.indicatorEl();
13536 this.store = Roo.factory(this.store, Roo.data);
13537 this.store.parent = this;
13539 // if we are building from html. then this element is so complex, that we can not really
13540 // use the rendered HTML.
13541 // so we have to trash and replace the previous code.
13542 if (Roo.XComponent.build_from_html) {
13543 // remove this element....
13544 var e = this.el.dom, k=0;
13545 while (e ) { e = e.previousSibling; ++k;}
13550 this.rendered = false;
13552 this.render(this.parent().getChildContainer(true), k);
13555 if(Roo.isIOS && this.useNativeIOS){
13556 this.initIOSView();
13564 if(Roo.isTouch && this.mobileTouchView){
13565 this.initTouchView();
13570 this.initTickableEvents();
13574 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13576 if(this.hiddenName){
13578 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13580 this.hiddenField.dom.value =
13581 this.hiddenValue !== undefined ? this.hiddenValue :
13582 this.value !== undefined ? this.value : '';
13584 // prevent input submission
13585 this.el.dom.removeAttribute('name');
13586 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13591 // this.el.dom.setAttribute('autocomplete', 'off');
13594 var cls = 'x-combo-list';
13596 //this.list = new Roo.Layer({
13597 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13603 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13604 _this.list.setWidth(lw);
13607 this.list.on('mouseover', this.onViewOver, this);
13608 this.list.on('mousemove', this.onViewMove, this);
13609 this.list.on('scroll', this.onViewScroll, this);
13612 this.list.swallowEvent('mousewheel');
13613 this.assetHeight = 0;
13616 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13617 this.assetHeight += this.header.getHeight();
13620 this.innerList = this.list.createChild({cls:cls+'-inner'});
13621 this.innerList.on('mouseover', this.onViewOver, this);
13622 this.innerList.on('mousemove', this.onViewMove, this);
13623 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13625 if(this.allowBlank && !this.pageSize && !this.disableClear){
13626 this.footer = this.list.createChild({cls:cls+'-ft'});
13627 this.pageTb = new Roo.Toolbar(this.footer);
13631 this.footer = this.list.createChild({cls:cls+'-ft'});
13632 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13633 {pageSize: this.pageSize});
13637 if (this.pageTb && this.allowBlank && !this.disableClear) {
13639 this.pageTb.add(new Roo.Toolbar.Fill(), {
13640 cls: 'x-btn-icon x-btn-clear',
13642 handler: function()
13645 _this.clearValue();
13646 _this.onSelect(false, -1);
13651 this.assetHeight += this.footer.getHeight();
13656 this.tpl = Roo.bootstrap.version == 4 ?
13657 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13658 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13661 this.view = new Roo.View(this.list, this.tpl, {
13662 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13664 //this.view.wrapEl.setDisplayed(false);
13665 this.view.on('click', this.onViewClick, this);
13668 this.store.on('beforeload', this.onBeforeLoad, this);
13669 this.store.on('load', this.onLoad, this);
13670 this.store.on('loadexception', this.onLoadException, this);
13672 if(this.resizable){
13673 this.resizer = new Roo.Resizable(this.list, {
13674 pinned:true, handles:'se'
13676 this.resizer.on('resize', function(r, w, h){
13677 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13678 this.listWidth = w;
13679 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13680 this.restrictHeight();
13682 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13685 if(!this.editable){
13686 this.editable = true;
13687 this.setEditable(false);
13692 if (typeof(this.events.add.listeners) != 'undefined') {
13694 this.addicon = this.wrap.createChild(
13695 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13697 this.addicon.on('click', function(e) {
13698 this.fireEvent('add', this);
13701 if (typeof(this.events.edit.listeners) != 'undefined') {
13703 this.editicon = this.wrap.createChild(
13704 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13705 if (this.addicon) {
13706 this.editicon.setStyle('margin-left', '40px');
13708 this.editicon.on('click', function(e) {
13710 // we fire even if inothing is selected..
13711 this.fireEvent('edit', this, this.lastData );
13717 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13718 "up" : function(e){
13719 this.inKeyMode = true;
13723 "down" : function(e){
13724 if(!this.isExpanded()){
13725 this.onTriggerClick();
13727 this.inKeyMode = true;
13732 "enter" : function(e){
13733 // this.onViewClick();
13737 if(this.fireEvent("specialkey", this, e)){
13738 this.onViewClick(false);
13744 "esc" : function(e){
13748 "tab" : function(e){
13751 if(this.fireEvent("specialkey", this, e)){
13752 this.onViewClick(false);
13760 doRelay : function(foo, bar, hname){
13761 if(hname == 'down' || this.scope.isExpanded()){
13762 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13771 this.queryDelay = Math.max(this.queryDelay || 10,
13772 this.mode == 'local' ? 10 : 250);
13775 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13777 if(this.typeAhead){
13778 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13780 if(this.editable !== false){
13781 this.inputEl().on("keyup", this.onKeyUp, this);
13783 if(this.forceSelection){
13784 this.inputEl().on('blur', this.doForce, this);
13788 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13789 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13793 initTickableEvents: function()
13797 if(this.hiddenName){
13799 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13801 this.hiddenField.dom.value =
13802 this.hiddenValue !== undefined ? this.hiddenValue :
13803 this.value !== undefined ? this.value : '';
13805 // prevent input submission
13806 this.el.dom.removeAttribute('name');
13807 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13812 // this.list = this.el.select('ul.dropdown-menu',true).first();
13814 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13815 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13816 if(this.triggerList){
13817 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13820 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13821 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13823 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13824 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13826 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13827 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13829 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13830 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13831 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13834 this.cancelBtn.hide();
13839 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13840 _this.list.setWidth(lw);
13843 this.list.on('mouseover', this.onViewOver, this);
13844 this.list.on('mousemove', this.onViewMove, this);
13846 this.list.on('scroll', this.onViewScroll, this);
13849 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13850 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13853 this.view = new Roo.View(this.list, this.tpl, {
13858 selectedClass: this.selectedClass
13861 //this.view.wrapEl.setDisplayed(false);
13862 this.view.on('click', this.onViewClick, this);
13866 this.store.on('beforeload', this.onBeforeLoad, this);
13867 this.store.on('load', this.onLoad, this);
13868 this.store.on('loadexception', this.onLoadException, this);
13871 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13872 "up" : function(e){
13873 this.inKeyMode = true;
13877 "down" : function(e){
13878 this.inKeyMode = true;
13882 "enter" : function(e){
13883 if(this.fireEvent("specialkey", this, e)){
13884 this.onViewClick(false);
13890 "esc" : function(e){
13891 this.onTickableFooterButtonClick(e, false, false);
13894 "tab" : function(e){
13895 this.fireEvent("specialkey", this, e);
13897 this.onTickableFooterButtonClick(e, false, false);
13904 doRelay : function(e, fn, key){
13905 if(this.scope.isExpanded()){
13906 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13915 this.queryDelay = Math.max(this.queryDelay || 10,
13916 this.mode == 'local' ? 10 : 250);
13919 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13921 if(this.typeAhead){
13922 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13925 if(this.editable !== false){
13926 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13929 this.indicator = this.indicatorEl();
13931 if(this.indicator){
13932 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13933 this.indicator.hide();
13938 onDestroy : function(){
13940 this.view.setStore(null);
13941 this.view.el.removeAllListeners();
13942 this.view.el.remove();
13943 this.view.purgeListeners();
13946 this.list.dom.innerHTML = '';
13950 this.store.un('beforeload', this.onBeforeLoad, this);
13951 this.store.un('load', this.onLoad, this);
13952 this.store.un('loadexception', this.onLoadException, this);
13954 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13958 fireKey : function(e){
13959 if(e.isNavKeyPress() && !this.list.isVisible()){
13960 this.fireEvent("specialkey", this, e);
13965 onResize: function(w, h){
13966 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13968 // if(typeof w != 'number'){
13969 // // we do not handle it!?!?
13972 // var tw = this.trigger.getWidth();
13973 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13974 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13976 // this.inputEl().setWidth( this.adjustWidth('input', x));
13978 // //this.trigger.setStyle('left', x+'px');
13980 // if(this.list && this.listWidth === undefined){
13981 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13982 // this.list.setWidth(lw);
13983 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13991 * Allow or prevent the user from directly editing the field text. If false is passed,
13992 * the user will only be able to select from the items defined in the dropdown list. This method
13993 * is the runtime equivalent of setting the 'editable' config option at config time.
13994 * @param {Boolean} value True to allow the user to directly edit the field text
13996 setEditable : function(value){
13997 if(value == this.editable){
14000 this.editable = value;
14002 this.inputEl().dom.setAttribute('readOnly', true);
14003 this.inputEl().on('mousedown', this.onTriggerClick, this);
14004 this.inputEl().addClass('x-combo-noedit');
14006 this.inputEl().dom.setAttribute('readOnly', false);
14007 this.inputEl().un('mousedown', this.onTriggerClick, this);
14008 this.inputEl().removeClass('x-combo-noedit');
14014 onBeforeLoad : function(combo,opts){
14015 if(!this.hasFocus){
14019 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14021 this.restrictHeight();
14022 this.selectedIndex = -1;
14026 onLoad : function(){
14028 this.hasQuery = false;
14030 if(!this.hasFocus){
14034 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14035 this.loading.hide();
14038 if(this.store.getCount() > 0){
14041 this.restrictHeight();
14042 if(this.lastQuery == this.allQuery){
14043 if(this.editable && !this.tickable){
14044 this.inputEl().dom.select();
14048 !this.selectByValue(this.value, true) &&
14051 !this.store.lastOptions ||
14052 typeof(this.store.lastOptions.add) == 'undefined' ||
14053 this.store.lastOptions.add != true
14056 this.select(0, true);
14059 if(this.autoFocus){
14062 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14063 this.taTask.delay(this.typeAheadDelay);
14067 this.onEmptyResults();
14073 onLoadException : function()
14075 this.hasQuery = false;
14077 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14078 this.loading.hide();
14081 if(this.tickable && this.editable){
14086 // only causes errors at present
14087 //Roo.log(this.store.reader.jsonData);
14088 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14090 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14096 onTypeAhead : function(){
14097 if(this.store.getCount() > 0){
14098 var r = this.store.getAt(0);
14099 var newValue = r.data[this.displayField];
14100 var len = newValue.length;
14101 var selStart = this.getRawValue().length;
14103 if(selStart != len){
14104 this.setRawValue(newValue);
14105 this.selectText(selStart, newValue.length);
14111 onSelect : function(record, index){
14113 if(this.fireEvent('beforeselect', this, record, index) !== false){
14115 this.setFromData(index > -1 ? record.data : false);
14118 this.fireEvent('select', this, record, index);
14123 * Returns the currently selected field value or empty string if no value is set.
14124 * @return {String} value The selected value
14126 getValue : function()
14128 if(Roo.isIOS && this.useNativeIOS){
14129 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14133 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14136 if(this.valueField){
14137 return typeof this.value != 'undefined' ? this.value : '';
14139 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14143 getRawValue : function()
14145 if(Roo.isIOS && this.useNativeIOS){
14146 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14149 var v = this.inputEl().getValue();
14155 * Clears any text/value currently set in the field
14157 clearValue : function(){
14159 if(this.hiddenField){
14160 this.hiddenField.dom.value = '';
14163 this.setRawValue('');
14164 this.lastSelectionText = '';
14165 this.lastData = false;
14167 var close = this.closeTriggerEl();
14178 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14179 * will be displayed in the field. If the value does not match the data value of an existing item,
14180 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14181 * Otherwise the field will be blank (although the value will still be set).
14182 * @param {String} value The value to match
14184 setValue : function(v)
14186 if(Roo.isIOS && this.useNativeIOS){
14187 this.setIOSValue(v);
14197 if(this.valueField){
14198 var r = this.findRecord(this.valueField, v);
14200 text = r.data[this.displayField];
14201 }else if(this.valueNotFoundText !== undefined){
14202 text = this.valueNotFoundText;
14205 this.lastSelectionText = text;
14206 if(this.hiddenField){
14207 this.hiddenField.dom.value = v;
14209 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14212 var close = this.closeTriggerEl();
14215 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14221 * @property {Object} the last set data for the element
14226 * Sets the value of the field based on a object which is related to the record format for the store.
14227 * @param {Object} value the value to set as. or false on reset?
14229 setFromData : function(o){
14236 var dv = ''; // display value
14237 var vv = ''; // value value..
14239 if (this.displayField) {
14240 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14242 // this is an error condition!!!
14243 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14246 if(this.valueField){
14247 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14250 var close = this.closeTriggerEl();
14253 if(dv.length || vv * 1 > 0){
14255 this.blockFocus=true;
14261 if(this.hiddenField){
14262 this.hiddenField.dom.value = vv;
14264 this.lastSelectionText = dv;
14265 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14269 // no hidden field.. - we store the value in 'value', but still display
14270 // display field!!!!
14271 this.lastSelectionText = dv;
14272 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14279 reset : function(){
14280 // overridden so that last data is reset..
14287 this.setValue(this.originalValue);
14288 //this.clearInvalid();
14289 this.lastData = false;
14291 this.view.clearSelections();
14297 findRecord : function(prop, value){
14299 if(this.store.getCount() > 0){
14300 this.store.each(function(r){
14301 if(r.data[prop] == value){
14311 getName: function()
14313 // returns hidden if it's set..
14314 if (!this.rendered) {return ''};
14315 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14319 onViewMove : function(e, t){
14320 this.inKeyMode = false;
14324 onViewOver : function(e, t){
14325 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14328 var item = this.view.findItemFromChild(t);
14331 var index = this.view.indexOf(item);
14332 this.select(index, false);
14337 onViewClick : function(view, doFocus, el, e)
14339 var index = this.view.getSelectedIndexes()[0];
14341 var r = this.store.getAt(index);
14345 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14352 Roo.each(this.tickItems, function(v,k){
14354 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14356 _this.tickItems.splice(k, 1);
14358 if(typeof(e) == 'undefined' && view == false){
14359 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14371 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14372 this.tickItems.push(r.data);
14375 if(typeof(e) == 'undefined' && view == false){
14376 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14383 this.onSelect(r, index);
14385 if(doFocus !== false && !this.blockFocus){
14386 this.inputEl().focus();
14391 restrictHeight : function(){
14392 //this.innerList.dom.style.height = '';
14393 //var inner = this.innerList.dom;
14394 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14395 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14396 //this.list.beginUpdate();
14397 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14398 this.list.alignTo(this.inputEl(), this.listAlign);
14399 this.list.alignTo(this.inputEl(), this.listAlign);
14400 //this.list.endUpdate();
14404 onEmptyResults : function(){
14406 if(this.tickable && this.editable){
14407 this.hasFocus = false;
14408 this.restrictHeight();
14416 * Returns true if the dropdown list is expanded, else false.
14418 isExpanded : function(){
14419 return this.list.isVisible();
14423 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14424 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14425 * @param {String} value The data value of the item to select
14426 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14427 * selected item if it is not currently in view (defaults to true)
14428 * @return {Boolean} True if the value matched an item in the list, else false
14430 selectByValue : function(v, scrollIntoView){
14431 if(v !== undefined && v !== null){
14432 var r = this.findRecord(this.valueField || this.displayField, v);
14434 this.select(this.store.indexOf(r), scrollIntoView);
14442 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14443 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14444 * @param {Number} index The zero-based index of the list item to select
14445 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14446 * selected item if it is not currently in view (defaults to true)
14448 select : function(index, scrollIntoView){
14449 this.selectedIndex = index;
14450 this.view.select(index);
14451 if(scrollIntoView !== false){
14452 var el = this.view.getNode(index);
14454 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14457 this.list.scrollChildIntoView(el, false);
14463 selectNext : function(){
14464 var ct = this.store.getCount();
14466 if(this.selectedIndex == -1){
14468 }else if(this.selectedIndex < ct-1){
14469 this.select(this.selectedIndex+1);
14475 selectPrev : function(){
14476 var ct = this.store.getCount();
14478 if(this.selectedIndex == -1){
14480 }else if(this.selectedIndex != 0){
14481 this.select(this.selectedIndex-1);
14487 onKeyUp : function(e){
14488 if(this.editable !== false && !e.isSpecialKey()){
14489 this.lastKey = e.getKey();
14490 this.dqTask.delay(this.queryDelay);
14495 validateBlur : function(){
14496 return !this.list || !this.list.isVisible();
14500 initQuery : function(){
14502 var v = this.getRawValue();
14504 if(this.tickable && this.editable){
14505 v = this.tickableInputEl().getValue();
14512 doForce : function(){
14513 if(this.inputEl().dom.value.length > 0){
14514 this.inputEl().dom.value =
14515 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14521 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14522 * query allowing the query action to be canceled if needed.
14523 * @param {String} query The SQL query to execute
14524 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14525 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14526 * saved in the current store (defaults to false)
14528 doQuery : function(q, forceAll){
14530 if(q === undefined || q === null){
14535 forceAll: forceAll,
14539 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14544 forceAll = qe.forceAll;
14545 if(forceAll === true || (q.length >= this.minChars)){
14547 this.hasQuery = true;
14549 if(this.lastQuery != q || this.alwaysQuery){
14550 this.lastQuery = q;
14551 if(this.mode == 'local'){
14552 this.selectedIndex = -1;
14554 this.store.clearFilter();
14557 if(this.specialFilter){
14558 this.fireEvent('specialfilter', this);
14563 this.store.filter(this.displayField, q);
14566 this.store.fireEvent("datachanged", this.store);
14573 this.store.baseParams[this.queryParam] = q;
14575 var options = {params : this.getParams(q)};
14578 options.add = true;
14579 options.params.start = this.page * this.pageSize;
14582 this.store.load(options);
14585 * this code will make the page width larger, at the beginning, the list not align correctly,
14586 * we should expand the list on onLoad
14587 * so command out it
14592 this.selectedIndex = -1;
14597 this.loadNext = false;
14601 getParams : function(q){
14603 //p[this.queryParam] = q;
14607 p.limit = this.pageSize;
14613 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14615 collapse : function(){
14616 if(!this.isExpanded()){
14622 this.hasFocus = false;
14626 this.cancelBtn.hide();
14627 this.trigger.show();
14630 this.tickableInputEl().dom.value = '';
14631 this.tickableInputEl().blur();
14636 Roo.get(document).un('mousedown', this.collapseIf, this);
14637 Roo.get(document).un('mousewheel', this.collapseIf, this);
14638 if (!this.editable) {
14639 Roo.get(document).un('keydown', this.listKeyPress, this);
14641 this.fireEvent('collapse', this);
14647 collapseIf : function(e){
14648 var in_combo = e.within(this.el);
14649 var in_list = e.within(this.list);
14650 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14652 if (in_combo || in_list || is_list) {
14653 //e.stopPropagation();
14658 this.onTickableFooterButtonClick(e, false, false);
14666 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14668 expand : function(){
14670 if(this.isExpanded() || !this.hasFocus){
14674 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14675 this.list.setWidth(lw);
14681 this.restrictHeight();
14685 this.tickItems = Roo.apply([], this.item);
14688 this.cancelBtn.show();
14689 this.trigger.hide();
14692 this.tickableInputEl().focus();
14697 Roo.get(document).on('mousedown', this.collapseIf, this);
14698 Roo.get(document).on('mousewheel', this.collapseIf, this);
14699 if (!this.editable) {
14700 Roo.get(document).on('keydown', this.listKeyPress, this);
14703 this.fireEvent('expand', this);
14707 // Implements the default empty TriggerField.onTriggerClick function
14708 onTriggerClick : function(e)
14710 Roo.log('trigger click');
14712 if(this.disabled || !this.triggerList){
14717 this.loadNext = false;
14719 if(this.isExpanded()){
14721 if (!this.blockFocus) {
14722 this.inputEl().focus();
14726 this.hasFocus = true;
14727 if(this.triggerAction == 'all') {
14728 this.doQuery(this.allQuery, true);
14730 this.doQuery(this.getRawValue());
14732 if (!this.blockFocus) {
14733 this.inputEl().focus();
14738 onTickableTriggerClick : function(e)
14745 this.loadNext = false;
14746 this.hasFocus = true;
14748 if(this.triggerAction == 'all') {
14749 this.doQuery(this.allQuery, true);
14751 this.doQuery(this.getRawValue());
14755 onSearchFieldClick : function(e)
14757 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14758 this.onTickableFooterButtonClick(e, false, false);
14762 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14767 this.loadNext = false;
14768 this.hasFocus = true;
14770 if(this.triggerAction == 'all') {
14771 this.doQuery(this.allQuery, true);
14773 this.doQuery(this.getRawValue());
14777 listKeyPress : function(e)
14779 //Roo.log('listkeypress');
14780 // scroll to first matching element based on key pres..
14781 if (e.isSpecialKey()) {
14784 var k = String.fromCharCode(e.getKey()).toUpperCase();
14787 var csel = this.view.getSelectedNodes();
14788 var cselitem = false;
14790 var ix = this.view.indexOf(csel[0]);
14791 cselitem = this.store.getAt(ix);
14792 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14798 this.store.each(function(v) {
14800 // start at existing selection.
14801 if (cselitem.id == v.id) {
14807 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14808 match = this.store.indexOf(v);
14814 if (match === false) {
14815 return true; // no more action?
14818 this.view.select(match);
14819 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14820 sn.scrollIntoView(sn.dom.parentNode, false);
14823 onViewScroll : function(e, t){
14825 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){
14829 this.hasQuery = true;
14831 this.loading = this.list.select('.loading', true).first();
14833 if(this.loading === null){
14834 this.list.createChild({
14836 cls: 'loading roo-select2-more-results roo-select2-active',
14837 html: 'Loading more results...'
14840 this.loading = this.list.select('.loading', true).first();
14842 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14844 this.loading.hide();
14847 this.loading.show();
14852 this.loadNext = true;
14854 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14859 addItem : function(o)
14861 var dv = ''; // display value
14863 if (this.displayField) {
14864 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14866 // this is an error condition!!!
14867 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14874 var choice = this.choices.createChild({
14876 cls: 'roo-select2-search-choice',
14885 cls: 'roo-select2-search-choice-close fa fa-times',
14890 }, this.searchField);
14892 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14894 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14902 this.inputEl().dom.value = '';
14907 onRemoveItem : function(e, _self, o)
14909 e.preventDefault();
14911 this.lastItem = Roo.apply([], this.item);
14913 var index = this.item.indexOf(o.data) * 1;
14916 Roo.log('not this item?!');
14920 this.item.splice(index, 1);
14925 this.fireEvent('remove', this, e);
14931 syncValue : function()
14933 if(!this.item.length){
14940 Roo.each(this.item, function(i){
14941 if(_this.valueField){
14942 value.push(i[_this.valueField]);
14949 this.value = value.join(',');
14951 if(this.hiddenField){
14952 this.hiddenField.dom.value = this.value;
14955 this.store.fireEvent("datachanged", this.store);
14960 clearItem : function()
14962 if(!this.multiple){
14968 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14976 if(this.tickable && !Roo.isTouch){
14977 this.view.refresh();
14981 inputEl: function ()
14983 if(Roo.isIOS && this.useNativeIOS){
14984 return this.el.select('select.roo-ios-select', true).first();
14987 if(Roo.isTouch && this.mobileTouchView){
14988 return this.el.select('input.form-control',true).first();
14992 return this.searchField;
14995 return this.el.select('input.form-control',true).first();
14998 onTickableFooterButtonClick : function(e, btn, el)
15000 e.preventDefault();
15002 this.lastItem = Roo.apply([], this.item);
15004 if(btn && btn.name == 'cancel'){
15005 this.tickItems = Roo.apply([], this.item);
15014 Roo.each(this.tickItems, function(o){
15022 validate : function()
15024 if(this.getVisibilityEl().hasClass('hidden')){
15028 var v = this.getRawValue();
15031 v = this.getValue();
15034 if(this.disabled || this.allowBlank || v.length){
15039 this.markInvalid();
15043 tickableInputEl : function()
15045 if(!this.tickable || !this.editable){
15046 return this.inputEl();
15049 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15053 getAutoCreateTouchView : function()
15058 cls: 'form-group' //input-group
15064 type : this.inputType,
15065 cls : 'form-control x-combo-noedit',
15066 autocomplete: 'new-password',
15067 placeholder : this.placeholder || '',
15072 input.name = this.name;
15076 input.cls += ' input-' + this.size;
15079 if (this.disabled) {
15080 input.disabled = true;
15091 inputblock.cls += ' input-group';
15093 inputblock.cn.unshift({
15095 cls : 'input-group-addon input-group-prepend input-group-text',
15100 if(this.removable && !this.multiple){
15101 inputblock.cls += ' roo-removable';
15103 inputblock.cn.push({
15106 cls : 'roo-combo-removable-btn close'
15110 if(this.hasFeedback && !this.allowBlank){
15112 inputblock.cls += ' has-feedback';
15114 inputblock.cn.push({
15116 cls: 'glyphicon form-control-feedback'
15123 inputblock.cls += (this.before) ? '' : ' input-group';
15125 inputblock.cn.push({
15127 cls : 'input-group-addon input-group-append input-group-text',
15133 var ibwrap = inputblock;
15138 cls: 'roo-select2-choices',
15142 cls: 'roo-select2-search-field',
15155 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15160 cls: 'form-hidden-field'
15166 if(!this.multiple && this.showToggleBtn){
15173 if (this.caret != false) {
15176 cls: 'fa fa-' + this.caret
15183 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15188 cls: 'combobox-clear',
15202 combobox.cls += ' roo-select2-container-multi';
15205 var align = this.labelAlign || this.parentLabelAlign();
15207 if (align ==='left' && this.fieldLabel.length) {
15212 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15213 tooltip : 'This field is required'
15217 cls : 'control-label col-form-label',
15218 html : this.fieldLabel
15229 var labelCfg = cfg.cn[1];
15230 var contentCfg = cfg.cn[2];
15233 if(this.indicatorpos == 'right'){
15238 cls : 'control-label col-form-label',
15242 html : this.fieldLabel
15246 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15247 tooltip : 'This field is required'
15260 labelCfg = cfg.cn[0];
15261 contentCfg = cfg.cn[1];
15266 if(this.labelWidth > 12){
15267 labelCfg.style = "width: " + this.labelWidth + 'px';
15270 if(this.labelWidth < 13 && this.labelmd == 0){
15271 this.labelmd = this.labelWidth;
15274 if(this.labellg > 0){
15275 labelCfg.cls += ' col-lg-' + this.labellg;
15276 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15279 if(this.labelmd > 0){
15280 labelCfg.cls += ' col-md-' + this.labelmd;
15281 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15284 if(this.labelsm > 0){
15285 labelCfg.cls += ' col-sm-' + this.labelsm;
15286 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15289 if(this.labelxs > 0){
15290 labelCfg.cls += ' col-xs-' + this.labelxs;
15291 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15295 } else if ( this.fieldLabel.length) {
15299 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15300 tooltip : 'This field is required'
15304 cls : 'control-label',
15305 html : this.fieldLabel
15316 if(this.indicatorpos == 'right'){
15320 cls : 'control-label',
15321 html : this.fieldLabel,
15325 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15326 tooltip : 'This field is required'
15343 var settings = this;
15345 ['xs','sm','md','lg'].map(function(size){
15346 if (settings[size]) {
15347 cfg.cls += ' col-' + size + '-' + settings[size];
15354 initTouchView : function()
15356 this.renderTouchView();
15358 this.touchViewEl.on('scroll', function(){
15359 this.el.dom.scrollTop = 0;
15362 this.originalValue = this.getValue();
15364 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15366 this.inputEl().on("click", this.showTouchView, this);
15367 if (this.triggerEl) {
15368 this.triggerEl.on("click", this.showTouchView, this);
15372 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15373 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15375 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15377 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15378 this.store.on('load', this.onTouchViewLoad, this);
15379 this.store.on('loadexception', this.onTouchViewLoadException, this);
15381 if(this.hiddenName){
15383 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15385 this.hiddenField.dom.value =
15386 this.hiddenValue !== undefined ? this.hiddenValue :
15387 this.value !== undefined ? this.value : '';
15389 this.el.dom.removeAttribute('name');
15390 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15394 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15395 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15398 if(this.removable && !this.multiple){
15399 var close = this.closeTriggerEl();
15401 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15402 close.on('click', this.removeBtnClick, this, close);
15406 * fix the bug in Safari iOS8
15408 this.inputEl().on("focus", function(e){
15409 document.activeElement.blur();
15412 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15419 renderTouchView : function()
15421 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15422 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15424 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15425 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15427 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15428 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15429 this.touchViewBodyEl.setStyle('overflow', 'auto');
15431 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15432 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15434 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15435 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15439 showTouchView : function()
15445 this.touchViewHeaderEl.hide();
15447 if(this.modalTitle.length){
15448 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15449 this.touchViewHeaderEl.show();
15452 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15453 this.touchViewEl.show();
15455 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15457 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15458 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15460 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15462 if(this.modalTitle.length){
15463 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15466 this.touchViewBodyEl.setHeight(bodyHeight);
15470 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15472 this.touchViewEl.addClass('in');
15475 if(this._touchViewMask){
15476 Roo.get(document.body).addClass("x-body-masked");
15477 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15478 this._touchViewMask.setStyle('z-index', 10000);
15479 this._touchViewMask.addClass('show');
15482 this.doTouchViewQuery();
15486 hideTouchView : function()
15488 this.touchViewEl.removeClass('in');
15492 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15494 this.touchViewEl.setStyle('display', 'none');
15497 if(this._touchViewMask){
15498 this._touchViewMask.removeClass('show');
15499 Roo.get(document.body).removeClass("x-body-masked");
15503 setTouchViewValue : function()
15510 Roo.each(this.tickItems, function(o){
15515 this.hideTouchView();
15518 doTouchViewQuery : function()
15527 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15531 if(!this.alwaysQuery || this.mode == 'local'){
15532 this.onTouchViewLoad();
15539 onTouchViewBeforeLoad : function(combo,opts)
15545 onTouchViewLoad : function()
15547 if(this.store.getCount() < 1){
15548 this.onTouchViewEmptyResults();
15552 this.clearTouchView();
15554 var rawValue = this.getRawValue();
15556 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15558 this.tickItems = [];
15560 this.store.data.each(function(d, rowIndex){
15561 var row = this.touchViewListGroup.createChild(template);
15563 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15564 row.addClass(d.data.cls);
15567 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15570 html : d.data[this.displayField]
15573 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15574 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15577 row.removeClass('selected');
15578 if(!this.multiple && this.valueField &&
15579 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15582 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15583 row.addClass('selected');
15586 if(this.multiple && this.valueField &&
15587 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15591 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15592 this.tickItems.push(d.data);
15595 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15599 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15601 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15603 if(this.modalTitle.length){
15604 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15607 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15609 if(this.mobile_restrict_height && listHeight < bodyHeight){
15610 this.touchViewBodyEl.setHeight(listHeight);
15615 if(firstChecked && listHeight > bodyHeight){
15616 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15621 onTouchViewLoadException : function()
15623 this.hideTouchView();
15626 onTouchViewEmptyResults : function()
15628 this.clearTouchView();
15630 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15632 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15636 clearTouchView : function()
15638 this.touchViewListGroup.dom.innerHTML = '';
15641 onTouchViewClick : function(e, el, o)
15643 e.preventDefault();
15646 var rowIndex = o.rowIndex;
15648 var r = this.store.getAt(rowIndex);
15650 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15652 if(!this.multiple){
15653 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15654 c.dom.removeAttribute('checked');
15657 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15659 this.setFromData(r.data);
15661 var close = this.closeTriggerEl();
15667 this.hideTouchView();
15669 this.fireEvent('select', this, r, rowIndex);
15674 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15675 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15676 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15680 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15681 this.addItem(r.data);
15682 this.tickItems.push(r.data);
15686 getAutoCreateNativeIOS : function()
15689 cls: 'form-group' //input-group,
15694 cls : 'roo-ios-select'
15698 combobox.name = this.name;
15701 if (this.disabled) {
15702 combobox.disabled = true;
15705 var settings = this;
15707 ['xs','sm','md','lg'].map(function(size){
15708 if (settings[size]) {
15709 cfg.cls += ' col-' + size + '-' + settings[size];
15719 initIOSView : function()
15721 this.store.on('load', this.onIOSViewLoad, this);
15726 onIOSViewLoad : function()
15728 if(this.store.getCount() < 1){
15732 this.clearIOSView();
15734 if(this.allowBlank) {
15736 var default_text = '-- SELECT --';
15738 if(this.placeholder.length){
15739 default_text = this.placeholder;
15742 if(this.emptyTitle.length){
15743 default_text += ' - ' + this.emptyTitle + ' -';
15746 var opt = this.inputEl().createChild({
15749 html : default_text
15753 o[this.valueField] = 0;
15754 o[this.displayField] = default_text;
15756 this.ios_options.push({
15763 this.store.data.each(function(d, rowIndex){
15767 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15768 html = d.data[this.displayField];
15773 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15774 value = d.data[this.valueField];
15783 if(this.value == d.data[this.valueField]){
15784 option['selected'] = true;
15787 var opt = this.inputEl().createChild(option);
15789 this.ios_options.push({
15796 this.inputEl().on('change', function(){
15797 this.fireEvent('select', this);
15802 clearIOSView: function()
15804 this.inputEl().dom.innerHTML = '';
15806 this.ios_options = [];
15809 setIOSValue: function(v)
15813 if(!this.ios_options){
15817 Roo.each(this.ios_options, function(opts){
15819 opts.el.dom.removeAttribute('selected');
15821 if(opts.data[this.valueField] != v){
15825 opts.el.dom.setAttribute('selected', true);
15831 * @cfg {Boolean} grow
15835 * @cfg {Number} growMin
15839 * @cfg {Number} growMax
15848 Roo.apply(Roo.bootstrap.ComboBox, {
15852 cls: 'modal-header',
15874 cls: 'list-group-item',
15878 cls: 'roo-combobox-list-group-item-value'
15882 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15896 listItemCheckbox : {
15898 cls: 'list-group-item',
15902 cls: 'roo-combobox-list-group-item-value'
15906 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15922 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15927 cls: 'modal-footer',
15935 cls: 'col-xs-6 text-left',
15938 cls: 'btn btn-danger roo-touch-view-cancel',
15944 cls: 'col-xs-6 text-right',
15947 cls: 'btn btn-success roo-touch-view-ok',
15958 Roo.apply(Roo.bootstrap.ComboBox, {
15960 touchViewTemplate : {
15962 cls: 'modal fade roo-combobox-touch-view',
15966 cls: 'modal-dialog',
15967 style : 'position:fixed', // we have to fix position....
15971 cls: 'modal-content',
15973 Roo.bootstrap.ComboBox.header,
15974 Roo.bootstrap.ComboBox.body,
15975 Roo.bootstrap.ComboBox.footer
15984 * Ext JS Library 1.1.1
15985 * Copyright(c) 2006-2007, Ext JS, LLC.
15987 * Originally Released Under LGPL - original licence link has changed is not relivant.
15990 * <script type="text/javascript">
15995 * @extends Roo.util.Observable
15996 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15997 * This class also supports single and multi selection modes. <br>
15998 * Create a data model bound view:
16000 var store = new Roo.data.Store(...);
16002 var view = new Roo.View({
16004 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16006 singleSelect: true,
16007 selectedClass: "ydataview-selected",
16011 // listen for node click?
16012 view.on("click", function(vw, index, node, e){
16013 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16017 dataModel.load("foobar.xml");
16019 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16021 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16022 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16024 * Note: old style constructor is still suported (container, template, config)
16027 * Create a new View
16028 * @param {Object} config The config object
16031 Roo.View = function(config, depreciated_tpl, depreciated_config){
16033 this.parent = false;
16035 if (typeof(depreciated_tpl) == 'undefined') {
16036 // new way.. - universal constructor.
16037 Roo.apply(this, config);
16038 this.el = Roo.get(this.el);
16041 this.el = Roo.get(config);
16042 this.tpl = depreciated_tpl;
16043 Roo.apply(this, depreciated_config);
16045 this.wrapEl = this.el.wrap().wrap();
16046 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16049 if(typeof(this.tpl) == "string"){
16050 this.tpl = new Roo.Template(this.tpl);
16052 // support xtype ctors..
16053 this.tpl = new Roo.factory(this.tpl, Roo);
16057 this.tpl.compile();
16062 * @event beforeclick
16063 * Fires before a click is processed. Returns false to cancel the default action.
16064 * @param {Roo.View} this
16065 * @param {Number} index The index of the target node
16066 * @param {HTMLElement} node The target node
16067 * @param {Roo.EventObject} e The raw event object
16069 "beforeclick" : true,
16072 * Fires when a template node is clicked.
16073 * @param {Roo.View} this
16074 * @param {Number} index The index of the target node
16075 * @param {HTMLElement} node The target node
16076 * @param {Roo.EventObject} e The raw event object
16081 * Fires when a template node is double clicked.
16082 * @param {Roo.View} this
16083 * @param {Number} index The index of the target node
16084 * @param {HTMLElement} node The target node
16085 * @param {Roo.EventObject} e The raw event object
16089 * @event contextmenu
16090 * Fires when a template node is right clicked.
16091 * @param {Roo.View} this
16092 * @param {Number} index The index of the target node
16093 * @param {HTMLElement} node The target node
16094 * @param {Roo.EventObject} e The raw event object
16096 "contextmenu" : true,
16098 * @event selectionchange
16099 * Fires when the selected nodes change.
16100 * @param {Roo.View} this
16101 * @param {Array} selections Array of the selected nodes
16103 "selectionchange" : true,
16106 * @event beforeselect
16107 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16108 * @param {Roo.View} this
16109 * @param {HTMLElement} node The node to be selected
16110 * @param {Array} selections Array of currently selected nodes
16112 "beforeselect" : true,
16114 * @event preparedata
16115 * Fires on every row to render, to allow you to change the data.
16116 * @param {Roo.View} this
16117 * @param {Object} data to be rendered (change this)
16119 "preparedata" : true
16127 "click": this.onClick,
16128 "dblclick": this.onDblClick,
16129 "contextmenu": this.onContextMenu,
16133 this.selections = [];
16135 this.cmp = new Roo.CompositeElementLite([]);
16137 this.store = Roo.factory(this.store, Roo.data);
16138 this.setStore(this.store, true);
16141 if ( this.footer && this.footer.xtype) {
16143 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16145 this.footer.dataSource = this.store;
16146 this.footer.container = fctr;
16147 this.footer = Roo.factory(this.footer, Roo);
16148 fctr.insertFirst(this.el);
16150 // this is a bit insane - as the paging toolbar seems to detach the el..
16151 // dom.parentNode.parentNode.parentNode
16152 // they get detached?
16156 Roo.View.superclass.constructor.call(this);
16161 Roo.extend(Roo.View, Roo.util.Observable, {
16164 * @cfg {Roo.data.Store} store Data store to load data from.
16169 * @cfg {String|Roo.Element} el The container element.
16174 * @cfg {String|Roo.Template} tpl The template used by this View
16178 * @cfg {String} dataName the named area of the template to use as the data area
16179 * Works with domtemplates roo-name="name"
16183 * @cfg {String} selectedClass The css class to add to selected nodes
16185 selectedClass : "x-view-selected",
16187 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16192 * @cfg {String} text to display on mask (default Loading)
16196 * @cfg {Boolean} multiSelect Allow multiple selection
16198 multiSelect : false,
16200 * @cfg {Boolean} singleSelect Allow single selection
16202 singleSelect: false,
16205 * @cfg {Boolean} toggleSelect - selecting
16207 toggleSelect : false,
16210 * @cfg {Boolean} tickable - selecting
16215 * Returns the element this view is bound to.
16216 * @return {Roo.Element}
16218 getEl : function(){
16219 return this.wrapEl;
16225 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16227 refresh : function(){
16228 //Roo.log('refresh');
16231 // if we are using something like 'domtemplate', then
16232 // the what gets used is:
16233 // t.applySubtemplate(NAME, data, wrapping data..)
16234 // the outer template then get' applied with
16235 // the store 'extra data'
16236 // and the body get's added to the
16237 // roo-name="data" node?
16238 // <span class='roo-tpl-{name}'></span> ?????
16242 this.clearSelections();
16243 this.el.update("");
16245 var records = this.store.getRange();
16246 if(records.length < 1) {
16248 // is this valid?? = should it render a template??
16250 this.el.update(this.emptyText);
16254 if (this.dataName) {
16255 this.el.update(t.apply(this.store.meta)); //????
16256 el = this.el.child('.roo-tpl-' + this.dataName);
16259 for(var i = 0, len = records.length; i < len; i++){
16260 var data = this.prepareData(records[i].data, i, records[i]);
16261 this.fireEvent("preparedata", this, data, i, records[i]);
16263 var d = Roo.apply({}, data);
16266 Roo.apply(d, {'roo-id' : Roo.id()});
16270 Roo.each(this.parent.item, function(item){
16271 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16274 Roo.apply(d, {'roo-data-checked' : 'checked'});
16278 html[html.length] = Roo.util.Format.trim(
16280 t.applySubtemplate(this.dataName, d, this.store.meta) :
16287 el.update(html.join(""));
16288 this.nodes = el.dom.childNodes;
16289 this.updateIndexes(0);
16294 * Function to override to reformat the data that is sent to
16295 * the template for each node.
16296 * DEPRICATED - use the preparedata event handler.
16297 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16298 * a JSON object for an UpdateManager bound view).
16300 prepareData : function(data, index, record)
16302 this.fireEvent("preparedata", this, data, index, record);
16306 onUpdate : function(ds, record){
16307 // Roo.log('on update');
16308 this.clearSelections();
16309 var index = this.store.indexOf(record);
16310 var n = this.nodes[index];
16311 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16312 n.parentNode.removeChild(n);
16313 this.updateIndexes(index, index);
16319 onAdd : function(ds, records, index)
16321 //Roo.log(['on Add', ds, records, index] );
16322 this.clearSelections();
16323 if(this.nodes.length == 0){
16327 var n = this.nodes[index];
16328 for(var i = 0, len = records.length; i < len; i++){
16329 var d = this.prepareData(records[i].data, i, records[i]);
16331 this.tpl.insertBefore(n, d);
16334 this.tpl.append(this.el, d);
16337 this.updateIndexes(index);
16340 onRemove : function(ds, record, index){
16341 // Roo.log('onRemove');
16342 this.clearSelections();
16343 var el = this.dataName ?
16344 this.el.child('.roo-tpl-' + this.dataName) :
16347 el.dom.removeChild(this.nodes[index]);
16348 this.updateIndexes(index);
16352 * Refresh an individual node.
16353 * @param {Number} index
16355 refreshNode : function(index){
16356 this.onUpdate(this.store, this.store.getAt(index));
16359 updateIndexes : function(startIndex, endIndex){
16360 var ns = this.nodes;
16361 startIndex = startIndex || 0;
16362 endIndex = endIndex || ns.length - 1;
16363 for(var i = startIndex; i <= endIndex; i++){
16364 ns[i].nodeIndex = i;
16369 * Changes the data store this view uses and refresh the view.
16370 * @param {Store} store
16372 setStore : function(store, initial){
16373 if(!initial && this.store){
16374 this.store.un("datachanged", this.refresh);
16375 this.store.un("add", this.onAdd);
16376 this.store.un("remove", this.onRemove);
16377 this.store.un("update", this.onUpdate);
16378 this.store.un("clear", this.refresh);
16379 this.store.un("beforeload", this.onBeforeLoad);
16380 this.store.un("load", this.onLoad);
16381 this.store.un("loadexception", this.onLoad);
16385 store.on("datachanged", this.refresh, this);
16386 store.on("add", this.onAdd, this);
16387 store.on("remove", this.onRemove, this);
16388 store.on("update", this.onUpdate, this);
16389 store.on("clear", this.refresh, this);
16390 store.on("beforeload", this.onBeforeLoad, this);
16391 store.on("load", this.onLoad, this);
16392 store.on("loadexception", this.onLoad, this);
16400 * onbeforeLoad - masks the loading area.
16403 onBeforeLoad : function(store,opts)
16405 //Roo.log('onBeforeLoad');
16407 this.el.update("");
16409 this.el.mask(this.mask ? this.mask : "Loading" );
16411 onLoad : function ()
16418 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16419 * @param {HTMLElement} node
16420 * @return {HTMLElement} The template node
16422 findItemFromChild : function(node){
16423 var el = this.dataName ?
16424 this.el.child('.roo-tpl-' + this.dataName,true) :
16427 if(!node || node.parentNode == el){
16430 var p = node.parentNode;
16431 while(p && p != el){
16432 if(p.parentNode == el){
16441 onClick : function(e){
16442 var item = this.findItemFromChild(e.getTarget());
16444 var index = this.indexOf(item);
16445 if(this.onItemClick(item, index, e) !== false){
16446 this.fireEvent("click", this, index, item, e);
16449 this.clearSelections();
16454 onContextMenu : function(e){
16455 var item = this.findItemFromChild(e.getTarget());
16457 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16462 onDblClick : function(e){
16463 var item = this.findItemFromChild(e.getTarget());
16465 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16469 onItemClick : function(item, index, e)
16471 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16474 if (this.toggleSelect) {
16475 var m = this.isSelected(item) ? 'unselect' : 'select';
16478 _t[m](item, true, false);
16481 if(this.multiSelect || this.singleSelect){
16482 if(this.multiSelect && e.shiftKey && this.lastSelection){
16483 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16485 this.select(item, this.multiSelect && e.ctrlKey);
16486 this.lastSelection = item;
16489 if(!this.tickable){
16490 e.preventDefault();
16498 * Get the number of selected nodes.
16501 getSelectionCount : function(){
16502 return this.selections.length;
16506 * Get the currently selected nodes.
16507 * @return {Array} An array of HTMLElements
16509 getSelectedNodes : function(){
16510 return this.selections;
16514 * Get the indexes of the selected nodes.
16517 getSelectedIndexes : function(){
16518 var indexes = [], s = this.selections;
16519 for(var i = 0, len = s.length; i < len; i++){
16520 indexes.push(s[i].nodeIndex);
16526 * Clear all selections
16527 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16529 clearSelections : function(suppressEvent){
16530 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16531 this.cmp.elements = this.selections;
16532 this.cmp.removeClass(this.selectedClass);
16533 this.selections = [];
16534 if(!suppressEvent){
16535 this.fireEvent("selectionchange", this, this.selections);
16541 * Returns true if the passed node is selected
16542 * @param {HTMLElement/Number} node The node or node index
16543 * @return {Boolean}
16545 isSelected : function(node){
16546 var s = this.selections;
16550 node = this.getNode(node);
16551 return s.indexOf(node) !== -1;
16556 * @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
16557 * @param {Boolean} keepExisting (optional) true to keep existing selections
16558 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16560 select : function(nodeInfo, keepExisting, suppressEvent){
16561 if(nodeInfo instanceof Array){
16563 this.clearSelections(true);
16565 for(var i = 0, len = nodeInfo.length; i < len; i++){
16566 this.select(nodeInfo[i], true, true);
16570 var node = this.getNode(nodeInfo);
16571 if(!node || this.isSelected(node)){
16572 return; // already selected.
16575 this.clearSelections(true);
16578 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16579 Roo.fly(node).addClass(this.selectedClass);
16580 this.selections.push(node);
16581 if(!suppressEvent){
16582 this.fireEvent("selectionchange", this, this.selections);
16590 * @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
16591 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16592 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16594 unselect : function(nodeInfo, keepExisting, suppressEvent)
16596 if(nodeInfo instanceof Array){
16597 Roo.each(this.selections, function(s) {
16598 this.unselect(s, nodeInfo);
16602 var node = this.getNode(nodeInfo);
16603 if(!node || !this.isSelected(node)){
16604 //Roo.log("not selected");
16605 return; // not selected.
16609 Roo.each(this.selections, function(s) {
16611 Roo.fly(node).removeClass(this.selectedClass);
16618 this.selections= ns;
16619 this.fireEvent("selectionchange", this, this.selections);
16623 * Gets a template node.
16624 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16625 * @return {HTMLElement} The node or null if it wasn't found
16627 getNode : function(nodeInfo){
16628 if(typeof nodeInfo == "string"){
16629 return document.getElementById(nodeInfo);
16630 }else if(typeof nodeInfo == "number"){
16631 return this.nodes[nodeInfo];
16637 * Gets a range template nodes.
16638 * @param {Number} startIndex
16639 * @param {Number} endIndex
16640 * @return {Array} An array of nodes
16642 getNodes : function(start, end){
16643 var ns = this.nodes;
16644 start = start || 0;
16645 end = typeof end == "undefined" ? ns.length - 1 : end;
16648 for(var i = start; i <= end; i++){
16652 for(var i = start; i >= end; i--){
16660 * Finds the index of the passed node
16661 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16662 * @return {Number} The index of the node or -1
16664 indexOf : function(node){
16665 node = this.getNode(node);
16666 if(typeof node.nodeIndex == "number"){
16667 return node.nodeIndex;
16669 var ns = this.nodes;
16670 for(var i = 0, len = ns.length; i < len; i++){
16681 * based on jquery fullcalendar
16685 Roo.bootstrap = Roo.bootstrap || {};
16687 * @class Roo.bootstrap.Calendar
16688 * @extends Roo.bootstrap.Component
16689 * Bootstrap Calendar class
16690 * @cfg {Boolean} loadMask (true|false) default false
16691 * @cfg {Object} header generate the user specific header of the calendar, default false
16694 * Create a new Container
16695 * @param {Object} config The config object
16700 Roo.bootstrap.Calendar = function(config){
16701 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16705 * Fires when a date is selected
16706 * @param {DatePicker} this
16707 * @param {Date} date The selected date
16711 * @event monthchange
16712 * Fires when the displayed month changes
16713 * @param {DatePicker} this
16714 * @param {Date} date The selected month
16716 'monthchange': true,
16718 * @event evententer
16719 * Fires when mouse over an event
16720 * @param {Calendar} this
16721 * @param {event} Event
16723 'evententer': true,
16725 * @event eventleave
16726 * Fires when the mouse leaves an
16727 * @param {Calendar} this
16730 'eventleave': true,
16732 * @event eventclick
16733 * Fires when the mouse click an
16734 * @param {Calendar} this
16743 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16746 * @cfg {Number} startDay
16747 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16755 getAutoCreate : function(){
16758 var fc_button = function(name, corner, style, content ) {
16759 return Roo.apply({},{
16761 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16763 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16766 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16777 style : 'width:100%',
16784 cls : 'fc-header-left',
16786 fc_button('prev', 'left', 'arrow', '‹' ),
16787 fc_button('next', 'right', 'arrow', '›' ),
16788 { tag: 'span', cls: 'fc-header-space' },
16789 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16797 cls : 'fc-header-center',
16801 cls: 'fc-header-title',
16804 html : 'month / year'
16812 cls : 'fc-header-right',
16814 /* fc_button('month', 'left', '', 'month' ),
16815 fc_button('week', '', '', 'week' ),
16816 fc_button('day', 'right', '', 'day' )
16828 header = this.header;
16831 var cal_heads = function() {
16833 // fixme - handle this.
16835 for (var i =0; i < Date.dayNames.length; i++) {
16836 var d = Date.dayNames[i];
16839 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16840 html : d.substring(0,3)
16844 ret[0].cls += ' fc-first';
16845 ret[6].cls += ' fc-last';
16848 var cal_cell = function(n) {
16851 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16856 cls: 'fc-day-number',
16860 cls: 'fc-day-content',
16864 style: 'position: relative;' // height: 17px;
16876 var cal_rows = function() {
16879 for (var r = 0; r < 6; r++) {
16886 for (var i =0; i < Date.dayNames.length; i++) {
16887 var d = Date.dayNames[i];
16888 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16891 row.cn[0].cls+=' fc-first';
16892 row.cn[0].cn[0].style = 'min-height:90px';
16893 row.cn[6].cls+=' fc-last';
16897 ret[0].cls += ' fc-first';
16898 ret[4].cls += ' fc-prev-last';
16899 ret[5].cls += ' fc-last';
16906 cls: 'fc-border-separate',
16907 style : 'width:100%',
16915 cls : 'fc-first fc-last',
16933 cls : 'fc-content',
16934 style : "position: relative;",
16937 cls : 'fc-view fc-view-month fc-grid',
16938 style : 'position: relative',
16939 unselectable : 'on',
16942 cls : 'fc-event-container',
16943 style : 'position:absolute;z-index:8;top:0;left:0;'
16961 initEvents : function()
16964 throw "can not find store for calendar";
16970 style: "text-align:center",
16974 style: "background-color:white;width:50%;margin:250 auto",
16978 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16989 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16991 var size = this.el.select('.fc-content', true).first().getSize();
16992 this.maskEl.setSize(size.width, size.height);
16993 this.maskEl.enableDisplayMode("block");
16994 if(!this.loadMask){
16995 this.maskEl.hide();
16998 this.store = Roo.factory(this.store, Roo.data);
16999 this.store.on('load', this.onLoad, this);
17000 this.store.on('beforeload', this.onBeforeLoad, this);
17004 this.cells = this.el.select('.fc-day',true);
17005 //Roo.log(this.cells);
17006 this.textNodes = this.el.query('.fc-day-number');
17007 this.cells.addClassOnOver('fc-state-hover');
17009 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17010 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17011 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17012 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17014 this.on('monthchange', this.onMonthChange, this);
17016 this.update(new Date().clearTime());
17019 resize : function() {
17020 var sz = this.el.getSize();
17022 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17023 this.el.select('.fc-day-content div',true).setHeight(34);
17028 showPrevMonth : function(e){
17029 this.update(this.activeDate.add("mo", -1));
17031 showToday : function(e){
17032 this.update(new Date().clearTime());
17035 showNextMonth : function(e){
17036 this.update(this.activeDate.add("mo", 1));
17040 showPrevYear : function(){
17041 this.update(this.activeDate.add("y", -1));
17045 showNextYear : function(){
17046 this.update(this.activeDate.add("y", 1));
17051 update : function(date)
17053 var vd = this.activeDate;
17054 this.activeDate = date;
17055 // if(vd && this.el){
17056 // var t = date.getTime();
17057 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17058 // Roo.log('using add remove');
17060 // this.fireEvent('monthchange', this, date);
17062 // this.cells.removeClass("fc-state-highlight");
17063 // this.cells.each(function(c){
17064 // if(c.dateValue == t){
17065 // c.addClass("fc-state-highlight");
17066 // setTimeout(function(){
17067 // try{c.dom.firstChild.focus();}catch(e){}
17077 var days = date.getDaysInMonth();
17079 var firstOfMonth = date.getFirstDateOfMonth();
17080 var startingPos = firstOfMonth.getDay()-this.startDay;
17082 if(startingPos < this.startDay){
17086 var pm = date.add(Date.MONTH, -1);
17087 var prevStart = pm.getDaysInMonth()-startingPos;
17089 this.cells = this.el.select('.fc-day',true);
17090 this.textNodes = this.el.query('.fc-day-number');
17091 this.cells.addClassOnOver('fc-state-hover');
17093 var cells = this.cells.elements;
17094 var textEls = this.textNodes;
17096 Roo.each(cells, function(cell){
17097 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17100 days += startingPos;
17102 // convert everything to numbers so it's fast
17103 var day = 86400000;
17104 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17107 //Roo.log(prevStart);
17109 var today = new Date().clearTime().getTime();
17110 var sel = date.clearTime().getTime();
17111 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17112 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17113 var ddMatch = this.disabledDatesRE;
17114 var ddText = this.disabledDatesText;
17115 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17116 var ddaysText = this.disabledDaysText;
17117 var format = this.format;
17119 var setCellClass = function(cal, cell){
17123 //Roo.log('set Cell Class');
17125 var t = d.getTime();
17129 cell.dateValue = t;
17131 cell.className += " fc-today";
17132 cell.className += " fc-state-highlight";
17133 cell.title = cal.todayText;
17136 // disable highlight in other month..
17137 //cell.className += " fc-state-highlight";
17142 cell.className = " fc-state-disabled";
17143 cell.title = cal.minText;
17147 cell.className = " fc-state-disabled";
17148 cell.title = cal.maxText;
17152 if(ddays.indexOf(d.getDay()) != -1){
17153 cell.title = ddaysText;
17154 cell.className = " fc-state-disabled";
17157 if(ddMatch && format){
17158 var fvalue = d.dateFormat(format);
17159 if(ddMatch.test(fvalue)){
17160 cell.title = ddText.replace("%0", fvalue);
17161 cell.className = " fc-state-disabled";
17165 if (!cell.initialClassName) {
17166 cell.initialClassName = cell.dom.className;
17169 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17174 for(; i < startingPos; i++) {
17175 textEls[i].innerHTML = (++prevStart);
17176 d.setDate(d.getDate()+1);
17178 cells[i].className = "fc-past fc-other-month";
17179 setCellClass(this, cells[i]);
17184 for(; i < days; i++){
17185 intDay = i - startingPos + 1;
17186 textEls[i].innerHTML = (intDay);
17187 d.setDate(d.getDate()+1);
17189 cells[i].className = ''; // "x-date-active";
17190 setCellClass(this, cells[i]);
17194 for(; i < 42; i++) {
17195 textEls[i].innerHTML = (++extraDays);
17196 d.setDate(d.getDate()+1);
17198 cells[i].className = "fc-future fc-other-month";
17199 setCellClass(this, cells[i]);
17202 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17204 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17206 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17207 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17209 if(totalRows != 6){
17210 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17211 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17214 this.fireEvent('monthchange', this, date);
17218 if(!this.internalRender){
17219 var main = this.el.dom.firstChild;
17220 var w = main.offsetWidth;
17221 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17222 Roo.fly(main).setWidth(w);
17223 this.internalRender = true;
17224 // opera does not respect the auto grow header center column
17225 // then, after it gets a width opera refuses to recalculate
17226 // without a second pass
17227 if(Roo.isOpera && !this.secondPass){
17228 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17229 this.secondPass = true;
17230 this.update.defer(10, this, [date]);
17237 findCell : function(dt) {
17238 dt = dt.clearTime().getTime();
17240 this.cells.each(function(c){
17241 //Roo.log("check " +c.dateValue + '?=' + dt);
17242 if(c.dateValue == dt){
17252 findCells : function(ev) {
17253 var s = ev.start.clone().clearTime().getTime();
17255 var e= ev.end.clone().clearTime().getTime();
17258 this.cells.each(function(c){
17259 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17261 if(c.dateValue > e){
17264 if(c.dateValue < s){
17273 // findBestRow: function(cells)
17277 // for (var i =0 ; i < cells.length;i++) {
17278 // ret = Math.max(cells[i].rows || 0,ret);
17285 addItem : function(ev)
17287 // look for vertical location slot in
17288 var cells = this.findCells(ev);
17290 // ev.row = this.findBestRow(cells);
17292 // work out the location.
17296 for(var i =0; i < cells.length; i++) {
17298 cells[i].row = cells[0].row;
17301 cells[i].row = cells[i].row + 1;
17311 if (crow.start.getY() == cells[i].getY()) {
17313 crow.end = cells[i];
17330 cells[0].events.push(ev);
17332 this.calevents.push(ev);
17335 clearEvents: function() {
17337 if(!this.calevents){
17341 Roo.each(this.cells.elements, function(c){
17347 Roo.each(this.calevents, function(e) {
17348 Roo.each(e.els, function(el) {
17349 el.un('mouseenter' ,this.onEventEnter, this);
17350 el.un('mouseleave' ,this.onEventLeave, this);
17355 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17361 renderEvents: function()
17365 this.cells.each(function(c) {
17374 if(c.row != c.events.length){
17375 r = 4 - (4 - (c.row - c.events.length));
17378 c.events = ev.slice(0, r);
17379 c.more = ev.slice(r);
17381 if(c.more.length && c.more.length == 1){
17382 c.events.push(c.more.pop());
17385 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17389 this.cells.each(function(c) {
17391 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17394 for (var e = 0; e < c.events.length; e++){
17395 var ev = c.events[e];
17396 var rows = ev.rows;
17398 for(var i = 0; i < rows.length; i++) {
17400 // how many rows should it span..
17403 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17404 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17406 unselectable : "on",
17409 cls: 'fc-event-inner',
17413 // cls: 'fc-event-time',
17414 // html : cells.length > 1 ? '' : ev.time
17418 cls: 'fc-event-title',
17419 html : String.format('{0}', ev.title)
17426 cls: 'ui-resizable-handle ui-resizable-e',
17427 html : '  '
17434 cfg.cls += ' fc-event-start';
17436 if ((i+1) == rows.length) {
17437 cfg.cls += ' fc-event-end';
17440 var ctr = _this.el.select('.fc-event-container',true).first();
17441 var cg = ctr.createChild(cfg);
17443 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17444 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17446 var r = (c.more.length) ? 1 : 0;
17447 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17448 cg.setWidth(ebox.right - sbox.x -2);
17450 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17451 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17452 cg.on('click', _this.onEventClick, _this, ev);
17463 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17464 style : 'position: absolute',
17465 unselectable : "on",
17468 cls: 'fc-event-inner',
17472 cls: 'fc-event-title',
17480 cls: 'ui-resizable-handle ui-resizable-e',
17481 html : '  '
17487 var ctr = _this.el.select('.fc-event-container',true).first();
17488 var cg = ctr.createChild(cfg);
17490 var sbox = c.select('.fc-day-content',true).first().getBox();
17491 var ebox = c.select('.fc-day-content',true).first().getBox();
17493 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17494 cg.setWidth(ebox.right - sbox.x -2);
17496 cg.on('click', _this.onMoreEventClick, _this, c.more);
17506 onEventEnter: function (e, el,event,d) {
17507 this.fireEvent('evententer', this, el, event);
17510 onEventLeave: function (e, el,event,d) {
17511 this.fireEvent('eventleave', this, el, event);
17514 onEventClick: function (e, el,event,d) {
17515 this.fireEvent('eventclick', this, el, event);
17518 onMonthChange: function () {
17522 onMoreEventClick: function(e, el, more)
17526 this.calpopover.placement = 'right';
17527 this.calpopover.setTitle('More');
17529 this.calpopover.setContent('');
17531 var ctr = this.calpopover.el.select('.popover-content', true).first();
17533 Roo.each(more, function(m){
17535 cls : 'fc-event-hori fc-event-draggable',
17538 var cg = ctr.createChild(cfg);
17540 cg.on('click', _this.onEventClick, _this, m);
17543 this.calpopover.show(el);
17548 onLoad: function ()
17550 this.calevents = [];
17553 if(this.store.getCount() > 0){
17554 this.store.data.each(function(d){
17557 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17558 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17559 time : d.data.start_time,
17560 title : d.data.title,
17561 description : d.data.description,
17562 venue : d.data.venue
17567 this.renderEvents();
17569 if(this.calevents.length && this.loadMask){
17570 this.maskEl.hide();
17574 onBeforeLoad: function()
17576 this.clearEvents();
17578 this.maskEl.show();
17592 * @class Roo.bootstrap.Popover
17593 * @extends Roo.bootstrap.Component
17594 * Bootstrap Popover class
17595 * @cfg {String} html contents of the popover (or false to use children..)
17596 * @cfg {String} title of popover (or false to hide)
17597 * @cfg {String} placement how it is placed
17598 * @cfg {String} trigger click || hover (or false to trigger manually)
17599 * @cfg {String} over what (parent or false to trigger manually.)
17600 * @cfg {Number} delay - delay before showing
17603 * Create a new Popover
17604 * @param {Object} config The config object
17607 Roo.bootstrap.Popover = function(config){
17608 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17614 * After the popover show
17616 * @param {Roo.bootstrap.Popover} this
17621 * After the popover hide
17623 * @param {Roo.bootstrap.Popover} this
17629 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17631 title: 'Fill in a title',
17634 placement : 'right',
17635 trigger : 'hover', // hover
17641 can_build_overlaid : false,
17643 getChildContainer : function()
17645 return this.el.select('.popover-content',true).first();
17648 getAutoCreate : function(){
17651 cls : 'popover roo-dynamic',
17652 style: 'display:block',
17658 cls : 'popover-inner',
17662 cls: 'popover-title popover-header',
17666 cls : 'popover-content popover-body',
17677 setTitle: function(str)
17680 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17682 setContent: function(str)
17685 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17687 // as it get's added to the bottom of the page.
17688 onRender : function(ct, position)
17690 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17692 var cfg = Roo.apply({}, this.getAutoCreate());
17696 cfg.cls += ' ' + this.cls;
17699 cfg.style = this.style;
17701 //Roo.log("adding to ");
17702 this.el = Roo.get(document.body).createChild(cfg, position);
17703 // Roo.log(this.el);
17708 initEvents : function()
17710 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17711 this.el.enableDisplayMode('block');
17713 if (this.over === false) {
17716 if (this.triggers === false) {
17719 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17720 var triggers = this.trigger ? this.trigger.split(' ') : [];
17721 Roo.each(triggers, function(trigger) {
17723 if (trigger == 'click') {
17724 on_el.on('click', this.toggle, this);
17725 } else if (trigger != 'manual') {
17726 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17727 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17729 on_el.on(eventIn ,this.enter, this);
17730 on_el.on(eventOut, this.leave, this);
17741 toggle : function () {
17742 this.hoverState == 'in' ? this.leave() : this.enter();
17745 enter : function () {
17747 clearTimeout(this.timeout);
17749 this.hoverState = 'in';
17751 if (!this.delay || !this.delay.show) {
17756 this.timeout = setTimeout(function () {
17757 if (_t.hoverState == 'in') {
17760 }, this.delay.show)
17763 leave : function() {
17764 clearTimeout(this.timeout);
17766 this.hoverState = 'out';
17768 if (!this.delay || !this.delay.hide) {
17773 this.timeout = setTimeout(function () {
17774 if (_t.hoverState == 'out') {
17777 }, this.delay.hide)
17780 show : function (on_el)
17783 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17787 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17788 if (this.html !== false) {
17789 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17791 this.el.removeClass([
17792 'fade','top','bottom', 'left', 'right','in',
17793 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17795 if (!this.title.length) {
17796 this.el.select('.popover-title',true).hide();
17799 var placement = typeof this.placement == 'function' ?
17800 this.placement.call(this, this.el, on_el) :
17803 var autoToken = /\s?auto?\s?/i;
17804 var autoPlace = autoToken.test(placement);
17806 placement = placement.replace(autoToken, '') || 'top';
17810 //this.el.setXY([0,0]);
17812 this.el.dom.style.display='block';
17813 this.el.addClass(placement);
17815 //this.el.appendTo(on_el);
17817 var p = this.getPosition();
17818 var box = this.el.getBox();
17823 var align = Roo.bootstrap.Popover.alignment[placement];
17826 this.el.alignTo(on_el, align[0],align[1]);
17827 //var arrow = this.el.select('.arrow',true).first();
17828 //arrow.set(align[2],
17830 this.el.addClass('in');
17833 if (this.el.hasClass('fade')) {
17837 this.hoverState = 'in';
17839 this.fireEvent('show', this);
17844 this.el.setXY([0,0]);
17845 this.el.removeClass('in');
17847 this.hoverState = null;
17849 this.fireEvent('hide', this);
17854 Roo.bootstrap.Popover.alignment = {
17855 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17856 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17857 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17858 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17869 * @class Roo.bootstrap.Progress
17870 * @extends Roo.bootstrap.Component
17871 * Bootstrap Progress class
17872 * @cfg {Boolean} striped striped of the progress bar
17873 * @cfg {Boolean} active animated of the progress bar
17877 * Create a new Progress
17878 * @param {Object} config The config object
17881 Roo.bootstrap.Progress = function(config){
17882 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17885 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17890 getAutoCreate : function(){
17898 cfg.cls += ' progress-striped';
17902 cfg.cls += ' active';
17921 * @class Roo.bootstrap.ProgressBar
17922 * @extends Roo.bootstrap.Component
17923 * Bootstrap ProgressBar class
17924 * @cfg {Number} aria_valuenow aria-value now
17925 * @cfg {Number} aria_valuemin aria-value min
17926 * @cfg {Number} aria_valuemax aria-value max
17927 * @cfg {String} label label for the progress bar
17928 * @cfg {String} panel (success | info | warning | danger )
17929 * @cfg {String} role role of the progress bar
17930 * @cfg {String} sr_only text
17934 * Create a new ProgressBar
17935 * @param {Object} config The config object
17938 Roo.bootstrap.ProgressBar = function(config){
17939 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17942 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17946 aria_valuemax : 100,
17952 getAutoCreate : function()
17957 cls: 'progress-bar',
17958 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17970 cfg.role = this.role;
17973 if(this.aria_valuenow){
17974 cfg['aria-valuenow'] = this.aria_valuenow;
17977 if(this.aria_valuemin){
17978 cfg['aria-valuemin'] = this.aria_valuemin;
17981 if(this.aria_valuemax){
17982 cfg['aria-valuemax'] = this.aria_valuemax;
17985 if(this.label && !this.sr_only){
17986 cfg.html = this.label;
17990 cfg.cls += ' progress-bar-' + this.panel;
17996 update : function(aria_valuenow)
17998 this.aria_valuenow = aria_valuenow;
18000 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18015 * @class Roo.bootstrap.TabGroup
18016 * @extends Roo.bootstrap.Column
18017 * Bootstrap Column class
18018 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18019 * @cfg {Boolean} carousel true to make the group behave like a carousel
18020 * @cfg {Boolean} bullets show bullets for the panels
18021 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18022 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18023 * @cfg {Boolean} showarrow (true|false) show arrow default true
18026 * Create a new TabGroup
18027 * @param {Object} config The config object
18030 Roo.bootstrap.TabGroup = function(config){
18031 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18033 this.navId = Roo.id();
18036 Roo.bootstrap.TabGroup.register(this);
18040 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18043 transition : false,
18048 slideOnTouch : false,
18051 getAutoCreate : function()
18053 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18055 cfg.cls += ' tab-content';
18057 if (this.carousel) {
18058 cfg.cls += ' carousel slide';
18061 cls : 'carousel-inner',
18065 if(this.bullets && !Roo.isTouch){
18068 cls : 'carousel-bullets',
18072 if(this.bullets_cls){
18073 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18080 cfg.cn[0].cn.push(bullets);
18083 if(this.showarrow){
18084 cfg.cn[0].cn.push({
18086 class : 'carousel-arrow',
18090 class : 'carousel-prev',
18094 class : 'fa fa-chevron-left'
18100 class : 'carousel-next',
18104 class : 'fa fa-chevron-right'
18117 initEvents: function()
18119 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18120 // this.el.on("touchstart", this.onTouchStart, this);
18123 if(this.autoslide){
18126 this.slideFn = window.setInterval(function() {
18127 _this.showPanelNext();
18131 if(this.showarrow){
18132 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18133 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18139 // onTouchStart : function(e, el, o)
18141 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18145 // this.showPanelNext();
18149 getChildContainer : function()
18151 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18155 * register a Navigation item
18156 * @param {Roo.bootstrap.NavItem} the navitem to add
18158 register : function(item)
18160 this.tabs.push( item);
18161 item.navId = this.navId; // not really needed..
18166 getActivePanel : function()
18169 Roo.each(this.tabs, function(t) {
18179 getPanelByName : function(n)
18182 Roo.each(this.tabs, function(t) {
18183 if (t.tabId == n) {
18191 indexOfPanel : function(p)
18194 Roo.each(this.tabs, function(t,i) {
18195 if (t.tabId == p.tabId) {
18204 * show a specific panel
18205 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18206 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18208 showPanel : function (pan)
18210 if(this.transition || typeof(pan) == 'undefined'){
18211 Roo.log("waiting for the transitionend");
18215 if (typeof(pan) == 'number') {
18216 pan = this.tabs[pan];
18219 if (typeof(pan) == 'string') {
18220 pan = this.getPanelByName(pan);
18223 var cur = this.getActivePanel();
18226 Roo.log('pan or acitve pan is undefined');
18230 if (pan.tabId == this.getActivePanel().tabId) {
18234 if (false === cur.fireEvent('beforedeactivate')) {
18238 if(this.bullets > 0 && !Roo.isTouch){
18239 this.setActiveBullet(this.indexOfPanel(pan));
18242 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18244 this.transition = true;
18245 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18246 var lr = dir == 'next' ? 'left' : 'right';
18247 pan.el.addClass(dir); // or prev
18248 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18249 cur.el.addClass(lr); // or right
18250 pan.el.addClass(lr);
18253 cur.el.on('transitionend', function() {
18254 Roo.log("trans end?");
18256 pan.el.removeClass([lr,dir]);
18257 pan.setActive(true);
18259 cur.el.removeClass([lr]);
18260 cur.setActive(false);
18262 _this.transition = false;
18264 }, this, { single: true } );
18269 cur.setActive(false);
18270 pan.setActive(true);
18275 showPanelNext : function()
18277 var i = this.indexOfPanel(this.getActivePanel());
18279 if (i >= this.tabs.length - 1 && !this.autoslide) {
18283 if (i >= this.tabs.length - 1 && this.autoslide) {
18287 this.showPanel(this.tabs[i+1]);
18290 showPanelPrev : function()
18292 var i = this.indexOfPanel(this.getActivePanel());
18294 if (i < 1 && !this.autoslide) {
18298 if (i < 1 && this.autoslide) {
18299 i = this.tabs.length;
18302 this.showPanel(this.tabs[i-1]);
18306 addBullet: function()
18308 if(!this.bullets || Roo.isTouch){
18311 var ctr = this.el.select('.carousel-bullets',true).first();
18312 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18313 var bullet = ctr.createChild({
18314 cls : 'bullet bullet-' + i
18315 },ctr.dom.lastChild);
18320 bullet.on('click', (function(e, el, o, ii, t){
18322 e.preventDefault();
18324 this.showPanel(ii);
18326 if(this.autoslide && this.slideFn){
18327 clearInterval(this.slideFn);
18328 this.slideFn = window.setInterval(function() {
18329 _this.showPanelNext();
18333 }).createDelegate(this, [i, bullet], true));
18338 setActiveBullet : function(i)
18344 Roo.each(this.el.select('.bullet', true).elements, function(el){
18345 el.removeClass('selected');
18348 var bullet = this.el.select('.bullet-' + i, true).first();
18354 bullet.addClass('selected');
18365 Roo.apply(Roo.bootstrap.TabGroup, {
18369 * register a Navigation Group
18370 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18372 register : function(navgrp)
18374 this.groups[navgrp.navId] = navgrp;
18378 * fetch a Navigation Group based on the navigation ID
18379 * if one does not exist , it will get created.
18380 * @param {string} the navgroup to add
18381 * @returns {Roo.bootstrap.NavGroup} the navgroup
18383 get: function(navId) {
18384 if (typeof(this.groups[navId]) == 'undefined') {
18385 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18387 return this.groups[navId] ;
18402 * @class Roo.bootstrap.TabPanel
18403 * @extends Roo.bootstrap.Component
18404 * Bootstrap TabPanel class
18405 * @cfg {Boolean} active panel active
18406 * @cfg {String} html panel content
18407 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18408 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18409 * @cfg {String} href click to link..
18413 * Create a new TabPanel
18414 * @param {Object} config The config object
18417 Roo.bootstrap.TabPanel = function(config){
18418 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18422 * Fires when the active status changes
18423 * @param {Roo.bootstrap.TabPanel} this
18424 * @param {Boolean} state the new state
18429 * @event beforedeactivate
18430 * Fires before a tab is de-activated - can be used to do validation on a form.
18431 * @param {Roo.bootstrap.TabPanel} this
18432 * @return {Boolean} false if there is an error
18435 'beforedeactivate': true
18438 this.tabId = this.tabId || Roo.id();
18442 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18450 getAutoCreate : function(){
18453 // item is needed for carousel - not sure if it has any effect otherwise
18454 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18455 html: this.html || ''
18459 cfg.cls += ' active';
18463 cfg.tabId = this.tabId;
18470 initEvents: function()
18472 var p = this.parent();
18474 this.navId = this.navId || p.navId;
18476 if (typeof(this.navId) != 'undefined') {
18477 // not really needed.. but just in case.. parent should be a NavGroup.
18478 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18482 var i = tg.tabs.length - 1;
18484 if(this.active && tg.bullets > 0 && i < tg.bullets){
18485 tg.setActiveBullet(i);
18489 this.el.on('click', this.onClick, this);
18492 this.el.on("touchstart", this.onTouchStart, this);
18493 this.el.on("touchmove", this.onTouchMove, this);
18494 this.el.on("touchend", this.onTouchEnd, this);
18499 onRender : function(ct, position)
18501 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18504 setActive : function(state)
18506 Roo.log("panel - set active " + this.tabId + "=" + state);
18508 this.active = state;
18510 this.el.removeClass('active');
18512 } else if (!this.el.hasClass('active')) {
18513 this.el.addClass('active');
18516 this.fireEvent('changed', this, state);
18519 onClick : function(e)
18521 e.preventDefault();
18523 if(!this.href.length){
18527 window.location.href = this.href;
18536 onTouchStart : function(e)
18538 this.swiping = false;
18540 this.startX = e.browserEvent.touches[0].clientX;
18541 this.startY = e.browserEvent.touches[0].clientY;
18544 onTouchMove : function(e)
18546 this.swiping = true;
18548 this.endX = e.browserEvent.touches[0].clientX;
18549 this.endY = e.browserEvent.touches[0].clientY;
18552 onTouchEnd : function(e)
18559 var tabGroup = this.parent();
18561 if(this.endX > this.startX){ // swiping right
18562 tabGroup.showPanelPrev();
18566 if(this.startX > this.endX){ // swiping left
18567 tabGroup.showPanelNext();
18586 * @class Roo.bootstrap.DateField
18587 * @extends Roo.bootstrap.Input
18588 * Bootstrap DateField class
18589 * @cfg {Number} weekStart default 0
18590 * @cfg {String} viewMode default empty, (months|years)
18591 * @cfg {String} minViewMode default empty, (months|years)
18592 * @cfg {Number} startDate default -Infinity
18593 * @cfg {Number} endDate default Infinity
18594 * @cfg {Boolean} todayHighlight default false
18595 * @cfg {Boolean} todayBtn default false
18596 * @cfg {Boolean} calendarWeeks default false
18597 * @cfg {Object} daysOfWeekDisabled default empty
18598 * @cfg {Boolean} singleMode default false (true | false)
18600 * @cfg {Boolean} keyboardNavigation default true
18601 * @cfg {String} language default en
18604 * Create a new DateField
18605 * @param {Object} config The config object
18608 Roo.bootstrap.DateField = function(config){
18609 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18613 * Fires when this field show.
18614 * @param {Roo.bootstrap.DateField} this
18615 * @param {Mixed} date The date value
18620 * Fires when this field hide.
18621 * @param {Roo.bootstrap.DateField} this
18622 * @param {Mixed} date The date value
18627 * Fires when select a date.
18628 * @param {Roo.bootstrap.DateField} this
18629 * @param {Mixed} date The date value
18633 * @event beforeselect
18634 * Fires when before select a date.
18635 * @param {Roo.bootstrap.DateField} this
18636 * @param {Mixed} date The date value
18638 beforeselect : true
18642 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18645 * @cfg {String} format
18646 * The default date format string which can be overriden for localization support. The format must be
18647 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18651 * @cfg {String} altFormats
18652 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18653 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18655 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18663 todayHighlight : false,
18669 keyboardNavigation: true,
18671 calendarWeeks: false,
18673 startDate: -Infinity,
18677 daysOfWeekDisabled: [],
18681 singleMode : false,
18683 UTCDate: function()
18685 return new Date(Date.UTC.apply(Date, arguments));
18688 UTCToday: function()
18690 var today = new Date();
18691 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18694 getDate: function() {
18695 var d = this.getUTCDate();
18696 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18699 getUTCDate: function() {
18703 setDate: function(d) {
18704 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18707 setUTCDate: function(d) {
18709 this.setValue(this.formatDate(this.date));
18712 onRender: function(ct, position)
18715 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18717 this.language = this.language || 'en';
18718 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18719 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18721 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18722 this.format = this.format || 'm/d/y';
18723 this.isInline = false;
18724 this.isInput = true;
18725 this.component = this.el.select('.add-on', true).first() || false;
18726 this.component = (this.component && this.component.length === 0) ? false : this.component;
18727 this.hasInput = this.component && this.inputEl().length;
18729 if (typeof(this.minViewMode === 'string')) {
18730 switch (this.minViewMode) {
18732 this.minViewMode = 1;
18735 this.minViewMode = 2;
18738 this.minViewMode = 0;
18743 if (typeof(this.viewMode === 'string')) {
18744 switch (this.viewMode) {
18757 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18759 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18761 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18763 this.picker().on('mousedown', this.onMousedown, this);
18764 this.picker().on('click', this.onClick, this);
18766 this.picker().addClass('datepicker-dropdown');
18768 this.startViewMode = this.viewMode;
18770 if(this.singleMode){
18771 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18772 v.setVisibilityMode(Roo.Element.DISPLAY);
18776 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18777 v.setStyle('width', '189px');
18781 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18782 if(!this.calendarWeeks){
18787 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18788 v.attr('colspan', function(i, val){
18789 return parseInt(val) + 1;
18794 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18796 this.setStartDate(this.startDate);
18797 this.setEndDate(this.endDate);
18799 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18806 if(this.isInline) {
18811 picker : function()
18813 return this.pickerEl;
18814 // return this.el.select('.datepicker', true).first();
18817 fillDow: function()
18819 var dowCnt = this.weekStart;
18828 if(this.calendarWeeks){
18836 while (dowCnt < this.weekStart + 7) {
18840 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18844 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18847 fillMonths: function()
18850 var months = this.picker().select('>.datepicker-months td', true).first();
18852 months.dom.innerHTML = '';
18858 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18861 months.createChild(month);
18868 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;
18870 if (this.date < this.startDate) {
18871 this.viewDate = new Date(this.startDate);
18872 } else if (this.date > this.endDate) {
18873 this.viewDate = new Date(this.endDate);
18875 this.viewDate = new Date(this.date);
18883 var d = new Date(this.viewDate),
18884 year = d.getUTCFullYear(),
18885 month = d.getUTCMonth(),
18886 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18887 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18888 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18889 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18890 currentDate = this.date && this.date.valueOf(),
18891 today = this.UTCToday();
18893 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18895 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18897 // this.picker.select('>tfoot th.today').
18898 // .text(dates[this.language].today)
18899 // .toggle(this.todayBtn !== false);
18901 this.updateNavArrows();
18904 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18906 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18908 prevMonth.setUTCDate(day);
18910 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18912 var nextMonth = new Date(prevMonth);
18914 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18916 nextMonth = nextMonth.valueOf();
18918 var fillMonths = false;
18920 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18922 while(prevMonth.valueOf() <= nextMonth) {
18925 if (prevMonth.getUTCDay() === this.weekStart) {
18927 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18935 if(this.calendarWeeks){
18936 // ISO 8601: First week contains first thursday.
18937 // ISO also states week starts on Monday, but we can be more abstract here.
18939 // Start of current week: based on weekstart/current date
18940 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18941 // Thursday of this week
18942 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18943 // First Thursday of year, year from thursday
18944 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18945 // Calendar week: ms between thursdays, div ms per day, div 7 days
18946 calWeek = (th - yth) / 864e5 / 7 + 1;
18948 fillMonths.cn.push({
18956 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18958 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18961 if (this.todayHighlight &&
18962 prevMonth.getUTCFullYear() == today.getFullYear() &&
18963 prevMonth.getUTCMonth() == today.getMonth() &&
18964 prevMonth.getUTCDate() == today.getDate()) {
18965 clsName += ' today';
18968 if (currentDate && prevMonth.valueOf() === currentDate) {
18969 clsName += ' active';
18972 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18973 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18974 clsName += ' disabled';
18977 fillMonths.cn.push({
18979 cls: 'day ' + clsName,
18980 html: prevMonth.getDate()
18983 prevMonth.setDate(prevMonth.getDate()+1);
18986 var currentYear = this.date && this.date.getUTCFullYear();
18987 var currentMonth = this.date && this.date.getUTCMonth();
18989 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18991 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18992 v.removeClass('active');
18994 if(currentYear === year && k === currentMonth){
18995 v.addClass('active');
18998 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18999 v.addClass('disabled');
19005 year = parseInt(year/10, 10) * 10;
19007 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19009 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19012 for (var i = -1; i < 11; i++) {
19013 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19015 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19023 showMode: function(dir)
19026 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19029 Roo.each(this.picker().select('>div',true).elements, function(v){
19030 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19033 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19038 if(this.isInline) {
19042 this.picker().removeClass(['bottom', 'top']);
19044 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19046 * place to the top of element!
19050 this.picker().addClass('top');
19051 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19056 this.picker().addClass('bottom');
19058 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19061 parseDate : function(value)
19063 if(!value || value instanceof Date){
19066 var v = Date.parseDate(value, this.format);
19067 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19068 v = Date.parseDate(value, 'Y-m-d');
19070 if(!v && this.altFormats){
19071 if(!this.altFormatsArray){
19072 this.altFormatsArray = this.altFormats.split("|");
19074 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19075 v = Date.parseDate(value, this.altFormatsArray[i]);
19081 formatDate : function(date, fmt)
19083 return (!date || !(date instanceof Date)) ?
19084 date : date.dateFormat(fmt || this.format);
19087 onFocus : function()
19089 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19093 onBlur : function()
19095 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19097 var d = this.inputEl().getValue();
19104 showPopup : function()
19106 this.picker().show();
19110 this.fireEvent('showpopup', this, this.date);
19113 hidePopup : function()
19115 if(this.isInline) {
19118 this.picker().hide();
19119 this.viewMode = this.startViewMode;
19122 this.fireEvent('hidepopup', this, this.date);
19126 onMousedown: function(e)
19128 e.stopPropagation();
19129 e.preventDefault();
19134 Roo.bootstrap.DateField.superclass.keyup.call(this);
19138 setValue: function(v)
19140 if(this.fireEvent('beforeselect', this, v) !== false){
19141 var d = new Date(this.parseDate(v) ).clearTime();
19143 if(isNaN(d.getTime())){
19144 this.date = this.viewDate = '';
19145 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19149 v = this.formatDate(d);
19151 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19153 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19157 this.fireEvent('select', this, this.date);
19161 getValue: function()
19163 return this.formatDate(this.date);
19166 fireKey: function(e)
19168 if (!this.picker().isVisible()){
19169 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19175 var dateChanged = false,
19177 newDate, newViewDate;
19182 e.preventDefault();
19186 if (!this.keyboardNavigation) {
19189 dir = e.keyCode == 37 ? -1 : 1;
19192 newDate = this.moveYear(this.date, dir);
19193 newViewDate = this.moveYear(this.viewDate, dir);
19194 } else if (e.shiftKey){
19195 newDate = this.moveMonth(this.date, dir);
19196 newViewDate = this.moveMonth(this.viewDate, dir);
19198 newDate = new Date(this.date);
19199 newDate.setUTCDate(this.date.getUTCDate() + dir);
19200 newViewDate = new Date(this.viewDate);
19201 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19203 if (this.dateWithinRange(newDate)){
19204 this.date = newDate;
19205 this.viewDate = newViewDate;
19206 this.setValue(this.formatDate(this.date));
19208 e.preventDefault();
19209 dateChanged = true;
19214 if (!this.keyboardNavigation) {
19217 dir = e.keyCode == 38 ? -1 : 1;
19219 newDate = this.moveYear(this.date, dir);
19220 newViewDate = this.moveYear(this.viewDate, dir);
19221 } else if (e.shiftKey){
19222 newDate = this.moveMonth(this.date, dir);
19223 newViewDate = this.moveMonth(this.viewDate, dir);
19225 newDate = new Date(this.date);
19226 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19227 newViewDate = new Date(this.viewDate);
19228 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19230 if (this.dateWithinRange(newDate)){
19231 this.date = newDate;
19232 this.viewDate = newViewDate;
19233 this.setValue(this.formatDate(this.date));
19235 e.preventDefault();
19236 dateChanged = true;
19240 this.setValue(this.formatDate(this.date));
19242 e.preventDefault();
19245 this.setValue(this.formatDate(this.date));
19259 onClick: function(e)
19261 e.stopPropagation();
19262 e.preventDefault();
19264 var target = e.getTarget();
19266 if(target.nodeName.toLowerCase() === 'i'){
19267 target = Roo.get(target).dom.parentNode;
19270 var nodeName = target.nodeName;
19271 var className = target.className;
19272 var html = target.innerHTML;
19273 //Roo.log(nodeName);
19275 switch(nodeName.toLowerCase()) {
19277 switch(className) {
19283 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19284 switch(this.viewMode){
19286 this.viewDate = this.moveMonth(this.viewDate, dir);
19290 this.viewDate = this.moveYear(this.viewDate, dir);
19296 var date = new Date();
19297 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19299 this.setValue(this.formatDate(this.date));
19306 if (className.indexOf('disabled') < 0) {
19307 this.viewDate.setUTCDate(1);
19308 if (className.indexOf('month') > -1) {
19309 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19311 var year = parseInt(html, 10) || 0;
19312 this.viewDate.setUTCFullYear(year);
19316 if(this.singleMode){
19317 this.setValue(this.formatDate(this.viewDate));
19328 //Roo.log(className);
19329 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19330 var day = parseInt(html, 10) || 1;
19331 var year = this.viewDate.getUTCFullYear(),
19332 month = this.viewDate.getUTCMonth();
19334 if (className.indexOf('old') > -1) {
19341 } else if (className.indexOf('new') > -1) {
19349 //Roo.log([year,month,day]);
19350 this.date = this.UTCDate(year, month, day,0,0,0,0);
19351 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19353 //Roo.log(this.formatDate(this.date));
19354 this.setValue(this.formatDate(this.date));
19361 setStartDate: function(startDate)
19363 this.startDate = startDate || -Infinity;
19364 if (this.startDate !== -Infinity) {
19365 this.startDate = this.parseDate(this.startDate);
19368 this.updateNavArrows();
19371 setEndDate: function(endDate)
19373 this.endDate = endDate || Infinity;
19374 if (this.endDate !== Infinity) {
19375 this.endDate = this.parseDate(this.endDate);
19378 this.updateNavArrows();
19381 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19383 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19384 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19385 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19387 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19388 return parseInt(d, 10);
19391 this.updateNavArrows();
19394 updateNavArrows: function()
19396 if(this.singleMode){
19400 var d = new Date(this.viewDate),
19401 year = d.getUTCFullYear(),
19402 month = d.getUTCMonth();
19404 Roo.each(this.picker().select('.prev', true).elements, function(v){
19406 switch (this.viewMode) {
19409 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19415 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19422 Roo.each(this.picker().select('.next', true).elements, function(v){
19424 switch (this.viewMode) {
19427 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19433 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19441 moveMonth: function(date, dir)
19446 var new_date = new Date(date.valueOf()),
19447 day = new_date.getUTCDate(),
19448 month = new_date.getUTCMonth(),
19449 mag = Math.abs(dir),
19451 dir = dir > 0 ? 1 : -1;
19454 // If going back one month, make sure month is not current month
19455 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19457 return new_date.getUTCMonth() == month;
19459 // If going forward one month, make sure month is as expected
19460 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19462 return new_date.getUTCMonth() != new_month;
19464 new_month = month + dir;
19465 new_date.setUTCMonth(new_month);
19466 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19467 if (new_month < 0 || new_month > 11) {
19468 new_month = (new_month + 12) % 12;
19471 // For magnitudes >1, move one month at a time...
19472 for (var i=0; i<mag; i++) {
19473 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19474 new_date = this.moveMonth(new_date, dir);
19476 // ...then reset the day, keeping it in the new month
19477 new_month = new_date.getUTCMonth();
19478 new_date.setUTCDate(day);
19480 return new_month != new_date.getUTCMonth();
19483 // Common date-resetting loop -- if date is beyond end of month, make it
19486 new_date.setUTCDate(--day);
19487 new_date.setUTCMonth(new_month);
19492 moveYear: function(date, dir)
19494 return this.moveMonth(date, dir*12);
19497 dateWithinRange: function(date)
19499 return date >= this.startDate && date <= this.endDate;
19505 this.picker().remove();
19508 validateValue : function(value)
19510 if(this.getVisibilityEl().hasClass('hidden')){
19514 if(value.length < 1) {
19515 if(this.allowBlank){
19521 if(value.length < this.minLength){
19524 if(value.length > this.maxLength){
19528 var vt = Roo.form.VTypes;
19529 if(!vt[this.vtype](value, this)){
19533 if(typeof this.validator == "function"){
19534 var msg = this.validator(value);
19540 if(this.regex && !this.regex.test(value)){
19544 if(typeof(this.parseDate(value)) == 'undefined'){
19548 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19552 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19562 this.date = this.viewDate = '';
19564 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19569 Roo.apply(Roo.bootstrap.DateField, {
19580 html: '<i class="fa fa-arrow-left"/>'
19590 html: '<i class="fa fa-arrow-right"/>'
19632 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19633 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19634 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19635 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19636 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19649 navFnc: 'FullYear',
19654 navFnc: 'FullYear',
19659 Roo.apply(Roo.bootstrap.DateField, {
19663 cls: 'datepicker dropdown-menu roo-dynamic',
19667 cls: 'datepicker-days',
19671 cls: 'table-condensed',
19673 Roo.bootstrap.DateField.head,
19677 Roo.bootstrap.DateField.footer
19684 cls: 'datepicker-months',
19688 cls: 'table-condensed',
19690 Roo.bootstrap.DateField.head,
19691 Roo.bootstrap.DateField.content,
19692 Roo.bootstrap.DateField.footer
19699 cls: 'datepicker-years',
19703 cls: 'table-condensed',
19705 Roo.bootstrap.DateField.head,
19706 Roo.bootstrap.DateField.content,
19707 Roo.bootstrap.DateField.footer
19726 * @class Roo.bootstrap.TimeField
19727 * @extends Roo.bootstrap.Input
19728 * Bootstrap DateField class
19732 * Create a new TimeField
19733 * @param {Object} config The config object
19736 Roo.bootstrap.TimeField = function(config){
19737 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19741 * Fires when this field show.
19742 * @param {Roo.bootstrap.DateField} thisthis
19743 * @param {Mixed} date The date value
19748 * Fires when this field hide.
19749 * @param {Roo.bootstrap.DateField} this
19750 * @param {Mixed} date The date value
19755 * Fires when select a date.
19756 * @param {Roo.bootstrap.DateField} this
19757 * @param {Mixed} date The date value
19763 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19766 * @cfg {String} format
19767 * The default time format string which can be overriden for localization support. The format must be
19768 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19772 onRender: function(ct, position)
19775 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19777 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19779 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19781 this.pop = this.picker().select('>.datepicker-time',true).first();
19782 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19784 this.picker().on('mousedown', this.onMousedown, this);
19785 this.picker().on('click', this.onClick, this);
19787 this.picker().addClass('datepicker-dropdown');
19792 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19793 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19794 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19795 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19796 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19797 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19801 fireKey: function(e){
19802 if (!this.picker().isVisible()){
19803 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19809 e.preventDefault();
19817 this.onTogglePeriod();
19820 this.onIncrementMinutes();
19823 this.onDecrementMinutes();
19832 onClick: function(e) {
19833 e.stopPropagation();
19834 e.preventDefault();
19837 picker : function()
19839 return this.el.select('.datepicker', true).first();
19842 fillTime: function()
19844 var time = this.pop.select('tbody', true).first();
19846 time.dom.innerHTML = '';
19861 cls: 'hours-up glyphicon glyphicon-chevron-up'
19881 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19902 cls: 'timepicker-hour',
19917 cls: 'timepicker-minute',
19932 cls: 'btn btn-primary period',
19954 cls: 'hours-down glyphicon glyphicon-chevron-down'
19974 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19992 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19999 var hours = this.time.getHours();
20000 var minutes = this.time.getMinutes();
20013 hours = hours - 12;
20017 hours = '0' + hours;
20021 minutes = '0' + minutes;
20024 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20025 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20026 this.pop.select('button', true).first().dom.innerHTML = period;
20032 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20034 var cls = ['bottom'];
20036 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20043 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20048 this.picker().addClass(cls.join('-'));
20052 Roo.each(cls, function(c){
20054 _this.picker().setTop(_this.inputEl().getHeight());
20058 _this.picker().setTop(0 - _this.picker().getHeight());
20063 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20067 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20074 onFocus : function()
20076 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20080 onBlur : function()
20082 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20088 this.picker().show();
20093 this.fireEvent('show', this, this.date);
20098 this.picker().hide();
20101 this.fireEvent('hide', this, this.date);
20104 setTime : function()
20107 this.setValue(this.time.format(this.format));
20109 this.fireEvent('select', this, this.date);
20114 onMousedown: function(e){
20115 e.stopPropagation();
20116 e.preventDefault();
20119 onIncrementHours: function()
20121 Roo.log('onIncrementHours');
20122 this.time = this.time.add(Date.HOUR, 1);
20127 onDecrementHours: function()
20129 Roo.log('onDecrementHours');
20130 this.time = this.time.add(Date.HOUR, -1);
20134 onIncrementMinutes: function()
20136 Roo.log('onIncrementMinutes');
20137 this.time = this.time.add(Date.MINUTE, 1);
20141 onDecrementMinutes: function()
20143 Roo.log('onDecrementMinutes');
20144 this.time = this.time.add(Date.MINUTE, -1);
20148 onTogglePeriod: function()
20150 Roo.log('onTogglePeriod');
20151 this.time = this.time.add(Date.HOUR, 12);
20158 Roo.apply(Roo.bootstrap.TimeField, {
20188 cls: 'btn btn-info ok',
20200 Roo.apply(Roo.bootstrap.TimeField, {
20204 cls: 'datepicker dropdown-menu',
20208 cls: 'datepicker-time',
20212 cls: 'table-condensed',
20214 Roo.bootstrap.TimeField.content,
20215 Roo.bootstrap.TimeField.footer
20234 * @class Roo.bootstrap.MonthField
20235 * @extends Roo.bootstrap.Input
20236 * Bootstrap MonthField class
20238 * @cfg {String} language default en
20241 * Create a new MonthField
20242 * @param {Object} config The config object
20245 Roo.bootstrap.MonthField = function(config){
20246 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20251 * Fires when this field show.
20252 * @param {Roo.bootstrap.MonthField} this
20253 * @param {Mixed} date The date value
20258 * Fires when this field hide.
20259 * @param {Roo.bootstrap.MonthField} this
20260 * @param {Mixed} date The date value
20265 * Fires when select a date.
20266 * @param {Roo.bootstrap.MonthField} this
20267 * @param {String} oldvalue The old value
20268 * @param {String} newvalue The new value
20274 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20276 onRender: function(ct, position)
20279 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20281 this.language = this.language || 'en';
20282 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20283 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20285 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20286 this.isInline = false;
20287 this.isInput = true;
20288 this.component = this.el.select('.add-on', true).first() || false;
20289 this.component = (this.component && this.component.length === 0) ? false : this.component;
20290 this.hasInput = this.component && this.inputEL().length;
20292 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20294 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20296 this.picker().on('mousedown', this.onMousedown, this);
20297 this.picker().on('click', this.onClick, this);
20299 this.picker().addClass('datepicker-dropdown');
20301 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20302 v.setStyle('width', '189px');
20309 if(this.isInline) {
20315 setValue: function(v, suppressEvent)
20317 var o = this.getValue();
20319 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20323 if(suppressEvent !== true){
20324 this.fireEvent('select', this, o, v);
20329 getValue: function()
20334 onClick: function(e)
20336 e.stopPropagation();
20337 e.preventDefault();
20339 var target = e.getTarget();
20341 if(target.nodeName.toLowerCase() === 'i'){
20342 target = Roo.get(target).dom.parentNode;
20345 var nodeName = target.nodeName;
20346 var className = target.className;
20347 var html = target.innerHTML;
20349 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20353 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20355 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20361 picker : function()
20363 return this.pickerEl;
20366 fillMonths: function()
20369 var months = this.picker().select('>.datepicker-months td', true).first();
20371 months.dom.innerHTML = '';
20377 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20380 months.createChild(month);
20389 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20390 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20393 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20394 e.removeClass('active');
20396 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20397 e.addClass('active');
20404 if(this.isInline) {
20408 this.picker().removeClass(['bottom', 'top']);
20410 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20412 * place to the top of element!
20416 this.picker().addClass('top');
20417 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20422 this.picker().addClass('bottom');
20424 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20427 onFocus : function()
20429 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20433 onBlur : function()
20435 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20437 var d = this.inputEl().getValue();
20446 this.picker().show();
20447 this.picker().select('>.datepicker-months', true).first().show();
20451 this.fireEvent('show', this, this.date);
20456 if(this.isInline) {
20459 this.picker().hide();
20460 this.fireEvent('hide', this, this.date);
20464 onMousedown: function(e)
20466 e.stopPropagation();
20467 e.preventDefault();
20472 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20476 fireKey: function(e)
20478 if (!this.picker().isVisible()){
20479 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20490 e.preventDefault();
20494 dir = e.keyCode == 37 ? -1 : 1;
20496 this.vIndex = this.vIndex + dir;
20498 if(this.vIndex < 0){
20502 if(this.vIndex > 11){
20506 if(isNaN(this.vIndex)){
20510 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20516 dir = e.keyCode == 38 ? -1 : 1;
20518 this.vIndex = this.vIndex + dir * 4;
20520 if(this.vIndex < 0){
20524 if(this.vIndex > 11){
20528 if(isNaN(this.vIndex)){
20532 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20537 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20538 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20542 e.preventDefault();
20545 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20546 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20562 this.picker().remove();
20567 Roo.apply(Roo.bootstrap.MonthField, {
20586 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20587 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20592 Roo.apply(Roo.bootstrap.MonthField, {
20596 cls: 'datepicker dropdown-menu roo-dynamic',
20600 cls: 'datepicker-months',
20604 cls: 'table-condensed',
20606 Roo.bootstrap.DateField.content
20626 * @class Roo.bootstrap.CheckBox
20627 * @extends Roo.bootstrap.Input
20628 * Bootstrap CheckBox class
20630 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20631 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20632 * @cfg {String} boxLabel The text that appears beside the checkbox
20633 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20634 * @cfg {Boolean} checked initnal the element
20635 * @cfg {Boolean} inline inline the element (default false)
20636 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20637 * @cfg {String} tooltip label tooltip
20640 * Create a new CheckBox
20641 * @param {Object} config The config object
20644 Roo.bootstrap.CheckBox = function(config){
20645 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20650 * Fires when the element is checked or unchecked.
20651 * @param {Roo.bootstrap.CheckBox} this This input
20652 * @param {Boolean} checked The new checked value
20657 * Fires when the element is click.
20658 * @param {Roo.bootstrap.CheckBox} this This input
20665 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20667 inputType: 'checkbox',
20676 getAutoCreate : function()
20678 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20684 cfg.cls = 'form-group ' + this.inputType; //input-group
20687 cfg.cls += ' ' + this.inputType + '-inline';
20693 type : this.inputType,
20694 value : this.inputValue,
20695 cls : 'roo-' + this.inputType, //'form-box',
20696 placeholder : this.placeholder || ''
20700 if(this.inputType != 'radio'){
20704 cls : 'roo-hidden-value',
20705 value : this.checked ? this.inputValue : this.valueOff
20710 if (this.weight) { // Validity check?
20711 cfg.cls += " " + this.inputType + "-" + this.weight;
20714 if (this.disabled) {
20715 input.disabled=true;
20719 input.checked = this.checked;
20724 input.name = this.name;
20726 if(this.inputType != 'radio'){
20727 hidden.name = this.name;
20728 input.name = '_hidden_' + this.name;
20733 input.cls += ' input-' + this.size;
20738 ['xs','sm','md','lg'].map(function(size){
20739 if (settings[size]) {
20740 cfg.cls += ' col-' + size + '-' + settings[size];
20744 var inputblock = input;
20746 if (this.before || this.after) {
20749 cls : 'input-group',
20754 inputblock.cn.push({
20756 cls : 'input-group-addon',
20761 inputblock.cn.push(input);
20763 if(this.inputType != 'radio'){
20764 inputblock.cn.push(hidden);
20768 inputblock.cn.push({
20770 cls : 'input-group-addon',
20777 if (align ==='left' && this.fieldLabel.length) {
20778 // Roo.log("left and has label");
20783 cls : 'control-label',
20784 html : this.fieldLabel
20794 if(this.labelWidth > 12){
20795 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20798 if(this.labelWidth < 13 && this.labelmd == 0){
20799 this.labelmd = this.labelWidth;
20802 if(this.labellg > 0){
20803 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20804 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20807 if(this.labelmd > 0){
20808 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20809 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20812 if(this.labelsm > 0){
20813 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20814 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20817 if(this.labelxs > 0){
20818 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20819 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20822 } else if ( this.fieldLabel.length) {
20823 // Roo.log(" label");
20827 tag: this.boxLabel ? 'span' : 'label',
20829 cls: 'control-label box-input-label',
20830 //cls : 'input-group-addon',
20831 html : this.fieldLabel
20840 // Roo.log(" no label && no align");
20841 cfg.cn = [ inputblock ] ;
20847 var boxLabelCfg = {
20849 //'for': id, // box label is handled by onclick - so no for...
20851 html: this.boxLabel
20855 boxLabelCfg.tooltip = this.tooltip;
20858 cfg.cn.push(boxLabelCfg);
20861 if(this.inputType != 'radio'){
20862 cfg.cn.push(hidden);
20870 * return the real input element.
20872 inputEl: function ()
20874 return this.el.select('input.roo-' + this.inputType,true).first();
20876 hiddenEl: function ()
20878 return this.el.select('input.roo-hidden-value',true).first();
20881 labelEl: function()
20883 return this.el.select('label.control-label',true).first();
20885 /* depricated... */
20889 return this.labelEl();
20892 boxLabelEl: function()
20894 return this.el.select('label.box-label',true).first();
20897 initEvents : function()
20899 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20901 this.inputEl().on('click', this.onClick, this);
20903 if (this.boxLabel) {
20904 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20907 this.startValue = this.getValue();
20910 Roo.bootstrap.CheckBox.register(this);
20914 onClick : function(e)
20916 if(this.fireEvent('click', this, e) !== false){
20917 this.setChecked(!this.checked);
20922 setChecked : function(state,suppressEvent)
20924 this.startValue = this.getValue();
20926 if(this.inputType == 'radio'){
20928 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20929 e.dom.checked = false;
20932 this.inputEl().dom.checked = true;
20934 this.inputEl().dom.value = this.inputValue;
20936 if(suppressEvent !== true){
20937 this.fireEvent('check', this, true);
20945 this.checked = state;
20947 this.inputEl().dom.checked = state;
20950 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20952 if(suppressEvent !== true){
20953 this.fireEvent('check', this, state);
20959 getValue : function()
20961 if(this.inputType == 'radio'){
20962 return this.getGroupValue();
20965 return this.hiddenEl().dom.value;
20969 getGroupValue : function()
20971 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20975 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20978 setValue : function(v,suppressEvent)
20980 if(this.inputType == 'radio'){
20981 this.setGroupValue(v, suppressEvent);
20985 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20990 setGroupValue : function(v, suppressEvent)
20992 this.startValue = this.getValue();
20994 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20995 e.dom.checked = false;
20997 if(e.dom.value == v){
20998 e.dom.checked = true;
21002 if(suppressEvent !== true){
21003 this.fireEvent('check', this, true);
21011 validate : function()
21013 if(this.getVisibilityEl().hasClass('hidden')){
21019 (this.inputType == 'radio' && this.validateRadio()) ||
21020 (this.inputType == 'checkbox' && this.validateCheckbox())
21026 this.markInvalid();
21030 validateRadio : function()
21032 if(this.getVisibilityEl().hasClass('hidden')){
21036 if(this.allowBlank){
21042 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21043 if(!e.dom.checked){
21055 validateCheckbox : function()
21058 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21059 //return (this.getValue() == this.inputValue) ? true : false;
21062 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21070 for(var i in group){
21071 if(group[i].el.isVisible(true)){
21079 for(var i in group){
21084 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21091 * Mark this field as valid
21093 markValid : function()
21097 this.fireEvent('valid', this);
21099 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21102 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21109 if(this.inputType == 'radio'){
21110 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21111 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21112 e.findParent('.form-group', false, true).addClass(_this.validClass);
21119 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21120 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21124 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21130 for(var i in group){
21131 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21132 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21137 * Mark this field as invalid
21138 * @param {String} msg The validation message
21140 markInvalid : function(msg)
21142 if(this.allowBlank){
21148 this.fireEvent('invalid', this, msg);
21150 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21153 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21157 label.markInvalid();
21160 if(this.inputType == 'radio'){
21161 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21162 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21163 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21170 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21171 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21175 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21181 for(var i in group){
21182 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21183 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21188 clearInvalid : function()
21190 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21192 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21194 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21196 if (label && label.iconEl) {
21197 label.iconEl.removeClass(label.validClass);
21198 label.iconEl.removeClass(label.invalidClass);
21202 disable : function()
21204 if(this.inputType != 'radio'){
21205 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21212 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21213 _this.getActionEl().addClass(this.disabledClass);
21214 e.dom.disabled = true;
21218 this.disabled = true;
21219 this.fireEvent("disable", this);
21223 enable : function()
21225 if(this.inputType != 'radio'){
21226 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21233 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21234 _this.getActionEl().removeClass(this.disabledClass);
21235 e.dom.disabled = false;
21239 this.disabled = false;
21240 this.fireEvent("enable", this);
21244 setBoxLabel : function(v)
21249 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21255 Roo.apply(Roo.bootstrap.CheckBox, {
21260 * register a CheckBox Group
21261 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21263 register : function(checkbox)
21265 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21266 this.groups[checkbox.groupId] = {};
21269 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21273 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21277 * fetch a CheckBox Group based on the group ID
21278 * @param {string} the group ID
21279 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21281 get: function(groupId) {
21282 if (typeof(this.groups[groupId]) == 'undefined') {
21286 return this.groups[groupId] ;
21299 * @class Roo.bootstrap.Radio
21300 * @extends Roo.bootstrap.Component
21301 * Bootstrap Radio class
21302 * @cfg {String} boxLabel - the label associated
21303 * @cfg {String} value - the value of radio
21306 * Create a new Radio
21307 * @param {Object} config The config object
21309 Roo.bootstrap.Radio = function(config){
21310 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21314 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21320 getAutoCreate : function()
21324 cls : 'form-group radio',
21329 html : this.boxLabel
21337 initEvents : function()
21339 this.parent().register(this);
21341 this.el.on('click', this.onClick, this);
21345 onClick : function(e)
21347 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21348 this.setChecked(true);
21352 setChecked : function(state, suppressEvent)
21354 this.parent().setValue(this.value, suppressEvent);
21358 setBoxLabel : function(v)
21363 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21378 * @class Roo.bootstrap.SecurePass
21379 * @extends Roo.bootstrap.Input
21380 * Bootstrap SecurePass class
21384 * Create a new SecurePass
21385 * @param {Object} config The config object
21388 Roo.bootstrap.SecurePass = function (config) {
21389 // these go here, so the translation tool can replace them..
21391 PwdEmpty: "Please type a password, and then retype it to confirm.",
21392 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21393 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21394 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21395 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21396 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21397 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21398 TooWeak: "Your password is Too Weak."
21400 this.meterLabel = "Password strength:";
21401 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21402 this.meterClass = [
21403 "roo-password-meter-tooweak",
21404 "roo-password-meter-weak",
21405 "roo-password-meter-medium",
21406 "roo-password-meter-strong",
21407 "roo-password-meter-grey"
21412 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21415 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21417 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21419 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21420 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21421 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21422 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21423 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21424 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21425 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21435 * @cfg {String/Object} Label for the strength meter (defaults to
21436 * 'Password strength:')
21441 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21442 * ['Weak', 'Medium', 'Strong'])
21445 pwdStrengths: false,
21458 initEvents: function ()
21460 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21462 if (this.el.is('input[type=password]') && Roo.isSafari) {
21463 this.el.on('keydown', this.SafariOnKeyDown, this);
21466 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21469 onRender: function (ct, position)
21471 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21472 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21473 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21475 this.trigger.createChild({
21480 cls: 'roo-password-meter-grey col-xs-12',
21483 //width: this.meterWidth + 'px'
21487 cls: 'roo-password-meter-text'
21493 if (this.hideTrigger) {
21494 this.trigger.setDisplayed(false);
21496 this.setSize(this.width || '', this.height || '');
21499 onDestroy: function ()
21501 if (this.trigger) {
21502 this.trigger.removeAllListeners();
21503 this.trigger.remove();
21506 this.wrap.remove();
21508 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21511 checkStrength: function ()
21513 var pwd = this.inputEl().getValue();
21514 if (pwd == this._lastPwd) {
21519 if (this.ClientSideStrongPassword(pwd)) {
21521 } else if (this.ClientSideMediumPassword(pwd)) {
21523 } else if (this.ClientSideWeakPassword(pwd)) {
21529 Roo.log('strength1: ' + strength);
21531 //var pm = this.trigger.child('div/div/div').dom;
21532 var pm = this.trigger.child('div/div');
21533 pm.removeClass(this.meterClass);
21534 pm.addClass(this.meterClass[strength]);
21537 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21539 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21541 this._lastPwd = pwd;
21545 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21547 this._lastPwd = '';
21549 var pm = this.trigger.child('div/div');
21550 pm.removeClass(this.meterClass);
21551 pm.addClass('roo-password-meter-grey');
21554 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21557 this.inputEl().dom.type='password';
21560 validateValue: function (value)
21563 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21566 if (value.length == 0) {
21567 if (this.allowBlank) {
21568 this.clearInvalid();
21572 this.markInvalid(this.errors.PwdEmpty);
21573 this.errorMsg = this.errors.PwdEmpty;
21581 if ('[\x21-\x7e]*'.match(value)) {
21582 this.markInvalid(this.errors.PwdBadChar);
21583 this.errorMsg = this.errors.PwdBadChar;
21586 if (value.length < 6) {
21587 this.markInvalid(this.errors.PwdShort);
21588 this.errorMsg = this.errors.PwdShort;
21591 if (value.length > 16) {
21592 this.markInvalid(this.errors.PwdLong);
21593 this.errorMsg = this.errors.PwdLong;
21597 if (this.ClientSideStrongPassword(value)) {
21599 } else if (this.ClientSideMediumPassword(value)) {
21601 } else if (this.ClientSideWeakPassword(value)) {
21608 if (strength < 2) {
21609 //this.markInvalid(this.errors.TooWeak);
21610 this.errorMsg = this.errors.TooWeak;
21615 console.log('strength2: ' + strength);
21617 //var pm = this.trigger.child('div/div/div').dom;
21619 var pm = this.trigger.child('div/div');
21620 pm.removeClass(this.meterClass);
21621 pm.addClass(this.meterClass[strength]);
21623 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21625 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21627 this.errorMsg = '';
21631 CharacterSetChecks: function (type)
21634 this.fResult = false;
21637 isctype: function (character, type)
21640 case this.kCapitalLetter:
21641 if (character >= 'A' && character <= 'Z') {
21646 case this.kSmallLetter:
21647 if (character >= 'a' && character <= 'z') {
21653 if (character >= '0' && character <= '9') {
21658 case this.kPunctuation:
21659 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21670 IsLongEnough: function (pwd, size)
21672 return !(pwd == null || isNaN(size) || pwd.length < size);
21675 SpansEnoughCharacterSets: function (word, nb)
21677 if (!this.IsLongEnough(word, nb))
21682 var characterSetChecks = new Array(
21683 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21684 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21687 for (var index = 0; index < word.length; ++index) {
21688 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21689 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21690 characterSetChecks[nCharSet].fResult = true;
21697 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21698 if (characterSetChecks[nCharSet].fResult) {
21703 if (nCharSets < nb) {
21709 ClientSideStrongPassword: function (pwd)
21711 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21714 ClientSideMediumPassword: function (pwd)
21716 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21719 ClientSideWeakPassword: function (pwd)
21721 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21724 })//<script type="text/javascript">
21727 * Based Ext JS Library 1.1.1
21728 * Copyright(c) 2006-2007, Ext JS, LLC.
21734 * @class Roo.HtmlEditorCore
21735 * @extends Roo.Component
21736 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21738 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21741 Roo.HtmlEditorCore = function(config){
21744 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21749 * @event initialize
21750 * Fires when the editor is fully initialized (including the iframe)
21751 * @param {Roo.HtmlEditorCore} this
21756 * Fires when the editor is first receives the focus. Any insertion must wait
21757 * until after this event.
21758 * @param {Roo.HtmlEditorCore} this
21762 * @event beforesync
21763 * Fires before the textarea is updated with content from the editor iframe. Return false
21764 * to cancel the sync.
21765 * @param {Roo.HtmlEditorCore} this
21766 * @param {String} html
21770 * @event beforepush
21771 * Fires before the iframe editor is updated with content from the textarea. Return false
21772 * to cancel the push.
21773 * @param {Roo.HtmlEditorCore} this
21774 * @param {String} html
21779 * Fires when the textarea is updated with content from the editor iframe.
21780 * @param {Roo.HtmlEditorCore} this
21781 * @param {String} html
21786 * Fires when the iframe editor is updated with content from the textarea.
21787 * @param {Roo.HtmlEditorCore} this
21788 * @param {String} html
21793 * @event editorevent
21794 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21795 * @param {Roo.HtmlEditorCore} this
21801 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21803 // defaults : white / black...
21804 this.applyBlacklists();
21811 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21815 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21821 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21826 * @cfg {Number} height (in pixels)
21830 * @cfg {Number} width (in pixels)
21835 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21838 stylesheets: false,
21843 // private properties
21844 validationEvent : false,
21846 initialized : false,
21848 sourceEditMode : false,
21849 onFocus : Roo.emptyFn,
21851 hideMode:'offsets',
21855 // blacklist + whitelisted elements..
21862 * Protected method that will not generally be called directly. It
21863 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21864 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21866 getDocMarkup : function(){
21870 // inherit styels from page...??
21871 if (this.stylesheets === false) {
21873 Roo.get(document.head).select('style').each(function(node) {
21874 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21877 Roo.get(document.head).select('link').each(function(node) {
21878 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21881 } else if (!this.stylesheets.length) {
21883 st = '<style type="text/css">' +
21884 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21887 st = '<style type="text/css">' +
21892 st += '<style type="text/css">' +
21893 'IMG { cursor: pointer } ' +
21896 var cls = 'roo-htmleditor-body';
21898 if(this.bodyCls.length){
21899 cls += ' ' + this.bodyCls;
21902 return '<html><head>' + st +
21903 //<style type="text/css">' +
21904 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21906 ' </head><body class="' + cls + '"></body></html>';
21910 onRender : function(ct, position)
21913 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21914 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21917 this.el.dom.style.border = '0 none';
21918 this.el.dom.setAttribute('tabIndex', -1);
21919 this.el.addClass('x-hidden hide');
21923 if(Roo.isIE){ // fix IE 1px bogus margin
21924 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21928 this.frameId = Roo.id();
21932 var iframe = this.owner.wrap.createChild({
21934 cls: 'form-control', // bootstrap..
21936 name: this.frameId,
21937 frameBorder : 'no',
21938 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21943 this.iframe = iframe.dom;
21945 this.assignDocWin();
21947 this.doc.designMode = 'on';
21950 this.doc.write(this.getDocMarkup());
21954 var task = { // must defer to wait for browser to be ready
21956 //console.log("run task?" + this.doc.readyState);
21957 this.assignDocWin();
21958 if(this.doc.body || this.doc.readyState == 'complete'){
21960 this.doc.designMode="on";
21964 Roo.TaskMgr.stop(task);
21965 this.initEditor.defer(10, this);
21972 Roo.TaskMgr.start(task);
21977 onResize : function(w, h)
21979 Roo.log('resize: ' +w + ',' + h );
21980 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21984 if(typeof w == 'number'){
21986 this.iframe.style.width = w + 'px';
21988 if(typeof h == 'number'){
21990 this.iframe.style.height = h + 'px';
21992 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21999 * Toggles the editor between standard and source edit mode.
22000 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22002 toggleSourceEdit : function(sourceEditMode){
22004 this.sourceEditMode = sourceEditMode === true;
22006 if(this.sourceEditMode){
22008 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22011 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22012 //this.iframe.className = '';
22015 //this.setSize(this.owner.wrap.getSize());
22016 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22023 * Protected method that will not generally be called directly. If you need/want
22024 * custom HTML cleanup, this is the method you should override.
22025 * @param {String} html The HTML to be cleaned
22026 * return {String} The cleaned HTML
22028 cleanHtml : function(html){
22029 html = String(html);
22030 if(html.length > 5){
22031 if(Roo.isSafari){ // strip safari nonsense
22032 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22035 if(html == ' '){
22042 * HTML Editor -> Textarea
22043 * Protected method that will not generally be called directly. Syncs the contents
22044 * of the editor iframe with the textarea.
22046 syncValue : function(){
22047 if(this.initialized){
22048 var bd = (this.doc.body || this.doc.documentElement);
22049 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22050 var html = bd.innerHTML;
22052 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22053 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22055 html = '<div style="'+m[0]+'">' + html + '</div>';
22058 html = this.cleanHtml(html);
22059 // fix up the special chars.. normaly like back quotes in word...
22060 // however we do not want to do this with chinese..
22061 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22062 var cc = b.charCodeAt();
22064 (cc >= 0x4E00 && cc < 0xA000 ) ||
22065 (cc >= 0x3400 && cc < 0x4E00 ) ||
22066 (cc >= 0xf900 && cc < 0xfb00 )
22072 if(this.owner.fireEvent('beforesync', this, html) !== false){
22073 this.el.dom.value = html;
22074 this.owner.fireEvent('sync', this, html);
22080 * Protected method that will not generally be called directly. Pushes the value of the textarea
22081 * into the iframe editor.
22083 pushValue : function(){
22084 if(this.initialized){
22085 var v = this.el.dom.value.trim();
22087 // if(v.length < 1){
22091 if(this.owner.fireEvent('beforepush', this, v) !== false){
22092 var d = (this.doc.body || this.doc.documentElement);
22094 this.cleanUpPaste();
22095 this.el.dom.value = d.innerHTML;
22096 this.owner.fireEvent('push', this, v);
22102 deferFocus : function(){
22103 this.focus.defer(10, this);
22107 focus : function(){
22108 if(this.win && !this.sourceEditMode){
22115 assignDocWin: function()
22117 var iframe = this.iframe;
22120 this.doc = iframe.contentWindow.document;
22121 this.win = iframe.contentWindow;
22123 // if (!Roo.get(this.frameId)) {
22126 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22127 // this.win = Roo.get(this.frameId).dom.contentWindow;
22129 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22133 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22134 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22139 initEditor : function(){
22140 //console.log("INIT EDITOR");
22141 this.assignDocWin();
22145 this.doc.designMode="on";
22147 this.doc.write(this.getDocMarkup());
22150 var dbody = (this.doc.body || this.doc.documentElement);
22151 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22152 // this copies styles from the containing element into thsi one..
22153 // not sure why we need all of this..
22154 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22156 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22157 //ss['background-attachment'] = 'fixed'; // w3c
22158 dbody.bgProperties = 'fixed'; // ie
22159 //Roo.DomHelper.applyStyles(dbody, ss);
22160 Roo.EventManager.on(this.doc, {
22161 //'mousedown': this.onEditorEvent,
22162 'mouseup': this.onEditorEvent,
22163 'dblclick': this.onEditorEvent,
22164 'click': this.onEditorEvent,
22165 'keyup': this.onEditorEvent,
22170 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22172 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22173 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22175 this.initialized = true;
22177 this.owner.fireEvent('initialize', this);
22182 onDestroy : function(){
22188 //for (var i =0; i < this.toolbars.length;i++) {
22189 // // fixme - ask toolbars for heights?
22190 // this.toolbars[i].onDestroy();
22193 //this.wrap.dom.innerHTML = '';
22194 //this.wrap.remove();
22199 onFirstFocus : function(){
22201 this.assignDocWin();
22204 this.activated = true;
22207 if(Roo.isGecko){ // prevent silly gecko errors
22209 var s = this.win.getSelection();
22210 if(!s.focusNode || s.focusNode.nodeType != 3){
22211 var r = s.getRangeAt(0);
22212 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22217 this.execCmd('useCSS', true);
22218 this.execCmd('styleWithCSS', false);
22221 this.owner.fireEvent('activate', this);
22225 adjustFont: function(btn){
22226 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22227 //if(Roo.isSafari){ // safari
22230 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22231 if(Roo.isSafari){ // safari
22232 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22233 v = (v < 10) ? 10 : v;
22234 v = (v > 48) ? 48 : v;
22235 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22240 v = Math.max(1, v+adjust);
22242 this.execCmd('FontSize', v );
22245 onEditorEvent : function(e)
22247 this.owner.fireEvent('editorevent', this, e);
22248 // this.updateToolbar();
22249 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22252 insertTag : function(tg)
22254 // could be a bit smarter... -> wrap the current selected tRoo..
22255 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22257 range = this.createRange(this.getSelection());
22258 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22259 wrappingNode.appendChild(range.extractContents());
22260 range.insertNode(wrappingNode);
22267 this.execCmd("formatblock", tg);
22271 insertText : function(txt)
22275 var range = this.createRange();
22276 range.deleteContents();
22277 //alert(Sender.getAttribute('label'));
22279 range.insertNode(this.doc.createTextNode(txt));
22285 * Executes a Midas editor command on the editor document and performs necessary focus and
22286 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22287 * @param {String} cmd The Midas command
22288 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22290 relayCmd : function(cmd, value){
22292 this.execCmd(cmd, value);
22293 this.owner.fireEvent('editorevent', this);
22294 //this.updateToolbar();
22295 this.owner.deferFocus();
22299 * Executes a Midas editor command directly on the editor document.
22300 * For visual commands, you should use {@link #relayCmd} instead.
22301 * <b>This should only be called after the editor is initialized.</b>
22302 * @param {String} cmd The Midas command
22303 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22305 execCmd : function(cmd, value){
22306 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22313 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22315 * @param {String} text | dom node..
22317 insertAtCursor : function(text)
22320 if(!this.activated){
22326 var r = this.doc.selection.createRange();
22337 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22341 // from jquery ui (MIT licenced)
22343 var win = this.win;
22345 if (win.getSelection && win.getSelection().getRangeAt) {
22346 range = win.getSelection().getRangeAt(0);
22347 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22348 range.insertNode(node);
22349 } else if (win.document.selection && win.document.selection.createRange) {
22350 // no firefox support
22351 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22352 win.document.selection.createRange().pasteHTML(txt);
22354 // no firefox support
22355 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22356 this.execCmd('InsertHTML', txt);
22365 mozKeyPress : function(e){
22367 var c = e.getCharCode(), cmd;
22370 c = String.fromCharCode(c).toLowerCase();
22384 this.cleanUpPaste.defer(100, this);
22392 e.preventDefault();
22400 fixKeys : function(){ // load time branching for fastest keydown performance
22402 return function(e){
22403 var k = e.getKey(), r;
22406 r = this.doc.selection.createRange();
22409 r.pasteHTML('    ');
22416 r = this.doc.selection.createRange();
22418 var target = r.parentElement();
22419 if(!target || target.tagName.toLowerCase() != 'li'){
22421 r.pasteHTML('<br />');
22427 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22428 this.cleanUpPaste.defer(100, this);
22434 }else if(Roo.isOpera){
22435 return function(e){
22436 var k = e.getKey();
22440 this.execCmd('InsertHTML','    ');
22443 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22444 this.cleanUpPaste.defer(100, this);
22449 }else if(Roo.isSafari){
22450 return function(e){
22451 var k = e.getKey();
22455 this.execCmd('InsertText','\t');
22459 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22460 this.cleanUpPaste.defer(100, this);
22468 getAllAncestors: function()
22470 var p = this.getSelectedNode();
22473 a.push(p); // push blank onto stack..
22474 p = this.getParentElement();
22478 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22482 a.push(this.doc.body);
22486 lastSelNode : false,
22489 getSelection : function()
22491 this.assignDocWin();
22492 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22495 getSelectedNode: function()
22497 // this may only work on Gecko!!!
22499 // should we cache this!!!!
22504 var range = this.createRange(this.getSelection()).cloneRange();
22507 var parent = range.parentElement();
22509 var testRange = range.duplicate();
22510 testRange.moveToElementText(parent);
22511 if (testRange.inRange(range)) {
22514 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22517 parent = parent.parentElement;
22522 // is ancestor a text element.
22523 var ac = range.commonAncestorContainer;
22524 if (ac.nodeType == 3) {
22525 ac = ac.parentNode;
22528 var ar = ac.childNodes;
22531 var other_nodes = [];
22532 var has_other_nodes = false;
22533 for (var i=0;i<ar.length;i++) {
22534 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22537 // fullly contained node.
22539 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22544 // probably selected..
22545 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22546 other_nodes.push(ar[i]);
22550 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22555 has_other_nodes = true;
22557 if (!nodes.length && other_nodes.length) {
22558 nodes= other_nodes;
22560 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22566 createRange: function(sel)
22568 // this has strange effects when using with
22569 // top toolbar - not sure if it's a great idea.
22570 //this.editor.contentWindow.focus();
22571 if (typeof sel != "undefined") {
22573 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22575 return this.doc.createRange();
22578 return this.doc.createRange();
22581 getParentElement: function()
22584 this.assignDocWin();
22585 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22587 var range = this.createRange(sel);
22590 var p = range.commonAncestorContainer;
22591 while (p.nodeType == 3) { // text node
22602 * Range intersection.. the hard stuff...
22606 * [ -- selected range --- ]
22610 * if end is before start or hits it. fail.
22611 * if start is after end or hits it fail.
22613 * if either hits (but other is outside. - then it's not
22619 // @see http://www.thismuchiknow.co.uk/?p=64.
22620 rangeIntersectsNode : function(range, node)
22622 var nodeRange = node.ownerDocument.createRange();
22624 nodeRange.selectNode(node);
22626 nodeRange.selectNodeContents(node);
22629 var rangeStartRange = range.cloneRange();
22630 rangeStartRange.collapse(true);
22632 var rangeEndRange = range.cloneRange();
22633 rangeEndRange.collapse(false);
22635 var nodeStartRange = nodeRange.cloneRange();
22636 nodeStartRange.collapse(true);
22638 var nodeEndRange = nodeRange.cloneRange();
22639 nodeEndRange.collapse(false);
22641 return rangeStartRange.compareBoundaryPoints(
22642 Range.START_TO_START, nodeEndRange) == -1 &&
22643 rangeEndRange.compareBoundaryPoints(
22644 Range.START_TO_START, nodeStartRange) == 1;
22648 rangeCompareNode : function(range, node)
22650 var nodeRange = node.ownerDocument.createRange();
22652 nodeRange.selectNode(node);
22654 nodeRange.selectNodeContents(node);
22658 range.collapse(true);
22660 nodeRange.collapse(true);
22662 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22663 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22665 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22667 var nodeIsBefore = ss == 1;
22668 var nodeIsAfter = ee == -1;
22670 if (nodeIsBefore && nodeIsAfter) {
22673 if (!nodeIsBefore && nodeIsAfter) {
22674 return 1; //right trailed.
22677 if (nodeIsBefore && !nodeIsAfter) {
22678 return 2; // left trailed.
22684 // private? - in a new class?
22685 cleanUpPaste : function()
22687 // cleans up the whole document..
22688 Roo.log('cleanuppaste');
22690 this.cleanUpChildren(this.doc.body);
22691 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22692 if (clean != this.doc.body.innerHTML) {
22693 this.doc.body.innerHTML = clean;
22698 cleanWordChars : function(input) {// change the chars to hex code
22699 var he = Roo.HtmlEditorCore;
22701 var output = input;
22702 Roo.each(he.swapCodes, function(sw) {
22703 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22705 output = output.replace(swapper, sw[1]);
22712 cleanUpChildren : function (n)
22714 if (!n.childNodes.length) {
22717 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22718 this.cleanUpChild(n.childNodes[i]);
22725 cleanUpChild : function (node)
22728 //console.log(node);
22729 if (node.nodeName == "#text") {
22730 // clean up silly Windows -- stuff?
22733 if (node.nodeName == "#comment") {
22734 node.parentNode.removeChild(node);
22735 // clean up silly Windows -- stuff?
22738 var lcname = node.tagName.toLowerCase();
22739 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22740 // whitelist of tags..
22742 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22744 node.parentNode.removeChild(node);
22749 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22751 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22752 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22754 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22755 // remove_keep_children = true;
22758 if (remove_keep_children) {
22759 this.cleanUpChildren(node);
22760 // inserts everything just before this node...
22761 while (node.childNodes.length) {
22762 var cn = node.childNodes[0];
22763 node.removeChild(cn);
22764 node.parentNode.insertBefore(cn, node);
22766 node.parentNode.removeChild(node);
22770 if (!node.attributes || !node.attributes.length) {
22771 this.cleanUpChildren(node);
22775 function cleanAttr(n,v)
22778 if (v.match(/^\./) || v.match(/^\//)) {
22781 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22784 if (v.match(/^#/)) {
22787 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22788 node.removeAttribute(n);
22792 var cwhite = this.cwhite;
22793 var cblack = this.cblack;
22795 function cleanStyle(n,v)
22797 if (v.match(/expression/)) { //XSS?? should we even bother..
22798 node.removeAttribute(n);
22802 var parts = v.split(/;/);
22805 Roo.each(parts, function(p) {
22806 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22810 var l = p.split(':').shift().replace(/\s+/g,'');
22811 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22813 if ( cwhite.length && cblack.indexOf(l) > -1) {
22814 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22815 //node.removeAttribute(n);
22819 // only allow 'c whitelisted system attributes'
22820 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22821 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22822 //node.removeAttribute(n);
22832 if (clean.length) {
22833 node.setAttribute(n, clean.join(';'));
22835 node.removeAttribute(n);
22841 for (var i = node.attributes.length-1; i > -1 ; i--) {
22842 var a = node.attributes[i];
22845 if (a.name.toLowerCase().substr(0,2)=='on') {
22846 node.removeAttribute(a.name);
22849 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22850 node.removeAttribute(a.name);
22853 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22854 cleanAttr(a.name,a.value); // fixme..
22857 if (a.name == 'style') {
22858 cleanStyle(a.name,a.value);
22861 /// clean up MS crap..
22862 // tecnically this should be a list of valid class'es..
22865 if (a.name == 'class') {
22866 if (a.value.match(/^Mso/)) {
22867 node.className = '';
22870 if (a.value.match(/^body$/)) {
22871 node.className = '';
22882 this.cleanUpChildren(node);
22888 * Clean up MS wordisms...
22890 cleanWord : function(node)
22895 this.cleanWord(this.doc.body);
22898 if (node.nodeName == "#text") {
22899 // clean up silly Windows -- stuff?
22902 if (node.nodeName == "#comment") {
22903 node.parentNode.removeChild(node);
22904 // clean up silly Windows -- stuff?
22908 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22909 node.parentNode.removeChild(node);
22913 // remove - but keep children..
22914 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22915 while (node.childNodes.length) {
22916 var cn = node.childNodes[0];
22917 node.removeChild(cn);
22918 node.parentNode.insertBefore(cn, node);
22920 node.parentNode.removeChild(node);
22921 this.iterateChildren(node, this.cleanWord);
22925 if (node.className.length) {
22927 var cn = node.className.split(/\W+/);
22929 Roo.each(cn, function(cls) {
22930 if (cls.match(/Mso[a-zA-Z]+/)) {
22935 node.className = cna.length ? cna.join(' ') : '';
22937 node.removeAttribute("class");
22941 if (node.hasAttribute("lang")) {
22942 node.removeAttribute("lang");
22945 if (node.hasAttribute("style")) {
22947 var styles = node.getAttribute("style").split(";");
22949 Roo.each(styles, function(s) {
22950 if (!s.match(/:/)) {
22953 var kv = s.split(":");
22954 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22957 // what ever is left... we allow.
22960 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22961 if (!nstyle.length) {
22962 node.removeAttribute('style');
22965 this.iterateChildren(node, this.cleanWord);
22971 * iterateChildren of a Node, calling fn each time, using this as the scole..
22972 * @param {DomNode} node node to iterate children of.
22973 * @param {Function} fn method of this class to call on each item.
22975 iterateChildren : function(node, fn)
22977 if (!node.childNodes.length) {
22980 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22981 fn.call(this, node.childNodes[i])
22987 * cleanTableWidths.
22989 * Quite often pasting from word etc.. results in tables with column and widths.
22990 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22993 cleanTableWidths : function(node)
22998 this.cleanTableWidths(this.doc.body);
23003 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23006 Roo.log(node.tagName);
23007 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23008 this.iterateChildren(node, this.cleanTableWidths);
23011 if (node.hasAttribute('width')) {
23012 node.removeAttribute('width');
23016 if (node.hasAttribute("style")) {
23019 var styles = node.getAttribute("style").split(";");
23021 Roo.each(styles, function(s) {
23022 if (!s.match(/:/)) {
23025 var kv = s.split(":");
23026 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23029 // what ever is left... we allow.
23032 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23033 if (!nstyle.length) {
23034 node.removeAttribute('style');
23038 this.iterateChildren(node, this.cleanTableWidths);
23046 domToHTML : function(currentElement, depth, nopadtext) {
23048 depth = depth || 0;
23049 nopadtext = nopadtext || false;
23051 if (!currentElement) {
23052 return this.domToHTML(this.doc.body);
23055 //Roo.log(currentElement);
23057 var allText = false;
23058 var nodeName = currentElement.nodeName;
23059 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23061 if (nodeName == '#text') {
23063 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23068 if (nodeName != 'BODY') {
23071 // Prints the node tagName, such as <A>, <IMG>, etc
23074 for(i = 0; i < currentElement.attributes.length;i++) {
23076 var aname = currentElement.attributes.item(i).name;
23077 if (!currentElement.attributes.item(i).value.length) {
23080 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23083 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23092 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23095 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23100 // Traverse the tree
23102 var currentElementChild = currentElement.childNodes.item(i);
23103 var allText = true;
23104 var innerHTML = '';
23106 while (currentElementChild) {
23107 // Formatting code (indent the tree so it looks nice on the screen)
23108 var nopad = nopadtext;
23109 if (lastnode == 'SPAN') {
23113 if (currentElementChild.nodeName == '#text') {
23114 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23115 toadd = nopadtext ? toadd : toadd.trim();
23116 if (!nopad && toadd.length > 80) {
23117 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23119 innerHTML += toadd;
23122 currentElementChild = currentElement.childNodes.item(i);
23128 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23130 // Recursively traverse the tree structure of the child node
23131 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23132 lastnode = currentElementChild.nodeName;
23134 currentElementChild=currentElement.childNodes.item(i);
23140 // The remaining code is mostly for formatting the tree
23141 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23146 ret+= "</"+tagName+">";
23152 applyBlacklists : function()
23154 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23155 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23159 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23160 if (b.indexOf(tag) > -1) {
23163 this.white.push(tag);
23167 Roo.each(w, function(tag) {
23168 if (b.indexOf(tag) > -1) {
23171 if (this.white.indexOf(tag) > -1) {
23174 this.white.push(tag);
23179 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23180 if (w.indexOf(tag) > -1) {
23183 this.black.push(tag);
23187 Roo.each(b, function(tag) {
23188 if (w.indexOf(tag) > -1) {
23191 if (this.black.indexOf(tag) > -1) {
23194 this.black.push(tag);
23199 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23200 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23204 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23205 if (b.indexOf(tag) > -1) {
23208 this.cwhite.push(tag);
23212 Roo.each(w, function(tag) {
23213 if (b.indexOf(tag) > -1) {
23216 if (this.cwhite.indexOf(tag) > -1) {
23219 this.cwhite.push(tag);
23224 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23225 if (w.indexOf(tag) > -1) {
23228 this.cblack.push(tag);
23232 Roo.each(b, function(tag) {
23233 if (w.indexOf(tag) > -1) {
23236 if (this.cblack.indexOf(tag) > -1) {
23239 this.cblack.push(tag);
23244 setStylesheets : function(stylesheets)
23246 if(typeof(stylesheets) == 'string'){
23247 Roo.get(this.iframe.contentDocument.head).createChild({
23249 rel : 'stylesheet',
23258 Roo.each(stylesheets, function(s) {
23263 Roo.get(_this.iframe.contentDocument.head).createChild({
23265 rel : 'stylesheet',
23274 removeStylesheets : function()
23278 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23283 setStyle : function(style)
23285 Roo.get(this.iframe.contentDocument.head).createChild({
23294 // hide stuff that is not compatible
23308 * @event specialkey
23312 * @cfg {String} fieldClass @hide
23315 * @cfg {String} focusClass @hide
23318 * @cfg {String} autoCreate @hide
23321 * @cfg {String} inputType @hide
23324 * @cfg {String} invalidClass @hide
23327 * @cfg {String} invalidText @hide
23330 * @cfg {String} msgFx @hide
23333 * @cfg {String} validateOnBlur @hide
23337 Roo.HtmlEditorCore.white = [
23338 'area', 'br', 'img', 'input', 'hr', 'wbr',
23340 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23341 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23342 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23343 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23344 'table', 'ul', 'xmp',
23346 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23349 'dir', 'menu', 'ol', 'ul', 'dl',
23355 Roo.HtmlEditorCore.black = [
23356 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23358 'base', 'basefont', 'bgsound', 'blink', 'body',
23359 'frame', 'frameset', 'head', 'html', 'ilayer',
23360 'iframe', 'layer', 'link', 'meta', 'object',
23361 'script', 'style' ,'title', 'xml' // clean later..
23363 Roo.HtmlEditorCore.clean = [
23364 'script', 'style', 'title', 'xml'
23366 Roo.HtmlEditorCore.remove = [
23371 Roo.HtmlEditorCore.ablack = [
23375 Roo.HtmlEditorCore.aclean = [
23376 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23380 Roo.HtmlEditorCore.pwhite= [
23381 'http', 'https', 'mailto'
23384 // white listed style attributes.
23385 Roo.HtmlEditorCore.cwhite= [
23386 // 'text-align', /// default is to allow most things..
23392 // black listed style attributes.
23393 Roo.HtmlEditorCore.cblack= [
23394 // 'font-size' -- this can be set by the project
23398 Roo.HtmlEditorCore.swapCodes =[
23417 * @class Roo.bootstrap.HtmlEditor
23418 * @extends Roo.bootstrap.TextArea
23419 * Bootstrap HtmlEditor class
23422 * Create a new HtmlEditor
23423 * @param {Object} config The config object
23426 Roo.bootstrap.HtmlEditor = function(config){
23427 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23428 if (!this.toolbars) {
23429 this.toolbars = [];
23432 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23435 * @event initialize
23436 * Fires when the editor is fully initialized (including the iframe)
23437 * @param {HtmlEditor} this
23442 * Fires when the editor is first receives the focus. Any insertion must wait
23443 * until after this event.
23444 * @param {HtmlEditor} this
23448 * @event beforesync
23449 * Fires before the textarea is updated with content from the editor iframe. Return false
23450 * to cancel the sync.
23451 * @param {HtmlEditor} this
23452 * @param {String} html
23456 * @event beforepush
23457 * Fires before the iframe editor is updated with content from the textarea. Return false
23458 * to cancel the push.
23459 * @param {HtmlEditor} this
23460 * @param {String} html
23465 * Fires when the textarea is updated with content from the editor iframe.
23466 * @param {HtmlEditor} this
23467 * @param {String} html
23472 * Fires when the iframe editor is updated with content from the textarea.
23473 * @param {HtmlEditor} this
23474 * @param {String} html
23478 * @event editmodechange
23479 * Fires when the editor switches edit modes
23480 * @param {HtmlEditor} this
23481 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23483 editmodechange: true,
23485 * @event editorevent
23486 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23487 * @param {HtmlEditor} this
23491 * @event firstfocus
23492 * Fires when on first focus - needed by toolbars..
23493 * @param {HtmlEditor} this
23498 * Auto save the htmlEditor value as a file into Events
23499 * @param {HtmlEditor} this
23503 * @event savedpreview
23504 * preview the saved version of htmlEditor
23505 * @param {HtmlEditor} this
23512 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23516 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23521 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23526 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23531 * @cfg {Number} height (in pixels)
23535 * @cfg {Number} width (in pixels)
23540 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23543 stylesheets: false,
23548 // private properties
23549 validationEvent : false,
23551 initialized : false,
23554 onFocus : Roo.emptyFn,
23556 hideMode:'offsets',
23558 tbContainer : false,
23562 toolbarContainer :function() {
23563 return this.wrap.select('.x-html-editor-tb',true).first();
23567 * Protected method that will not generally be called directly. It
23568 * is called when the editor creates its toolbar. Override this method if you need to
23569 * add custom toolbar buttons.
23570 * @param {HtmlEditor} editor
23572 createToolbar : function(){
23573 Roo.log('renewing');
23574 Roo.log("create toolbars");
23576 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23577 this.toolbars[0].render(this.toolbarContainer());
23581 // if (!editor.toolbars || !editor.toolbars.length) {
23582 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23585 // for (var i =0 ; i < editor.toolbars.length;i++) {
23586 // editor.toolbars[i] = Roo.factory(
23587 // typeof(editor.toolbars[i]) == 'string' ?
23588 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23589 // Roo.bootstrap.HtmlEditor);
23590 // editor.toolbars[i].init(editor);
23596 onRender : function(ct, position)
23598 // Roo.log("Call onRender: " + this.xtype);
23600 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23602 this.wrap = this.inputEl().wrap({
23603 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23606 this.editorcore.onRender(ct, position);
23608 if (this.resizable) {
23609 this.resizeEl = new Roo.Resizable(this.wrap, {
23613 minHeight : this.height,
23614 height: this.height,
23615 handles : this.resizable,
23618 resize : function(r, w, h) {
23619 _t.onResize(w,h); // -something
23625 this.createToolbar(this);
23628 if(!this.width && this.resizable){
23629 this.setSize(this.wrap.getSize());
23631 if (this.resizeEl) {
23632 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23633 // should trigger onReize..
23639 onResize : function(w, h)
23641 Roo.log('resize: ' +w + ',' + h );
23642 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23646 if(this.inputEl() ){
23647 if(typeof w == 'number'){
23648 var aw = w - this.wrap.getFrameWidth('lr');
23649 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23652 if(typeof h == 'number'){
23653 var tbh = -11; // fixme it needs to tool bar size!
23654 for (var i =0; i < this.toolbars.length;i++) {
23655 // fixme - ask toolbars for heights?
23656 tbh += this.toolbars[i].el.getHeight();
23657 //if (this.toolbars[i].footer) {
23658 // tbh += this.toolbars[i].footer.el.getHeight();
23666 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23667 ah -= 5; // knock a few pixes off for look..
23668 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23672 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23673 this.editorcore.onResize(ew,eh);
23678 * Toggles the editor between standard and source edit mode.
23679 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23681 toggleSourceEdit : function(sourceEditMode)
23683 this.editorcore.toggleSourceEdit(sourceEditMode);
23685 if(this.editorcore.sourceEditMode){
23686 Roo.log('editor - showing textarea');
23689 // Roo.log(this.syncValue());
23691 this.inputEl().removeClass(['hide', 'x-hidden']);
23692 this.inputEl().dom.removeAttribute('tabIndex');
23693 this.inputEl().focus();
23695 Roo.log('editor - hiding textarea');
23697 // Roo.log(this.pushValue());
23700 this.inputEl().addClass(['hide', 'x-hidden']);
23701 this.inputEl().dom.setAttribute('tabIndex', -1);
23702 //this.deferFocus();
23705 if(this.resizable){
23706 this.setSize(this.wrap.getSize());
23709 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23712 // private (for BoxComponent)
23713 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23715 // private (for BoxComponent)
23716 getResizeEl : function(){
23720 // private (for BoxComponent)
23721 getPositionEl : function(){
23726 initEvents : function(){
23727 this.originalValue = this.getValue();
23731 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23734 // markInvalid : Roo.emptyFn,
23736 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23739 // clearInvalid : Roo.emptyFn,
23741 setValue : function(v){
23742 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23743 this.editorcore.pushValue();
23748 deferFocus : function(){
23749 this.focus.defer(10, this);
23753 focus : function(){
23754 this.editorcore.focus();
23760 onDestroy : function(){
23766 for (var i =0; i < this.toolbars.length;i++) {
23767 // fixme - ask toolbars for heights?
23768 this.toolbars[i].onDestroy();
23771 this.wrap.dom.innerHTML = '';
23772 this.wrap.remove();
23777 onFirstFocus : function(){
23778 //Roo.log("onFirstFocus");
23779 this.editorcore.onFirstFocus();
23780 for (var i =0; i < this.toolbars.length;i++) {
23781 this.toolbars[i].onFirstFocus();
23787 syncValue : function()
23789 this.editorcore.syncValue();
23792 pushValue : function()
23794 this.editorcore.pushValue();
23798 // hide stuff that is not compatible
23812 * @event specialkey
23816 * @cfg {String} fieldClass @hide
23819 * @cfg {String} focusClass @hide
23822 * @cfg {String} autoCreate @hide
23825 * @cfg {String} inputType @hide
23828 * @cfg {String} invalidClass @hide
23831 * @cfg {String} invalidText @hide
23834 * @cfg {String} msgFx @hide
23837 * @cfg {String} validateOnBlur @hide
23846 Roo.namespace('Roo.bootstrap.htmleditor');
23848 * @class Roo.bootstrap.HtmlEditorToolbar1
23853 new Roo.bootstrap.HtmlEditor({
23856 new Roo.bootstrap.HtmlEditorToolbar1({
23857 disable : { fonts: 1 , format: 1, ..., ... , ...],
23863 * @cfg {Object} disable List of elements to disable..
23864 * @cfg {Array} btns List of additional buttons.
23868 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23871 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23874 Roo.apply(this, config);
23876 // default disabled, based on 'good practice'..
23877 this.disable = this.disable || {};
23878 Roo.applyIf(this.disable, {
23881 specialElements : true
23883 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23885 this.editor = config.editor;
23886 this.editorcore = config.editor.editorcore;
23888 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23890 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23891 // dont call parent... till later.
23893 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23898 editorcore : false,
23903 "h1","h2","h3","h4","h5","h6",
23905 "abbr", "acronym", "address", "cite", "samp", "var",
23909 onRender : function(ct, position)
23911 // Roo.log("Call onRender: " + this.xtype);
23913 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23915 this.el.dom.style.marginBottom = '0';
23917 var editorcore = this.editorcore;
23918 var editor= this.editor;
23921 var btn = function(id,cmd , toggle, handler, html){
23923 var event = toggle ? 'toggle' : 'click';
23928 xns: Roo.bootstrap,
23931 enableToggle:toggle !== false,
23933 pressed : toggle ? false : null,
23936 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23937 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23943 // var cb_box = function...
23948 xns: Roo.bootstrap,
23949 glyphicon : 'font',
23953 xns: Roo.bootstrap,
23957 Roo.each(this.formats, function(f) {
23958 style.menu.items.push({
23960 xns: Roo.bootstrap,
23961 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23966 editorcore.insertTag(this.tagname);
23973 children.push(style);
23975 btn('bold',false,true);
23976 btn('italic',false,true);
23977 btn('align-left', 'justifyleft',true);
23978 btn('align-center', 'justifycenter',true);
23979 btn('align-right' , 'justifyright',true);
23980 btn('link', false, false, function(btn) {
23981 //Roo.log("create link?");
23982 var url = prompt(this.createLinkText, this.defaultLinkValue);
23983 if(url && url != 'http:/'+'/'){
23984 this.editorcore.relayCmd('createlink', url);
23987 btn('list','insertunorderedlist',true);
23988 btn('pencil', false,true, function(btn){
23990 this.toggleSourceEdit(btn.pressed);
23993 if (this.editor.btns.length > 0) {
23994 for (var i = 0; i<this.editor.btns.length; i++) {
23995 children.push(this.editor.btns[i]);
24003 xns: Roo.bootstrap,
24008 xns: Roo.bootstrap,
24013 cog.menu.items.push({
24015 xns: Roo.bootstrap,
24016 html : Clean styles,
24021 editorcore.insertTag(this.tagname);
24030 this.xtype = 'NavSimplebar';
24032 for(var i=0;i< children.length;i++) {
24034 this.buttons.add(this.addxtypeChild(children[i]));
24038 editor.on('editorevent', this.updateToolbar, this);
24040 onBtnClick : function(id)
24042 this.editorcore.relayCmd(id);
24043 this.editorcore.focus();
24047 * Protected method that will not generally be called directly. It triggers
24048 * a toolbar update by reading the markup state of the current selection in the editor.
24050 updateToolbar: function(){
24052 if(!this.editorcore.activated){
24053 this.editor.onFirstFocus(); // is this neeed?
24057 var btns = this.buttons;
24058 var doc = this.editorcore.doc;
24059 btns.get('bold').setActive(doc.queryCommandState('bold'));
24060 btns.get('italic').setActive(doc.queryCommandState('italic'));
24061 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24063 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24064 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24065 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24067 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24068 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24071 var ans = this.editorcore.getAllAncestors();
24072 if (this.formatCombo) {
24075 var store = this.formatCombo.store;
24076 this.formatCombo.setValue("");
24077 for (var i =0; i < ans.length;i++) {
24078 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24080 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24088 // hides menus... - so this cant be on a menu...
24089 Roo.bootstrap.MenuMgr.hideAll();
24091 Roo.bootstrap.MenuMgr.hideAll();
24092 //this.editorsyncValue();
24094 onFirstFocus: function() {
24095 this.buttons.each(function(item){
24099 toggleSourceEdit : function(sourceEditMode){
24102 if(sourceEditMode){
24103 Roo.log("disabling buttons");
24104 this.buttons.each( function(item){
24105 if(item.cmd != 'pencil'){
24111 Roo.log("enabling buttons");
24112 if(this.editorcore.initialized){
24113 this.buttons.each( function(item){
24119 Roo.log("calling toggole on editor");
24120 // tell the editor that it's been pressed..
24121 this.editor.toggleSourceEdit(sourceEditMode);
24131 * @class Roo.bootstrap.Table.AbstractSelectionModel
24132 * @extends Roo.util.Observable
24133 * Abstract base class for grid SelectionModels. It provides the interface that should be
24134 * implemented by descendant classes. This class should not be directly instantiated.
24137 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24138 this.locked = false;
24139 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24143 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24144 /** @ignore Called by the grid automatically. Do not call directly. */
24145 init : function(grid){
24151 * Locks the selections.
24154 this.locked = true;
24158 * Unlocks the selections.
24160 unlock : function(){
24161 this.locked = false;
24165 * Returns true if the selections are locked.
24166 * @return {Boolean}
24168 isLocked : function(){
24169 return this.locked;
24173 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24174 * @class Roo.bootstrap.Table.RowSelectionModel
24175 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24176 * It supports multiple selections and keyboard selection/navigation.
24178 * @param {Object} config
24181 Roo.bootstrap.Table.RowSelectionModel = function(config){
24182 Roo.apply(this, config);
24183 this.selections = new Roo.util.MixedCollection(false, function(o){
24188 this.lastActive = false;
24192 * @event selectionchange
24193 * Fires when the selection changes
24194 * @param {SelectionModel} this
24196 "selectionchange" : true,
24198 * @event afterselectionchange
24199 * Fires after the selection changes (eg. by key press or clicking)
24200 * @param {SelectionModel} this
24202 "afterselectionchange" : true,
24204 * @event beforerowselect
24205 * Fires when a row is selected being selected, return false to cancel.
24206 * @param {SelectionModel} this
24207 * @param {Number} rowIndex The selected index
24208 * @param {Boolean} keepExisting False if other selections will be cleared
24210 "beforerowselect" : true,
24213 * Fires when a row is selected.
24214 * @param {SelectionModel} this
24215 * @param {Number} rowIndex The selected index
24216 * @param {Roo.data.Record} r The record
24218 "rowselect" : true,
24220 * @event rowdeselect
24221 * Fires when a row is deselected.
24222 * @param {SelectionModel} this
24223 * @param {Number} rowIndex The selected index
24225 "rowdeselect" : true
24227 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24228 this.locked = false;
24231 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24233 * @cfg {Boolean} singleSelect
24234 * True to allow selection of only one row at a time (defaults to false)
24236 singleSelect : false,
24239 initEvents : function()
24242 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24243 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24244 //}else{ // allow click to work like normal
24245 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24247 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24248 this.grid.on("rowclick", this.handleMouseDown, this);
24250 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24251 "up" : function(e){
24253 this.selectPrevious(e.shiftKey);
24254 }else if(this.last !== false && this.lastActive !== false){
24255 var last = this.last;
24256 this.selectRange(this.last, this.lastActive-1);
24257 this.grid.getView().focusRow(this.lastActive);
24258 if(last !== false){
24262 this.selectFirstRow();
24264 this.fireEvent("afterselectionchange", this);
24266 "down" : function(e){
24268 this.selectNext(e.shiftKey);
24269 }else if(this.last !== false && this.lastActive !== false){
24270 var last = this.last;
24271 this.selectRange(this.last, this.lastActive+1);
24272 this.grid.getView().focusRow(this.lastActive);
24273 if(last !== false){
24277 this.selectFirstRow();
24279 this.fireEvent("afterselectionchange", this);
24283 this.grid.store.on('load', function(){
24284 this.selections.clear();
24287 var view = this.grid.view;
24288 view.on("refresh", this.onRefresh, this);
24289 view.on("rowupdated", this.onRowUpdated, this);
24290 view.on("rowremoved", this.onRemove, this);
24295 onRefresh : function()
24297 var ds = this.grid.store, i, v = this.grid.view;
24298 var s = this.selections;
24299 s.each(function(r){
24300 if((i = ds.indexOfId(r.id)) != -1){
24309 onRemove : function(v, index, r){
24310 this.selections.remove(r);
24314 onRowUpdated : function(v, index, r){
24315 if(this.isSelected(r)){
24316 v.onRowSelect(index);
24322 * @param {Array} records The records to select
24323 * @param {Boolean} keepExisting (optional) True to keep existing selections
24325 selectRecords : function(records, keepExisting)
24328 this.clearSelections();
24330 var ds = this.grid.store;
24331 for(var i = 0, len = records.length; i < len; i++){
24332 this.selectRow(ds.indexOf(records[i]), true);
24337 * Gets the number of selected rows.
24340 getCount : function(){
24341 return this.selections.length;
24345 * Selects the first row in the grid.
24347 selectFirstRow : function(){
24352 * Select the last row.
24353 * @param {Boolean} keepExisting (optional) True to keep existing selections
24355 selectLastRow : function(keepExisting){
24356 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24357 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24361 * Selects the row immediately following the last selected row.
24362 * @param {Boolean} keepExisting (optional) True to keep existing selections
24364 selectNext : function(keepExisting)
24366 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24367 this.selectRow(this.last+1, keepExisting);
24368 this.grid.getView().focusRow(this.last);
24373 * Selects the row that precedes the last selected row.
24374 * @param {Boolean} keepExisting (optional) True to keep existing selections
24376 selectPrevious : function(keepExisting){
24378 this.selectRow(this.last-1, keepExisting);
24379 this.grid.getView().focusRow(this.last);
24384 * Returns the selected records
24385 * @return {Array} Array of selected records
24387 getSelections : function(){
24388 return [].concat(this.selections.items);
24392 * Returns the first selected record.
24395 getSelected : function(){
24396 return this.selections.itemAt(0);
24401 * Clears all selections.
24403 clearSelections : function(fast)
24409 var ds = this.grid.store;
24410 var s = this.selections;
24411 s.each(function(r){
24412 this.deselectRow(ds.indexOfId(r.id));
24416 this.selections.clear();
24423 * Selects all rows.
24425 selectAll : function(){
24429 this.selections.clear();
24430 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24431 this.selectRow(i, true);
24436 * Returns True if there is a selection.
24437 * @return {Boolean}
24439 hasSelection : function(){
24440 return this.selections.length > 0;
24444 * Returns True if the specified row is selected.
24445 * @param {Number/Record} record The record or index of the record to check
24446 * @return {Boolean}
24448 isSelected : function(index){
24449 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24450 return (r && this.selections.key(r.id) ? true : false);
24454 * Returns True if the specified record id is selected.
24455 * @param {String} id The id of record to check
24456 * @return {Boolean}
24458 isIdSelected : function(id){
24459 return (this.selections.key(id) ? true : false);
24464 handleMouseDBClick : function(e, t){
24468 handleMouseDown : function(e, t)
24470 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24471 if(this.isLocked() || rowIndex < 0 ){
24474 if(e.shiftKey && this.last !== false){
24475 var last = this.last;
24476 this.selectRange(last, rowIndex, e.ctrlKey);
24477 this.last = last; // reset the last
24481 var isSelected = this.isSelected(rowIndex);
24482 //Roo.log("select row:" + rowIndex);
24484 this.deselectRow(rowIndex);
24486 this.selectRow(rowIndex, true);
24490 if(e.button !== 0 && isSelected){
24491 alert('rowIndex 2: ' + rowIndex);
24492 view.focusRow(rowIndex);
24493 }else if(e.ctrlKey && isSelected){
24494 this.deselectRow(rowIndex);
24495 }else if(!isSelected){
24496 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24497 view.focusRow(rowIndex);
24501 this.fireEvent("afterselectionchange", this);
24504 handleDragableRowClick : function(grid, rowIndex, e)
24506 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24507 this.selectRow(rowIndex, false);
24508 grid.view.focusRow(rowIndex);
24509 this.fireEvent("afterselectionchange", this);
24514 * Selects multiple rows.
24515 * @param {Array} rows Array of the indexes of the row to select
24516 * @param {Boolean} keepExisting (optional) True to keep existing selections
24518 selectRows : function(rows, keepExisting){
24520 this.clearSelections();
24522 for(var i = 0, len = rows.length; i < len; i++){
24523 this.selectRow(rows[i], true);
24528 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24529 * @param {Number} startRow The index of the first row in the range
24530 * @param {Number} endRow The index of the last row in the range
24531 * @param {Boolean} keepExisting (optional) True to retain existing selections
24533 selectRange : function(startRow, endRow, keepExisting){
24538 this.clearSelections();
24540 if(startRow <= endRow){
24541 for(var i = startRow; i <= endRow; i++){
24542 this.selectRow(i, true);
24545 for(var i = startRow; i >= endRow; i--){
24546 this.selectRow(i, true);
24552 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24553 * @param {Number} startRow The index of the first row in the range
24554 * @param {Number} endRow The index of the last row in the range
24556 deselectRange : function(startRow, endRow, preventViewNotify){
24560 for(var i = startRow; i <= endRow; i++){
24561 this.deselectRow(i, preventViewNotify);
24567 * @param {Number} row The index of the row to select
24568 * @param {Boolean} keepExisting (optional) True to keep existing selections
24570 selectRow : function(index, keepExisting, preventViewNotify)
24572 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24575 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24576 if(!keepExisting || this.singleSelect){
24577 this.clearSelections();
24580 var r = this.grid.store.getAt(index);
24581 //console.log('selectRow - record id :' + r.id);
24583 this.selections.add(r);
24584 this.last = this.lastActive = index;
24585 if(!preventViewNotify){
24586 var proxy = new Roo.Element(
24587 this.grid.getRowDom(index)
24589 proxy.addClass('bg-info info');
24591 this.fireEvent("rowselect", this, index, r);
24592 this.fireEvent("selectionchange", this);
24598 * @param {Number} row The index of the row to deselect
24600 deselectRow : function(index, preventViewNotify)
24605 if(this.last == index){
24608 if(this.lastActive == index){
24609 this.lastActive = false;
24612 var r = this.grid.store.getAt(index);
24617 this.selections.remove(r);
24618 //.console.log('deselectRow - record id :' + r.id);
24619 if(!preventViewNotify){
24621 var proxy = new Roo.Element(
24622 this.grid.getRowDom(index)
24624 proxy.removeClass('bg-info info');
24626 this.fireEvent("rowdeselect", this, index);
24627 this.fireEvent("selectionchange", this);
24631 restoreLast : function(){
24633 this.last = this._last;
24638 acceptsNav : function(row, col, cm){
24639 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24643 onEditorKey : function(field, e){
24644 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24649 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24651 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24653 }else if(k == e.ENTER && !e.ctrlKey){
24657 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24659 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24661 }else if(k == e.ESC){
24665 g.startEditing(newCell[0], newCell[1]);
24671 * Ext JS Library 1.1.1
24672 * Copyright(c) 2006-2007, Ext JS, LLC.
24674 * Originally Released Under LGPL - original licence link has changed is not relivant.
24677 * <script type="text/javascript">
24681 * @class Roo.bootstrap.PagingToolbar
24682 * @extends Roo.bootstrap.NavSimplebar
24683 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24685 * Create a new PagingToolbar
24686 * @param {Object} config The config object
24687 * @param {Roo.data.Store} store
24689 Roo.bootstrap.PagingToolbar = function(config)
24691 // old args format still supported... - xtype is prefered..
24692 // created from xtype...
24694 this.ds = config.dataSource;
24696 if (config.store && !this.ds) {
24697 this.store= Roo.factory(config.store, Roo.data);
24698 this.ds = this.store;
24699 this.ds.xmodule = this.xmodule || false;
24702 this.toolbarItems = [];
24703 if (config.items) {
24704 this.toolbarItems = config.items;
24707 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24712 this.bind(this.ds);
24715 if (Roo.bootstrap.version == 4) {
24716 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24718 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24723 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24725 * @cfg {Roo.data.Store} dataSource
24726 * The underlying data store providing the paged data
24729 * @cfg {String/HTMLElement/Element} container
24730 * container The id or element that will contain the toolbar
24733 * @cfg {Boolean} displayInfo
24734 * True to display the displayMsg (defaults to false)
24737 * @cfg {Number} pageSize
24738 * The number of records to display per page (defaults to 20)
24742 * @cfg {String} displayMsg
24743 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24745 displayMsg : 'Displaying {0} - {1} of {2}',
24747 * @cfg {String} emptyMsg
24748 * The message to display when no records are found (defaults to "No data to display")
24750 emptyMsg : 'No data to display',
24752 * Customizable piece of the default paging text (defaults to "Page")
24755 beforePageText : "Page",
24757 * Customizable piece of the default paging text (defaults to "of %0")
24760 afterPageText : "of {0}",
24762 * Customizable piece of the default paging text (defaults to "First Page")
24765 firstText : "First Page",
24767 * Customizable piece of the default paging text (defaults to "Previous Page")
24770 prevText : "Previous Page",
24772 * Customizable piece of the default paging text (defaults to "Next Page")
24775 nextText : "Next Page",
24777 * Customizable piece of the default paging text (defaults to "Last Page")
24780 lastText : "Last Page",
24782 * Customizable piece of the default paging text (defaults to "Refresh")
24785 refreshText : "Refresh",
24789 onRender : function(ct, position)
24791 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24792 this.navgroup.parentId = this.id;
24793 this.navgroup.onRender(this.el, null);
24794 // add the buttons to the navgroup
24796 if(this.displayInfo){
24797 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24798 this.displayEl = this.el.select('.x-paging-info', true).first();
24799 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24800 // this.displayEl = navel.el.select('span',true).first();
24806 Roo.each(_this.buttons, function(e){ // this might need to use render????
24807 Roo.factory(e).render(_this.el);
24811 Roo.each(_this.toolbarItems, function(e) {
24812 _this.navgroup.addItem(e);
24816 this.first = this.navgroup.addItem({
24817 tooltip: this.firstText,
24818 cls: "prev btn-outline-secondary",
24819 html : ' <i class="fa fa-step-backward"></i>',
24821 preventDefault: true,
24822 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24825 this.prev = this.navgroup.addItem({
24826 tooltip: this.prevText,
24827 cls: "prev btn-outline-secondary",
24828 html : ' <i class="fa fa-backward"></i>',
24830 preventDefault: true,
24831 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24833 //this.addSeparator();
24836 var field = this.navgroup.addItem( {
24838 cls : 'x-paging-position btn-outline-secondary',
24840 html : this.beforePageText +
24841 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24842 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24845 this.field = field.el.select('input', true).first();
24846 this.field.on("keydown", this.onPagingKeydown, this);
24847 this.field.on("focus", function(){this.dom.select();});
24850 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24851 //this.field.setHeight(18);
24852 //this.addSeparator();
24853 this.next = this.navgroup.addItem({
24854 tooltip: this.nextText,
24855 cls: "next btn-outline-secondary",
24856 html : ' <i class="fa fa-forward"></i>',
24858 preventDefault: true,
24859 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24861 this.last = this.navgroup.addItem({
24862 tooltip: this.lastText,
24863 html : ' <i class="fa fa-step-forward"></i>',
24864 cls: "next btn-outline-secondary",
24866 preventDefault: true,
24867 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24869 //this.addSeparator();
24870 this.loading = this.navgroup.addItem({
24871 tooltip: this.refreshText,
24872 cls: "btn-outline-secondary",
24873 html : ' <i class="fa fa-refresh"></i>',
24874 preventDefault: true,
24875 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24881 updateInfo : function(){
24882 if(this.displayEl){
24883 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24884 var msg = count == 0 ?
24888 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24890 this.displayEl.update(msg);
24895 onLoad : function(ds, r, o)
24897 this.cursor = o.params.start ? o.params.start : 0;
24899 var d = this.getPageData(),
24904 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24905 this.field.dom.value = ap;
24906 this.first.setDisabled(ap == 1);
24907 this.prev.setDisabled(ap == 1);
24908 this.next.setDisabled(ap == ps);
24909 this.last.setDisabled(ap == ps);
24910 this.loading.enable();
24915 getPageData : function(){
24916 var total = this.ds.getTotalCount();
24919 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24920 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24925 onLoadError : function(){
24926 this.loading.enable();
24930 onPagingKeydown : function(e){
24931 var k = e.getKey();
24932 var d = this.getPageData();
24934 var v = this.field.dom.value, pageNum;
24935 if(!v || isNaN(pageNum = parseInt(v, 10))){
24936 this.field.dom.value = d.activePage;
24939 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24940 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24943 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))
24945 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24946 this.field.dom.value = pageNum;
24947 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24950 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24952 var v = this.field.dom.value, pageNum;
24953 var increment = (e.shiftKey) ? 10 : 1;
24954 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24957 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24958 this.field.dom.value = d.activePage;
24961 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24963 this.field.dom.value = parseInt(v, 10) + increment;
24964 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24965 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24972 beforeLoad : function(){
24974 this.loading.disable();
24979 onClick : function(which){
24988 ds.load({params:{start: 0, limit: this.pageSize}});
24991 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24994 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24997 var total = ds.getTotalCount();
24998 var extra = total % this.pageSize;
24999 var lastStart = extra ? (total - extra) : total-this.pageSize;
25000 ds.load({params:{start: lastStart, limit: this.pageSize}});
25003 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25009 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25010 * @param {Roo.data.Store} store The data store to unbind
25012 unbind : function(ds){
25013 ds.un("beforeload", this.beforeLoad, this);
25014 ds.un("load", this.onLoad, this);
25015 ds.un("loadexception", this.onLoadError, this);
25016 ds.un("remove", this.updateInfo, this);
25017 ds.un("add", this.updateInfo, this);
25018 this.ds = undefined;
25022 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25023 * @param {Roo.data.Store} store The data store to bind
25025 bind : function(ds){
25026 ds.on("beforeload", this.beforeLoad, this);
25027 ds.on("load", this.onLoad, this);
25028 ds.on("loadexception", this.onLoadError, this);
25029 ds.on("remove", this.updateInfo, this);
25030 ds.on("add", this.updateInfo, this);
25041 * @class Roo.bootstrap.MessageBar
25042 * @extends Roo.bootstrap.Component
25043 * Bootstrap MessageBar class
25044 * @cfg {String} html contents of the MessageBar
25045 * @cfg {String} weight (info | success | warning | danger) default info
25046 * @cfg {String} beforeClass insert the bar before the given class
25047 * @cfg {Boolean} closable (true | false) default false
25048 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25051 * Create a new Element
25052 * @param {Object} config The config object
25055 Roo.bootstrap.MessageBar = function(config){
25056 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25059 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25065 beforeClass: 'bootstrap-sticky-wrap',
25067 getAutoCreate : function(){
25071 cls: 'alert alert-dismissable alert-' + this.weight,
25076 html: this.html || ''
25082 cfg.cls += ' alert-messages-fixed';
25096 onRender : function(ct, position)
25098 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25101 var cfg = Roo.apply({}, this.getAutoCreate());
25105 cfg.cls += ' ' + this.cls;
25108 cfg.style = this.style;
25110 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25112 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25115 this.el.select('>button.close').on('click', this.hide, this);
25121 if (!this.rendered) {
25127 this.fireEvent('show', this);
25133 if (!this.rendered) {
25139 this.fireEvent('hide', this);
25142 update : function()
25144 // var e = this.el.dom.firstChild;
25146 // if(this.closable){
25147 // e = e.nextSibling;
25150 // e.data = this.html || '';
25152 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25168 * @class Roo.bootstrap.Graph
25169 * @extends Roo.bootstrap.Component
25170 * Bootstrap Graph class
25174 @cfg {String} graphtype bar | vbar | pie
25175 @cfg {number} g_x coodinator | centre x (pie)
25176 @cfg {number} g_y coodinator | centre y (pie)
25177 @cfg {number} g_r radius (pie)
25178 @cfg {number} g_height height of the chart (respected by all elements in the set)
25179 @cfg {number} g_width width of the chart (respected by all elements in the set)
25180 @cfg {Object} title The title of the chart
25183 -opts (object) options for the chart
25185 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25186 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25188 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.
25189 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25191 o stretch (boolean)
25193 -opts (object) options for the pie
25196 o startAngle (number)
25197 o endAngle (number)
25201 * Create a new Input
25202 * @param {Object} config The config object
25205 Roo.bootstrap.Graph = function(config){
25206 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25212 * The img click event for the img.
25213 * @param {Roo.EventObject} e
25219 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25230 //g_colors: this.colors,
25237 getAutoCreate : function(){
25248 onRender : function(ct,position){
25251 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25253 if (typeof(Raphael) == 'undefined') {
25254 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25258 this.raphael = Raphael(this.el.dom);
25260 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25261 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25262 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25263 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25265 r.text(160, 10, "Single Series Chart").attr(txtattr);
25266 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25267 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25268 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25270 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25271 r.barchart(330, 10, 300, 220, data1);
25272 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25273 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25276 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25277 // r.barchart(30, 30, 560, 250, xdata, {
25278 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25279 // axis : "0 0 1 1",
25280 // axisxlabels : xdata
25281 // //yvalues : cols,
25284 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25286 // this.load(null,xdata,{
25287 // axis : "0 0 1 1",
25288 // axisxlabels : xdata
25293 load : function(graphtype,xdata,opts)
25295 this.raphael.clear();
25297 graphtype = this.graphtype;
25302 var r = this.raphael,
25303 fin = function () {
25304 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25306 fout = function () {
25307 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25309 pfin = function() {
25310 this.sector.stop();
25311 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25314 this.label[0].stop();
25315 this.label[0].attr({ r: 7.5 });
25316 this.label[1].attr({ "font-weight": 800 });
25319 pfout = function() {
25320 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25323 this.label[0].animate({ r: 5 }, 500, "bounce");
25324 this.label[1].attr({ "font-weight": 400 });
25330 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25333 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25336 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25337 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25339 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25346 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25351 setTitle: function(o)
25356 initEvents: function() {
25359 this.el.on('click', this.onClick, this);
25363 onClick : function(e)
25365 Roo.log('img onclick');
25366 this.fireEvent('click', this, e);
25378 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25381 * @class Roo.bootstrap.dash.NumberBox
25382 * @extends Roo.bootstrap.Component
25383 * Bootstrap NumberBox class
25384 * @cfg {String} headline Box headline
25385 * @cfg {String} content Box content
25386 * @cfg {String} icon Box icon
25387 * @cfg {String} footer Footer text
25388 * @cfg {String} fhref Footer href
25391 * Create a new NumberBox
25392 * @param {Object} config The config object
25396 Roo.bootstrap.dash.NumberBox = function(config){
25397 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25401 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25410 getAutoCreate : function(){
25414 cls : 'small-box ',
25422 cls : 'roo-headline',
25423 html : this.headline
25427 cls : 'roo-content',
25428 html : this.content
25442 cls : 'ion ' + this.icon
25451 cls : 'small-box-footer',
25452 href : this.fhref || '#',
25456 cfg.cn.push(footer);
25463 onRender : function(ct,position){
25464 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25471 setHeadline: function (value)
25473 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25476 setFooter: function (value, href)
25478 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25481 this.el.select('a.small-box-footer',true).first().attr('href', href);
25486 setContent: function (value)
25488 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25491 initEvents: function()
25505 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25508 * @class Roo.bootstrap.dash.TabBox
25509 * @extends Roo.bootstrap.Component
25510 * Bootstrap TabBox class
25511 * @cfg {String} title Title of the TabBox
25512 * @cfg {String} icon Icon of the TabBox
25513 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25514 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25517 * Create a new TabBox
25518 * @param {Object} config The config object
25522 Roo.bootstrap.dash.TabBox = function(config){
25523 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25528 * When a pane is added
25529 * @param {Roo.bootstrap.dash.TabPane} pane
25533 * @event activatepane
25534 * When a pane is activated
25535 * @param {Roo.bootstrap.dash.TabPane} pane
25537 "activatepane" : true
25545 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25550 tabScrollable : false,
25552 getChildContainer : function()
25554 return this.el.select('.tab-content', true).first();
25557 getAutoCreate : function(){
25561 cls: 'pull-left header',
25569 cls: 'fa ' + this.icon
25575 cls: 'nav nav-tabs pull-right',
25581 if(this.tabScrollable){
25588 cls: 'nav nav-tabs pull-right',
25599 cls: 'nav-tabs-custom',
25604 cls: 'tab-content no-padding',
25612 initEvents : function()
25614 //Roo.log('add add pane handler');
25615 this.on('addpane', this.onAddPane, this);
25618 * Updates the box title
25619 * @param {String} html to set the title to.
25621 setTitle : function(value)
25623 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25625 onAddPane : function(pane)
25627 this.panes.push(pane);
25628 //Roo.log('addpane');
25630 // tabs are rendere left to right..
25631 if(!this.showtabs){
25635 var ctr = this.el.select('.nav-tabs', true).first();
25638 var existing = ctr.select('.nav-tab',true);
25639 var qty = existing.getCount();;
25642 var tab = ctr.createChild({
25644 cls : 'nav-tab' + (qty ? '' : ' active'),
25652 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25655 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25657 pane.el.addClass('active');
25662 onTabClick : function(ev,un,ob,pane)
25664 //Roo.log('tab - prev default');
25665 ev.preventDefault();
25668 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25669 pane.tab.addClass('active');
25670 //Roo.log(pane.title);
25671 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25672 // technically we should have a deactivate event.. but maybe add later.
25673 // and it should not de-activate the selected tab...
25674 this.fireEvent('activatepane', pane);
25675 pane.el.addClass('active');
25676 pane.fireEvent('activate');
25681 getActivePane : function()
25684 Roo.each(this.panes, function(p) {
25685 if(p.el.hasClass('active')){
25706 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25708 * @class Roo.bootstrap.TabPane
25709 * @extends Roo.bootstrap.Component
25710 * Bootstrap TabPane class
25711 * @cfg {Boolean} active (false | true) Default false
25712 * @cfg {String} title title of panel
25716 * Create a new TabPane
25717 * @param {Object} config The config object
25720 Roo.bootstrap.dash.TabPane = function(config){
25721 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25727 * When a pane is activated
25728 * @param {Roo.bootstrap.dash.TabPane} pane
25735 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25740 // the tabBox that this is attached to.
25743 getAutoCreate : function()
25751 cfg.cls += ' active';
25756 initEvents : function()
25758 //Roo.log('trigger add pane handler');
25759 this.parent().fireEvent('addpane', this)
25763 * Updates the tab title
25764 * @param {String} html to set the title to.
25766 setTitle: function(str)
25772 this.tab.select('a', true).first().dom.innerHTML = str;
25789 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25792 * @class Roo.bootstrap.menu.Menu
25793 * @extends Roo.bootstrap.Component
25794 * Bootstrap Menu class - container for Menu
25795 * @cfg {String} html Text of the menu
25796 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25797 * @cfg {String} icon Font awesome icon
25798 * @cfg {String} pos Menu align to (top | bottom) default bottom
25802 * Create a new Menu
25803 * @param {Object} config The config object
25807 Roo.bootstrap.menu.Menu = function(config){
25808 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25812 * @event beforeshow
25813 * Fires before this menu is displayed
25814 * @param {Roo.bootstrap.menu.Menu} this
25818 * @event beforehide
25819 * Fires before this menu is hidden
25820 * @param {Roo.bootstrap.menu.Menu} this
25825 * Fires after this menu is displayed
25826 * @param {Roo.bootstrap.menu.Menu} this
25831 * Fires after this menu is hidden
25832 * @param {Roo.bootstrap.menu.Menu} this
25837 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25838 * @param {Roo.bootstrap.menu.Menu} this
25839 * @param {Roo.EventObject} e
25846 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25850 weight : 'default',
25855 getChildContainer : function() {
25856 if(this.isSubMenu){
25860 return this.el.select('ul.dropdown-menu', true).first();
25863 getAutoCreate : function()
25868 cls : 'roo-menu-text',
25876 cls : 'fa ' + this.icon
25887 cls : 'dropdown-button btn btn-' + this.weight,
25892 cls : 'dropdown-toggle btn btn-' + this.weight,
25902 cls : 'dropdown-menu'
25908 if(this.pos == 'top'){
25909 cfg.cls += ' dropup';
25912 if(this.isSubMenu){
25915 cls : 'dropdown-menu'
25922 onRender : function(ct, position)
25924 this.isSubMenu = ct.hasClass('dropdown-submenu');
25926 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25929 initEvents : function()
25931 if(this.isSubMenu){
25935 this.hidden = true;
25937 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25938 this.triggerEl.on('click', this.onTriggerPress, this);
25940 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25941 this.buttonEl.on('click', this.onClick, this);
25947 if(this.isSubMenu){
25951 return this.el.select('ul.dropdown-menu', true).first();
25954 onClick : function(e)
25956 this.fireEvent("click", this, e);
25959 onTriggerPress : function(e)
25961 if (this.isVisible()) {
25968 isVisible : function(){
25969 return !this.hidden;
25974 this.fireEvent("beforeshow", this);
25976 this.hidden = false;
25977 this.el.addClass('open');
25979 Roo.get(document).on("mouseup", this.onMouseUp, this);
25981 this.fireEvent("show", this);
25988 this.fireEvent("beforehide", this);
25990 this.hidden = true;
25991 this.el.removeClass('open');
25993 Roo.get(document).un("mouseup", this.onMouseUp);
25995 this.fireEvent("hide", this);
25998 onMouseUp : function()
26012 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26015 * @class Roo.bootstrap.menu.Item
26016 * @extends Roo.bootstrap.Component
26017 * Bootstrap MenuItem class
26018 * @cfg {Boolean} submenu (true | false) default false
26019 * @cfg {String} html text of the item
26020 * @cfg {String} href the link
26021 * @cfg {Boolean} disable (true | false) default false
26022 * @cfg {Boolean} preventDefault (true | false) default true
26023 * @cfg {String} icon Font awesome icon
26024 * @cfg {String} pos Submenu align to (left | right) default right
26028 * Create a new Item
26029 * @param {Object} config The config object
26033 Roo.bootstrap.menu.Item = function(config){
26034 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26038 * Fires when the mouse is hovering over this menu
26039 * @param {Roo.bootstrap.menu.Item} this
26040 * @param {Roo.EventObject} e
26045 * Fires when the mouse exits this menu
26046 * @param {Roo.bootstrap.menu.Item} this
26047 * @param {Roo.EventObject} e
26053 * The raw click event for the entire grid.
26054 * @param {Roo.EventObject} e
26060 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26065 preventDefault: true,
26070 getAutoCreate : function()
26075 cls : 'roo-menu-item-text',
26083 cls : 'fa ' + this.icon
26092 href : this.href || '#',
26099 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26103 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26105 if(this.pos == 'left'){
26106 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26113 initEvents : function()
26115 this.el.on('mouseover', this.onMouseOver, this);
26116 this.el.on('mouseout', this.onMouseOut, this);
26118 this.el.select('a', true).first().on('click', this.onClick, this);
26122 onClick : function(e)
26124 if(this.preventDefault){
26125 e.preventDefault();
26128 this.fireEvent("click", this, e);
26131 onMouseOver : function(e)
26133 if(this.submenu && this.pos == 'left'){
26134 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26137 this.fireEvent("mouseover", this, e);
26140 onMouseOut : function(e)
26142 this.fireEvent("mouseout", this, e);
26154 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26157 * @class Roo.bootstrap.menu.Separator
26158 * @extends Roo.bootstrap.Component
26159 * Bootstrap Separator class
26162 * Create a new Separator
26163 * @param {Object} config The config object
26167 Roo.bootstrap.menu.Separator = function(config){
26168 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26171 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26173 getAutoCreate : function(){
26194 * @class Roo.bootstrap.Tooltip
26195 * Bootstrap Tooltip class
26196 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26197 * to determine which dom element triggers the tooltip.
26199 * It needs to add support for additional attributes like tooltip-position
26202 * Create a new Toolti
26203 * @param {Object} config The config object
26206 Roo.bootstrap.Tooltip = function(config){
26207 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26209 this.alignment = Roo.bootstrap.Tooltip.alignment;
26211 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26212 this.alignment = config.alignment;
26217 Roo.apply(Roo.bootstrap.Tooltip, {
26219 * @function init initialize tooltip monitoring.
26223 currentTip : false,
26224 currentRegion : false,
26230 Roo.get(document).on('mouseover', this.enter ,this);
26231 Roo.get(document).on('mouseout', this.leave, this);
26234 this.currentTip = new Roo.bootstrap.Tooltip();
26237 enter : function(ev)
26239 var dom = ev.getTarget();
26241 //Roo.log(['enter',dom]);
26242 var el = Roo.fly(dom);
26243 if (this.currentEl) {
26245 //Roo.log(this.currentEl);
26246 //Roo.log(this.currentEl.contains(dom));
26247 if (this.currentEl == el) {
26250 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26256 if (this.currentTip.el) {
26257 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26261 if(!el || el.dom == document){
26267 // you can not look for children, as if el is the body.. then everythign is the child..
26268 if (!el.attr('tooltip')) { //
26269 if (!el.select("[tooltip]").elements.length) {
26272 // is the mouse over this child...?
26273 bindEl = el.select("[tooltip]").first();
26274 var xy = ev.getXY();
26275 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26276 //Roo.log("not in region.");
26279 //Roo.log("child element over..");
26282 this.currentEl = bindEl;
26283 this.currentTip.bind(bindEl);
26284 this.currentRegion = Roo.lib.Region.getRegion(dom);
26285 this.currentTip.enter();
26288 leave : function(ev)
26290 var dom = ev.getTarget();
26291 //Roo.log(['leave',dom]);
26292 if (!this.currentEl) {
26297 if (dom != this.currentEl.dom) {
26300 var xy = ev.getXY();
26301 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26304 // only activate leave if mouse cursor is outside... bounding box..
26309 if (this.currentTip) {
26310 this.currentTip.leave();
26312 //Roo.log('clear currentEl');
26313 this.currentEl = false;
26318 'left' : ['r-l', [-2,0], 'right'],
26319 'right' : ['l-r', [2,0], 'left'],
26320 'bottom' : ['t-b', [0,2], 'top'],
26321 'top' : [ 'b-t', [0,-2], 'bottom']
26327 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26332 delay : null, // can be { show : 300 , hide: 500}
26336 hoverState : null, //???
26338 placement : 'bottom',
26342 getAutoCreate : function(){
26349 cls : 'tooltip-arrow'
26352 cls : 'tooltip-inner'
26359 bind : function(el)
26365 enter : function () {
26367 if (this.timeout != null) {
26368 clearTimeout(this.timeout);
26371 this.hoverState = 'in';
26372 //Roo.log("enter - show");
26373 if (!this.delay || !this.delay.show) {
26378 this.timeout = setTimeout(function () {
26379 if (_t.hoverState == 'in') {
26382 }, this.delay.show);
26386 clearTimeout(this.timeout);
26388 this.hoverState = 'out';
26389 if (!this.delay || !this.delay.hide) {
26395 this.timeout = setTimeout(function () {
26396 //Roo.log("leave - timeout");
26398 if (_t.hoverState == 'out') {
26400 Roo.bootstrap.Tooltip.currentEl = false;
26405 show : function (msg)
26408 this.render(document.body);
26411 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26413 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26415 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26417 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26419 var placement = typeof this.placement == 'function' ?
26420 this.placement.call(this, this.el, on_el) :
26423 var autoToken = /\s?auto?\s?/i;
26424 var autoPlace = autoToken.test(placement);
26426 placement = placement.replace(autoToken, '') || 'top';
26430 //this.el.setXY([0,0]);
26432 //this.el.dom.style.display='block';
26434 //this.el.appendTo(on_el);
26436 var p = this.getPosition();
26437 var box = this.el.getBox();
26443 var align = this.alignment[placement];
26445 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26447 if(placement == 'top' || placement == 'bottom'){
26449 placement = 'right';
26452 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26453 placement = 'left';
26456 var scroll = Roo.select('body', true).first().getScroll();
26458 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26462 align = this.alignment[placement];
26465 this.el.alignTo(this.bindEl, align[0],align[1]);
26466 //var arrow = this.el.select('.arrow',true).first();
26467 //arrow.set(align[2],
26469 this.el.addClass(placement);
26471 this.el.addClass('in fade');
26473 this.hoverState = null;
26475 if (this.el.hasClass('fade')) {
26486 //this.el.setXY([0,0]);
26487 this.el.removeClass('in');
26503 * @class Roo.bootstrap.LocationPicker
26504 * @extends Roo.bootstrap.Component
26505 * Bootstrap LocationPicker class
26506 * @cfg {Number} latitude Position when init default 0
26507 * @cfg {Number} longitude Position when init default 0
26508 * @cfg {Number} zoom default 15
26509 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26510 * @cfg {Boolean} mapTypeControl default false
26511 * @cfg {Boolean} disableDoubleClickZoom default false
26512 * @cfg {Boolean} scrollwheel default true
26513 * @cfg {Boolean} streetViewControl default false
26514 * @cfg {Number} radius default 0
26515 * @cfg {String} locationName
26516 * @cfg {Boolean} draggable default true
26517 * @cfg {Boolean} enableAutocomplete default false
26518 * @cfg {Boolean} enableReverseGeocode default true
26519 * @cfg {String} markerTitle
26522 * Create a new LocationPicker
26523 * @param {Object} config The config object
26527 Roo.bootstrap.LocationPicker = function(config){
26529 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26534 * Fires when the picker initialized.
26535 * @param {Roo.bootstrap.LocationPicker} this
26536 * @param {Google Location} location
26540 * @event positionchanged
26541 * Fires when the picker position changed.
26542 * @param {Roo.bootstrap.LocationPicker} this
26543 * @param {Google Location} location
26545 positionchanged : true,
26548 * Fires when the map resize.
26549 * @param {Roo.bootstrap.LocationPicker} this
26554 * Fires when the map show.
26555 * @param {Roo.bootstrap.LocationPicker} this
26560 * Fires when the map hide.
26561 * @param {Roo.bootstrap.LocationPicker} this
26566 * Fires when click the map.
26567 * @param {Roo.bootstrap.LocationPicker} this
26568 * @param {Map event} e
26572 * @event mapRightClick
26573 * Fires when right click the map.
26574 * @param {Roo.bootstrap.LocationPicker} this
26575 * @param {Map event} e
26577 mapRightClick : true,
26579 * @event markerClick
26580 * Fires when click the marker.
26581 * @param {Roo.bootstrap.LocationPicker} this
26582 * @param {Map event} e
26584 markerClick : true,
26586 * @event markerRightClick
26587 * Fires when right click the marker.
26588 * @param {Roo.bootstrap.LocationPicker} this
26589 * @param {Map event} e
26591 markerRightClick : true,
26593 * @event OverlayViewDraw
26594 * Fires when OverlayView Draw
26595 * @param {Roo.bootstrap.LocationPicker} this
26597 OverlayViewDraw : true,
26599 * @event OverlayViewOnAdd
26600 * Fires when OverlayView Draw
26601 * @param {Roo.bootstrap.LocationPicker} this
26603 OverlayViewOnAdd : true,
26605 * @event OverlayViewOnRemove
26606 * Fires when OverlayView Draw
26607 * @param {Roo.bootstrap.LocationPicker} this
26609 OverlayViewOnRemove : true,
26611 * @event OverlayViewShow
26612 * Fires when OverlayView Draw
26613 * @param {Roo.bootstrap.LocationPicker} this
26614 * @param {Pixel} cpx
26616 OverlayViewShow : true,
26618 * @event OverlayViewHide
26619 * Fires when OverlayView Draw
26620 * @param {Roo.bootstrap.LocationPicker} this
26622 OverlayViewHide : true,
26624 * @event loadexception
26625 * Fires when load google lib failed.
26626 * @param {Roo.bootstrap.LocationPicker} this
26628 loadexception : true
26633 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26635 gMapContext: false,
26641 mapTypeControl: false,
26642 disableDoubleClickZoom: false,
26644 streetViewControl: false,
26648 enableAutocomplete: false,
26649 enableReverseGeocode: true,
26652 getAutoCreate: function()
26657 cls: 'roo-location-picker'
26663 initEvents: function(ct, position)
26665 if(!this.el.getWidth() || this.isApplied()){
26669 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26674 initial: function()
26676 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26677 this.fireEvent('loadexception', this);
26681 if(!this.mapTypeId){
26682 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26685 this.gMapContext = this.GMapContext();
26687 this.initOverlayView();
26689 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26693 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26694 _this.setPosition(_this.gMapContext.marker.position);
26697 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26698 _this.fireEvent('mapClick', this, event);
26702 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26703 _this.fireEvent('mapRightClick', this, event);
26707 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26708 _this.fireEvent('markerClick', this, event);
26712 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26713 _this.fireEvent('markerRightClick', this, event);
26717 this.setPosition(this.gMapContext.location);
26719 this.fireEvent('initial', this, this.gMapContext.location);
26722 initOverlayView: function()
26726 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26730 _this.fireEvent('OverlayViewDraw', _this);
26735 _this.fireEvent('OverlayViewOnAdd', _this);
26738 onRemove: function()
26740 _this.fireEvent('OverlayViewOnRemove', _this);
26743 show: function(cpx)
26745 _this.fireEvent('OverlayViewShow', _this, cpx);
26750 _this.fireEvent('OverlayViewHide', _this);
26756 fromLatLngToContainerPixel: function(event)
26758 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26761 isApplied: function()
26763 return this.getGmapContext() == false ? false : true;
26766 getGmapContext: function()
26768 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26771 GMapContext: function()
26773 var position = new google.maps.LatLng(this.latitude, this.longitude);
26775 var _map = new google.maps.Map(this.el.dom, {
26778 mapTypeId: this.mapTypeId,
26779 mapTypeControl: this.mapTypeControl,
26780 disableDoubleClickZoom: this.disableDoubleClickZoom,
26781 scrollwheel: this.scrollwheel,
26782 streetViewControl: this.streetViewControl,
26783 locationName: this.locationName,
26784 draggable: this.draggable,
26785 enableAutocomplete: this.enableAutocomplete,
26786 enableReverseGeocode: this.enableReverseGeocode
26789 var _marker = new google.maps.Marker({
26790 position: position,
26792 title: this.markerTitle,
26793 draggable: this.draggable
26800 location: position,
26801 radius: this.radius,
26802 locationName: this.locationName,
26803 addressComponents: {
26804 formatted_address: null,
26805 addressLine1: null,
26806 addressLine2: null,
26808 streetNumber: null,
26812 stateOrProvince: null
26815 domContainer: this.el.dom,
26816 geodecoder: new google.maps.Geocoder()
26820 drawCircle: function(center, radius, options)
26822 if (this.gMapContext.circle != null) {
26823 this.gMapContext.circle.setMap(null);
26827 options = Roo.apply({}, options, {
26828 strokeColor: "#0000FF",
26829 strokeOpacity: .35,
26831 fillColor: "#0000FF",
26835 options.map = this.gMapContext.map;
26836 options.radius = radius;
26837 options.center = center;
26838 this.gMapContext.circle = new google.maps.Circle(options);
26839 return this.gMapContext.circle;
26845 setPosition: function(location)
26847 this.gMapContext.location = location;
26848 this.gMapContext.marker.setPosition(location);
26849 this.gMapContext.map.panTo(location);
26850 this.drawCircle(location, this.gMapContext.radius, {});
26854 if (this.gMapContext.settings.enableReverseGeocode) {
26855 this.gMapContext.geodecoder.geocode({
26856 latLng: this.gMapContext.location
26857 }, function(results, status) {
26859 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26860 _this.gMapContext.locationName = results[0].formatted_address;
26861 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26863 _this.fireEvent('positionchanged', this, location);
26870 this.fireEvent('positionchanged', this, location);
26875 google.maps.event.trigger(this.gMapContext.map, "resize");
26877 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26879 this.fireEvent('resize', this);
26882 setPositionByLatLng: function(latitude, longitude)
26884 this.setPosition(new google.maps.LatLng(latitude, longitude));
26887 getCurrentPosition: function()
26890 latitude: this.gMapContext.location.lat(),
26891 longitude: this.gMapContext.location.lng()
26895 getAddressName: function()
26897 return this.gMapContext.locationName;
26900 getAddressComponents: function()
26902 return this.gMapContext.addressComponents;
26905 address_component_from_google_geocode: function(address_components)
26909 for (var i = 0; i < address_components.length; i++) {
26910 var component = address_components[i];
26911 if (component.types.indexOf("postal_code") >= 0) {
26912 result.postalCode = component.short_name;
26913 } else if (component.types.indexOf("street_number") >= 0) {
26914 result.streetNumber = component.short_name;
26915 } else if (component.types.indexOf("route") >= 0) {
26916 result.streetName = component.short_name;
26917 } else if (component.types.indexOf("neighborhood") >= 0) {
26918 result.city = component.short_name;
26919 } else if (component.types.indexOf("locality") >= 0) {
26920 result.city = component.short_name;
26921 } else if (component.types.indexOf("sublocality") >= 0) {
26922 result.district = component.short_name;
26923 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26924 result.stateOrProvince = component.short_name;
26925 } else if (component.types.indexOf("country") >= 0) {
26926 result.country = component.short_name;
26930 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26931 result.addressLine2 = "";
26935 setZoomLevel: function(zoom)
26937 this.gMapContext.map.setZoom(zoom);
26950 this.fireEvent('show', this);
26961 this.fireEvent('hide', this);
26966 Roo.apply(Roo.bootstrap.LocationPicker, {
26968 OverlayView : function(map, options)
26970 options = options || {};
26984 * @class Roo.bootstrap.Alert
26985 * @extends Roo.bootstrap.Component
26986 * Bootstrap Alert class
26987 * @cfg {String} title The title of alert
26988 * @cfg {String} html The content of alert
26989 * @cfg {String} weight ( success | info | warning | danger )
26990 * @cfg {String} faicon font-awesomeicon
26993 * Create a new alert
26994 * @param {Object} config The config object
26998 Roo.bootstrap.Alert = function(config){
26999 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27003 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27010 getAutoCreate : function()
27019 cls : 'roo-alert-icon'
27024 cls : 'roo-alert-title',
27029 cls : 'roo-alert-text',
27036 cfg.cn[0].cls += ' fa ' + this.faicon;
27040 cfg.cls += ' alert-' + this.weight;
27046 initEvents: function()
27048 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27051 setTitle : function(str)
27053 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27056 setText : function(str)
27058 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27061 setWeight : function(weight)
27064 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27067 this.weight = weight;
27069 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27072 setIcon : function(icon)
27075 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27078 this.faicon = icon;
27080 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27101 * @class Roo.bootstrap.UploadCropbox
27102 * @extends Roo.bootstrap.Component
27103 * Bootstrap UploadCropbox class
27104 * @cfg {String} emptyText show when image has been loaded
27105 * @cfg {String} rotateNotify show when image too small to rotate
27106 * @cfg {Number} errorTimeout default 3000
27107 * @cfg {Number} minWidth default 300
27108 * @cfg {Number} minHeight default 300
27109 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27110 * @cfg {Boolean} isDocument (true|false) default false
27111 * @cfg {String} url action url
27112 * @cfg {String} paramName default 'imageUpload'
27113 * @cfg {String} method default POST
27114 * @cfg {Boolean} loadMask (true|false) default true
27115 * @cfg {Boolean} loadingText default 'Loading...'
27118 * Create a new UploadCropbox
27119 * @param {Object} config The config object
27122 Roo.bootstrap.UploadCropbox = function(config){
27123 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27127 * @event beforeselectfile
27128 * Fire before select file
27129 * @param {Roo.bootstrap.UploadCropbox} this
27131 "beforeselectfile" : true,
27134 * Fire after initEvent
27135 * @param {Roo.bootstrap.UploadCropbox} this
27140 * Fire after initEvent
27141 * @param {Roo.bootstrap.UploadCropbox} this
27142 * @param {String} data
27147 * Fire when preparing the file data
27148 * @param {Roo.bootstrap.UploadCropbox} this
27149 * @param {Object} file
27154 * Fire when get exception
27155 * @param {Roo.bootstrap.UploadCropbox} this
27156 * @param {XMLHttpRequest} xhr
27158 "exception" : true,
27160 * @event beforeloadcanvas
27161 * Fire before load the canvas
27162 * @param {Roo.bootstrap.UploadCropbox} this
27163 * @param {String} src
27165 "beforeloadcanvas" : true,
27168 * Fire when trash image
27169 * @param {Roo.bootstrap.UploadCropbox} this
27174 * Fire when download the image
27175 * @param {Roo.bootstrap.UploadCropbox} this
27179 * @event footerbuttonclick
27180 * Fire when footerbuttonclick
27181 * @param {Roo.bootstrap.UploadCropbox} this
27182 * @param {String} type
27184 "footerbuttonclick" : true,
27188 * @param {Roo.bootstrap.UploadCropbox} this
27193 * Fire when rotate the image
27194 * @param {Roo.bootstrap.UploadCropbox} this
27195 * @param {String} pos
27200 * Fire when inspect the file
27201 * @param {Roo.bootstrap.UploadCropbox} this
27202 * @param {Object} file
27207 * Fire when xhr upload the file
27208 * @param {Roo.bootstrap.UploadCropbox} this
27209 * @param {Object} data
27214 * Fire when arrange the file data
27215 * @param {Roo.bootstrap.UploadCropbox} this
27216 * @param {Object} formData
27221 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27224 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27226 emptyText : 'Click to upload image',
27227 rotateNotify : 'Image is too small to rotate',
27228 errorTimeout : 3000,
27242 cropType : 'image/jpeg',
27244 canvasLoaded : false,
27245 isDocument : false,
27247 paramName : 'imageUpload',
27249 loadingText : 'Loading...',
27252 getAutoCreate : function()
27256 cls : 'roo-upload-cropbox',
27260 cls : 'roo-upload-cropbox-selector',
27265 cls : 'roo-upload-cropbox-body',
27266 style : 'cursor:pointer',
27270 cls : 'roo-upload-cropbox-preview'
27274 cls : 'roo-upload-cropbox-thumb'
27278 cls : 'roo-upload-cropbox-empty-notify',
27279 html : this.emptyText
27283 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27284 html : this.rotateNotify
27290 cls : 'roo-upload-cropbox-footer',
27293 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27303 onRender : function(ct, position)
27305 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27307 if (this.buttons.length) {
27309 Roo.each(this.buttons, function(bb) {
27311 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27313 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27319 this.maskEl = this.el;
27323 initEvents : function()
27325 this.urlAPI = (window.createObjectURL && window) ||
27326 (window.URL && URL.revokeObjectURL && URL) ||
27327 (window.webkitURL && webkitURL);
27329 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27330 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27332 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27333 this.selectorEl.hide();
27335 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27336 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27338 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27339 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27340 this.thumbEl.hide();
27342 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27343 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27345 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27346 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347 this.errorEl.hide();
27349 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27350 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27351 this.footerEl.hide();
27353 this.setThumbBoxSize();
27359 this.fireEvent('initial', this);
27366 window.addEventListener("resize", function() { _this.resize(); } );
27368 this.bodyEl.on('click', this.beforeSelectFile, this);
27371 this.bodyEl.on('touchstart', this.onTouchStart, this);
27372 this.bodyEl.on('touchmove', this.onTouchMove, this);
27373 this.bodyEl.on('touchend', this.onTouchEnd, this);
27377 this.bodyEl.on('mousedown', this.onMouseDown, this);
27378 this.bodyEl.on('mousemove', this.onMouseMove, this);
27379 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27380 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27381 Roo.get(document).on('mouseup', this.onMouseUp, this);
27384 this.selectorEl.on('change', this.onFileSelected, this);
27390 this.baseScale = 1;
27392 this.baseRotate = 1;
27393 this.dragable = false;
27394 this.pinching = false;
27397 this.cropData = false;
27398 this.notifyEl.dom.innerHTML = this.emptyText;
27400 this.selectorEl.dom.value = '';
27404 resize : function()
27406 if(this.fireEvent('resize', this) != false){
27407 this.setThumbBoxPosition();
27408 this.setCanvasPosition();
27412 onFooterButtonClick : function(e, el, o, type)
27415 case 'rotate-left' :
27416 this.onRotateLeft(e);
27418 case 'rotate-right' :
27419 this.onRotateRight(e);
27422 this.beforeSelectFile(e);
27437 this.fireEvent('footerbuttonclick', this, type);
27440 beforeSelectFile : function(e)
27442 e.preventDefault();
27444 if(this.fireEvent('beforeselectfile', this) != false){
27445 this.selectorEl.dom.click();
27449 onFileSelected : function(e)
27451 e.preventDefault();
27453 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27457 var file = this.selectorEl.dom.files[0];
27459 if(this.fireEvent('inspect', this, file) != false){
27460 this.prepare(file);
27465 trash : function(e)
27467 this.fireEvent('trash', this);
27470 download : function(e)
27472 this.fireEvent('download', this);
27475 loadCanvas : function(src)
27477 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27481 this.imageEl = document.createElement('img');
27485 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27487 this.imageEl.src = src;
27491 onLoadCanvas : function()
27493 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27494 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27496 this.bodyEl.un('click', this.beforeSelectFile, this);
27498 this.notifyEl.hide();
27499 this.thumbEl.show();
27500 this.footerEl.show();
27502 this.baseRotateLevel();
27504 if(this.isDocument){
27505 this.setThumbBoxSize();
27508 this.setThumbBoxPosition();
27510 this.baseScaleLevel();
27516 this.canvasLoaded = true;
27519 this.maskEl.unmask();
27524 setCanvasPosition : function()
27526 if(!this.canvasEl){
27530 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27531 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27533 this.previewEl.setLeft(pw);
27534 this.previewEl.setTop(ph);
27538 onMouseDown : function(e)
27542 this.dragable = true;
27543 this.pinching = false;
27545 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27546 this.dragable = false;
27550 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27551 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27555 onMouseMove : function(e)
27559 if(!this.canvasLoaded){
27563 if (!this.dragable){
27567 var minX = Math.ceil(this.thumbEl.getLeft(true));
27568 var minY = Math.ceil(this.thumbEl.getTop(true));
27570 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27571 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27573 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27574 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27576 x = x - this.mouseX;
27577 y = y - this.mouseY;
27579 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27580 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27582 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27583 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27585 this.previewEl.setLeft(bgX);
27586 this.previewEl.setTop(bgY);
27588 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27589 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27592 onMouseUp : function(e)
27596 this.dragable = false;
27599 onMouseWheel : function(e)
27603 this.startScale = this.scale;
27605 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27607 if(!this.zoomable()){
27608 this.scale = this.startScale;
27617 zoomable : function()
27619 var minScale = this.thumbEl.getWidth() / this.minWidth;
27621 if(this.minWidth < this.minHeight){
27622 minScale = this.thumbEl.getHeight() / this.minHeight;
27625 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27626 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27630 (this.rotate == 0 || this.rotate == 180) &&
27632 width > this.imageEl.OriginWidth ||
27633 height > this.imageEl.OriginHeight ||
27634 (width < this.minWidth && height < this.minHeight)
27642 (this.rotate == 90 || this.rotate == 270) &&
27644 width > this.imageEl.OriginWidth ||
27645 height > this.imageEl.OriginHeight ||
27646 (width < this.minHeight && height < this.minWidth)
27653 !this.isDocument &&
27654 (this.rotate == 0 || this.rotate == 180) &&
27656 width < this.minWidth ||
27657 width > this.imageEl.OriginWidth ||
27658 height < this.minHeight ||
27659 height > this.imageEl.OriginHeight
27666 !this.isDocument &&
27667 (this.rotate == 90 || this.rotate == 270) &&
27669 width < this.minHeight ||
27670 width > this.imageEl.OriginWidth ||
27671 height < this.minWidth ||
27672 height > this.imageEl.OriginHeight
27682 onRotateLeft : function(e)
27684 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27686 var minScale = this.thumbEl.getWidth() / this.minWidth;
27688 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27689 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27691 this.startScale = this.scale;
27693 while (this.getScaleLevel() < minScale){
27695 this.scale = this.scale + 1;
27697 if(!this.zoomable()){
27702 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27703 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27708 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27715 this.scale = this.startScale;
27717 this.onRotateFail();
27722 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27724 if(this.isDocument){
27725 this.setThumbBoxSize();
27726 this.setThumbBoxPosition();
27727 this.setCanvasPosition();
27732 this.fireEvent('rotate', this, 'left');
27736 onRotateRight : function(e)
27738 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27740 var minScale = this.thumbEl.getWidth() / this.minWidth;
27742 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27743 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27745 this.startScale = this.scale;
27747 while (this.getScaleLevel() < minScale){
27749 this.scale = this.scale + 1;
27751 if(!this.zoomable()){
27756 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27757 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27762 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27769 this.scale = this.startScale;
27771 this.onRotateFail();
27776 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27778 if(this.isDocument){
27779 this.setThumbBoxSize();
27780 this.setThumbBoxPosition();
27781 this.setCanvasPosition();
27786 this.fireEvent('rotate', this, 'right');
27789 onRotateFail : function()
27791 this.errorEl.show(true);
27795 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27800 this.previewEl.dom.innerHTML = '';
27802 var canvasEl = document.createElement("canvas");
27804 var contextEl = canvasEl.getContext("2d");
27806 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27807 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27808 var center = this.imageEl.OriginWidth / 2;
27810 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27811 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27812 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27813 center = this.imageEl.OriginHeight / 2;
27816 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27818 contextEl.translate(center, center);
27819 contextEl.rotate(this.rotate * Math.PI / 180);
27821 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27823 this.canvasEl = document.createElement("canvas");
27825 this.contextEl = this.canvasEl.getContext("2d");
27827 switch (this.rotate) {
27830 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27831 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27833 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27838 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27839 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27841 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27842 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);
27846 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27851 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27852 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27854 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27855 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);
27859 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);
27864 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27865 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27867 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27868 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27872 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);
27879 this.previewEl.appendChild(this.canvasEl);
27881 this.setCanvasPosition();
27886 if(!this.canvasLoaded){
27890 var imageCanvas = document.createElement("canvas");
27892 var imageContext = imageCanvas.getContext("2d");
27894 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27895 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27897 var center = imageCanvas.width / 2;
27899 imageContext.translate(center, center);
27901 imageContext.rotate(this.rotate * Math.PI / 180);
27903 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27905 var canvas = document.createElement("canvas");
27907 var context = canvas.getContext("2d");
27909 canvas.width = this.minWidth;
27910 canvas.height = this.minHeight;
27912 switch (this.rotate) {
27915 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27916 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27918 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27919 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27921 var targetWidth = this.minWidth - 2 * x;
27922 var targetHeight = this.minHeight - 2 * y;
27926 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27927 scale = targetWidth / width;
27930 if(x > 0 && y == 0){
27931 scale = targetHeight / height;
27934 if(x > 0 && y > 0){
27935 scale = targetWidth / width;
27937 if(width < height){
27938 scale = targetHeight / height;
27942 context.scale(scale, scale);
27944 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27945 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27947 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27948 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27950 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27955 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27956 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27958 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27959 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961 var targetWidth = this.minWidth - 2 * x;
27962 var targetHeight = this.minHeight - 2 * y;
27966 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27967 scale = targetWidth / width;
27970 if(x > 0 && y == 0){
27971 scale = targetHeight / height;
27974 if(x > 0 && y > 0){
27975 scale = targetWidth / width;
27977 if(width < height){
27978 scale = targetHeight / height;
27982 context.scale(scale, scale);
27984 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27985 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27988 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27992 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27997 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27998 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28000 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28001 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28003 var targetWidth = this.minWidth - 2 * x;
28004 var targetHeight = this.minHeight - 2 * y;
28008 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28009 scale = targetWidth / width;
28012 if(x > 0 && y == 0){
28013 scale = targetHeight / height;
28016 if(x > 0 && y > 0){
28017 scale = targetWidth / width;
28019 if(width < height){
28020 scale = targetHeight / height;
28024 context.scale(scale, scale);
28026 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28027 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28029 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28030 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28032 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28033 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28035 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28040 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28041 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28043 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28044 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28046 var targetWidth = this.minWidth - 2 * x;
28047 var targetHeight = this.minHeight - 2 * y;
28051 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28052 scale = targetWidth / width;
28055 if(x > 0 && y == 0){
28056 scale = targetHeight / height;
28059 if(x > 0 && y > 0){
28060 scale = targetWidth / width;
28062 if(width < height){
28063 scale = targetHeight / height;
28067 context.scale(scale, scale);
28069 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28070 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28072 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28073 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28075 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28077 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28084 this.cropData = canvas.toDataURL(this.cropType);
28086 if(this.fireEvent('crop', this, this.cropData) !== false){
28087 this.process(this.file, this.cropData);
28094 setThumbBoxSize : function()
28098 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28099 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28100 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28102 this.minWidth = width;
28103 this.minHeight = height;
28105 if(this.rotate == 90 || this.rotate == 270){
28106 this.minWidth = height;
28107 this.minHeight = width;
28112 width = Math.ceil(this.minWidth * height / this.minHeight);
28114 if(this.minWidth > this.minHeight){
28116 height = Math.ceil(this.minHeight * width / this.minWidth);
28119 this.thumbEl.setStyle({
28120 width : width + 'px',
28121 height : height + 'px'
28128 setThumbBoxPosition : function()
28130 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28131 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28133 this.thumbEl.setLeft(x);
28134 this.thumbEl.setTop(y);
28138 baseRotateLevel : function()
28140 this.baseRotate = 1;
28143 typeof(this.exif) != 'undefined' &&
28144 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28145 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28147 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28150 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28154 baseScaleLevel : function()
28158 if(this.isDocument){
28160 if(this.baseRotate == 6 || this.baseRotate == 8){
28162 height = this.thumbEl.getHeight();
28163 this.baseScale = height / this.imageEl.OriginWidth;
28165 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28166 width = this.thumbEl.getWidth();
28167 this.baseScale = width / this.imageEl.OriginHeight;
28173 height = this.thumbEl.getHeight();
28174 this.baseScale = height / this.imageEl.OriginHeight;
28176 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28177 width = this.thumbEl.getWidth();
28178 this.baseScale = width / this.imageEl.OriginWidth;
28184 if(this.baseRotate == 6 || this.baseRotate == 8){
28186 width = this.thumbEl.getHeight();
28187 this.baseScale = width / this.imageEl.OriginHeight;
28189 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28190 height = this.thumbEl.getWidth();
28191 this.baseScale = height / this.imageEl.OriginHeight;
28194 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28195 height = this.thumbEl.getWidth();
28196 this.baseScale = height / this.imageEl.OriginHeight;
28198 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28199 width = this.thumbEl.getHeight();
28200 this.baseScale = width / this.imageEl.OriginWidth;
28207 width = this.thumbEl.getWidth();
28208 this.baseScale = width / this.imageEl.OriginWidth;
28210 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28211 height = this.thumbEl.getHeight();
28212 this.baseScale = height / this.imageEl.OriginHeight;
28215 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28217 height = this.thumbEl.getHeight();
28218 this.baseScale = height / this.imageEl.OriginHeight;
28220 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28221 width = this.thumbEl.getWidth();
28222 this.baseScale = width / this.imageEl.OriginWidth;
28230 getScaleLevel : function()
28232 return this.baseScale * Math.pow(1.1, this.scale);
28235 onTouchStart : function(e)
28237 if(!this.canvasLoaded){
28238 this.beforeSelectFile(e);
28242 var touches = e.browserEvent.touches;
28248 if(touches.length == 1){
28249 this.onMouseDown(e);
28253 if(touches.length != 2){
28259 for(var i = 0, finger; finger = touches[i]; i++){
28260 coords.push(finger.pageX, finger.pageY);
28263 var x = Math.pow(coords[0] - coords[2], 2);
28264 var y = Math.pow(coords[1] - coords[3], 2);
28266 this.startDistance = Math.sqrt(x + y);
28268 this.startScale = this.scale;
28270 this.pinching = true;
28271 this.dragable = false;
28275 onTouchMove : function(e)
28277 if(!this.pinching && !this.dragable){
28281 var touches = e.browserEvent.touches;
28288 this.onMouseMove(e);
28294 for(var i = 0, finger; finger = touches[i]; i++){
28295 coords.push(finger.pageX, finger.pageY);
28298 var x = Math.pow(coords[0] - coords[2], 2);
28299 var y = Math.pow(coords[1] - coords[3], 2);
28301 this.endDistance = Math.sqrt(x + y);
28303 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28305 if(!this.zoomable()){
28306 this.scale = this.startScale;
28314 onTouchEnd : function(e)
28316 this.pinching = false;
28317 this.dragable = false;
28321 process : function(file, crop)
28324 this.maskEl.mask(this.loadingText);
28327 this.xhr = new XMLHttpRequest();
28329 file.xhr = this.xhr;
28331 this.xhr.open(this.method, this.url, true);
28334 "Accept": "application/json",
28335 "Cache-Control": "no-cache",
28336 "X-Requested-With": "XMLHttpRequest"
28339 for (var headerName in headers) {
28340 var headerValue = headers[headerName];
28342 this.xhr.setRequestHeader(headerName, headerValue);
28348 this.xhr.onload = function()
28350 _this.xhrOnLoad(_this.xhr);
28353 this.xhr.onerror = function()
28355 _this.xhrOnError(_this.xhr);
28358 var formData = new FormData();
28360 formData.append('returnHTML', 'NO');
28363 formData.append('crop', crop);
28366 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28367 formData.append(this.paramName, file, file.name);
28370 if(typeof(file.filename) != 'undefined'){
28371 formData.append('filename', file.filename);
28374 if(typeof(file.mimetype) != 'undefined'){
28375 formData.append('mimetype', file.mimetype);
28378 if(this.fireEvent('arrange', this, formData) != false){
28379 this.xhr.send(formData);
28383 xhrOnLoad : function(xhr)
28386 this.maskEl.unmask();
28389 if (xhr.readyState !== 4) {
28390 this.fireEvent('exception', this, xhr);
28394 var response = Roo.decode(xhr.responseText);
28396 if(!response.success){
28397 this.fireEvent('exception', this, xhr);
28401 var response = Roo.decode(xhr.responseText);
28403 this.fireEvent('upload', this, response);
28407 xhrOnError : function()
28410 this.maskEl.unmask();
28413 Roo.log('xhr on error');
28415 var response = Roo.decode(xhr.responseText);
28421 prepare : function(file)
28424 this.maskEl.mask(this.loadingText);
28430 if(typeof(file) === 'string'){
28431 this.loadCanvas(file);
28435 if(!file || !this.urlAPI){
28440 this.cropType = file.type;
28444 if(this.fireEvent('prepare', this, this.file) != false){
28446 var reader = new FileReader();
28448 reader.onload = function (e) {
28449 if (e.target.error) {
28450 Roo.log(e.target.error);
28454 var buffer = e.target.result,
28455 dataView = new DataView(buffer),
28457 maxOffset = dataView.byteLength - 4,
28461 if (dataView.getUint16(0) === 0xffd8) {
28462 while (offset < maxOffset) {
28463 markerBytes = dataView.getUint16(offset);
28465 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28466 markerLength = dataView.getUint16(offset + 2) + 2;
28467 if (offset + markerLength > dataView.byteLength) {
28468 Roo.log('Invalid meta data: Invalid segment size.');
28472 if(markerBytes == 0xffe1){
28473 _this.parseExifData(
28480 offset += markerLength;
28490 var url = _this.urlAPI.createObjectURL(_this.file);
28492 _this.loadCanvas(url);
28497 reader.readAsArrayBuffer(this.file);
28503 parseExifData : function(dataView, offset, length)
28505 var tiffOffset = offset + 10,
28509 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28510 // No Exif data, might be XMP data instead
28514 // Check for the ASCII code for "Exif" (0x45786966):
28515 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28516 // No Exif data, might be XMP data instead
28519 if (tiffOffset + 8 > dataView.byteLength) {
28520 Roo.log('Invalid Exif data: Invalid segment size.');
28523 // Check for the two null bytes:
28524 if (dataView.getUint16(offset + 8) !== 0x0000) {
28525 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28528 // Check the byte alignment:
28529 switch (dataView.getUint16(tiffOffset)) {
28531 littleEndian = true;
28534 littleEndian = false;
28537 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28540 // Check for the TIFF tag marker (0x002A):
28541 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28542 Roo.log('Invalid Exif data: Missing TIFF marker.');
28545 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28546 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28548 this.parseExifTags(
28551 tiffOffset + dirOffset,
28556 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28561 if (dirOffset + 6 > dataView.byteLength) {
28562 Roo.log('Invalid Exif data: Invalid directory offset.');
28565 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28566 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28567 if (dirEndOffset + 4 > dataView.byteLength) {
28568 Roo.log('Invalid Exif data: Invalid directory size.');
28571 for (i = 0; i < tagsNumber; i += 1) {
28575 dirOffset + 2 + 12 * i, // tag offset
28579 // Return the offset to the next directory:
28580 return dataView.getUint32(dirEndOffset, littleEndian);
28583 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28585 var tag = dataView.getUint16(offset, littleEndian);
28587 this.exif[tag] = this.getExifValue(
28591 dataView.getUint16(offset + 2, littleEndian), // tag type
28592 dataView.getUint32(offset + 4, littleEndian), // tag length
28597 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28599 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28608 Roo.log('Invalid Exif data: Invalid tag type.');
28612 tagSize = tagType.size * length;
28613 // Determine if the value is contained in the dataOffset bytes,
28614 // or if the value at the dataOffset is a pointer to the actual data:
28615 dataOffset = tagSize > 4 ?
28616 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28617 if (dataOffset + tagSize > dataView.byteLength) {
28618 Roo.log('Invalid Exif data: Invalid data offset.');
28621 if (length === 1) {
28622 return tagType.getValue(dataView, dataOffset, littleEndian);
28625 for (i = 0; i < length; i += 1) {
28626 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28629 if (tagType.ascii) {
28631 // Concatenate the chars:
28632 for (i = 0; i < values.length; i += 1) {
28634 // Ignore the terminating NULL byte(s):
28635 if (c === '\u0000') {
28647 Roo.apply(Roo.bootstrap.UploadCropbox, {
28649 'Orientation': 0x0112
28653 1: 0, //'top-left',
28655 3: 180, //'bottom-right',
28656 // 4: 'bottom-left',
28658 6: 90, //'right-top',
28659 // 7: 'right-bottom',
28660 8: 270 //'left-bottom'
28664 // byte, 8-bit unsigned int:
28666 getValue: function (dataView, dataOffset) {
28667 return dataView.getUint8(dataOffset);
28671 // ascii, 8-bit byte:
28673 getValue: function (dataView, dataOffset) {
28674 return String.fromCharCode(dataView.getUint8(dataOffset));
28679 // short, 16 bit int:
28681 getValue: function (dataView, dataOffset, littleEndian) {
28682 return dataView.getUint16(dataOffset, littleEndian);
28686 // long, 32 bit int:
28688 getValue: function (dataView, dataOffset, littleEndian) {
28689 return dataView.getUint32(dataOffset, littleEndian);
28693 // rational = two long values, first is numerator, second is denominator:
28695 getValue: function (dataView, dataOffset, littleEndian) {
28696 return dataView.getUint32(dataOffset, littleEndian) /
28697 dataView.getUint32(dataOffset + 4, littleEndian);
28701 // slong, 32 bit signed int:
28703 getValue: function (dataView, dataOffset, littleEndian) {
28704 return dataView.getInt32(dataOffset, littleEndian);
28708 // srational, two slongs, first is numerator, second is denominator:
28710 getValue: function (dataView, dataOffset, littleEndian) {
28711 return dataView.getInt32(dataOffset, littleEndian) /
28712 dataView.getInt32(dataOffset + 4, littleEndian);
28722 cls : 'btn-group roo-upload-cropbox-rotate-left',
28723 action : 'rotate-left',
28727 cls : 'btn btn-default',
28728 html : '<i class="fa fa-undo"></i>'
28734 cls : 'btn-group roo-upload-cropbox-picture',
28735 action : 'picture',
28739 cls : 'btn btn-default',
28740 html : '<i class="fa fa-picture-o"></i>'
28746 cls : 'btn-group roo-upload-cropbox-rotate-right',
28747 action : 'rotate-right',
28751 cls : 'btn btn-default',
28752 html : '<i class="fa fa-repeat"></i>'
28760 cls : 'btn-group roo-upload-cropbox-rotate-left',
28761 action : 'rotate-left',
28765 cls : 'btn btn-default',
28766 html : '<i class="fa fa-undo"></i>'
28772 cls : 'btn-group roo-upload-cropbox-download',
28773 action : 'download',
28777 cls : 'btn btn-default',
28778 html : '<i class="fa fa-download"></i>'
28784 cls : 'btn-group roo-upload-cropbox-crop',
28789 cls : 'btn btn-default',
28790 html : '<i class="fa fa-crop"></i>'
28796 cls : 'btn-group roo-upload-cropbox-trash',
28801 cls : 'btn btn-default',
28802 html : '<i class="fa fa-trash"></i>'
28808 cls : 'btn-group roo-upload-cropbox-rotate-right',
28809 action : 'rotate-right',
28813 cls : 'btn btn-default',
28814 html : '<i class="fa fa-repeat"></i>'
28822 cls : 'btn-group roo-upload-cropbox-rotate-left',
28823 action : 'rotate-left',
28827 cls : 'btn btn-default',
28828 html : '<i class="fa fa-undo"></i>'
28834 cls : 'btn-group roo-upload-cropbox-rotate-right',
28835 action : 'rotate-right',
28839 cls : 'btn btn-default',
28840 html : '<i class="fa fa-repeat"></i>'
28853 * @class Roo.bootstrap.DocumentManager
28854 * @extends Roo.bootstrap.Component
28855 * Bootstrap DocumentManager class
28856 * @cfg {String} paramName default 'imageUpload'
28857 * @cfg {String} toolTipName default 'filename'
28858 * @cfg {String} method default POST
28859 * @cfg {String} url action url
28860 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28861 * @cfg {Boolean} multiple multiple upload default true
28862 * @cfg {Number} thumbSize default 300
28863 * @cfg {String} fieldLabel
28864 * @cfg {Number} labelWidth default 4
28865 * @cfg {String} labelAlign (left|top) default left
28866 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28867 * @cfg {Number} labellg set the width of label (1-12)
28868 * @cfg {Number} labelmd set the width of label (1-12)
28869 * @cfg {Number} labelsm set the width of label (1-12)
28870 * @cfg {Number} labelxs set the width of label (1-12)
28873 * Create a new DocumentManager
28874 * @param {Object} config The config object
28877 Roo.bootstrap.DocumentManager = function(config){
28878 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28881 this.delegates = [];
28886 * Fire when initial the DocumentManager
28887 * @param {Roo.bootstrap.DocumentManager} this
28892 * inspect selected file
28893 * @param {Roo.bootstrap.DocumentManager} this
28894 * @param {File} file
28899 * Fire when xhr load exception
28900 * @param {Roo.bootstrap.DocumentManager} this
28901 * @param {XMLHttpRequest} xhr
28903 "exception" : true,
28905 * @event afterupload
28906 * Fire when xhr load exception
28907 * @param {Roo.bootstrap.DocumentManager} this
28908 * @param {XMLHttpRequest} xhr
28910 "afterupload" : true,
28913 * prepare the form data
28914 * @param {Roo.bootstrap.DocumentManager} this
28915 * @param {Object} formData
28920 * Fire when remove the file
28921 * @param {Roo.bootstrap.DocumentManager} this
28922 * @param {Object} file
28927 * Fire after refresh the file
28928 * @param {Roo.bootstrap.DocumentManager} this
28933 * Fire after click the image
28934 * @param {Roo.bootstrap.DocumentManager} this
28935 * @param {Object} file
28940 * Fire when upload a image and editable set to true
28941 * @param {Roo.bootstrap.DocumentManager} this
28942 * @param {Object} file
28946 * @event beforeselectfile
28947 * Fire before select file
28948 * @param {Roo.bootstrap.DocumentManager} this
28950 "beforeselectfile" : true,
28953 * Fire before process file
28954 * @param {Roo.bootstrap.DocumentManager} this
28955 * @param {Object} file
28959 * @event previewrendered
28960 * Fire when preview rendered
28961 * @param {Roo.bootstrap.DocumentManager} this
28962 * @param {Object} file
28964 "previewrendered" : true,
28967 "previewResize" : true
28972 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28981 paramName : 'imageUpload',
28982 toolTipName : 'filename',
28985 labelAlign : 'left',
28995 getAutoCreate : function()
28997 var managerWidget = {
28999 cls : 'roo-document-manager',
29003 cls : 'roo-document-manager-selector',
29008 cls : 'roo-document-manager-uploader',
29012 cls : 'roo-document-manager-upload-btn',
29013 html : '<i class="fa fa-plus"></i>'
29024 cls : 'column col-md-12',
29029 if(this.fieldLabel.length){
29034 cls : 'column col-md-12',
29035 html : this.fieldLabel
29039 cls : 'column col-md-12',
29044 if(this.labelAlign == 'left'){
29049 html : this.fieldLabel
29058 if(this.labelWidth > 12){
29059 content[0].style = "width: " + this.labelWidth + 'px';
29062 if(this.labelWidth < 13 && this.labelmd == 0){
29063 this.labelmd = this.labelWidth;
29066 if(this.labellg > 0){
29067 content[0].cls += ' col-lg-' + this.labellg;
29068 content[1].cls += ' col-lg-' + (12 - this.labellg);
29071 if(this.labelmd > 0){
29072 content[0].cls += ' col-md-' + this.labelmd;
29073 content[1].cls += ' col-md-' + (12 - this.labelmd);
29076 if(this.labelsm > 0){
29077 content[0].cls += ' col-sm-' + this.labelsm;
29078 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29081 if(this.labelxs > 0){
29082 content[0].cls += ' col-xs-' + this.labelxs;
29083 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29091 cls : 'row clearfix',
29099 initEvents : function()
29101 this.managerEl = this.el.select('.roo-document-manager', true).first();
29102 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29104 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29105 this.selectorEl.hide();
29108 this.selectorEl.attr('multiple', 'multiple');
29111 this.selectorEl.on('change', this.onFileSelected, this);
29113 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29114 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29116 this.uploader.on('click', this.onUploaderClick, this);
29118 this.renderProgressDialog();
29122 window.addEventListener("resize", function() { _this.refresh(); } );
29124 this.fireEvent('initial', this);
29127 renderProgressDialog : function()
29131 this.progressDialog = new Roo.bootstrap.Modal({
29132 cls : 'roo-document-manager-progress-dialog',
29133 allow_close : false,
29143 btnclick : function() {
29144 _this.uploadCancel();
29150 this.progressDialog.render(Roo.get(document.body));
29152 this.progress = new Roo.bootstrap.Progress({
29153 cls : 'roo-document-manager-progress',
29158 this.progress.render(this.progressDialog.getChildContainer());
29160 this.progressBar = new Roo.bootstrap.ProgressBar({
29161 cls : 'roo-document-manager-progress-bar',
29164 aria_valuemax : 12,
29168 this.progressBar.render(this.progress.getChildContainer());
29171 onUploaderClick : function(e)
29173 e.preventDefault();
29175 if(this.fireEvent('beforeselectfile', this) != false){
29176 this.selectorEl.dom.click();
29181 onFileSelected : function(e)
29183 e.preventDefault();
29185 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29189 Roo.each(this.selectorEl.dom.files, function(file){
29190 if(this.fireEvent('inspect', this, file) != false){
29191 this.files.push(file);
29201 this.selectorEl.dom.value = '';
29203 if(!this.files || !this.files.length){
29207 if(this.boxes > 0 && this.files.length > this.boxes){
29208 this.files = this.files.slice(0, this.boxes);
29211 this.uploader.show();
29213 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29214 this.uploader.hide();
29223 Roo.each(this.files, function(file){
29225 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29226 var f = this.renderPreview(file);
29231 if(file.type.indexOf('image') != -1){
29232 this.delegates.push(
29234 _this.process(file);
29235 }).createDelegate(this)
29243 _this.process(file);
29244 }).createDelegate(this)
29249 this.files = files;
29251 this.delegates = this.delegates.concat(docs);
29253 if(!this.delegates.length){
29258 this.progressBar.aria_valuemax = this.delegates.length;
29265 arrange : function()
29267 if(!this.delegates.length){
29268 this.progressDialog.hide();
29273 var delegate = this.delegates.shift();
29275 this.progressDialog.show();
29277 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29279 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29284 refresh : function()
29286 this.uploader.show();
29288 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29289 this.uploader.hide();
29292 Roo.isTouch ? this.closable(false) : this.closable(true);
29294 this.fireEvent('refresh', this);
29297 onRemove : function(e, el, o)
29299 e.preventDefault();
29301 this.fireEvent('remove', this, o);
29305 remove : function(o)
29309 Roo.each(this.files, function(file){
29310 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29319 this.files = files;
29326 Roo.each(this.files, function(file){
29331 file.target.remove();
29340 onClick : function(e, el, o)
29342 e.preventDefault();
29344 this.fireEvent('click', this, o);
29348 closable : function(closable)
29350 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29352 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29364 xhrOnLoad : function(xhr)
29366 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29370 if (xhr.readyState !== 4) {
29372 this.fireEvent('exception', this, xhr);
29376 var response = Roo.decode(xhr.responseText);
29378 if(!response.success){
29380 this.fireEvent('exception', this, xhr);
29384 var file = this.renderPreview(response.data);
29386 this.files.push(file);
29390 this.fireEvent('afterupload', this, xhr);
29394 xhrOnError : function(xhr)
29396 Roo.log('xhr on error');
29398 var response = Roo.decode(xhr.responseText);
29405 process : function(file)
29407 if(this.fireEvent('process', this, file) !== false){
29408 if(this.editable && file.type.indexOf('image') != -1){
29409 this.fireEvent('edit', this, file);
29413 this.uploadStart(file, false);
29420 uploadStart : function(file, crop)
29422 this.xhr = new XMLHttpRequest();
29424 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29429 file.xhr = this.xhr;
29431 this.managerEl.createChild({
29433 cls : 'roo-document-manager-loading',
29437 tooltip : file.name,
29438 cls : 'roo-document-manager-thumb',
29439 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29445 this.xhr.open(this.method, this.url, true);
29448 "Accept": "application/json",
29449 "Cache-Control": "no-cache",
29450 "X-Requested-With": "XMLHttpRequest"
29453 for (var headerName in headers) {
29454 var headerValue = headers[headerName];
29456 this.xhr.setRequestHeader(headerName, headerValue);
29462 this.xhr.onload = function()
29464 _this.xhrOnLoad(_this.xhr);
29467 this.xhr.onerror = function()
29469 _this.xhrOnError(_this.xhr);
29472 var formData = new FormData();
29474 formData.append('returnHTML', 'NO');
29477 formData.append('crop', crop);
29480 formData.append(this.paramName, file, file.name);
29487 if(this.fireEvent('prepare', this, formData, options) != false){
29489 if(options.manually){
29493 this.xhr.send(formData);
29497 this.uploadCancel();
29500 uploadCancel : function()
29506 this.delegates = [];
29508 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29515 renderPreview : function(file)
29517 if(typeof(file.target) != 'undefined' && file.target){
29521 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29523 var previewEl = this.managerEl.createChild({
29525 cls : 'roo-document-manager-preview',
29529 tooltip : file[this.toolTipName],
29530 cls : 'roo-document-manager-thumb',
29531 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29536 html : '<i class="fa fa-times-circle"></i>'
29541 var close = previewEl.select('button.close', true).first();
29543 close.on('click', this.onRemove, this, file);
29545 file.target = previewEl;
29547 var image = previewEl.select('img', true).first();
29551 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29553 image.on('click', this.onClick, this, file);
29555 this.fireEvent('previewrendered', this, file);
29561 onPreviewLoad : function(file, image)
29563 if(typeof(file.target) == 'undefined' || !file.target){
29567 var width = image.dom.naturalWidth || image.dom.width;
29568 var height = image.dom.naturalHeight || image.dom.height;
29570 if(!this.previewResize) {
29574 if(width > height){
29575 file.target.addClass('wide');
29579 file.target.addClass('tall');
29584 uploadFromSource : function(file, crop)
29586 this.xhr = new XMLHttpRequest();
29588 this.managerEl.createChild({
29590 cls : 'roo-document-manager-loading',
29594 tooltip : file.name,
29595 cls : 'roo-document-manager-thumb',
29596 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29602 this.xhr.open(this.method, this.url, true);
29605 "Accept": "application/json",
29606 "Cache-Control": "no-cache",
29607 "X-Requested-With": "XMLHttpRequest"
29610 for (var headerName in headers) {
29611 var headerValue = headers[headerName];
29613 this.xhr.setRequestHeader(headerName, headerValue);
29619 this.xhr.onload = function()
29621 _this.xhrOnLoad(_this.xhr);
29624 this.xhr.onerror = function()
29626 _this.xhrOnError(_this.xhr);
29629 var formData = new FormData();
29631 formData.append('returnHTML', 'NO');
29633 formData.append('crop', crop);
29635 if(typeof(file.filename) != 'undefined'){
29636 formData.append('filename', file.filename);
29639 if(typeof(file.mimetype) != 'undefined'){
29640 formData.append('mimetype', file.mimetype);
29645 if(this.fireEvent('prepare', this, formData) != false){
29646 this.xhr.send(formData);
29656 * @class Roo.bootstrap.DocumentViewer
29657 * @extends Roo.bootstrap.Component
29658 * Bootstrap DocumentViewer class
29659 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29660 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29663 * Create a new DocumentViewer
29664 * @param {Object} config The config object
29667 Roo.bootstrap.DocumentViewer = function(config){
29668 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29673 * Fire after initEvent
29674 * @param {Roo.bootstrap.DocumentViewer} this
29680 * @param {Roo.bootstrap.DocumentViewer} this
29685 * Fire after download button
29686 * @param {Roo.bootstrap.DocumentViewer} this
29691 * Fire after trash button
29692 * @param {Roo.bootstrap.DocumentViewer} this
29699 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29701 showDownload : true,
29705 getAutoCreate : function()
29709 cls : 'roo-document-viewer',
29713 cls : 'roo-document-viewer-body',
29717 cls : 'roo-document-viewer-thumb',
29721 cls : 'roo-document-viewer-image'
29729 cls : 'roo-document-viewer-footer',
29732 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29736 cls : 'btn-group roo-document-viewer-download',
29740 cls : 'btn btn-default',
29741 html : '<i class="fa fa-download"></i>'
29747 cls : 'btn-group roo-document-viewer-trash',
29751 cls : 'btn btn-default',
29752 html : '<i class="fa fa-trash"></i>'
29765 initEvents : function()
29767 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29768 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29770 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29771 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29773 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29774 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29776 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29777 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29779 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29780 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29782 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29783 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29785 this.bodyEl.on('click', this.onClick, this);
29786 this.downloadBtn.on('click', this.onDownload, this);
29787 this.trashBtn.on('click', this.onTrash, this);
29789 this.downloadBtn.hide();
29790 this.trashBtn.hide();
29792 if(this.showDownload){
29793 this.downloadBtn.show();
29796 if(this.showTrash){
29797 this.trashBtn.show();
29800 if(!this.showDownload && !this.showTrash) {
29801 this.footerEl.hide();
29806 initial : function()
29808 this.fireEvent('initial', this);
29812 onClick : function(e)
29814 e.preventDefault();
29816 this.fireEvent('click', this);
29819 onDownload : function(e)
29821 e.preventDefault();
29823 this.fireEvent('download', this);
29826 onTrash : function(e)
29828 e.preventDefault();
29830 this.fireEvent('trash', this);
29842 * @class Roo.bootstrap.NavProgressBar
29843 * @extends Roo.bootstrap.Component
29844 * Bootstrap NavProgressBar class
29847 * Create a new nav progress bar
29848 * @param {Object} config The config object
29851 Roo.bootstrap.NavProgressBar = function(config){
29852 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29854 this.bullets = this.bullets || [];
29856 // Roo.bootstrap.NavProgressBar.register(this);
29860 * Fires when the active item changes
29861 * @param {Roo.bootstrap.NavProgressBar} this
29862 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29863 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29870 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29875 getAutoCreate : function()
29877 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29881 cls : 'roo-navigation-bar-group',
29885 cls : 'roo-navigation-top-bar'
29889 cls : 'roo-navigation-bullets-bar',
29893 cls : 'roo-navigation-bar'
29900 cls : 'roo-navigation-bottom-bar'
29910 initEvents: function()
29915 onRender : function(ct, position)
29917 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29919 if(this.bullets.length){
29920 Roo.each(this.bullets, function(b){
29929 addItem : function(cfg)
29931 var item = new Roo.bootstrap.NavProgressItem(cfg);
29933 item.parentId = this.id;
29934 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29937 var top = new Roo.bootstrap.Element({
29939 cls : 'roo-navigation-bar-text'
29942 var bottom = new Roo.bootstrap.Element({
29944 cls : 'roo-navigation-bar-text'
29947 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29948 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29950 var topText = new Roo.bootstrap.Element({
29952 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29955 var bottomText = new Roo.bootstrap.Element({
29957 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29960 topText.onRender(top.el, null);
29961 bottomText.onRender(bottom.el, null);
29964 item.bottomEl = bottom;
29967 this.barItems.push(item);
29972 getActive : function()
29974 var active = false;
29976 Roo.each(this.barItems, function(v){
29978 if (!v.isActive()) {
29990 setActiveItem : function(item)
29994 Roo.each(this.barItems, function(v){
29995 if (v.rid == item.rid) {
29999 if (v.isActive()) {
30000 v.setActive(false);
30005 item.setActive(true);
30007 this.fireEvent('changed', this, item, prev);
30010 getBarItem: function(rid)
30014 Roo.each(this.barItems, function(e) {
30015 if (e.rid != rid) {
30026 indexOfItem : function(item)
30030 Roo.each(this.barItems, function(v, i){
30032 if (v.rid != item.rid) {
30043 setActiveNext : function()
30045 var i = this.indexOfItem(this.getActive());
30047 if (i > this.barItems.length) {
30051 this.setActiveItem(this.barItems[i+1]);
30054 setActivePrev : function()
30056 var i = this.indexOfItem(this.getActive());
30062 this.setActiveItem(this.barItems[i-1]);
30065 format : function()
30067 if(!this.barItems.length){
30071 var width = 100 / this.barItems.length;
30073 Roo.each(this.barItems, function(i){
30074 i.el.setStyle('width', width + '%');
30075 i.topEl.el.setStyle('width', width + '%');
30076 i.bottomEl.el.setStyle('width', width + '%');
30085 * Nav Progress Item
30090 * @class Roo.bootstrap.NavProgressItem
30091 * @extends Roo.bootstrap.Component
30092 * Bootstrap NavProgressItem class
30093 * @cfg {String} rid the reference id
30094 * @cfg {Boolean} active (true|false) Is item active default false
30095 * @cfg {Boolean} disabled (true|false) Is item active default false
30096 * @cfg {String} html
30097 * @cfg {String} position (top|bottom) text position default bottom
30098 * @cfg {String} icon show icon instead of number
30101 * Create a new NavProgressItem
30102 * @param {Object} config The config object
30104 Roo.bootstrap.NavProgressItem = function(config){
30105 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30110 * The raw click event for the entire grid.
30111 * @param {Roo.bootstrap.NavProgressItem} this
30112 * @param {Roo.EventObject} e
30119 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30125 position : 'bottom',
30128 getAutoCreate : function()
30130 var iconCls = 'roo-navigation-bar-item-icon';
30132 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30136 cls: 'roo-navigation-bar-item',
30146 cfg.cls += ' active';
30149 cfg.cls += ' disabled';
30155 disable : function()
30157 this.setDisabled(true);
30160 enable : function()
30162 this.setDisabled(false);
30165 initEvents: function()
30167 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30169 this.iconEl.on('click', this.onClick, this);
30172 onClick : function(e)
30174 e.preventDefault();
30180 if(this.fireEvent('click', this, e) === false){
30184 this.parent().setActiveItem(this);
30187 isActive: function ()
30189 return this.active;
30192 setActive : function(state)
30194 if(this.active == state){
30198 this.active = state;
30201 this.el.addClass('active');
30205 this.el.removeClass('active');
30210 setDisabled : function(state)
30212 if(this.disabled == state){
30216 this.disabled = state;
30219 this.el.addClass('disabled');
30223 this.el.removeClass('disabled');
30226 tooltipEl : function()
30228 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30241 * @class Roo.bootstrap.FieldLabel
30242 * @extends Roo.bootstrap.Component
30243 * Bootstrap FieldLabel class
30244 * @cfg {String} html contents of the element
30245 * @cfg {String} tag tag of the element default label
30246 * @cfg {String} cls class of the element
30247 * @cfg {String} target label target
30248 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30249 * @cfg {String} invalidClass default "text-warning"
30250 * @cfg {String} validClass default "text-success"
30251 * @cfg {String} iconTooltip default "This field is required"
30252 * @cfg {String} indicatorpos (left|right) default left
30255 * Create a new FieldLabel
30256 * @param {Object} config The config object
30259 Roo.bootstrap.FieldLabel = function(config){
30260 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30265 * Fires after the field has been marked as invalid.
30266 * @param {Roo.form.FieldLabel} this
30267 * @param {String} msg The validation message
30272 * Fires after the field has been validated with no errors.
30273 * @param {Roo.form.FieldLabel} this
30279 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30286 invalidClass : 'has-warning',
30287 validClass : 'has-success',
30288 iconTooltip : 'This field is required',
30289 indicatorpos : 'left',
30291 getAutoCreate : function(){
30294 if (!this.allowBlank) {
30300 cls : 'roo-bootstrap-field-label ' + this.cls,
30305 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30306 tooltip : this.iconTooltip
30315 if(this.indicatorpos == 'right'){
30318 cls : 'roo-bootstrap-field-label ' + this.cls,
30327 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30328 tooltip : this.iconTooltip
30337 initEvents: function()
30339 Roo.bootstrap.Element.superclass.initEvents.call(this);
30341 this.indicator = this.indicatorEl();
30343 if(this.indicator){
30344 this.indicator.removeClass('visible');
30345 this.indicator.addClass('invisible');
30348 Roo.bootstrap.FieldLabel.register(this);
30351 indicatorEl : function()
30353 var indicator = this.el.select('i.roo-required-indicator',true).first();
30364 * Mark this field as valid
30366 markValid : function()
30368 if(this.indicator){
30369 this.indicator.removeClass('visible');
30370 this.indicator.addClass('invisible');
30373 this.el.removeClass(this.invalidClass);
30375 this.el.addClass(this.validClass);
30377 this.fireEvent('valid', this);
30381 * Mark this field as invalid
30382 * @param {String} msg The validation message
30384 markInvalid : function(msg)
30386 if(this.indicator){
30387 this.indicator.removeClass('invisible');
30388 this.indicator.addClass('visible');
30391 this.el.removeClass(this.validClass);
30393 this.el.addClass(this.invalidClass);
30395 this.fireEvent('invalid', this, msg);
30401 Roo.apply(Roo.bootstrap.FieldLabel, {
30406 * register a FieldLabel Group
30407 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30409 register : function(label)
30411 if(this.groups.hasOwnProperty(label.target)){
30415 this.groups[label.target] = label;
30419 * fetch a FieldLabel Group based on the target
30420 * @param {string} target
30421 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30423 get: function(target) {
30424 if (typeof(this.groups[target]) == 'undefined') {
30428 return this.groups[target] ;
30437 * page DateSplitField.
30443 * @class Roo.bootstrap.DateSplitField
30444 * @extends Roo.bootstrap.Component
30445 * Bootstrap DateSplitField class
30446 * @cfg {string} fieldLabel - the label associated
30447 * @cfg {Number} labelWidth set the width of label (0-12)
30448 * @cfg {String} labelAlign (top|left)
30449 * @cfg {Boolean} dayAllowBlank (true|false) default false
30450 * @cfg {Boolean} monthAllowBlank (true|false) default false
30451 * @cfg {Boolean} yearAllowBlank (true|false) default false
30452 * @cfg {string} dayPlaceholder
30453 * @cfg {string} monthPlaceholder
30454 * @cfg {string} yearPlaceholder
30455 * @cfg {string} dayFormat default 'd'
30456 * @cfg {string} monthFormat default 'm'
30457 * @cfg {string} yearFormat default 'Y'
30458 * @cfg {Number} labellg set the width of label (1-12)
30459 * @cfg {Number} labelmd set the width of label (1-12)
30460 * @cfg {Number} labelsm set the width of label (1-12)
30461 * @cfg {Number} labelxs set the width of label (1-12)
30465 * Create a new DateSplitField
30466 * @param {Object} config The config object
30469 Roo.bootstrap.DateSplitField = function(config){
30470 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30476 * getting the data of years
30477 * @param {Roo.bootstrap.DateSplitField} this
30478 * @param {Object} years
30483 * getting the data of days
30484 * @param {Roo.bootstrap.DateSplitField} this
30485 * @param {Object} days
30490 * Fires after the field has been marked as invalid.
30491 * @param {Roo.form.Field} this
30492 * @param {String} msg The validation message
30497 * Fires after the field has been validated with no errors.
30498 * @param {Roo.form.Field} this
30504 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30507 labelAlign : 'top',
30509 dayAllowBlank : false,
30510 monthAllowBlank : false,
30511 yearAllowBlank : false,
30512 dayPlaceholder : '',
30513 monthPlaceholder : '',
30514 yearPlaceholder : '',
30518 isFormField : true,
30524 getAutoCreate : function()
30528 cls : 'row roo-date-split-field-group',
30533 cls : 'form-hidden-field roo-date-split-field-group-value',
30539 var labelCls = 'col-md-12';
30540 var contentCls = 'col-md-4';
30542 if(this.fieldLabel){
30546 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30550 html : this.fieldLabel
30555 if(this.labelAlign == 'left'){
30557 if(this.labelWidth > 12){
30558 label.style = "width: " + this.labelWidth + 'px';
30561 if(this.labelWidth < 13 && this.labelmd == 0){
30562 this.labelmd = this.labelWidth;
30565 if(this.labellg > 0){
30566 labelCls = ' col-lg-' + this.labellg;
30567 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30570 if(this.labelmd > 0){
30571 labelCls = ' col-md-' + this.labelmd;
30572 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30575 if(this.labelsm > 0){
30576 labelCls = ' col-sm-' + this.labelsm;
30577 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30580 if(this.labelxs > 0){
30581 labelCls = ' col-xs-' + this.labelxs;
30582 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30586 label.cls += ' ' + labelCls;
30588 cfg.cn.push(label);
30591 Roo.each(['day', 'month', 'year'], function(t){
30594 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30601 inputEl: function ()
30603 return this.el.select('.roo-date-split-field-group-value', true).first();
30606 onRender : function(ct, position)
30610 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30612 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30614 this.dayField = new Roo.bootstrap.ComboBox({
30615 allowBlank : this.dayAllowBlank,
30616 alwaysQuery : true,
30617 displayField : 'value',
30620 forceSelection : true,
30622 placeholder : this.dayPlaceholder,
30623 selectOnFocus : true,
30624 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30625 triggerAction : 'all',
30627 valueField : 'value',
30628 store : new Roo.data.SimpleStore({
30629 data : (function() {
30631 _this.fireEvent('days', _this, days);
30634 fields : [ 'value' ]
30637 select : function (_self, record, index)
30639 _this.setValue(_this.getValue());
30644 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30646 this.monthField = new Roo.bootstrap.MonthField({
30647 after : '<i class=\"fa fa-calendar\"></i>',
30648 allowBlank : this.monthAllowBlank,
30649 placeholder : this.monthPlaceholder,
30652 render : function (_self)
30654 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30655 e.preventDefault();
30659 select : function (_self, oldvalue, newvalue)
30661 _this.setValue(_this.getValue());
30666 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30668 this.yearField = new Roo.bootstrap.ComboBox({
30669 allowBlank : this.yearAllowBlank,
30670 alwaysQuery : true,
30671 displayField : 'value',
30674 forceSelection : true,
30676 placeholder : this.yearPlaceholder,
30677 selectOnFocus : true,
30678 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30679 triggerAction : 'all',
30681 valueField : 'value',
30682 store : new Roo.data.SimpleStore({
30683 data : (function() {
30685 _this.fireEvent('years', _this, years);
30688 fields : [ 'value' ]
30691 select : function (_self, record, index)
30693 _this.setValue(_this.getValue());
30698 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30701 setValue : function(v, format)
30703 this.inputEl.dom.value = v;
30705 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30707 var d = Date.parseDate(v, f);
30714 this.setDay(d.format(this.dayFormat));
30715 this.setMonth(d.format(this.monthFormat));
30716 this.setYear(d.format(this.yearFormat));
30723 setDay : function(v)
30725 this.dayField.setValue(v);
30726 this.inputEl.dom.value = this.getValue();
30731 setMonth : function(v)
30733 this.monthField.setValue(v, true);
30734 this.inputEl.dom.value = this.getValue();
30739 setYear : function(v)
30741 this.yearField.setValue(v);
30742 this.inputEl.dom.value = this.getValue();
30747 getDay : function()
30749 return this.dayField.getValue();
30752 getMonth : function()
30754 return this.monthField.getValue();
30757 getYear : function()
30759 return this.yearField.getValue();
30762 getValue : function()
30764 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30766 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30776 this.inputEl.dom.value = '';
30781 validate : function()
30783 var d = this.dayField.validate();
30784 var m = this.monthField.validate();
30785 var y = this.yearField.validate();
30790 (!this.dayAllowBlank && !d) ||
30791 (!this.monthAllowBlank && !m) ||
30792 (!this.yearAllowBlank && !y)
30797 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30806 this.markInvalid();
30811 markValid : function()
30814 var label = this.el.select('label', true).first();
30815 var icon = this.el.select('i.fa-star', true).first();
30821 this.fireEvent('valid', this);
30825 * Mark this field as invalid
30826 * @param {String} msg The validation message
30828 markInvalid : function(msg)
30831 var label = this.el.select('label', true).first();
30832 var icon = this.el.select('i.fa-star', true).first();
30834 if(label && !icon){
30835 this.el.select('.roo-date-split-field-label', true).createChild({
30837 cls : 'text-danger fa fa-lg fa-star',
30838 tooltip : 'This field is required',
30839 style : 'margin-right:5px;'
30843 this.fireEvent('invalid', this, msg);
30846 clearInvalid : function()
30848 var label = this.el.select('label', true).first();
30849 var icon = this.el.select('i.fa-star', true).first();
30855 this.fireEvent('valid', this);
30858 getName: function()
30868 * http://masonry.desandro.com
30870 * The idea is to render all the bricks based on vertical width...
30872 * The original code extends 'outlayer' - we might need to use that....
30878 * @class Roo.bootstrap.LayoutMasonry
30879 * @extends Roo.bootstrap.Component
30880 * Bootstrap Layout Masonry class
30883 * Create a new Element
30884 * @param {Object} config The config object
30887 Roo.bootstrap.LayoutMasonry = function(config){
30889 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30893 Roo.bootstrap.LayoutMasonry.register(this);
30899 * Fire after layout the items
30900 * @param {Roo.bootstrap.LayoutMasonry} this
30901 * @param {Roo.EventObject} e
30908 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30911 * @cfg {Boolean} isLayoutInstant = no animation?
30913 isLayoutInstant : false, // needed?
30916 * @cfg {Number} boxWidth width of the columns
30921 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30926 * @cfg {Number} padWidth padding below box..
30931 * @cfg {Number} gutter gutter width..
30936 * @cfg {Number} maxCols maximum number of columns
30942 * @cfg {Boolean} isAutoInitial defalut true
30944 isAutoInitial : true,
30949 * @cfg {Boolean} isHorizontal defalut false
30951 isHorizontal : false,
30953 currentSize : null,
30959 bricks: null, //CompositeElement
30963 _isLayoutInited : false,
30965 // isAlternative : false, // only use for vertical layout...
30968 * @cfg {Number} alternativePadWidth padding below box..
30970 alternativePadWidth : 50,
30972 selectedBrick : [],
30974 getAutoCreate : function(){
30976 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30980 cls: 'blog-masonary-wrapper ' + this.cls,
30982 cls : 'mas-boxes masonary'
30989 getChildContainer: function( )
30991 if (this.boxesEl) {
30992 return this.boxesEl;
30995 this.boxesEl = this.el.select('.mas-boxes').first();
30997 return this.boxesEl;
31001 initEvents : function()
31005 if(this.isAutoInitial){
31006 Roo.log('hook children rendered');
31007 this.on('childrenrendered', function() {
31008 Roo.log('children rendered');
31014 initial : function()
31016 this.selectedBrick = [];
31018 this.currentSize = this.el.getBox(true);
31020 Roo.EventManager.onWindowResize(this.resize, this);
31022 if(!this.isAutoInitial){
31030 //this.layout.defer(500,this);
31034 resize : function()
31036 var cs = this.el.getBox(true);
31039 this.currentSize.width == cs.width &&
31040 this.currentSize.x == cs.x &&
31041 this.currentSize.height == cs.height &&
31042 this.currentSize.y == cs.y
31044 Roo.log("no change in with or X or Y");
31048 this.currentSize = cs;
31054 layout : function()
31056 this._resetLayout();
31058 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31060 this.layoutItems( isInstant );
31062 this._isLayoutInited = true;
31064 this.fireEvent('layout', this);
31068 _resetLayout : function()
31070 if(this.isHorizontal){
31071 this.horizontalMeasureColumns();
31075 this.verticalMeasureColumns();
31079 verticalMeasureColumns : function()
31081 this.getContainerWidth();
31083 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31084 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31088 var boxWidth = this.boxWidth + this.padWidth;
31090 if(this.containerWidth < this.boxWidth){
31091 boxWidth = this.containerWidth
31094 var containerWidth = this.containerWidth;
31096 var cols = Math.floor(containerWidth / boxWidth);
31098 this.cols = Math.max( cols, 1 );
31100 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31102 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31104 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31106 this.colWidth = boxWidth + avail - this.padWidth;
31108 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31109 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31112 horizontalMeasureColumns : function()
31114 this.getContainerWidth();
31116 var boxWidth = this.boxWidth;
31118 if(this.containerWidth < boxWidth){
31119 boxWidth = this.containerWidth;
31122 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31124 this.el.setHeight(boxWidth);
31128 getContainerWidth : function()
31130 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31133 layoutItems : function( isInstant )
31135 Roo.log(this.bricks);
31137 var items = Roo.apply([], this.bricks);
31139 if(this.isHorizontal){
31140 this._horizontalLayoutItems( items , isInstant );
31144 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31145 // this._verticalAlternativeLayoutItems( items , isInstant );
31149 this._verticalLayoutItems( items , isInstant );
31153 _verticalLayoutItems : function ( items , isInstant)
31155 if ( !items || !items.length ) {
31160 ['xs', 'xs', 'xs', 'tall'],
31161 ['xs', 'xs', 'tall'],
31162 ['xs', 'xs', 'sm'],
31163 ['xs', 'xs', 'xs'],
31169 ['sm', 'xs', 'xs'],
31173 ['tall', 'xs', 'xs', 'xs'],
31174 ['tall', 'xs', 'xs'],
31186 Roo.each(items, function(item, k){
31188 switch (item.size) {
31189 // these layouts take up a full box,
31200 boxes.push([item]);
31223 var filterPattern = function(box, length)
31231 var pattern = box.slice(0, length);
31235 Roo.each(pattern, function(i){
31236 format.push(i.size);
31239 Roo.each(standard, function(s){
31241 if(String(s) != String(format)){
31250 if(!match && length == 1){
31255 filterPattern(box, length - 1);
31259 queue.push(pattern);
31261 box = box.slice(length, box.length);
31263 filterPattern(box, 4);
31269 Roo.each(boxes, function(box, k){
31275 if(box.length == 1){
31280 filterPattern(box, 4);
31284 this._processVerticalLayoutQueue( queue, isInstant );
31288 // _verticalAlternativeLayoutItems : function( items , isInstant )
31290 // if ( !items || !items.length ) {
31294 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31298 _horizontalLayoutItems : function ( items , isInstant)
31300 if ( !items || !items.length || items.length < 3) {
31306 var eItems = items.slice(0, 3);
31308 items = items.slice(3, items.length);
31311 ['xs', 'xs', 'xs', 'wide'],
31312 ['xs', 'xs', 'wide'],
31313 ['xs', 'xs', 'sm'],
31314 ['xs', 'xs', 'xs'],
31320 ['sm', 'xs', 'xs'],
31324 ['wide', 'xs', 'xs', 'xs'],
31325 ['wide', 'xs', 'xs'],
31338 Roo.each(items, function(item, k){
31340 switch (item.size) {
31351 boxes.push([item]);
31375 var filterPattern = function(box, length)
31383 var pattern = box.slice(0, length);
31387 Roo.each(pattern, function(i){
31388 format.push(i.size);
31391 Roo.each(standard, function(s){
31393 if(String(s) != String(format)){
31402 if(!match && length == 1){
31407 filterPattern(box, length - 1);
31411 queue.push(pattern);
31413 box = box.slice(length, box.length);
31415 filterPattern(box, 4);
31421 Roo.each(boxes, function(box, k){
31427 if(box.length == 1){
31432 filterPattern(box, 4);
31439 var pos = this.el.getBox(true);
31443 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31445 var hit_end = false;
31447 Roo.each(queue, function(box){
31451 Roo.each(box, function(b){
31453 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31463 Roo.each(box, function(b){
31465 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31468 mx = Math.max(mx, b.x);
31472 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31476 Roo.each(box, function(b){
31478 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31492 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31495 /** Sets position of item in DOM
31496 * @param {Element} item
31497 * @param {Number} x - horizontal position
31498 * @param {Number} y - vertical position
31499 * @param {Boolean} isInstant - disables transitions
31501 _processVerticalLayoutQueue : function( queue, isInstant )
31503 var pos = this.el.getBox(true);
31508 for (var i = 0; i < this.cols; i++){
31512 Roo.each(queue, function(box, k){
31514 var col = k % this.cols;
31516 Roo.each(box, function(b,kk){
31518 b.el.position('absolute');
31520 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31521 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31523 if(b.size == 'md-left' || b.size == 'md-right'){
31524 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31525 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31528 b.el.setWidth(width);
31529 b.el.setHeight(height);
31531 b.el.select('iframe',true).setSize(width,height);
31535 for (var i = 0; i < this.cols; i++){
31537 if(maxY[i] < maxY[col]){
31542 col = Math.min(col, i);
31546 x = pos.x + col * (this.colWidth + this.padWidth);
31550 var positions = [];
31552 switch (box.length){
31554 positions = this.getVerticalOneBoxColPositions(x, y, box);
31557 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31560 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31563 positions = this.getVerticalFourBoxColPositions(x, y, box);
31569 Roo.each(box, function(b,kk){
31571 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31573 var sz = b.el.getSize();
31575 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31583 for (var i = 0; i < this.cols; i++){
31584 mY = Math.max(mY, maxY[i]);
31587 this.el.setHeight(mY - pos.y);
31591 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31593 // var pos = this.el.getBox(true);
31596 // var maxX = pos.right;
31598 // var maxHeight = 0;
31600 // Roo.each(items, function(item, k){
31604 // item.el.position('absolute');
31606 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31608 // item.el.setWidth(width);
31610 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31612 // item.el.setHeight(height);
31615 // item.el.setXY([x, y], isInstant ? false : true);
31617 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31620 // y = y + height + this.alternativePadWidth;
31622 // maxHeight = maxHeight + height + this.alternativePadWidth;
31626 // this.el.setHeight(maxHeight);
31630 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31632 var pos = this.el.getBox(true);
31637 var maxX = pos.right;
31639 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31641 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31643 Roo.each(queue, function(box, k){
31645 Roo.each(box, function(b, kk){
31647 b.el.position('absolute');
31649 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31650 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31652 if(b.size == 'md-left' || b.size == 'md-right'){
31653 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31654 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31657 b.el.setWidth(width);
31658 b.el.setHeight(height);
31666 var positions = [];
31668 switch (box.length){
31670 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31673 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31676 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31679 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31685 Roo.each(box, function(b,kk){
31687 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31689 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31697 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31699 Roo.each(eItems, function(b,k){
31701 b.size = (k == 0) ? 'sm' : 'xs';
31702 b.x = (k == 0) ? 2 : 1;
31703 b.y = (k == 0) ? 2 : 1;
31705 b.el.position('absolute');
31707 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31709 b.el.setWidth(width);
31711 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31713 b.el.setHeight(height);
31717 var positions = [];
31720 x : maxX - this.unitWidth * 2 - this.gutter,
31725 x : maxX - this.unitWidth,
31726 y : minY + (this.unitWidth + this.gutter) * 2
31730 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31734 Roo.each(eItems, function(b,k){
31736 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31742 getVerticalOneBoxColPositions : function(x, y, box)
31746 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31748 if(box[0].size == 'md-left'){
31752 if(box[0].size == 'md-right'){
31757 x : x + (this.unitWidth + this.gutter) * rand,
31764 getVerticalTwoBoxColPositions : function(x, y, box)
31768 if(box[0].size == 'xs'){
31772 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31776 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31790 x : x + (this.unitWidth + this.gutter) * 2,
31791 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31798 getVerticalThreeBoxColPositions : function(x, y, box)
31802 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31810 x : x + (this.unitWidth + this.gutter) * 1,
31815 x : x + (this.unitWidth + this.gutter) * 2,
31823 if(box[0].size == 'xs' && box[1].size == 'xs'){
31832 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31836 x : x + (this.unitWidth + this.gutter) * 1,
31850 x : x + (this.unitWidth + this.gutter) * 2,
31855 x : x + (this.unitWidth + this.gutter) * 2,
31856 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31863 getVerticalFourBoxColPositions : function(x, y, box)
31867 if(box[0].size == 'xs'){
31876 y : y + (this.unitHeight + this.gutter) * 1
31881 y : y + (this.unitHeight + this.gutter) * 2
31885 x : x + (this.unitWidth + this.gutter) * 1,
31899 x : x + (this.unitWidth + this.gutter) * 2,
31904 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31905 y : y + (this.unitHeight + this.gutter) * 1
31909 x : x + (this.unitWidth + this.gutter) * 2,
31910 y : y + (this.unitWidth + this.gutter) * 2
31917 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31921 if(box[0].size == 'md-left'){
31923 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31930 if(box[0].size == 'md-right'){
31932 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31933 y : minY + (this.unitWidth + this.gutter) * 1
31939 var rand = Math.floor(Math.random() * (4 - box[0].y));
31942 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31943 y : minY + (this.unitWidth + this.gutter) * rand
31950 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31954 if(box[0].size == 'xs'){
31957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31962 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31963 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31971 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31976 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31977 y : minY + (this.unitWidth + this.gutter) * 2
31984 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31988 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31991 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31997 y : minY + (this.unitWidth + this.gutter) * 1
32001 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32002 y : minY + (this.unitWidth + this.gutter) * 2
32009 if(box[0].size == 'xs' && box[1].size == 'xs'){
32012 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32017 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32022 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32023 y : minY + (this.unitWidth + this.gutter) * 1
32031 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32036 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32037 y : minY + (this.unitWidth + this.gutter) * 2
32041 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32042 y : minY + (this.unitWidth + this.gutter) * 2
32049 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32053 if(box[0].size == 'xs'){
32056 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32061 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32066 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),
32071 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32072 y : minY + (this.unitWidth + this.gutter) * 1
32080 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32085 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32086 y : minY + (this.unitWidth + this.gutter) * 2
32090 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32091 y : minY + (this.unitWidth + this.gutter) * 2
32095 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),
32096 y : minY + (this.unitWidth + this.gutter) * 2
32104 * remove a Masonry Brick
32105 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32107 removeBrick : function(brick_id)
32113 for (var i = 0; i<this.bricks.length; i++) {
32114 if (this.bricks[i].id == brick_id) {
32115 this.bricks.splice(i,1);
32116 this.el.dom.removeChild(Roo.get(brick_id).dom);
32123 * adds a Masonry Brick
32124 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32126 addBrick : function(cfg)
32128 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32129 //this.register(cn);
32130 cn.parentId = this.id;
32131 cn.render(this.el);
32136 * register a Masonry Brick
32137 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32140 register : function(brick)
32142 this.bricks.push(brick);
32143 brick.masonryId = this.id;
32147 * clear all the Masonry Brick
32149 clearAll : function()
32152 //this.getChildContainer().dom.innerHTML = "";
32153 this.el.dom.innerHTML = '';
32156 getSelected : function()
32158 if (!this.selectedBrick) {
32162 return this.selectedBrick;
32166 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32170 * register a Masonry Layout
32171 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32174 register : function(layout)
32176 this.groups[layout.id] = layout;
32179 * fetch a Masonry Layout based on the masonry layout ID
32180 * @param {string} the masonry layout to add
32181 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32184 get: function(layout_id) {
32185 if (typeof(this.groups[layout_id]) == 'undefined') {
32188 return this.groups[layout_id] ;
32200 * http://masonry.desandro.com
32202 * The idea is to render all the bricks based on vertical width...
32204 * The original code extends 'outlayer' - we might need to use that....
32210 * @class Roo.bootstrap.LayoutMasonryAuto
32211 * @extends Roo.bootstrap.Component
32212 * Bootstrap Layout Masonry class
32215 * Create a new Element
32216 * @param {Object} config The config object
32219 Roo.bootstrap.LayoutMasonryAuto = function(config){
32220 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32223 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32226 * @cfg {Boolean} isFitWidth - resize the width..
32228 isFitWidth : false, // options..
32230 * @cfg {Boolean} isOriginLeft = left align?
32232 isOriginLeft : true,
32234 * @cfg {Boolean} isOriginTop = top align?
32236 isOriginTop : false,
32238 * @cfg {Boolean} isLayoutInstant = no animation?
32240 isLayoutInstant : false, // needed?
32242 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32244 isResizingContainer : true,
32246 * @cfg {Number} columnWidth width of the columns
32252 * @cfg {Number} maxCols maximum number of columns
32257 * @cfg {Number} padHeight padding below box..
32263 * @cfg {Boolean} isAutoInitial defalut true
32266 isAutoInitial : true,
32272 initialColumnWidth : 0,
32273 currentSize : null,
32275 colYs : null, // array.
32282 bricks: null, //CompositeElement
32283 cols : 0, // array?
32284 // element : null, // wrapped now this.el
32285 _isLayoutInited : null,
32288 getAutoCreate : function(){
32292 cls: 'blog-masonary-wrapper ' + this.cls,
32294 cls : 'mas-boxes masonary'
32301 getChildContainer: function( )
32303 if (this.boxesEl) {
32304 return this.boxesEl;
32307 this.boxesEl = this.el.select('.mas-boxes').first();
32309 return this.boxesEl;
32313 initEvents : function()
32317 if(this.isAutoInitial){
32318 Roo.log('hook children rendered');
32319 this.on('childrenrendered', function() {
32320 Roo.log('children rendered');
32327 initial : function()
32329 this.reloadItems();
32331 this.currentSize = this.el.getBox(true);
32333 /// was window resize... - let's see if this works..
32334 Roo.EventManager.onWindowResize(this.resize, this);
32336 if(!this.isAutoInitial){
32341 this.layout.defer(500,this);
32344 reloadItems: function()
32346 this.bricks = this.el.select('.masonry-brick', true);
32348 this.bricks.each(function(b) {
32349 //Roo.log(b.getSize());
32350 if (!b.attr('originalwidth')) {
32351 b.attr('originalwidth', b.getSize().width);
32356 Roo.log(this.bricks.elements.length);
32359 resize : function()
32362 var cs = this.el.getBox(true);
32364 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32365 Roo.log("no change in with or X");
32368 this.currentSize = cs;
32372 layout : function()
32375 this._resetLayout();
32376 //this._manageStamps();
32378 // don't animate first layout
32379 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32380 this.layoutItems( isInstant );
32382 // flag for initalized
32383 this._isLayoutInited = true;
32386 layoutItems : function( isInstant )
32388 //var items = this._getItemsForLayout( this.items );
32389 // original code supports filtering layout items.. we just ignore it..
32391 this._layoutItems( this.bricks , isInstant );
32393 this._postLayout();
32395 _layoutItems : function ( items , isInstant)
32397 //this.fireEvent( 'layout', this, items );
32400 if ( !items || !items.elements.length ) {
32401 // no items, emit event with empty array
32406 items.each(function(item) {
32407 Roo.log("layout item");
32409 // get x/y object from method
32410 var position = this._getItemLayoutPosition( item );
32412 position.item = item;
32413 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32414 queue.push( position );
32417 this._processLayoutQueue( queue );
32419 /** Sets position of item in DOM
32420 * @param {Element} item
32421 * @param {Number} x - horizontal position
32422 * @param {Number} y - vertical position
32423 * @param {Boolean} isInstant - disables transitions
32425 _processLayoutQueue : function( queue )
32427 for ( var i=0, len = queue.length; i < len; i++ ) {
32428 var obj = queue[i];
32429 obj.item.position('absolute');
32430 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32436 * Any logic you want to do after each layout,
32437 * i.e. size the container
32439 _postLayout : function()
32441 this.resizeContainer();
32444 resizeContainer : function()
32446 if ( !this.isResizingContainer ) {
32449 var size = this._getContainerSize();
32451 this.el.setSize(size.width,size.height);
32452 this.boxesEl.setSize(size.width,size.height);
32458 _resetLayout : function()
32460 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32461 this.colWidth = this.el.getWidth();
32462 //this.gutter = this.el.getWidth();
32464 this.measureColumns();
32470 this.colYs.push( 0 );
32476 measureColumns : function()
32478 this.getContainerWidth();
32479 // if columnWidth is 0, default to outerWidth of first item
32480 if ( !this.columnWidth ) {
32481 var firstItem = this.bricks.first();
32482 Roo.log(firstItem);
32483 this.columnWidth = this.containerWidth;
32484 if (firstItem && firstItem.attr('originalwidth') ) {
32485 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32487 // columnWidth fall back to item of first element
32488 Roo.log("set column width?");
32489 this.initialColumnWidth = this.columnWidth ;
32491 // if first elem has no width, default to size of container
32496 if (this.initialColumnWidth) {
32497 this.columnWidth = this.initialColumnWidth;
32502 // column width is fixed at the top - however if container width get's smaller we should
32505 // this bit calcs how man columns..
32507 var columnWidth = this.columnWidth += this.gutter;
32509 // calculate columns
32510 var containerWidth = this.containerWidth + this.gutter;
32512 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32513 // fix rounding errors, typically with gutters
32514 var excess = columnWidth - containerWidth % columnWidth;
32517 // if overshoot is less than a pixel, round up, otherwise floor it
32518 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32519 cols = Math[ mathMethod ]( cols );
32520 this.cols = Math.max( cols, 1 );
32521 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32523 // padding positioning..
32524 var totalColWidth = this.cols * this.columnWidth;
32525 var padavail = this.containerWidth - totalColWidth;
32526 // so for 2 columns - we need 3 'pads'
32528 var padNeeded = (1+this.cols) * this.padWidth;
32530 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32532 this.columnWidth += padExtra
32533 //this.padWidth = Math.floor(padavail / ( this.cols));
32535 // adjust colum width so that padding is fixed??
32537 // we have 3 columns ... total = width * 3
32538 // we have X left over... that should be used by
32540 //if (this.expandC) {
32548 getContainerWidth : function()
32550 /* // container is parent if fit width
32551 var container = this.isFitWidth ? this.element.parentNode : this.element;
32552 // check that this.size and size are there
32553 // IE8 triggers resize on body size change, so they might not be
32555 var size = getSize( container ); //FIXME
32556 this.containerWidth = size && size.innerWidth; //FIXME
32559 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32563 _getItemLayoutPosition : function( item ) // what is item?
32565 // we resize the item to our columnWidth..
32567 item.setWidth(this.columnWidth);
32568 item.autoBoxAdjust = false;
32570 var sz = item.getSize();
32572 // how many columns does this brick span
32573 var remainder = this.containerWidth % this.columnWidth;
32575 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32576 // round if off by 1 pixel, otherwise use ceil
32577 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32578 colSpan = Math.min( colSpan, this.cols );
32580 // normally this should be '1' as we dont' currently allow multi width columns..
32582 var colGroup = this._getColGroup( colSpan );
32583 // get the minimum Y value from the columns
32584 var minimumY = Math.min.apply( Math, colGroup );
32585 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32587 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32589 // position the brick
32591 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32592 y: this.currentSize.y + minimumY + this.padHeight
32596 // apply setHeight to necessary columns
32597 var setHeight = minimumY + sz.height + this.padHeight;
32598 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32600 var setSpan = this.cols + 1 - colGroup.length;
32601 for ( var i = 0; i < setSpan; i++ ) {
32602 this.colYs[ shortColIndex + i ] = setHeight ;
32609 * @param {Number} colSpan - number of columns the element spans
32610 * @returns {Array} colGroup
32612 _getColGroup : function( colSpan )
32614 if ( colSpan < 2 ) {
32615 // if brick spans only one column, use all the column Ys
32620 // how many different places could this brick fit horizontally
32621 var groupCount = this.cols + 1 - colSpan;
32622 // for each group potential horizontal position
32623 for ( var i = 0; i < groupCount; i++ ) {
32624 // make an array of colY values for that one group
32625 var groupColYs = this.colYs.slice( i, i + colSpan );
32626 // and get the max value of the array
32627 colGroup[i] = Math.max.apply( Math, groupColYs );
32632 _manageStamp : function( stamp )
32634 var stampSize = stamp.getSize();
32635 var offset = stamp.getBox();
32636 // get the columns that this stamp affects
32637 var firstX = this.isOriginLeft ? offset.x : offset.right;
32638 var lastX = firstX + stampSize.width;
32639 var firstCol = Math.floor( firstX / this.columnWidth );
32640 firstCol = Math.max( 0, firstCol );
32642 var lastCol = Math.floor( lastX / this.columnWidth );
32643 // lastCol should not go over if multiple of columnWidth #425
32644 lastCol -= lastX % this.columnWidth ? 0 : 1;
32645 lastCol = Math.min( this.cols - 1, lastCol );
32647 // set colYs to bottom of the stamp
32648 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32651 for ( var i = firstCol; i <= lastCol; i++ ) {
32652 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32657 _getContainerSize : function()
32659 this.maxY = Math.max.apply( Math, this.colYs );
32664 if ( this.isFitWidth ) {
32665 size.width = this._getContainerFitWidth();
32671 _getContainerFitWidth : function()
32673 var unusedCols = 0;
32674 // count unused columns
32677 if ( this.colYs[i] !== 0 ) {
32682 // fit container to columns that have been used
32683 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32686 needsResizeLayout : function()
32688 var previousWidth = this.containerWidth;
32689 this.getContainerWidth();
32690 return previousWidth !== this.containerWidth;
32705 * @class Roo.bootstrap.MasonryBrick
32706 * @extends Roo.bootstrap.Component
32707 * Bootstrap MasonryBrick class
32710 * Create a new MasonryBrick
32711 * @param {Object} config The config object
32714 Roo.bootstrap.MasonryBrick = function(config){
32716 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32718 Roo.bootstrap.MasonryBrick.register(this);
32724 * When a MasonryBrick is clcik
32725 * @param {Roo.bootstrap.MasonryBrick} this
32726 * @param {Roo.EventObject} e
32732 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32735 * @cfg {String} title
32739 * @cfg {String} html
32743 * @cfg {String} bgimage
32747 * @cfg {String} videourl
32751 * @cfg {String} cls
32755 * @cfg {String} href
32759 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32764 * @cfg {String} placetitle (center|bottom)
32769 * @cfg {Boolean} isFitContainer defalut true
32771 isFitContainer : true,
32774 * @cfg {Boolean} preventDefault defalut false
32776 preventDefault : false,
32779 * @cfg {Boolean} inverse defalut false
32781 maskInverse : false,
32783 getAutoCreate : function()
32785 if(!this.isFitContainer){
32786 return this.getSplitAutoCreate();
32789 var cls = 'masonry-brick masonry-brick-full';
32791 if(this.href.length){
32792 cls += ' masonry-brick-link';
32795 if(this.bgimage.length){
32796 cls += ' masonry-brick-image';
32799 if(this.maskInverse){
32800 cls += ' mask-inverse';
32803 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32804 cls += ' enable-mask';
32808 cls += ' masonry-' + this.size + '-brick';
32811 if(this.placetitle.length){
32813 switch (this.placetitle) {
32815 cls += ' masonry-center-title';
32818 cls += ' masonry-bottom-title';
32825 if(!this.html.length && !this.bgimage.length){
32826 cls += ' masonry-center-title';
32829 if(!this.html.length && this.bgimage.length){
32830 cls += ' masonry-bottom-title';
32835 cls += ' ' + this.cls;
32839 tag: (this.href.length) ? 'a' : 'div',
32844 cls: 'masonry-brick-mask'
32848 cls: 'masonry-brick-paragraph',
32854 if(this.href.length){
32855 cfg.href = this.href;
32858 var cn = cfg.cn[1].cn;
32860 if(this.title.length){
32863 cls: 'masonry-brick-title',
32868 if(this.html.length){
32871 cls: 'masonry-brick-text',
32876 if (!this.title.length && !this.html.length) {
32877 cfg.cn[1].cls += ' hide';
32880 if(this.bgimage.length){
32883 cls: 'masonry-brick-image-view',
32888 if(this.videourl.length){
32889 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32890 // youtube support only?
32893 cls: 'masonry-brick-image-view',
32896 allowfullscreen : true
32904 getSplitAutoCreate : function()
32906 var cls = 'masonry-brick masonry-brick-split';
32908 if(this.href.length){
32909 cls += ' masonry-brick-link';
32912 if(this.bgimage.length){
32913 cls += ' masonry-brick-image';
32917 cls += ' masonry-' + this.size + '-brick';
32920 switch (this.placetitle) {
32922 cls += ' masonry-center-title';
32925 cls += ' masonry-bottom-title';
32928 if(!this.bgimage.length){
32929 cls += ' masonry-center-title';
32932 if(this.bgimage.length){
32933 cls += ' masonry-bottom-title';
32939 cls += ' ' + this.cls;
32943 tag: (this.href.length) ? 'a' : 'div',
32948 cls: 'masonry-brick-split-head',
32952 cls: 'masonry-brick-paragraph',
32959 cls: 'masonry-brick-split-body',
32965 if(this.href.length){
32966 cfg.href = this.href;
32969 if(this.title.length){
32970 cfg.cn[0].cn[0].cn.push({
32972 cls: 'masonry-brick-title',
32977 if(this.html.length){
32978 cfg.cn[1].cn.push({
32980 cls: 'masonry-brick-text',
32985 if(this.bgimage.length){
32986 cfg.cn[0].cn.push({
32988 cls: 'masonry-brick-image-view',
32993 if(this.videourl.length){
32994 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32995 // youtube support only?
32996 cfg.cn[0].cn.cn.push({
32998 cls: 'masonry-brick-image-view',
33001 allowfullscreen : true
33008 initEvents: function()
33010 switch (this.size) {
33043 this.el.on('touchstart', this.onTouchStart, this);
33044 this.el.on('touchmove', this.onTouchMove, this);
33045 this.el.on('touchend', this.onTouchEnd, this);
33046 this.el.on('contextmenu', this.onContextMenu, this);
33048 this.el.on('mouseenter' ,this.enter, this);
33049 this.el.on('mouseleave', this.leave, this);
33050 this.el.on('click', this.onClick, this);
33053 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33054 this.parent().bricks.push(this);
33059 onClick: function(e, el)
33061 var time = this.endTimer - this.startTimer;
33062 // Roo.log(e.preventDefault());
33065 e.preventDefault();
33070 if(!this.preventDefault){
33074 e.preventDefault();
33076 if (this.activeClass != '') {
33077 this.selectBrick();
33080 this.fireEvent('click', this, e);
33083 enter: function(e, el)
33085 e.preventDefault();
33087 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33091 if(this.bgimage.length && this.html.length){
33092 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33096 leave: function(e, el)
33098 e.preventDefault();
33100 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33104 if(this.bgimage.length && this.html.length){
33105 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33109 onTouchStart: function(e, el)
33111 // e.preventDefault();
33113 this.touchmoved = false;
33115 if(!this.isFitContainer){
33119 if(!this.bgimage.length || !this.html.length){
33123 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33125 this.timer = new Date().getTime();
33129 onTouchMove: function(e, el)
33131 this.touchmoved = true;
33134 onContextMenu : function(e,el)
33136 e.preventDefault();
33137 e.stopPropagation();
33141 onTouchEnd: function(e, el)
33143 // e.preventDefault();
33145 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33152 if(!this.bgimage.length || !this.html.length){
33154 if(this.href.length){
33155 window.location.href = this.href;
33161 if(!this.isFitContainer){
33165 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33167 window.location.href = this.href;
33170 //selection on single brick only
33171 selectBrick : function() {
33173 if (!this.parentId) {
33177 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33178 var index = m.selectedBrick.indexOf(this.id);
33181 m.selectedBrick.splice(index,1);
33182 this.el.removeClass(this.activeClass);
33186 for(var i = 0; i < m.selectedBrick.length; i++) {
33187 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33188 b.el.removeClass(b.activeClass);
33191 m.selectedBrick = [];
33193 m.selectedBrick.push(this.id);
33194 this.el.addClass(this.activeClass);
33198 isSelected : function(){
33199 return this.el.hasClass(this.activeClass);
33204 Roo.apply(Roo.bootstrap.MasonryBrick, {
33207 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33209 * register a Masonry Brick
33210 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33213 register : function(brick)
33215 //this.groups[brick.id] = brick;
33216 this.groups.add(brick.id, brick);
33219 * fetch a masonry brick based on the masonry brick ID
33220 * @param {string} the masonry brick to add
33221 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33224 get: function(brick_id)
33226 // if (typeof(this.groups[brick_id]) == 'undefined') {
33229 // return this.groups[brick_id] ;
33231 if(this.groups.key(brick_id)) {
33232 return this.groups.key(brick_id);
33250 * @class Roo.bootstrap.Brick
33251 * @extends Roo.bootstrap.Component
33252 * Bootstrap Brick class
33255 * Create a new Brick
33256 * @param {Object} config The config object
33259 Roo.bootstrap.Brick = function(config){
33260 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33266 * When a Brick is click
33267 * @param {Roo.bootstrap.Brick} this
33268 * @param {Roo.EventObject} e
33274 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33277 * @cfg {String} title
33281 * @cfg {String} html
33285 * @cfg {String} bgimage
33289 * @cfg {String} cls
33293 * @cfg {String} href
33297 * @cfg {String} video
33301 * @cfg {Boolean} square
33305 getAutoCreate : function()
33307 var cls = 'roo-brick';
33309 if(this.href.length){
33310 cls += ' roo-brick-link';
33313 if(this.bgimage.length){
33314 cls += ' roo-brick-image';
33317 if(!this.html.length && !this.bgimage.length){
33318 cls += ' roo-brick-center-title';
33321 if(!this.html.length && this.bgimage.length){
33322 cls += ' roo-brick-bottom-title';
33326 cls += ' ' + this.cls;
33330 tag: (this.href.length) ? 'a' : 'div',
33335 cls: 'roo-brick-paragraph',
33341 if(this.href.length){
33342 cfg.href = this.href;
33345 var cn = cfg.cn[0].cn;
33347 if(this.title.length){
33350 cls: 'roo-brick-title',
33355 if(this.html.length){
33358 cls: 'roo-brick-text',
33365 if(this.bgimage.length){
33368 cls: 'roo-brick-image-view',
33376 initEvents: function()
33378 if(this.title.length || this.html.length){
33379 this.el.on('mouseenter' ,this.enter, this);
33380 this.el.on('mouseleave', this.leave, this);
33383 Roo.EventManager.onWindowResize(this.resize, this);
33385 if(this.bgimage.length){
33386 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33387 this.imageEl.on('load', this.onImageLoad, this);
33394 onImageLoad : function()
33399 resize : function()
33401 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33403 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33405 if(this.bgimage.length){
33406 var image = this.el.select('.roo-brick-image-view', true).first();
33408 image.setWidth(paragraph.getWidth());
33411 image.setHeight(paragraph.getWidth());
33414 this.el.setHeight(image.getHeight());
33415 paragraph.setHeight(image.getHeight());
33421 enter: function(e, el)
33423 e.preventDefault();
33425 if(this.bgimage.length){
33426 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33427 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33431 leave: function(e, el)
33433 e.preventDefault();
33435 if(this.bgimage.length){
33436 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33437 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33452 * @class Roo.bootstrap.NumberField
33453 * @extends Roo.bootstrap.Input
33454 * Bootstrap NumberField class
33460 * Create a new NumberField
33461 * @param {Object} config The config object
33464 Roo.bootstrap.NumberField = function(config){
33465 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33468 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33471 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33473 allowDecimals : true,
33475 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33477 decimalSeparator : ".",
33479 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33481 decimalPrecision : 2,
33483 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33485 allowNegative : true,
33488 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33492 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33494 minValue : Number.NEGATIVE_INFINITY,
33496 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33498 maxValue : Number.MAX_VALUE,
33500 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33502 minText : "The minimum value for this field is {0}",
33504 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33506 maxText : "The maximum value for this field is {0}",
33508 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33509 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33511 nanText : "{0} is not a valid number",
33513 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33515 thousandsDelimiter : false,
33517 * @cfg {String} valueAlign alignment of value
33519 valueAlign : "left",
33521 getAutoCreate : function()
33523 var hiddenInput = {
33527 cls: 'hidden-number-input'
33531 hiddenInput.name = this.name;
33536 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33538 this.name = hiddenInput.name;
33540 if(cfg.cn.length > 0) {
33541 cfg.cn.push(hiddenInput);
33548 initEvents : function()
33550 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33552 var allowed = "0123456789";
33554 if(this.allowDecimals){
33555 allowed += this.decimalSeparator;
33558 if(this.allowNegative){
33562 if(this.thousandsDelimiter) {
33566 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33568 var keyPress = function(e){
33570 var k = e.getKey();
33572 var c = e.getCharCode();
33575 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33576 allowed.indexOf(String.fromCharCode(c)) === -1
33582 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33586 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33591 this.el.on("keypress", keyPress, this);
33594 validateValue : function(value)
33597 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33601 var num = this.parseValue(value);
33604 this.markInvalid(String.format(this.nanText, value));
33608 if(num < this.minValue){
33609 this.markInvalid(String.format(this.minText, this.minValue));
33613 if(num > this.maxValue){
33614 this.markInvalid(String.format(this.maxText, this.maxValue));
33621 getValue : function()
33623 var v = this.hiddenEl().getValue();
33625 return this.fixPrecision(this.parseValue(v));
33628 parseValue : function(value)
33630 if(this.thousandsDelimiter) {
33632 r = new RegExp(",", "g");
33633 value = value.replace(r, "");
33636 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33637 return isNaN(value) ? '' : value;
33640 fixPrecision : function(value)
33642 if(this.thousandsDelimiter) {
33644 r = new RegExp(",", "g");
33645 value = value.replace(r, "");
33648 var nan = isNaN(value);
33650 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33651 return nan ? '' : value;
33653 return parseFloat(value).toFixed(this.decimalPrecision);
33656 setValue : function(v)
33658 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33664 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33666 this.inputEl().dom.value = (v == '') ? '' :
33667 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33669 if(!this.allowZero && v === '0') {
33670 this.hiddenEl().dom.value = '';
33671 this.inputEl().dom.value = '';
33678 decimalPrecisionFcn : function(v)
33680 return Math.floor(v);
33683 beforeBlur : function()
33685 var v = this.parseValue(this.getRawValue());
33687 if(v || v === 0 || v === ''){
33692 hiddenEl : function()
33694 return this.el.select('input.hidden-number-input',true).first();
33706 * @class Roo.bootstrap.DocumentSlider
33707 * @extends Roo.bootstrap.Component
33708 * Bootstrap DocumentSlider class
33711 * Create a new DocumentViewer
33712 * @param {Object} config The config object
33715 Roo.bootstrap.DocumentSlider = function(config){
33716 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33723 * Fire after initEvent
33724 * @param {Roo.bootstrap.DocumentSlider} this
33729 * Fire after update
33730 * @param {Roo.bootstrap.DocumentSlider} this
33736 * @param {Roo.bootstrap.DocumentSlider} this
33742 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33748 getAutoCreate : function()
33752 cls : 'roo-document-slider',
33756 cls : 'roo-document-slider-header',
33760 cls : 'roo-document-slider-header-title'
33766 cls : 'roo-document-slider-body',
33770 cls : 'roo-document-slider-prev',
33774 cls : 'fa fa-chevron-left'
33780 cls : 'roo-document-slider-thumb',
33784 cls : 'roo-document-slider-image'
33790 cls : 'roo-document-slider-next',
33794 cls : 'fa fa-chevron-right'
33806 initEvents : function()
33808 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33809 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33811 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33812 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33814 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33815 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33817 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33818 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33820 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33821 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33823 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33824 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33826 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33827 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33829 this.thumbEl.on('click', this.onClick, this);
33831 this.prevIndicator.on('click', this.prev, this);
33833 this.nextIndicator.on('click', this.next, this);
33837 initial : function()
33839 if(this.files.length){
33840 this.indicator = 1;
33844 this.fireEvent('initial', this);
33847 update : function()
33849 this.imageEl.attr('src', this.files[this.indicator - 1]);
33851 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33853 this.prevIndicator.show();
33855 if(this.indicator == 1){
33856 this.prevIndicator.hide();
33859 this.nextIndicator.show();
33861 if(this.indicator == this.files.length){
33862 this.nextIndicator.hide();
33865 this.thumbEl.scrollTo('top');
33867 this.fireEvent('update', this);
33870 onClick : function(e)
33872 e.preventDefault();
33874 this.fireEvent('click', this);
33879 e.preventDefault();
33881 this.indicator = Math.max(1, this.indicator - 1);
33888 e.preventDefault();
33890 this.indicator = Math.min(this.files.length, this.indicator + 1);
33904 * @class Roo.bootstrap.RadioSet
33905 * @extends Roo.bootstrap.Input
33906 * Bootstrap RadioSet class
33907 * @cfg {String} indicatorpos (left|right) default left
33908 * @cfg {Boolean} inline (true|false) inline the element (default true)
33909 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33911 * Create a new RadioSet
33912 * @param {Object} config The config object
33915 Roo.bootstrap.RadioSet = function(config){
33917 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33921 Roo.bootstrap.RadioSet.register(this);
33926 * Fires when the element is checked or unchecked.
33927 * @param {Roo.bootstrap.RadioSet} this This radio
33928 * @param {Roo.bootstrap.Radio} item The checked item
33933 * Fires when the element is click.
33934 * @param {Roo.bootstrap.RadioSet} this This radio set
33935 * @param {Roo.bootstrap.Radio} item The checked item
33936 * @param {Roo.EventObject} e The event object
33943 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33951 indicatorpos : 'left',
33953 getAutoCreate : function()
33957 cls : 'roo-radio-set-label',
33961 html : this.fieldLabel
33966 if(this.indicatorpos == 'left'){
33969 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33970 tooltip : 'This field is required'
33975 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33976 tooltip : 'This field is required'
33982 cls : 'roo-radio-set-items'
33985 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33987 if (align === 'left' && this.fieldLabel.length) {
33990 cls : "roo-radio-set-right",
33996 if(this.labelWidth > 12){
33997 label.style = "width: " + this.labelWidth + 'px';
34000 if(this.labelWidth < 13 && this.labelmd == 0){
34001 this.labelmd = this.labelWidth;
34004 if(this.labellg > 0){
34005 label.cls += ' col-lg-' + this.labellg;
34006 items.cls += ' col-lg-' + (12 - this.labellg);
34009 if(this.labelmd > 0){
34010 label.cls += ' col-md-' + this.labelmd;
34011 items.cls += ' col-md-' + (12 - this.labelmd);
34014 if(this.labelsm > 0){
34015 label.cls += ' col-sm-' + this.labelsm;
34016 items.cls += ' col-sm-' + (12 - this.labelsm);
34019 if(this.labelxs > 0){
34020 label.cls += ' col-xs-' + this.labelxs;
34021 items.cls += ' col-xs-' + (12 - this.labelxs);
34027 cls : 'roo-radio-set',
34031 cls : 'roo-radio-set-input',
34034 value : this.value ? this.value : ''
34041 if(this.weight.length){
34042 cfg.cls += ' roo-radio-' + this.weight;
34046 cfg.cls += ' roo-radio-set-inline';
34050 ['xs','sm','md','lg'].map(function(size){
34051 if (settings[size]) {
34052 cfg.cls += ' col-' + size + '-' + settings[size];
34060 initEvents : function()
34062 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34063 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34065 if(!this.fieldLabel.length){
34066 this.labelEl.hide();
34069 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34070 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34072 this.indicator = this.indicatorEl();
34074 if(this.indicator){
34075 this.indicator.addClass('invisible');
34078 this.originalValue = this.getValue();
34082 inputEl: function ()
34084 return this.el.select('.roo-radio-set-input', true).first();
34087 getChildContainer : function()
34089 return this.itemsEl;
34092 register : function(item)
34094 this.radioes.push(item);
34098 validate : function()
34100 if(this.getVisibilityEl().hasClass('hidden')){
34106 Roo.each(this.radioes, function(i){
34115 if(this.allowBlank) {
34119 if(this.disabled || valid){
34124 this.markInvalid();
34129 markValid : function()
34131 if(this.labelEl.isVisible(true)){
34132 this.indicatorEl().removeClass('visible');
34133 this.indicatorEl().addClass('invisible');
34136 this.el.removeClass([this.invalidClass, this.validClass]);
34137 this.el.addClass(this.validClass);
34139 this.fireEvent('valid', this);
34142 markInvalid : function(msg)
34144 if(this.allowBlank || this.disabled){
34148 if(this.labelEl.isVisible(true)){
34149 this.indicatorEl().removeClass('invisible');
34150 this.indicatorEl().addClass('visible');
34153 this.el.removeClass([this.invalidClass, this.validClass]);
34154 this.el.addClass(this.invalidClass);
34156 this.fireEvent('invalid', this, msg);
34160 setValue : function(v, suppressEvent)
34162 if(this.value === v){
34169 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34172 Roo.each(this.radioes, function(i){
34174 i.el.removeClass('checked');
34177 Roo.each(this.radioes, function(i){
34179 if(i.value === v || i.value.toString() === v.toString()){
34181 i.el.addClass('checked');
34183 if(suppressEvent !== true){
34184 this.fireEvent('check', this, i);
34195 clearInvalid : function(){
34197 if(!this.el || this.preventMark){
34201 this.el.removeClass([this.invalidClass]);
34203 this.fireEvent('valid', this);
34208 Roo.apply(Roo.bootstrap.RadioSet, {
34212 register : function(set)
34214 this.groups[set.name] = set;
34217 get: function(name)
34219 if (typeof(this.groups[name]) == 'undefined') {
34223 return this.groups[name] ;
34229 * Ext JS Library 1.1.1
34230 * Copyright(c) 2006-2007, Ext JS, LLC.
34232 * Originally Released Under LGPL - original licence link has changed is not relivant.
34235 * <script type="text/javascript">
34240 * @class Roo.bootstrap.SplitBar
34241 * @extends Roo.util.Observable
34242 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34246 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34247 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34248 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34249 split.minSize = 100;
34250 split.maxSize = 600;
34251 split.animate = true;
34252 split.on('moved', splitterMoved);
34255 * Create a new SplitBar
34256 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34257 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34258 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34259 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34260 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34261 position of the SplitBar).
34263 Roo.bootstrap.SplitBar = function(cfg){
34268 // dragElement : elm
34269 // resizingElement: el,
34271 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34272 // placement : Roo.bootstrap.SplitBar.LEFT ,
34273 // existingProxy ???
34276 this.el = Roo.get(cfg.dragElement, true);
34277 this.el.dom.unselectable = "on";
34279 this.resizingEl = Roo.get(cfg.resizingElement, true);
34283 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34284 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34287 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34290 * The minimum size of the resizing element. (Defaults to 0)
34296 * The maximum size of the resizing element. (Defaults to 2000)
34299 this.maxSize = 2000;
34302 * Whether to animate the transition to the new size
34305 this.animate = false;
34308 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34311 this.useShim = false;
34316 if(!cfg.existingProxy){
34318 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34320 this.proxy = Roo.get(cfg.existingProxy).dom;
34323 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34326 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34329 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34332 this.dragSpecs = {};
34335 * @private The adapter to use to positon and resize elements
34337 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34338 this.adapter.init(this);
34340 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34342 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34343 this.el.addClass("roo-splitbar-h");
34346 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34347 this.el.addClass("roo-splitbar-v");
34353 * Fires when the splitter is moved (alias for {@link #event-moved})
34354 * @param {Roo.bootstrap.SplitBar} this
34355 * @param {Number} newSize the new width or height
34360 * Fires when the splitter is moved
34361 * @param {Roo.bootstrap.SplitBar} this
34362 * @param {Number} newSize the new width or height
34366 * @event beforeresize
34367 * Fires before the splitter is dragged
34368 * @param {Roo.bootstrap.SplitBar} this
34370 "beforeresize" : true,
34372 "beforeapply" : true
34375 Roo.util.Observable.call(this);
34378 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34379 onStartProxyDrag : function(x, y){
34380 this.fireEvent("beforeresize", this);
34382 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34384 o.enableDisplayMode("block");
34385 // all splitbars share the same overlay
34386 Roo.bootstrap.SplitBar.prototype.overlay = o;
34388 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34389 this.overlay.show();
34390 Roo.get(this.proxy).setDisplayed("block");
34391 var size = this.adapter.getElementSize(this);
34392 this.activeMinSize = this.getMinimumSize();;
34393 this.activeMaxSize = this.getMaximumSize();;
34394 var c1 = size - this.activeMinSize;
34395 var c2 = Math.max(this.activeMaxSize - size, 0);
34396 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34397 this.dd.resetConstraints();
34398 this.dd.setXConstraint(
34399 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34400 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34402 this.dd.setYConstraint(0, 0);
34404 this.dd.resetConstraints();
34405 this.dd.setXConstraint(0, 0);
34406 this.dd.setYConstraint(
34407 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34408 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34411 this.dragSpecs.startSize = size;
34412 this.dragSpecs.startPoint = [x, y];
34413 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34417 * @private Called after the drag operation by the DDProxy
34419 onEndProxyDrag : function(e){
34420 Roo.get(this.proxy).setDisplayed(false);
34421 var endPoint = Roo.lib.Event.getXY(e);
34423 this.overlay.hide();
34426 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34427 newSize = this.dragSpecs.startSize +
34428 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34429 endPoint[0] - this.dragSpecs.startPoint[0] :
34430 this.dragSpecs.startPoint[0] - endPoint[0]
34433 newSize = this.dragSpecs.startSize +
34434 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34435 endPoint[1] - this.dragSpecs.startPoint[1] :
34436 this.dragSpecs.startPoint[1] - endPoint[1]
34439 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34440 if(newSize != this.dragSpecs.startSize){
34441 if(this.fireEvent('beforeapply', this, newSize) !== false){
34442 this.adapter.setElementSize(this, newSize);
34443 this.fireEvent("moved", this, newSize);
34444 this.fireEvent("resize", this, newSize);
34450 * Get the adapter this SplitBar uses
34451 * @return The adapter object
34453 getAdapter : function(){
34454 return this.adapter;
34458 * Set the adapter this SplitBar uses
34459 * @param {Object} adapter A SplitBar adapter object
34461 setAdapter : function(adapter){
34462 this.adapter = adapter;
34463 this.adapter.init(this);
34467 * Gets the minimum size for the resizing element
34468 * @return {Number} The minimum size
34470 getMinimumSize : function(){
34471 return this.minSize;
34475 * Sets the minimum size for the resizing element
34476 * @param {Number} minSize The minimum size
34478 setMinimumSize : function(minSize){
34479 this.minSize = minSize;
34483 * Gets the maximum size for the resizing element
34484 * @return {Number} The maximum size
34486 getMaximumSize : function(){
34487 return this.maxSize;
34491 * Sets the maximum size for the resizing element
34492 * @param {Number} maxSize The maximum size
34494 setMaximumSize : function(maxSize){
34495 this.maxSize = maxSize;
34499 * Sets the initialize size for the resizing element
34500 * @param {Number} size The initial size
34502 setCurrentSize : function(size){
34503 var oldAnimate = this.animate;
34504 this.animate = false;
34505 this.adapter.setElementSize(this, size);
34506 this.animate = oldAnimate;
34510 * Destroy this splitbar.
34511 * @param {Boolean} removeEl True to remove the element
34513 destroy : function(removeEl){
34515 this.shim.remove();
34518 this.proxy.parentNode.removeChild(this.proxy);
34526 * @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.
34528 Roo.bootstrap.SplitBar.createProxy = function(dir){
34529 var proxy = new Roo.Element(document.createElement("div"));
34530 proxy.unselectable();
34531 var cls = 'roo-splitbar-proxy';
34532 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34533 document.body.appendChild(proxy.dom);
34538 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34539 * Default Adapter. It assumes the splitter and resizing element are not positioned
34540 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34542 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34545 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34546 // do nothing for now
34547 init : function(s){
34551 * Called before drag operations to get the current size of the resizing element.
34552 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34554 getElementSize : function(s){
34555 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34556 return s.resizingEl.getWidth();
34558 return s.resizingEl.getHeight();
34563 * Called after drag operations to set the size of the resizing element.
34564 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34565 * @param {Number} newSize The new size to set
34566 * @param {Function} onComplete A function to be invoked when resizing is complete
34568 setElementSize : function(s, newSize, onComplete){
34569 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34571 s.resizingEl.setWidth(newSize);
34573 onComplete(s, newSize);
34576 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34581 s.resizingEl.setHeight(newSize);
34583 onComplete(s, newSize);
34586 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34593 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34594 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34595 * Adapter that moves the splitter element to align with the resized sizing element.
34596 * Used with an absolute positioned SplitBar.
34597 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34598 * document.body, make sure you assign an id to the body element.
34600 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34601 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34602 this.container = Roo.get(container);
34605 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34606 init : function(s){
34607 this.basic.init(s);
34610 getElementSize : function(s){
34611 return this.basic.getElementSize(s);
34614 setElementSize : function(s, newSize, onComplete){
34615 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34618 moveSplitter : function(s){
34619 var yes = Roo.bootstrap.SplitBar;
34620 switch(s.placement){
34622 s.el.setX(s.resizingEl.getRight());
34625 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34628 s.el.setY(s.resizingEl.getBottom());
34631 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34638 * Orientation constant - Create a vertical SplitBar
34642 Roo.bootstrap.SplitBar.VERTICAL = 1;
34645 * Orientation constant - Create a horizontal SplitBar
34649 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34652 * Placement constant - The resizing element is to the left of the splitter element
34656 Roo.bootstrap.SplitBar.LEFT = 1;
34659 * Placement constant - The resizing element is to the right of the splitter element
34663 Roo.bootstrap.SplitBar.RIGHT = 2;
34666 * Placement constant - The resizing element is positioned above the splitter element
34670 Roo.bootstrap.SplitBar.TOP = 3;
34673 * Placement constant - The resizing element is positioned under splitter element
34677 Roo.bootstrap.SplitBar.BOTTOM = 4;
34678 Roo.namespace("Roo.bootstrap.layout");/*
34680 * Ext JS Library 1.1.1
34681 * Copyright(c) 2006-2007, Ext JS, LLC.
34683 * Originally Released Under LGPL - original licence link has changed is not relivant.
34686 * <script type="text/javascript">
34690 * @class Roo.bootstrap.layout.Manager
34691 * @extends Roo.bootstrap.Component
34692 * Base class for layout managers.
34694 Roo.bootstrap.layout.Manager = function(config)
34696 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34702 /** false to disable window resize monitoring @type Boolean */
34703 this.monitorWindowResize = true;
34708 * Fires when a layout is performed.
34709 * @param {Roo.LayoutManager} this
34713 * @event regionresized
34714 * Fires when the user resizes a region.
34715 * @param {Roo.LayoutRegion} region The resized region
34716 * @param {Number} newSize The new size (width for east/west, height for north/south)
34718 "regionresized" : true,
34720 * @event regioncollapsed
34721 * Fires when a region is collapsed.
34722 * @param {Roo.LayoutRegion} region The collapsed region
34724 "regioncollapsed" : true,
34726 * @event regionexpanded
34727 * Fires when a region is expanded.
34728 * @param {Roo.LayoutRegion} region The expanded region
34730 "regionexpanded" : true
34732 this.updating = false;
34735 this.el = Roo.get(config.el);
34741 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34746 monitorWindowResize : true,
34752 onRender : function(ct, position)
34755 this.el = Roo.get(ct);
34758 //this.fireEvent('render',this);
34762 initEvents: function()
34766 // ie scrollbar fix
34767 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34768 document.body.scroll = "no";
34769 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34770 this.el.position('relative');
34772 this.id = this.el.id;
34773 this.el.addClass("roo-layout-container");
34774 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34775 if(this.el.dom != document.body ) {
34776 this.el.on('resize', this.layout,this);
34777 this.el.on('show', this.layout,this);
34783 * Returns true if this layout is currently being updated
34784 * @return {Boolean}
34786 isUpdating : function(){
34787 return this.updating;
34791 * Suspend the LayoutManager from doing auto-layouts while
34792 * making multiple add or remove calls
34794 beginUpdate : function(){
34795 this.updating = true;
34799 * Restore auto-layouts and optionally disable the manager from performing a layout
34800 * @param {Boolean} noLayout true to disable a layout update
34802 endUpdate : function(noLayout){
34803 this.updating = false;
34809 layout: function(){
34813 onRegionResized : function(region, newSize){
34814 this.fireEvent("regionresized", region, newSize);
34818 onRegionCollapsed : function(region){
34819 this.fireEvent("regioncollapsed", region);
34822 onRegionExpanded : function(region){
34823 this.fireEvent("regionexpanded", region);
34827 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34828 * performs box-model adjustments.
34829 * @return {Object} The size as an object {width: (the width), height: (the height)}
34831 getViewSize : function()
34834 if(this.el.dom != document.body){
34835 size = this.el.getSize();
34837 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34839 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34840 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34845 * Returns the Element this layout is bound to.
34846 * @return {Roo.Element}
34848 getEl : function(){
34853 * Returns the specified region.
34854 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34855 * @return {Roo.LayoutRegion}
34857 getRegion : function(target){
34858 return this.regions[target.toLowerCase()];
34861 onWindowResize : function(){
34862 if(this.monitorWindowResize){
34869 * Ext JS Library 1.1.1
34870 * Copyright(c) 2006-2007, Ext JS, LLC.
34872 * Originally Released Under LGPL - original licence link has changed is not relivant.
34875 * <script type="text/javascript">
34878 * @class Roo.bootstrap.layout.Border
34879 * @extends Roo.bootstrap.layout.Manager
34880 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34881 * please see: examples/bootstrap/nested.html<br><br>
34883 <b>The container the layout is rendered into can be either the body element or any other element.
34884 If it is not the body element, the container needs to either be an absolute positioned element,
34885 or you will need to add "position:relative" to the css of the container. You will also need to specify
34886 the container size if it is not the body element.</b>
34889 * Create a new Border
34890 * @param {Object} config Configuration options
34892 Roo.bootstrap.layout.Border = function(config){
34893 config = config || {};
34894 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34898 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34899 if(config[region]){
34900 config[region].region = region;
34901 this.addRegion(config[region]);
34907 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34909 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34911 * Creates and adds a new region if it doesn't already exist.
34912 * @param {String} target The target region key (north, south, east, west or center).
34913 * @param {Object} config The regions config object
34914 * @return {BorderLayoutRegion} The new region
34916 addRegion : function(config)
34918 if(!this.regions[config.region]){
34919 var r = this.factory(config);
34920 this.bindRegion(r);
34922 return this.regions[config.region];
34926 bindRegion : function(r){
34927 this.regions[r.config.region] = r;
34929 r.on("visibilitychange", this.layout, this);
34930 r.on("paneladded", this.layout, this);
34931 r.on("panelremoved", this.layout, this);
34932 r.on("invalidated", this.layout, this);
34933 r.on("resized", this.onRegionResized, this);
34934 r.on("collapsed", this.onRegionCollapsed, this);
34935 r.on("expanded", this.onRegionExpanded, this);
34939 * Performs a layout update.
34941 layout : function()
34943 if(this.updating) {
34947 // render all the rebions if they have not been done alreayd?
34948 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34949 if(this.regions[region] && !this.regions[region].bodyEl){
34950 this.regions[region].onRender(this.el)
34954 var size = this.getViewSize();
34955 var w = size.width;
34956 var h = size.height;
34961 //var x = 0, y = 0;
34963 var rs = this.regions;
34964 var north = rs["north"];
34965 var south = rs["south"];
34966 var west = rs["west"];
34967 var east = rs["east"];
34968 var center = rs["center"];
34969 //if(this.hideOnLayout){ // not supported anymore
34970 //c.el.setStyle("display", "none");
34972 if(north && north.isVisible()){
34973 var b = north.getBox();
34974 var m = north.getMargins();
34975 b.width = w - (m.left+m.right);
34978 centerY = b.height + b.y + m.bottom;
34979 centerH -= centerY;
34980 north.updateBox(this.safeBox(b));
34982 if(south && south.isVisible()){
34983 var b = south.getBox();
34984 var m = south.getMargins();
34985 b.width = w - (m.left+m.right);
34987 var totalHeight = (b.height + m.top + m.bottom);
34988 b.y = h - totalHeight + m.top;
34989 centerH -= totalHeight;
34990 south.updateBox(this.safeBox(b));
34992 if(west && west.isVisible()){
34993 var b = west.getBox();
34994 var m = west.getMargins();
34995 b.height = centerH - (m.top+m.bottom);
34997 b.y = centerY + m.top;
34998 var totalWidth = (b.width + m.left + m.right);
34999 centerX += totalWidth;
35000 centerW -= totalWidth;
35001 west.updateBox(this.safeBox(b));
35003 if(east && east.isVisible()){
35004 var b = east.getBox();
35005 var m = east.getMargins();
35006 b.height = centerH - (m.top+m.bottom);
35007 var totalWidth = (b.width + m.left + m.right);
35008 b.x = w - totalWidth + m.left;
35009 b.y = centerY + m.top;
35010 centerW -= totalWidth;
35011 east.updateBox(this.safeBox(b));
35014 var m = center.getMargins();
35016 x: centerX + m.left,
35017 y: centerY + m.top,
35018 width: centerW - (m.left+m.right),
35019 height: centerH - (m.top+m.bottom)
35021 //if(this.hideOnLayout){
35022 //center.el.setStyle("display", "block");
35024 center.updateBox(this.safeBox(centerBox));
35027 this.fireEvent("layout", this);
35031 safeBox : function(box){
35032 box.width = Math.max(0, box.width);
35033 box.height = Math.max(0, box.height);
35038 * Adds a ContentPanel (or subclass) to this layout.
35039 * @param {String} target The target region key (north, south, east, west or center).
35040 * @param {Roo.ContentPanel} panel The panel to add
35041 * @return {Roo.ContentPanel} The added panel
35043 add : function(target, panel){
35045 target = target.toLowerCase();
35046 return this.regions[target].add(panel);
35050 * Remove a ContentPanel (or subclass) to this layout.
35051 * @param {String} target The target region key (north, south, east, west or center).
35052 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35053 * @return {Roo.ContentPanel} The removed panel
35055 remove : function(target, panel){
35056 target = target.toLowerCase();
35057 return this.regions[target].remove(panel);
35061 * Searches all regions for a panel with the specified id
35062 * @param {String} panelId
35063 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35065 findPanel : function(panelId){
35066 var rs = this.regions;
35067 for(var target in rs){
35068 if(typeof rs[target] != "function"){
35069 var p = rs[target].getPanel(panelId);
35079 * Searches all regions for a panel with the specified id and activates (shows) it.
35080 * @param {String/ContentPanel} panelId The panels id or the panel itself
35081 * @return {Roo.ContentPanel} The shown panel or null
35083 showPanel : function(panelId) {
35084 var rs = this.regions;
35085 for(var target in rs){
35086 var r = rs[target];
35087 if(typeof r != "function"){
35088 if(r.hasPanel(panelId)){
35089 return r.showPanel(panelId);
35097 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35098 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35101 restoreState : function(provider){
35103 provider = Roo.state.Manager;
35105 var sm = new Roo.LayoutStateManager();
35106 sm.init(this, provider);
35112 * Adds a xtype elements to the layout.
35116 xtype : 'ContentPanel',
35123 xtype : 'NestedLayoutPanel',
35129 items : [ ... list of content panels or nested layout panels.. ]
35133 * @param {Object} cfg Xtype definition of item to add.
35135 addxtype : function(cfg)
35137 // basically accepts a pannel...
35138 // can accept a layout region..!?!?
35139 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35142 // theory? children can only be panels??
35144 //if (!cfg.xtype.match(/Panel$/)) {
35149 if (typeof(cfg.region) == 'undefined') {
35150 Roo.log("Failed to add Panel, region was not set");
35154 var region = cfg.region;
35160 xitems = cfg.items;
35167 case 'Content': // ContentPanel (el, cfg)
35168 case 'Scroll': // ContentPanel (el, cfg)
35170 cfg.autoCreate = true;
35171 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35173 // var el = this.el.createChild();
35174 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35177 this.add(region, ret);
35181 case 'TreePanel': // our new panel!
35182 cfg.el = this.el.createChild();
35183 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35184 this.add(region, ret);
35189 // create a new Layout (which is a Border Layout...
35191 var clayout = cfg.layout;
35192 clayout.el = this.el.createChild();
35193 clayout.items = clayout.items || [];
35197 // replace this exitems with the clayout ones..
35198 xitems = clayout.items;
35200 // force background off if it's in center...
35201 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35202 cfg.background = false;
35204 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35207 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35208 //console.log('adding nested layout panel ' + cfg.toSource());
35209 this.add(region, ret);
35210 nb = {}; /// find first...
35215 // needs grid and region
35217 //var el = this.getRegion(region).el.createChild();
35219 *var el = this.el.createChild();
35220 // create the grid first...
35221 cfg.grid.container = el;
35222 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35225 if (region == 'center' && this.active ) {
35226 cfg.background = false;
35229 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35231 this.add(region, ret);
35233 if (cfg.background) {
35234 // render grid on panel activation (if panel background)
35235 ret.on('activate', function(gp) {
35236 if (!gp.grid.rendered) {
35237 // gp.grid.render(el);
35241 // cfg.grid.render(el);
35247 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35248 // it was the old xcomponent building that caused this before.
35249 // espeically if border is the top element in the tree.
35259 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35261 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35262 this.add(region, ret);
35266 throw "Can not add '" + cfg.xtype + "' to Border";
35272 this.beginUpdate();
35276 Roo.each(xitems, function(i) {
35277 region = nb && i.region ? i.region : false;
35279 var add = ret.addxtype(i);
35282 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35283 if (!i.background) {
35284 abn[region] = nb[region] ;
35291 // make the last non-background panel active..
35292 //if (nb) { Roo.log(abn); }
35295 for(var r in abn) {
35296 region = this.getRegion(r);
35298 // tried using nb[r], but it does not work..
35300 region.showPanel(abn[r]);
35311 factory : function(cfg)
35314 var validRegions = Roo.bootstrap.layout.Border.regions;
35316 var target = cfg.region;
35319 var r = Roo.bootstrap.layout;
35323 return new r.North(cfg);
35325 return new r.South(cfg);
35327 return new r.East(cfg);
35329 return new r.West(cfg);
35331 return new r.Center(cfg);
35333 throw 'Layout region "'+target+'" not supported.';
35340 * Ext JS Library 1.1.1
35341 * Copyright(c) 2006-2007, Ext JS, LLC.
35343 * Originally Released Under LGPL - original licence link has changed is not relivant.
35346 * <script type="text/javascript">
35350 * @class Roo.bootstrap.layout.Basic
35351 * @extends Roo.util.Observable
35352 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35353 * and does not have a titlebar, tabs or any other features. All it does is size and position
35354 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35355 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35356 * @cfg {string} region the region that it inhabits..
35357 * @cfg {bool} skipConfig skip config?
35361 Roo.bootstrap.layout.Basic = function(config){
35363 this.mgr = config.mgr;
35365 this.position = config.region;
35367 var skipConfig = config.skipConfig;
35371 * @scope Roo.BasicLayoutRegion
35375 * @event beforeremove
35376 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35377 * @param {Roo.LayoutRegion} this
35378 * @param {Roo.ContentPanel} panel The panel
35379 * @param {Object} e The cancel event object
35381 "beforeremove" : true,
35383 * @event invalidated
35384 * Fires when the layout for this region is changed.
35385 * @param {Roo.LayoutRegion} this
35387 "invalidated" : true,
35389 * @event visibilitychange
35390 * Fires when this region is shown or hidden
35391 * @param {Roo.LayoutRegion} this
35392 * @param {Boolean} visibility true or false
35394 "visibilitychange" : true,
35396 * @event paneladded
35397 * Fires when a panel is added.
35398 * @param {Roo.LayoutRegion} this
35399 * @param {Roo.ContentPanel} panel The panel
35401 "paneladded" : true,
35403 * @event panelremoved
35404 * Fires when a panel is removed.
35405 * @param {Roo.LayoutRegion} this
35406 * @param {Roo.ContentPanel} panel The panel
35408 "panelremoved" : true,
35410 * @event beforecollapse
35411 * Fires when this region before collapse.
35412 * @param {Roo.LayoutRegion} this
35414 "beforecollapse" : true,
35417 * Fires when this region is collapsed.
35418 * @param {Roo.LayoutRegion} this
35420 "collapsed" : true,
35423 * Fires when this region is expanded.
35424 * @param {Roo.LayoutRegion} this
35429 * Fires when this region is slid into view.
35430 * @param {Roo.LayoutRegion} this
35432 "slideshow" : true,
35435 * Fires when this region slides out of view.
35436 * @param {Roo.LayoutRegion} this
35438 "slidehide" : true,
35440 * @event panelactivated
35441 * Fires when a panel is activated.
35442 * @param {Roo.LayoutRegion} this
35443 * @param {Roo.ContentPanel} panel The activated panel
35445 "panelactivated" : true,
35448 * Fires when the user resizes this region.
35449 * @param {Roo.LayoutRegion} this
35450 * @param {Number} newSize The new size (width for east/west, height for north/south)
35454 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35455 this.panels = new Roo.util.MixedCollection();
35456 this.panels.getKey = this.getPanelId.createDelegate(this);
35458 this.activePanel = null;
35459 // ensure listeners are added...
35461 if (config.listeners || config.events) {
35462 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35463 listeners : config.listeners || {},
35464 events : config.events || {}
35468 if(skipConfig !== true){
35469 this.applyConfig(config);
35473 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35475 getPanelId : function(p){
35479 applyConfig : function(config){
35480 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35481 this.config = config;
35486 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35487 * the width, for horizontal (north, south) the height.
35488 * @param {Number} newSize The new width or height
35490 resizeTo : function(newSize){
35491 var el = this.el ? this.el :
35492 (this.activePanel ? this.activePanel.getEl() : null);
35494 switch(this.position){
35497 el.setWidth(newSize);
35498 this.fireEvent("resized", this, newSize);
35502 el.setHeight(newSize);
35503 this.fireEvent("resized", this, newSize);
35509 getBox : function(){
35510 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35513 getMargins : function(){
35514 return this.margins;
35517 updateBox : function(box){
35519 var el = this.activePanel.getEl();
35520 el.dom.style.left = box.x + "px";
35521 el.dom.style.top = box.y + "px";
35522 this.activePanel.setSize(box.width, box.height);
35526 * Returns the container element for this region.
35527 * @return {Roo.Element}
35529 getEl : function(){
35530 return this.activePanel;
35534 * Returns true if this region is currently visible.
35535 * @return {Boolean}
35537 isVisible : function(){
35538 return this.activePanel ? true : false;
35541 setActivePanel : function(panel){
35542 panel = this.getPanel(panel);
35543 if(this.activePanel && this.activePanel != panel){
35544 this.activePanel.setActiveState(false);
35545 this.activePanel.getEl().setLeftTop(-10000,-10000);
35547 this.activePanel = panel;
35548 panel.setActiveState(true);
35550 panel.setSize(this.box.width, this.box.height);
35552 this.fireEvent("panelactivated", this, panel);
35553 this.fireEvent("invalidated");
35557 * Show the specified panel.
35558 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35559 * @return {Roo.ContentPanel} The shown panel or null
35561 showPanel : function(panel){
35562 panel = this.getPanel(panel);
35564 this.setActivePanel(panel);
35570 * Get the active panel for this region.
35571 * @return {Roo.ContentPanel} The active panel or null
35573 getActivePanel : function(){
35574 return this.activePanel;
35578 * Add the passed ContentPanel(s)
35579 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35580 * @return {Roo.ContentPanel} The panel added (if only one was added)
35582 add : function(panel){
35583 if(arguments.length > 1){
35584 for(var i = 0, len = arguments.length; i < len; i++) {
35585 this.add(arguments[i]);
35589 if(this.hasPanel(panel)){
35590 this.showPanel(panel);
35593 var el = panel.getEl();
35594 if(el.dom.parentNode != this.mgr.el.dom){
35595 this.mgr.el.dom.appendChild(el.dom);
35597 if(panel.setRegion){
35598 panel.setRegion(this);
35600 this.panels.add(panel);
35601 el.setStyle("position", "absolute");
35602 if(!panel.background){
35603 this.setActivePanel(panel);
35604 if(this.config.initialSize && this.panels.getCount()==1){
35605 this.resizeTo(this.config.initialSize);
35608 this.fireEvent("paneladded", this, panel);
35613 * Returns true if the panel is in this region.
35614 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35615 * @return {Boolean}
35617 hasPanel : function(panel){
35618 if(typeof panel == "object"){ // must be panel obj
35619 panel = panel.getId();
35621 return this.getPanel(panel) ? true : false;
35625 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35626 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35627 * @param {Boolean} preservePanel Overrides the config preservePanel option
35628 * @return {Roo.ContentPanel} The panel that was removed
35630 remove : function(panel, preservePanel){
35631 panel = this.getPanel(panel);
35636 this.fireEvent("beforeremove", this, panel, e);
35637 if(e.cancel === true){
35640 var panelId = panel.getId();
35641 this.panels.removeKey(panelId);
35646 * Returns the panel specified or null if it's not in this region.
35647 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35648 * @return {Roo.ContentPanel}
35650 getPanel : function(id){
35651 if(typeof id == "object"){ // must be panel obj
35654 return this.panels.get(id);
35658 * Returns this regions position (north/south/east/west/center).
35661 getPosition: function(){
35662 return this.position;
35666 * Ext JS Library 1.1.1
35667 * Copyright(c) 2006-2007, Ext JS, LLC.
35669 * Originally Released Under LGPL - original licence link has changed is not relivant.
35672 * <script type="text/javascript">
35676 * @class Roo.bootstrap.layout.Region
35677 * @extends Roo.bootstrap.layout.Basic
35678 * This class represents a region in a layout manager.
35680 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35681 * @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})
35682 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35683 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35684 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35685 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35686 * @cfg {String} title The title for the region (overrides panel titles)
35687 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35688 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35689 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35690 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35691 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35692 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35693 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35694 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35695 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35696 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35698 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35699 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35700 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35701 * @cfg {Number} width For East/West panels
35702 * @cfg {Number} height For North/South panels
35703 * @cfg {Boolean} split To show the splitter
35704 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35706 * @cfg {string} cls Extra CSS classes to add to region
35708 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35709 * @cfg {string} region the region that it inhabits..
35712 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35713 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35715 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35716 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35717 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35719 Roo.bootstrap.layout.Region = function(config)
35721 this.applyConfig(config);
35723 var mgr = config.mgr;
35724 var pos = config.region;
35725 config.skipConfig = true;
35726 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35729 this.onRender(mgr.el);
35732 this.visible = true;
35733 this.collapsed = false;
35734 this.unrendered_panels = [];
35737 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35739 position: '', // set by wrapper (eg. north/south etc..)
35740 unrendered_panels : null, // unrendered panels.
35741 createBody : function(){
35742 /** This region's body element
35743 * @type Roo.Element */
35744 this.bodyEl = this.el.createChild({
35746 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35750 onRender: function(ctr, pos)
35752 var dh = Roo.DomHelper;
35753 /** This region's container element
35754 * @type Roo.Element */
35755 this.el = dh.append(ctr.dom, {
35757 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35759 /** This region's title element
35760 * @type Roo.Element */
35762 this.titleEl = dh.append(this.el.dom,
35765 unselectable: "on",
35766 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35768 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35769 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35772 this.titleEl.enableDisplayMode();
35773 /** This region's title text element
35774 * @type HTMLElement */
35775 this.titleTextEl = this.titleEl.dom.firstChild;
35776 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35778 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35779 this.closeBtn.enableDisplayMode();
35780 this.closeBtn.on("click", this.closeClicked, this);
35781 this.closeBtn.hide();
35783 this.createBody(this.config);
35784 if(this.config.hideWhenEmpty){
35786 this.on("paneladded", this.validateVisibility, this);
35787 this.on("panelremoved", this.validateVisibility, this);
35789 if(this.autoScroll){
35790 this.bodyEl.setStyle("overflow", "auto");
35792 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35794 //if(c.titlebar !== false){
35795 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35796 this.titleEl.hide();
35798 this.titleEl.show();
35799 if(this.config.title){
35800 this.titleTextEl.innerHTML = this.config.title;
35804 if(this.config.collapsed){
35805 this.collapse(true);
35807 if(this.config.hidden){
35811 if (this.unrendered_panels && this.unrendered_panels.length) {
35812 for (var i =0;i< this.unrendered_panels.length; i++) {
35813 this.add(this.unrendered_panels[i]);
35815 this.unrendered_panels = null;
35821 applyConfig : function(c)
35824 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35825 var dh = Roo.DomHelper;
35826 if(c.titlebar !== false){
35827 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35828 this.collapseBtn.on("click", this.collapse, this);
35829 this.collapseBtn.enableDisplayMode();
35831 if(c.showPin === true || this.showPin){
35832 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35833 this.stickBtn.enableDisplayMode();
35834 this.stickBtn.on("click", this.expand, this);
35835 this.stickBtn.hide();
35840 /** This region's collapsed element
35841 * @type Roo.Element */
35844 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35845 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35848 if(c.floatable !== false){
35849 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35850 this.collapsedEl.on("click", this.collapseClick, this);
35853 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35854 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35855 id: "message", unselectable: "on", style:{"float":"left"}});
35856 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35858 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35859 this.expandBtn.on("click", this.expand, this);
35863 if(this.collapseBtn){
35864 this.collapseBtn.setVisible(c.collapsible == true);
35867 this.cmargins = c.cmargins || this.cmargins ||
35868 (this.position == "west" || this.position == "east" ?
35869 {top: 0, left: 2, right:2, bottom: 0} :
35870 {top: 2, left: 0, right:0, bottom: 2});
35872 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35875 this.bottomTabs = c.tabPosition != "top";
35877 this.autoScroll = c.autoScroll || false;
35882 this.duration = c.duration || .30;
35883 this.slideDuration = c.slideDuration || .45;
35888 * Returns true if this region is currently visible.
35889 * @return {Boolean}
35891 isVisible : function(){
35892 return this.visible;
35896 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35897 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35899 //setCollapsedTitle : function(title){
35900 // title = title || " ";
35901 // if(this.collapsedTitleTextEl){
35902 // this.collapsedTitleTextEl.innerHTML = title;
35906 getBox : function(){
35908 // if(!this.collapsed){
35909 b = this.el.getBox(false, true);
35911 // b = this.collapsedEl.getBox(false, true);
35916 getMargins : function(){
35917 return this.margins;
35918 //return this.collapsed ? this.cmargins : this.margins;
35921 highlight : function(){
35922 this.el.addClass("x-layout-panel-dragover");
35925 unhighlight : function(){
35926 this.el.removeClass("x-layout-panel-dragover");
35929 updateBox : function(box)
35931 if (!this.bodyEl) {
35932 return; // not rendered yet..
35936 if(!this.collapsed){
35937 this.el.dom.style.left = box.x + "px";
35938 this.el.dom.style.top = box.y + "px";
35939 this.updateBody(box.width, box.height);
35941 this.collapsedEl.dom.style.left = box.x + "px";
35942 this.collapsedEl.dom.style.top = box.y + "px";
35943 this.collapsedEl.setSize(box.width, box.height);
35946 this.tabs.autoSizeTabs();
35950 updateBody : function(w, h)
35953 this.el.setWidth(w);
35954 w -= this.el.getBorderWidth("rl");
35955 if(this.config.adjustments){
35956 w += this.config.adjustments[0];
35959 if(h !== null && h > 0){
35960 this.el.setHeight(h);
35961 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35962 h -= this.el.getBorderWidth("tb");
35963 if(this.config.adjustments){
35964 h += this.config.adjustments[1];
35966 this.bodyEl.setHeight(h);
35968 h = this.tabs.syncHeight(h);
35971 if(this.panelSize){
35972 w = w !== null ? w : this.panelSize.width;
35973 h = h !== null ? h : this.panelSize.height;
35975 if(this.activePanel){
35976 var el = this.activePanel.getEl();
35977 w = w !== null ? w : el.getWidth();
35978 h = h !== null ? h : el.getHeight();
35979 this.panelSize = {width: w, height: h};
35980 this.activePanel.setSize(w, h);
35982 if(Roo.isIE && this.tabs){
35983 this.tabs.el.repaint();
35988 * Returns the container element for this region.
35989 * @return {Roo.Element}
35991 getEl : function(){
35996 * Hides this region.
35999 //if(!this.collapsed){
36000 this.el.dom.style.left = "-2000px";
36003 // this.collapsedEl.dom.style.left = "-2000px";
36004 // this.collapsedEl.hide();
36006 this.visible = false;
36007 this.fireEvent("visibilitychange", this, false);
36011 * Shows this region if it was previously hidden.
36014 //if(!this.collapsed){
36017 // this.collapsedEl.show();
36019 this.visible = true;
36020 this.fireEvent("visibilitychange", this, true);
36023 closeClicked : function(){
36024 if(this.activePanel){
36025 this.remove(this.activePanel);
36029 collapseClick : function(e){
36031 e.stopPropagation();
36034 e.stopPropagation();
36040 * Collapses this region.
36041 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36044 collapse : function(skipAnim, skipCheck = false){
36045 if(this.collapsed) {
36049 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36051 this.collapsed = true;
36053 this.split.el.hide();
36055 if(this.config.animate && skipAnim !== true){
36056 this.fireEvent("invalidated", this);
36057 this.animateCollapse();
36059 this.el.setLocation(-20000,-20000);
36061 this.collapsedEl.show();
36062 this.fireEvent("collapsed", this);
36063 this.fireEvent("invalidated", this);
36069 animateCollapse : function(){
36074 * Expands this region if it was previously collapsed.
36075 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36076 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36079 expand : function(e, skipAnim){
36081 e.stopPropagation();
36083 if(!this.collapsed || this.el.hasActiveFx()) {
36087 this.afterSlideIn();
36090 this.collapsed = false;
36091 if(this.config.animate && skipAnim !== true){
36092 this.animateExpand();
36096 this.split.el.show();
36098 this.collapsedEl.setLocation(-2000,-2000);
36099 this.collapsedEl.hide();
36100 this.fireEvent("invalidated", this);
36101 this.fireEvent("expanded", this);
36105 animateExpand : function(){
36109 initTabs : function()
36111 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36113 var ts = new Roo.bootstrap.panel.Tabs({
36114 el: this.bodyEl.dom,
36115 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36116 disableTooltips: this.config.disableTabTips,
36117 toolbar : this.config.toolbar
36120 if(this.config.hideTabs){
36121 ts.stripWrap.setDisplayed(false);
36124 ts.resizeTabs = this.config.resizeTabs === true;
36125 ts.minTabWidth = this.config.minTabWidth || 40;
36126 ts.maxTabWidth = this.config.maxTabWidth || 250;
36127 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36128 ts.monitorResize = false;
36129 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36130 ts.bodyEl.addClass('roo-layout-tabs-body');
36131 this.panels.each(this.initPanelAsTab, this);
36134 initPanelAsTab : function(panel){
36135 var ti = this.tabs.addTab(
36139 this.config.closeOnTab && panel.isClosable(),
36142 if(panel.tabTip !== undefined){
36143 ti.setTooltip(panel.tabTip);
36145 ti.on("activate", function(){
36146 this.setActivePanel(panel);
36149 if(this.config.closeOnTab){
36150 ti.on("beforeclose", function(t, e){
36152 this.remove(panel);
36156 panel.tabItem = ti;
36161 updatePanelTitle : function(panel, title)
36163 if(this.activePanel == panel){
36164 this.updateTitle(title);
36167 var ti = this.tabs.getTab(panel.getEl().id);
36169 if(panel.tabTip !== undefined){
36170 ti.setTooltip(panel.tabTip);
36175 updateTitle : function(title){
36176 if(this.titleTextEl && !this.config.title){
36177 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36181 setActivePanel : function(panel)
36183 panel = this.getPanel(panel);
36184 if(this.activePanel && this.activePanel != panel){
36185 if(this.activePanel.setActiveState(false) === false){
36189 this.activePanel = panel;
36190 panel.setActiveState(true);
36191 if(this.panelSize){
36192 panel.setSize(this.panelSize.width, this.panelSize.height);
36195 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36197 this.updateTitle(panel.getTitle());
36199 this.fireEvent("invalidated", this);
36201 this.fireEvent("panelactivated", this, panel);
36205 * Shows the specified panel.
36206 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36207 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36209 showPanel : function(panel)
36211 panel = this.getPanel(panel);
36214 var tab = this.tabs.getTab(panel.getEl().id);
36215 if(tab.isHidden()){
36216 this.tabs.unhideTab(tab.id);
36220 this.setActivePanel(panel);
36227 * Get the active panel for this region.
36228 * @return {Roo.ContentPanel} The active panel or null
36230 getActivePanel : function(){
36231 return this.activePanel;
36234 validateVisibility : function(){
36235 if(this.panels.getCount() < 1){
36236 this.updateTitle(" ");
36237 this.closeBtn.hide();
36240 if(!this.isVisible()){
36247 * Adds the passed ContentPanel(s) to this region.
36248 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36249 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36251 add : function(panel)
36253 if(arguments.length > 1){
36254 for(var i = 0, len = arguments.length; i < len; i++) {
36255 this.add(arguments[i]);
36260 // if we have not been rendered yet, then we can not really do much of this..
36261 if (!this.bodyEl) {
36262 this.unrendered_panels.push(panel);
36269 if(this.hasPanel(panel)){
36270 this.showPanel(panel);
36273 panel.setRegion(this);
36274 this.panels.add(panel);
36275 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36276 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36277 // and hide them... ???
36278 this.bodyEl.dom.appendChild(panel.getEl().dom);
36279 if(panel.background !== true){
36280 this.setActivePanel(panel);
36282 this.fireEvent("paneladded", this, panel);
36289 this.initPanelAsTab(panel);
36293 if(panel.background !== true){
36294 this.tabs.activate(panel.getEl().id);
36296 this.fireEvent("paneladded", this, panel);
36301 * Hides the tab for the specified panel.
36302 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36304 hidePanel : function(panel){
36305 if(this.tabs && (panel = this.getPanel(panel))){
36306 this.tabs.hideTab(panel.getEl().id);
36311 * Unhides the tab for a previously hidden panel.
36312 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36314 unhidePanel : function(panel){
36315 if(this.tabs && (panel = this.getPanel(panel))){
36316 this.tabs.unhideTab(panel.getEl().id);
36320 clearPanels : function(){
36321 while(this.panels.getCount() > 0){
36322 this.remove(this.panels.first());
36327 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36328 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36329 * @param {Boolean} preservePanel Overrides the config preservePanel option
36330 * @return {Roo.ContentPanel} The panel that was removed
36332 remove : function(panel, preservePanel)
36334 panel = this.getPanel(panel);
36339 this.fireEvent("beforeremove", this, panel, e);
36340 if(e.cancel === true){
36343 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36344 var panelId = panel.getId();
36345 this.panels.removeKey(panelId);
36347 document.body.appendChild(panel.getEl().dom);
36350 this.tabs.removeTab(panel.getEl().id);
36351 }else if (!preservePanel){
36352 this.bodyEl.dom.removeChild(panel.getEl().dom);
36354 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36355 var p = this.panels.first();
36356 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36357 tempEl.appendChild(p.getEl().dom);
36358 this.bodyEl.update("");
36359 this.bodyEl.dom.appendChild(p.getEl().dom);
36361 this.updateTitle(p.getTitle());
36363 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36364 this.setActivePanel(p);
36366 panel.setRegion(null);
36367 if(this.activePanel == panel){
36368 this.activePanel = null;
36370 if(this.config.autoDestroy !== false && preservePanel !== true){
36371 try{panel.destroy();}catch(e){}
36373 this.fireEvent("panelremoved", this, panel);
36378 * Returns the TabPanel component used by this region
36379 * @return {Roo.TabPanel}
36381 getTabs : function(){
36385 createTool : function(parentEl, className){
36386 var btn = Roo.DomHelper.append(parentEl, {
36388 cls: "x-layout-tools-button",
36391 cls: "roo-layout-tools-button-inner " + className,
36395 btn.addClassOnOver("roo-layout-tools-button-over");
36400 * Ext JS Library 1.1.1
36401 * Copyright(c) 2006-2007, Ext JS, LLC.
36403 * Originally Released Under LGPL - original licence link has changed is not relivant.
36406 * <script type="text/javascript">
36412 * @class Roo.SplitLayoutRegion
36413 * @extends Roo.LayoutRegion
36414 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36416 Roo.bootstrap.layout.Split = function(config){
36417 this.cursor = config.cursor;
36418 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36421 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36423 splitTip : "Drag to resize.",
36424 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36425 useSplitTips : false,
36427 applyConfig : function(config){
36428 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36431 onRender : function(ctr,pos) {
36433 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36434 if(!this.config.split){
36439 var splitEl = Roo.DomHelper.append(ctr.dom, {
36441 id: this.el.id + "-split",
36442 cls: "roo-layout-split roo-layout-split-"+this.position,
36445 /** The SplitBar for this region
36446 * @type Roo.SplitBar */
36447 // does not exist yet...
36448 Roo.log([this.position, this.orientation]);
36450 this.split = new Roo.bootstrap.SplitBar({
36451 dragElement : splitEl,
36452 resizingElement: this.el,
36453 orientation : this.orientation
36456 this.split.on("moved", this.onSplitMove, this);
36457 this.split.useShim = this.config.useShim === true;
36458 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36459 if(this.useSplitTips){
36460 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36462 //if(config.collapsible){
36463 // this.split.el.on("dblclick", this.collapse, this);
36466 if(typeof this.config.minSize != "undefined"){
36467 this.split.minSize = this.config.minSize;
36469 if(typeof this.config.maxSize != "undefined"){
36470 this.split.maxSize = this.config.maxSize;
36472 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36473 this.hideSplitter();
36478 getHMaxSize : function(){
36479 var cmax = this.config.maxSize || 10000;
36480 var center = this.mgr.getRegion("center");
36481 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36484 getVMaxSize : function(){
36485 var cmax = this.config.maxSize || 10000;
36486 var center = this.mgr.getRegion("center");
36487 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36490 onSplitMove : function(split, newSize){
36491 this.fireEvent("resized", this, newSize);
36495 * Returns the {@link Roo.SplitBar} for this region.
36496 * @return {Roo.SplitBar}
36498 getSplitBar : function(){
36503 this.hideSplitter();
36504 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36507 hideSplitter : function(){
36509 this.split.el.setLocation(-2000,-2000);
36510 this.split.el.hide();
36516 this.split.el.show();
36518 Roo.bootstrap.layout.Split.superclass.show.call(this);
36521 beforeSlide: function(){
36522 if(Roo.isGecko){// firefox overflow auto bug workaround
36523 this.bodyEl.clip();
36525 this.tabs.bodyEl.clip();
36527 if(this.activePanel){
36528 this.activePanel.getEl().clip();
36530 if(this.activePanel.beforeSlide){
36531 this.activePanel.beforeSlide();
36537 afterSlide : function(){
36538 if(Roo.isGecko){// firefox overflow auto bug workaround
36539 this.bodyEl.unclip();
36541 this.tabs.bodyEl.unclip();
36543 if(this.activePanel){
36544 this.activePanel.getEl().unclip();
36545 if(this.activePanel.afterSlide){
36546 this.activePanel.afterSlide();
36552 initAutoHide : function(){
36553 if(this.autoHide !== false){
36554 if(!this.autoHideHd){
36555 var st = new Roo.util.DelayedTask(this.slideIn, this);
36556 this.autoHideHd = {
36557 "mouseout": function(e){
36558 if(!e.within(this.el, true)){
36562 "mouseover" : function(e){
36568 this.el.on(this.autoHideHd);
36572 clearAutoHide : function(){
36573 if(this.autoHide !== false){
36574 this.el.un("mouseout", this.autoHideHd.mouseout);
36575 this.el.un("mouseover", this.autoHideHd.mouseover);
36579 clearMonitor : function(){
36580 Roo.get(document).un("click", this.slideInIf, this);
36583 // these names are backwards but not changed for compat
36584 slideOut : function(){
36585 if(this.isSlid || this.el.hasActiveFx()){
36588 this.isSlid = true;
36589 if(this.collapseBtn){
36590 this.collapseBtn.hide();
36592 this.closeBtnState = this.closeBtn.getStyle('display');
36593 this.closeBtn.hide();
36595 this.stickBtn.show();
36598 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36599 this.beforeSlide();
36600 this.el.setStyle("z-index", 10001);
36601 this.el.slideIn(this.getSlideAnchor(), {
36602 callback: function(){
36604 this.initAutoHide();
36605 Roo.get(document).on("click", this.slideInIf, this);
36606 this.fireEvent("slideshow", this);
36613 afterSlideIn : function(){
36614 this.clearAutoHide();
36615 this.isSlid = false;
36616 this.clearMonitor();
36617 this.el.setStyle("z-index", "");
36618 if(this.collapseBtn){
36619 this.collapseBtn.show();
36621 this.closeBtn.setStyle('display', this.closeBtnState);
36623 this.stickBtn.hide();
36625 this.fireEvent("slidehide", this);
36628 slideIn : function(cb){
36629 if(!this.isSlid || this.el.hasActiveFx()){
36633 this.isSlid = false;
36634 this.beforeSlide();
36635 this.el.slideOut(this.getSlideAnchor(), {
36636 callback: function(){
36637 this.el.setLeftTop(-10000, -10000);
36639 this.afterSlideIn();
36647 slideInIf : function(e){
36648 if(!e.within(this.el)){
36653 animateCollapse : function(){
36654 this.beforeSlide();
36655 this.el.setStyle("z-index", 20000);
36656 var anchor = this.getSlideAnchor();
36657 this.el.slideOut(anchor, {
36658 callback : function(){
36659 this.el.setStyle("z-index", "");
36660 this.collapsedEl.slideIn(anchor, {duration:.3});
36662 this.el.setLocation(-10000,-10000);
36664 this.fireEvent("collapsed", this);
36671 animateExpand : function(){
36672 this.beforeSlide();
36673 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36674 this.el.setStyle("z-index", 20000);
36675 this.collapsedEl.hide({
36678 this.el.slideIn(this.getSlideAnchor(), {
36679 callback : function(){
36680 this.el.setStyle("z-index", "");
36683 this.split.el.show();
36685 this.fireEvent("invalidated", this);
36686 this.fireEvent("expanded", this);
36714 getAnchor : function(){
36715 return this.anchors[this.position];
36718 getCollapseAnchor : function(){
36719 return this.canchors[this.position];
36722 getSlideAnchor : function(){
36723 return this.sanchors[this.position];
36726 getAlignAdj : function(){
36727 var cm = this.cmargins;
36728 switch(this.position){
36744 getExpandAdj : function(){
36745 var c = this.collapsedEl, cm = this.cmargins;
36746 switch(this.position){
36748 return [-(cm.right+c.getWidth()+cm.left), 0];
36751 return [cm.right+c.getWidth()+cm.left, 0];
36754 return [0, -(cm.top+cm.bottom+c.getHeight())];
36757 return [0, cm.top+cm.bottom+c.getHeight()];
36763 * Ext JS Library 1.1.1
36764 * Copyright(c) 2006-2007, Ext JS, LLC.
36766 * Originally Released Under LGPL - original licence link has changed is not relivant.
36769 * <script type="text/javascript">
36772 * These classes are private internal classes
36774 Roo.bootstrap.layout.Center = function(config){
36775 config.region = "center";
36776 Roo.bootstrap.layout.Region.call(this, config);
36777 this.visible = true;
36778 this.minWidth = config.minWidth || 20;
36779 this.minHeight = config.minHeight || 20;
36782 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36784 // center panel can't be hidden
36788 // center panel can't be hidden
36791 getMinWidth: function(){
36792 return this.minWidth;
36795 getMinHeight: function(){
36796 return this.minHeight;
36809 Roo.bootstrap.layout.North = function(config)
36811 config.region = 'north';
36812 config.cursor = 'n-resize';
36814 Roo.bootstrap.layout.Split.call(this, config);
36818 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36819 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36820 this.split.el.addClass("roo-layout-split-v");
36822 var size = config.initialSize || config.height;
36823 if(typeof size != "undefined"){
36824 this.el.setHeight(size);
36827 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36829 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36833 getBox : function(){
36834 if(this.collapsed){
36835 return this.collapsedEl.getBox();
36837 var box = this.el.getBox();
36839 box.height += this.split.el.getHeight();
36844 updateBox : function(box){
36845 if(this.split && !this.collapsed){
36846 box.height -= this.split.el.getHeight();
36847 this.split.el.setLeft(box.x);
36848 this.split.el.setTop(box.y+box.height);
36849 this.split.el.setWidth(box.width);
36851 if(this.collapsed){
36852 this.updateBody(box.width, null);
36854 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36862 Roo.bootstrap.layout.South = function(config){
36863 config.region = 'south';
36864 config.cursor = 's-resize';
36865 Roo.bootstrap.layout.Split.call(this, config);
36867 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36868 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36869 this.split.el.addClass("roo-layout-split-v");
36871 var size = config.initialSize || config.height;
36872 if(typeof size != "undefined"){
36873 this.el.setHeight(size);
36877 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36878 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36879 getBox : function(){
36880 if(this.collapsed){
36881 return this.collapsedEl.getBox();
36883 var box = this.el.getBox();
36885 var sh = this.split.el.getHeight();
36892 updateBox : function(box){
36893 if(this.split && !this.collapsed){
36894 var sh = this.split.el.getHeight();
36897 this.split.el.setLeft(box.x);
36898 this.split.el.setTop(box.y-sh);
36899 this.split.el.setWidth(box.width);
36901 if(this.collapsed){
36902 this.updateBody(box.width, null);
36904 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36908 Roo.bootstrap.layout.East = function(config){
36909 config.region = "east";
36910 config.cursor = "e-resize";
36911 Roo.bootstrap.layout.Split.call(this, config);
36913 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36914 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36915 this.split.el.addClass("roo-layout-split-h");
36917 var size = config.initialSize || config.width;
36918 if(typeof size != "undefined"){
36919 this.el.setWidth(size);
36922 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36923 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36924 getBox : function(){
36925 if(this.collapsed){
36926 return this.collapsedEl.getBox();
36928 var box = this.el.getBox();
36930 var sw = this.split.el.getWidth();
36937 updateBox : function(box){
36938 if(this.split && !this.collapsed){
36939 var sw = this.split.el.getWidth();
36941 this.split.el.setLeft(box.x);
36942 this.split.el.setTop(box.y);
36943 this.split.el.setHeight(box.height);
36946 if(this.collapsed){
36947 this.updateBody(null, box.height);
36949 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36953 Roo.bootstrap.layout.West = function(config){
36954 config.region = "west";
36955 config.cursor = "w-resize";
36957 Roo.bootstrap.layout.Split.call(this, config);
36959 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36960 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36961 this.split.el.addClass("roo-layout-split-h");
36965 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36966 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36968 onRender: function(ctr, pos)
36970 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36971 var size = this.config.initialSize || this.config.width;
36972 if(typeof size != "undefined"){
36973 this.el.setWidth(size);
36977 getBox : function(){
36978 if(this.collapsed){
36979 return this.collapsedEl.getBox();
36981 var box = this.el.getBox();
36983 box.width += this.split.el.getWidth();
36988 updateBox : function(box){
36989 if(this.split && !this.collapsed){
36990 var sw = this.split.el.getWidth();
36992 this.split.el.setLeft(box.x+box.width);
36993 this.split.el.setTop(box.y);
36994 this.split.el.setHeight(box.height);
36996 if(this.collapsed){
36997 this.updateBody(null, box.height);
36999 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37002 Roo.namespace("Roo.bootstrap.panel");/*
37004 * Ext JS Library 1.1.1
37005 * Copyright(c) 2006-2007, Ext JS, LLC.
37007 * Originally Released Under LGPL - original licence link has changed is not relivant.
37010 * <script type="text/javascript">
37013 * @class Roo.ContentPanel
37014 * @extends Roo.util.Observable
37015 * A basic ContentPanel element.
37016 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37017 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37018 * @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
37019 * @cfg {Boolean} closable True if the panel can be closed/removed
37020 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37021 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37022 * @cfg {Toolbar} toolbar A toolbar for this panel
37023 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37024 * @cfg {String} title The title for this panel
37025 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37026 * @cfg {String} url Calls {@link #setUrl} with this value
37027 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37028 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37029 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37030 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37031 * @cfg {Boolean} badges render the badges
37034 * Create a new ContentPanel.
37035 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37036 * @param {String/Object} config A string to set only the title or a config object
37037 * @param {String} content (optional) Set the HTML content for this panel
37038 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37040 Roo.bootstrap.panel.Content = function( config){
37042 this.tpl = config.tpl || false;
37044 var el = config.el;
37045 var content = config.content;
37047 if(config.autoCreate){ // xtype is available if this is called from factory
37050 this.el = Roo.get(el);
37051 if(!this.el && config && config.autoCreate){
37052 if(typeof config.autoCreate == "object"){
37053 if(!config.autoCreate.id){
37054 config.autoCreate.id = config.id||el;
37056 this.el = Roo.DomHelper.append(document.body,
37057 config.autoCreate, true);
37059 var elcfg = { tag: "div",
37060 cls: "roo-layout-inactive-content",
37064 elcfg.html = config.html;
37068 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37071 this.closable = false;
37072 this.loaded = false;
37073 this.active = false;
37076 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37078 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37080 this.wrapEl = this.el; //this.el.wrap();
37082 if (config.toolbar.items) {
37083 ti = config.toolbar.items ;
37084 delete config.toolbar.items ;
37088 this.toolbar.render(this.wrapEl, 'before');
37089 for(var i =0;i < ti.length;i++) {
37090 // Roo.log(['add child', items[i]]);
37091 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37093 this.toolbar.items = nitems;
37094 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37095 delete config.toolbar;
37099 // xtype created footer. - not sure if will work as we normally have to render first..
37100 if (this.footer && !this.footer.el && this.footer.xtype) {
37101 if (!this.wrapEl) {
37102 this.wrapEl = this.el.wrap();
37105 this.footer.container = this.wrapEl.createChild();
37107 this.footer = Roo.factory(this.footer, Roo);
37112 if(typeof config == "string"){
37113 this.title = config;
37115 Roo.apply(this, config);
37119 this.resizeEl = Roo.get(this.resizeEl, true);
37121 this.resizeEl = this.el;
37123 // handle view.xtype
37131 * Fires when this panel is activated.
37132 * @param {Roo.ContentPanel} this
37136 * @event deactivate
37137 * Fires when this panel is activated.
37138 * @param {Roo.ContentPanel} this
37140 "deactivate" : true,
37144 * Fires when this panel is resized if fitToFrame is true.
37145 * @param {Roo.ContentPanel} this
37146 * @param {Number} width The width after any component adjustments
37147 * @param {Number} height The height after any component adjustments
37153 * Fires when this tab is created
37154 * @param {Roo.ContentPanel} this
37165 if(this.autoScroll){
37166 this.resizeEl.setStyle("overflow", "auto");
37168 // fix randome scrolling
37169 //this.el.on('scroll', function() {
37170 // Roo.log('fix random scolling');
37171 // this.scrollTo('top',0);
37174 content = content || this.content;
37176 this.setContent(content);
37178 if(config && config.url){
37179 this.setUrl(this.url, this.params, this.loadOnce);
37184 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37186 if (this.view && typeof(this.view.xtype) != 'undefined') {
37187 this.view.el = this.el.appendChild(document.createElement("div"));
37188 this.view = Roo.factory(this.view);
37189 this.view.render && this.view.render(false, '');
37193 this.fireEvent('render', this);
37196 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37200 setRegion : function(region){
37201 this.region = region;
37202 this.setActiveClass(region && !this.background);
37206 setActiveClass: function(state)
37209 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37210 this.el.setStyle('position','relative');
37212 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37213 this.el.setStyle('position', 'absolute');
37218 * Returns the toolbar for this Panel if one was configured.
37219 * @return {Roo.Toolbar}
37221 getToolbar : function(){
37222 return this.toolbar;
37225 setActiveState : function(active)
37227 this.active = active;
37228 this.setActiveClass(active);
37230 if(this.fireEvent("deactivate", this) === false){
37235 this.fireEvent("activate", this);
37239 * Updates this panel's element
37240 * @param {String} content The new content
37241 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37243 setContent : function(content, loadScripts){
37244 this.el.update(content, loadScripts);
37247 ignoreResize : function(w, h){
37248 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37251 this.lastSize = {width: w, height: h};
37256 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37257 * @return {Roo.UpdateManager} The UpdateManager
37259 getUpdateManager : function(){
37260 return this.el.getUpdateManager();
37263 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37264 * @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:
37267 url: "your-url.php",
37268 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37269 callback: yourFunction,
37270 scope: yourObject, //(optional scope)
37273 text: "Loading...",
37278 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37279 * 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.
37280 * @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}
37281 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37282 * @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.
37283 * @return {Roo.ContentPanel} this
37286 var um = this.el.getUpdateManager();
37287 um.update.apply(um, arguments);
37293 * 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.
37294 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37295 * @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)
37296 * @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)
37297 * @return {Roo.UpdateManager} The UpdateManager
37299 setUrl : function(url, params, loadOnce){
37300 if(this.refreshDelegate){
37301 this.removeListener("activate", this.refreshDelegate);
37303 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37304 this.on("activate", this.refreshDelegate);
37305 return this.el.getUpdateManager();
37308 _handleRefresh : function(url, params, loadOnce){
37309 if(!loadOnce || !this.loaded){
37310 var updater = this.el.getUpdateManager();
37311 updater.update(url, params, this._setLoaded.createDelegate(this));
37315 _setLoaded : function(){
37316 this.loaded = true;
37320 * Returns this panel's id
37323 getId : function(){
37328 * Returns this panel's element - used by regiosn to add.
37329 * @return {Roo.Element}
37331 getEl : function(){
37332 return this.wrapEl || this.el;
37337 adjustForComponents : function(width, height)
37339 //Roo.log('adjustForComponents ');
37340 if(this.resizeEl != this.el){
37341 width -= this.el.getFrameWidth('lr');
37342 height -= this.el.getFrameWidth('tb');
37345 var te = this.toolbar.getEl();
37346 te.setWidth(width);
37347 height -= te.getHeight();
37350 var te = this.footer.getEl();
37351 te.setWidth(width);
37352 height -= te.getHeight();
37356 if(this.adjustments){
37357 width += this.adjustments[0];
37358 height += this.adjustments[1];
37360 return {"width": width, "height": height};
37363 setSize : function(width, height){
37364 if(this.fitToFrame && !this.ignoreResize(width, height)){
37365 if(this.fitContainer && this.resizeEl != this.el){
37366 this.el.setSize(width, height);
37368 var size = this.adjustForComponents(width, height);
37369 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37370 this.fireEvent('resize', this, size.width, size.height);
37375 * Returns this panel's title
37378 getTitle : function(){
37380 if (typeof(this.title) != 'object') {
37385 for (var k in this.title) {
37386 if (!this.title.hasOwnProperty(k)) {
37390 if (k.indexOf('-') >= 0) {
37391 var s = k.split('-');
37392 for (var i = 0; i<s.length; i++) {
37393 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37396 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37403 * Set this panel's title
37404 * @param {String} title
37406 setTitle : function(title){
37407 this.title = title;
37409 this.region.updatePanelTitle(this, title);
37414 * Returns true is this panel was configured to be closable
37415 * @return {Boolean}
37417 isClosable : function(){
37418 return this.closable;
37421 beforeSlide : function(){
37423 this.resizeEl.clip();
37426 afterSlide : function(){
37428 this.resizeEl.unclip();
37432 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37433 * Will fail silently if the {@link #setUrl} method has not been called.
37434 * This does not activate the panel, just updates its content.
37436 refresh : function(){
37437 if(this.refreshDelegate){
37438 this.loaded = false;
37439 this.refreshDelegate();
37444 * Destroys this panel
37446 destroy : function(){
37447 this.el.removeAllListeners();
37448 var tempEl = document.createElement("span");
37449 tempEl.appendChild(this.el.dom);
37450 tempEl.innerHTML = "";
37456 * form - if the content panel contains a form - this is a reference to it.
37457 * @type {Roo.form.Form}
37461 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37462 * This contains a reference to it.
37468 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37478 * @param {Object} cfg Xtype definition of item to add.
37482 getChildContainer: function () {
37483 return this.getEl();
37488 var ret = new Roo.factory(cfg);
37493 if (cfg.xtype.match(/^Form$/)) {
37496 //if (this.footer) {
37497 // el = this.footer.container.insertSibling(false, 'before');
37499 el = this.el.createChild();
37502 this.form = new Roo.form.Form(cfg);
37505 if ( this.form.allItems.length) {
37506 this.form.render(el.dom);
37510 // should only have one of theses..
37511 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37512 // views.. should not be just added - used named prop 'view''
37514 cfg.el = this.el.appendChild(document.createElement("div"));
37517 var ret = new Roo.factory(cfg);
37519 ret.render && ret.render(false, ''); // render blank..
37529 * @class Roo.bootstrap.panel.Grid
37530 * @extends Roo.bootstrap.panel.Content
37532 * Create a new GridPanel.
37533 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37534 * @param {Object} config A the config object
37540 Roo.bootstrap.panel.Grid = function(config)
37544 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37545 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37547 config.el = this.wrapper;
37548 //this.el = this.wrapper;
37550 if (config.container) {
37551 // ctor'ed from a Border/panel.grid
37554 this.wrapper.setStyle("overflow", "hidden");
37555 this.wrapper.addClass('roo-grid-container');
37560 if(config.toolbar){
37561 var tool_el = this.wrapper.createChild();
37562 this.toolbar = Roo.factory(config.toolbar);
37564 if (config.toolbar.items) {
37565 ti = config.toolbar.items ;
37566 delete config.toolbar.items ;
37570 this.toolbar.render(tool_el);
37571 for(var i =0;i < ti.length;i++) {
37572 // Roo.log(['add child', items[i]]);
37573 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37575 this.toolbar.items = nitems;
37577 delete config.toolbar;
37580 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37581 config.grid.scrollBody = true;;
37582 config.grid.monitorWindowResize = false; // turn off autosizing
37583 config.grid.autoHeight = false;
37584 config.grid.autoWidth = false;
37586 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37588 if (config.background) {
37589 // render grid on panel activation (if panel background)
37590 this.on('activate', function(gp) {
37591 if (!gp.grid.rendered) {
37592 gp.grid.render(this.wrapper);
37593 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37598 this.grid.render(this.wrapper);
37599 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37602 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37603 // ??? needed ??? config.el = this.wrapper;
37608 // xtype created footer. - not sure if will work as we normally have to render first..
37609 if (this.footer && !this.footer.el && this.footer.xtype) {
37611 var ctr = this.grid.getView().getFooterPanel(true);
37612 this.footer.dataSource = this.grid.dataSource;
37613 this.footer = Roo.factory(this.footer, Roo);
37614 this.footer.render(ctr);
37624 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37625 getId : function(){
37626 return this.grid.id;
37630 * Returns the grid for this panel
37631 * @return {Roo.bootstrap.Table}
37633 getGrid : function(){
37637 setSize : function(width, height){
37638 if(!this.ignoreResize(width, height)){
37639 var grid = this.grid;
37640 var size = this.adjustForComponents(width, height);
37641 var gridel = grid.getGridEl();
37642 gridel.setSize(size.width, size.height);
37644 var thd = grid.getGridEl().select('thead',true).first();
37645 var tbd = grid.getGridEl().select('tbody', true).first();
37647 tbd.setSize(width, height - thd.getHeight());
37656 beforeSlide : function(){
37657 this.grid.getView().scroller.clip();
37660 afterSlide : function(){
37661 this.grid.getView().scroller.unclip();
37664 destroy : function(){
37665 this.grid.destroy();
37667 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37672 * @class Roo.bootstrap.panel.Nest
37673 * @extends Roo.bootstrap.panel.Content
37675 * Create a new Panel, that can contain a layout.Border.
37678 * @param {Roo.BorderLayout} layout The layout for this panel
37679 * @param {String/Object} config A string to set only the title or a config object
37681 Roo.bootstrap.panel.Nest = function(config)
37683 // construct with only one argument..
37684 /* FIXME - implement nicer consturctors
37685 if (layout.layout) {
37687 layout = config.layout;
37688 delete config.layout;
37690 if (layout.xtype && !layout.getEl) {
37691 // then layout needs constructing..
37692 layout = Roo.factory(layout, Roo);
37696 config.el = config.layout.getEl();
37698 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37700 config.layout.monitorWindowResize = false; // turn off autosizing
37701 this.layout = config.layout;
37702 this.layout.getEl().addClass("roo-layout-nested-layout");
37709 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37711 setSize : function(width, height){
37712 if(!this.ignoreResize(width, height)){
37713 var size = this.adjustForComponents(width, height);
37714 var el = this.layout.getEl();
37715 if (size.height < 1) {
37716 el.setWidth(size.width);
37718 el.setSize(size.width, size.height);
37720 var touch = el.dom.offsetWidth;
37721 this.layout.layout();
37722 // ie requires a double layout on the first pass
37723 if(Roo.isIE && !this.initialized){
37724 this.initialized = true;
37725 this.layout.layout();
37730 // activate all subpanels if not currently active..
37732 setActiveState : function(active){
37733 this.active = active;
37734 this.setActiveClass(active);
37737 this.fireEvent("deactivate", this);
37741 this.fireEvent("activate", this);
37742 // not sure if this should happen before or after..
37743 if (!this.layout) {
37744 return; // should not happen..
37747 for (var r in this.layout.regions) {
37748 reg = this.layout.getRegion(r);
37749 if (reg.getActivePanel()) {
37750 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37751 reg.setActivePanel(reg.getActivePanel());
37754 if (!reg.panels.length) {
37757 reg.showPanel(reg.getPanel(0));
37766 * Returns the nested BorderLayout for this panel
37767 * @return {Roo.BorderLayout}
37769 getLayout : function(){
37770 return this.layout;
37774 * Adds a xtype elements to the layout of the nested panel
37778 xtype : 'ContentPanel',
37785 xtype : 'NestedLayoutPanel',
37791 items : [ ... list of content panels or nested layout panels.. ]
37795 * @param {Object} cfg Xtype definition of item to add.
37797 addxtype : function(cfg) {
37798 return this.layout.addxtype(cfg);
37803 * Ext JS Library 1.1.1
37804 * Copyright(c) 2006-2007, Ext JS, LLC.
37806 * Originally Released Under LGPL - original licence link has changed is not relivant.
37809 * <script type="text/javascript">
37812 * @class Roo.TabPanel
37813 * @extends Roo.util.Observable
37814 * A lightweight tab container.
37818 // basic tabs 1, built from existing content
37819 var tabs = new Roo.TabPanel("tabs1");
37820 tabs.addTab("script", "View Script");
37821 tabs.addTab("markup", "View Markup");
37822 tabs.activate("script");
37824 // more advanced tabs, built from javascript
37825 var jtabs = new Roo.TabPanel("jtabs");
37826 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37828 // set up the UpdateManager
37829 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37830 var updater = tab2.getUpdateManager();
37831 updater.setDefaultUrl("ajax1.htm");
37832 tab2.on('activate', updater.refresh, updater, true);
37834 // Use setUrl for Ajax loading
37835 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37836 tab3.setUrl("ajax2.htm", null, true);
37839 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37842 jtabs.activate("jtabs-1");
37845 * Create a new TabPanel.
37846 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37847 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37849 Roo.bootstrap.panel.Tabs = function(config){
37851 * The container element for this TabPanel.
37852 * @type Roo.Element
37854 this.el = Roo.get(config.el);
37857 if(typeof config == "boolean"){
37858 this.tabPosition = config ? "bottom" : "top";
37860 Roo.apply(this, config);
37864 if(this.tabPosition == "bottom"){
37865 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37866 this.el.addClass("roo-tabs-bottom");
37868 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37869 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37870 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37872 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37874 if(this.tabPosition != "bottom"){
37875 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37876 * @type Roo.Element
37878 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37879 this.el.addClass("roo-tabs-top");
37883 this.bodyEl.setStyle("position", "relative");
37885 this.active = null;
37886 this.activateDelegate = this.activate.createDelegate(this);
37891 * Fires when the active tab changes
37892 * @param {Roo.TabPanel} this
37893 * @param {Roo.TabPanelItem} activePanel The new active tab
37897 * @event beforetabchange
37898 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37899 * @param {Roo.TabPanel} this
37900 * @param {Object} e Set cancel to true on this object to cancel the tab change
37901 * @param {Roo.TabPanelItem} tab The tab being changed to
37903 "beforetabchange" : true
37906 Roo.EventManager.onWindowResize(this.onResize, this);
37907 this.cpad = this.el.getPadding("lr");
37908 this.hiddenCount = 0;
37911 // toolbar on the tabbar support...
37912 if (this.toolbar) {
37913 alert("no toolbar support yet");
37914 this.toolbar = false;
37916 var tcfg = this.toolbar;
37917 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37918 this.toolbar = new Roo.Toolbar(tcfg);
37919 if (Roo.isSafari) {
37920 var tbl = tcfg.container.child('table', true);
37921 tbl.setAttribute('width', '100%');
37929 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37932 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37934 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37936 tabPosition : "top",
37938 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37940 currentTabWidth : 0,
37942 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37946 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37950 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37952 preferredTabWidth : 175,
37954 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37956 resizeTabs : false,
37958 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37960 monitorResize : true,
37962 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37967 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37968 * @param {String} id The id of the div to use <b>or create</b>
37969 * @param {String} text The text for the tab
37970 * @param {String} content (optional) Content to put in the TabPanelItem body
37971 * @param {Boolean} closable (optional) True to create a close icon on the tab
37972 * @return {Roo.TabPanelItem} The created TabPanelItem
37974 addTab : function(id, text, content, closable, tpl)
37976 var item = new Roo.bootstrap.panel.TabItem({
37980 closable : closable,
37983 this.addTabItem(item);
37985 item.setContent(content);
37991 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37992 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37993 * @return {Roo.TabPanelItem}
37995 getTab : function(id){
37996 return this.items[id];
38000 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38001 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38003 hideTab : function(id){
38004 var t = this.items[id];
38007 this.hiddenCount++;
38008 this.autoSizeTabs();
38013 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38014 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38016 unhideTab : function(id){
38017 var t = this.items[id];
38019 t.setHidden(false);
38020 this.hiddenCount--;
38021 this.autoSizeTabs();
38026 * Adds an existing {@link Roo.TabPanelItem}.
38027 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38029 addTabItem : function(item){
38030 this.items[item.id] = item;
38031 this.items.push(item);
38032 // if(this.resizeTabs){
38033 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38034 // this.autoSizeTabs();
38036 // item.autoSize();
38041 * Removes a {@link Roo.TabPanelItem}.
38042 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38044 removeTab : function(id){
38045 var items = this.items;
38046 var tab = items[id];
38047 if(!tab) { return; }
38048 var index = items.indexOf(tab);
38049 if(this.active == tab && items.length > 1){
38050 var newTab = this.getNextAvailable(index);
38055 this.stripEl.dom.removeChild(tab.pnode.dom);
38056 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38057 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38059 items.splice(index, 1);
38060 delete this.items[tab.id];
38061 tab.fireEvent("close", tab);
38062 tab.purgeListeners();
38063 this.autoSizeTabs();
38066 getNextAvailable : function(start){
38067 var items = this.items;
38069 // look for a next tab that will slide over to
38070 // replace the one being removed
38071 while(index < items.length){
38072 var item = items[++index];
38073 if(item && !item.isHidden()){
38077 // if one isn't found select the previous tab (on the left)
38080 var item = items[--index];
38081 if(item && !item.isHidden()){
38089 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38090 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38092 disableTab : function(id){
38093 var tab = this.items[id];
38094 if(tab && this.active != tab){
38100 * Enables a {@link Roo.TabPanelItem} that is disabled.
38101 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38103 enableTab : function(id){
38104 var tab = this.items[id];
38109 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38110 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38111 * @return {Roo.TabPanelItem} The TabPanelItem.
38113 activate : function(id){
38114 var tab = this.items[id];
38118 if(tab == this.active || tab.disabled){
38122 this.fireEvent("beforetabchange", this, e, tab);
38123 if(e.cancel !== true && !tab.disabled){
38125 this.active.hide();
38127 this.active = this.items[id];
38128 this.active.show();
38129 this.fireEvent("tabchange", this, this.active);
38135 * Gets the active {@link Roo.TabPanelItem}.
38136 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38138 getActiveTab : function(){
38139 return this.active;
38143 * Updates the tab body element to fit the height of the container element
38144 * for overflow scrolling
38145 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38147 syncHeight : function(targetHeight){
38148 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38149 var bm = this.bodyEl.getMargins();
38150 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38151 this.bodyEl.setHeight(newHeight);
38155 onResize : function(){
38156 if(this.monitorResize){
38157 this.autoSizeTabs();
38162 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38164 beginUpdate : function(){
38165 this.updating = true;
38169 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38171 endUpdate : function(){
38172 this.updating = false;
38173 this.autoSizeTabs();
38177 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38179 autoSizeTabs : function(){
38180 var count = this.items.length;
38181 var vcount = count - this.hiddenCount;
38182 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38185 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38186 var availWidth = Math.floor(w / vcount);
38187 var b = this.stripBody;
38188 if(b.getWidth() > w){
38189 var tabs = this.items;
38190 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38191 if(availWidth < this.minTabWidth){
38192 /*if(!this.sleft){ // incomplete scrolling code
38193 this.createScrollButtons();
38196 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38199 if(this.currentTabWidth < this.preferredTabWidth){
38200 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38206 * Returns the number of tabs in this TabPanel.
38209 getCount : function(){
38210 return this.items.length;
38214 * Resizes all the tabs to the passed width
38215 * @param {Number} The new width
38217 setTabWidth : function(width){
38218 this.currentTabWidth = width;
38219 for(var i = 0, len = this.items.length; i < len; i++) {
38220 if(!this.items[i].isHidden()) {
38221 this.items[i].setWidth(width);
38227 * Destroys this TabPanel
38228 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38230 destroy : function(removeEl){
38231 Roo.EventManager.removeResizeListener(this.onResize, this);
38232 for(var i = 0, len = this.items.length; i < len; i++){
38233 this.items[i].purgeListeners();
38235 if(removeEl === true){
38236 this.el.update("");
38241 createStrip : function(container)
38243 var strip = document.createElement("nav");
38244 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38245 container.appendChild(strip);
38249 createStripList : function(strip)
38251 // div wrapper for retard IE
38252 // returns the "tr" element.
38253 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38254 //'<div class="x-tabs-strip-wrap">'+
38255 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38256 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38257 return strip.firstChild; //.firstChild.firstChild.firstChild;
38259 createBody : function(container)
38261 var body = document.createElement("div");
38262 Roo.id(body, "tab-body");
38263 //Roo.fly(body).addClass("x-tabs-body");
38264 Roo.fly(body).addClass("tab-content");
38265 container.appendChild(body);
38268 createItemBody :function(bodyEl, id){
38269 var body = Roo.getDom(id);
38271 body = document.createElement("div");
38274 //Roo.fly(body).addClass("x-tabs-item-body");
38275 Roo.fly(body).addClass("tab-pane");
38276 bodyEl.insertBefore(body, bodyEl.firstChild);
38280 createStripElements : function(stripEl, text, closable, tpl)
38282 var td = document.createElement("li"); // was td..
38285 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38288 stripEl.appendChild(td);
38290 td.className = "x-tabs-closable";
38291 if(!this.closeTpl){
38292 this.closeTpl = new Roo.Template(
38293 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38294 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38295 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38298 var el = this.closeTpl.overwrite(td, {"text": text});
38299 var close = el.getElementsByTagName("div")[0];
38300 var inner = el.getElementsByTagName("em")[0];
38301 return {"el": el, "close": close, "inner": inner};
38304 // not sure what this is..
38305 // if(!this.tabTpl){
38306 //this.tabTpl = new Roo.Template(
38307 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38308 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38310 // this.tabTpl = new Roo.Template(
38311 // '<a href="#">' +
38312 // '<span unselectable="on"' +
38313 // (this.disableTooltips ? '' : ' title="{text}"') +
38314 // ' >{text}</span></a>'
38320 var template = tpl || this.tabTpl || false;
38324 template = new Roo.Template(
38326 '<span unselectable="on"' +
38327 (this.disableTooltips ? '' : ' title="{text}"') +
38328 ' >{text}</span></a>'
38332 switch (typeof(template)) {
38336 template = new Roo.Template(template);
38342 var el = template.overwrite(td, {"text": text});
38344 var inner = el.getElementsByTagName("span")[0];
38346 return {"el": el, "inner": inner};
38354 * @class Roo.TabPanelItem
38355 * @extends Roo.util.Observable
38356 * Represents an individual item (tab plus body) in a TabPanel.
38357 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38358 * @param {String} id The id of this TabPanelItem
38359 * @param {String} text The text for the tab of this TabPanelItem
38360 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38362 Roo.bootstrap.panel.TabItem = function(config){
38364 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38365 * @type Roo.TabPanel
38367 this.tabPanel = config.panel;
38369 * The id for this TabPanelItem
38372 this.id = config.id;
38374 this.disabled = false;
38376 this.text = config.text;
38378 this.loaded = false;
38379 this.closable = config.closable;
38382 * The body element for this TabPanelItem.
38383 * @type Roo.Element
38385 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38386 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38387 this.bodyEl.setStyle("display", "block");
38388 this.bodyEl.setStyle("zoom", "1");
38389 //this.hideAction();
38391 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38393 this.el = Roo.get(els.el);
38394 this.inner = Roo.get(els.inner, true);
38395 this.textEl = Roo.get(this.el.dom.firstChild, true);
38396 this.pnode = Roo.get(els.el.parentNode, true);
38397 // this.el.on("mousedown", this.onTabMouseDown, this);
38398 this.el.on("click", this.onTabClick, this);
38400 if(config.closable){
38401 var c = Roo.get(els.close, true);
38402 c.dom.title = this.closeText;
38403 c.addClassOnOver("close-over");
38404 c.on("click", this.closeClick, this);
38410 * Fires when this tab becomes the active tab.
38411 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38412 * @param {Roo.TabPanelItem} this
38416 * @event beforeclose
38417 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38418 * @param {Roo.TabPanelItem} this
38419 * @param {Object} e Set cancel to true on this object to cancel the close.
38421 "beforeclose": true,
38424 * Fires when this tab is closed.
38425 * @param {Roo.TabPanelItem} this
38429 * @event deactivate
38430 * Fires when this tab is no longer the active tab.
38431 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38432 * @param {Roo.TabPanelItem} this
38434 "deactivate" : true
38436 this.hidden = false;
38438 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38441 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38443 purgeListeners : function(){
38444 Roo.util.Observable.prototype.purgeListeners.call(this);
38445 this.el.removeAllListeners();
38448 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38451 this.pnode.addClass("active");
38454 this.tabPanel.stripWrap.repaint();
38456 this.fireEvent("activate", this.tabPanel, this);
38460 * Returns true if this tab is the active tab.
38461 * @return {Boolean}
38463 isActive : function(){
38464 return this.tabPanel.getActiveTab() == this;
38468 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38471 this.pnode.removeClass("active");
38473 this.fireEvent("deactivate", this.tabPanel, this);
38476 hideAction : function(){
38477 this.bodyEl.hide();
38478 this.bodyEl.setStyle("position", "absolute");
38479 this.bodyEl.setLeft("-20000px");
38480 this.bodyEl.setTop("-20000px");
38483 showAction : function(){
38484 this.bodyEl.setStyle("position", "relative");
38485 this.bodyEl.setTop("");
38486 this.bodyEl.setLeft("");
38487 this.bodyEl.show();
38491 * Set the tooltip for the tab.
38492 * @param {String} tooltip The tab's tooltip
38494 setTooltip : function(text){
38495 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38496 this.textEl.dom.qtip = text;
38497 this.textEl.dom.removeAttribute('title');
38499 this.textEl.dom.title = text;
38503 onTabClick : function(e){
38504 e.preventDefault();
38505 this.tabPanel.activate(this.id);
38508 onTabMouseDown : function(e){
38509 e.preventDefault();
38510 this.tabPanel.activate(this.id);
38513 getWidth : function(){
38514 return this.inner.getWidth();
38517 setWidth : function(width){
38518 var iwidth = width - this.pnode.getPadding("lr");
38519 this.inner.setWidth(iwidth);
38520 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38521 this.pnode.setWidth(width);
38525 * Show or hide the tab
38526 * @param {Boolean} hidden True to hide or false to show.
38528 setHidden : function(hidden){
38529 this.hidden = hidden;
38530 this.pnode.setStyle("display", hidden ? "none" : "");
38534 * Returns true if this tab is "hidden"
38535 * @return {Boolean}
38537 isHidden : function(){
38538 return this.hidden;
38542 * Returns the text for this tab
38545 getText : function(){
38549 autoSize : function(){
38550 //this.el.beginMeasure();
38551 this.textEl.setWidth(1);
38553 * #2804 [new] Tabs in Roojs
38554 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38556 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38557 //this.el.endMeasure();
38561 * Sets the text for the tab (Note: this also sets the tooltip text)
38562 * @param {String} text The tab's text and tooltip
38564 setText : function(text){
38566 this.textEl.update(text);
38567 this.setTooltip(text);
38568 //if(!this.tabPanel.resizeTabs){
38569 // this.autoSize();
38573 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38575 activate : function(){
38576 this.tabPanel.activate(this.id);
38580 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38582 disable : function(){
38583 if(this.tabPanel.active != this){
38584 this.disabled = true;
38585 this.pnode.addClass("disabled");
38590 * Enables this TabPanelItem if it was previously disabled.
38592 enable : function(){
38593 this.disabled = false;
38594 this.pnode.removeClass("disabled");
38598 * Sets the content for this TabPanelItem.
38599 * @param {String} content The content
38600 * @param {Boolean} loadScripts true to look for and load scripts
38602 setContent : function(content, loadScripts){
38603 this.bodyEl.update(content, loadScripts);
38607 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38608 * @return {Roo.UpdateManager} The UpdateManager
38610 getUpdateManager : function(){
38611 return this.bodyEl.getUpdateManager();
38615 * Set a URL to be used to load the content for this TabPanelItem.
38616 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38617 * @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)
38618 * @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)
38619 * @return {Roo.UpdateManager} The UpdateManager
38621 setUrl : function(url, params, loadOnce){
38622 if(this.refreshDelegate){
38623 this.un('activate', this.refreshDelegate);
38625 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38626 this.on("activate", this.refreshDelegate);
38627 return this.bodyEl.getUpdateManager();
38631 _handleRefresh : function(url, params, loadOnce){
38632 if(!loadOnce || !this.loaded){
38633 var updater = this.bodyEl.getUpdateManager();
38634 updater.update(url, params, this._setLoaded.createDelegate(this));
38639 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38640 * Will fail silently if the setUrl method has not been called.
38641 * This does not activate the panel, just updates its content.
38643 refresh : function(){
38644 if(this.refreshDelegate){
38645 this.loaded = false;
38646 this.refreshDelegate();
38651 _setLoaded : function(){
38652 this.loaded = true;
38656 closeClick : function(e){
38659 this.fireEvent("beforeclose", this, o);
38660 if(o.cancel !== true){
38661 this.tabPanel.removeTab(this.id);
38665 * The text displayed in the tooltip for the close icon.
38668 closeText : "Close this tab"
38671 * This script refer to:
38672 * Title: International Telephone Input
38673 * Author: Jack O'Connor
38674 * Code version: v12.1.12
38675 * Availability: https://github.com/jackocnr/intl-tel-input.git
38678 Roo.bootstrap.PhoneInputData = function() {
38681 "Afghanistan (افغانستان)",
38686 "Albania (Shqipëri)",
38691 "Algeria (الجزائر)",
38716 "Antigua and Barbuda",
38726 "Armenia (Հայաստան)",
38742 "Austria (Österreich)",
38747 "Azerbaijan (Azərbaycan)",
38757 "Bahrain (البحرين)",
38762 "Bangladesh (বাংলাদেশ)",
38772 "Belarus (Беларусь)",
38777 "Belgium (België)",
38807 "Bosnia and Herzegovina (Босна и Херцеговина)",
38822 "British Indian Ocean Territory",
38827 "British Virgin Islands",
38837 "Bulgaria (България)",
38847 "Burundi (Uburundi)",
38852 "Cambodia (កម្ពុជា)",
38857 "Cameroon (Cameroun)",
38866 ["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"]
38869 "Cape Verde (Kabu Verdi)",
38874 "Caribbean Netherlands",
38885 "Central African Republic (République centrafricaine)",
38905 "Christmas Island",
38911 "Cocos (Keeling) Islands",
38922 "Comoros (جزر القمر)",
38927 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38932 "Congo (Republic) (Congo-Brazzaville)",
38952 "Croatia (Hrvatska)",
38973 "Czech Republic (Česká republika)",
38978 "Denmark (Danmark)",
38993 "Dominican Republic (República Dominicana)",
38997 ["809", "829", "849"]
39015 "Equatorial Guinea (Guinea Ecuatorial)",
39035 "Falkland Islands (Islas Malvinas)",
39040 "Faroe Islands (Føroyar)",
39061 "French Guiana (Guyane française)",
39066 "French Polynesia (Polynésie française)",
39081 "Georgia (საქართველო)",
39086 "Germany (Deutschland)",
39106 "Greenland (Kalaallit Nunaat)",
39143 "Guinea-Bissau (Guiné Bissau)",
39168 "Hungary (Magyarország)",
39173 "Iceland (Ísland)",
39193 "Iraq (العراق)",
39209 "Israel (ישראל)",
39236 "Jordan (الأردن)",
39241 "Kazakhstan (Казахстан)",
39262 "Kuwait (الكويت)",
39267 "Kyrgyzstan (Кыргызстан)",
39277 "Latvia (Latvija)",
39282 "Lebanon (لبنان)",
39297 "Libya (ليبيا)",
39307 "Lithuania (Lietuva)",
39322 "Macedonia (FYROM) (Македонија)",
39327 "Madagascar (Madagasikara)",
39357 "Marshall Islands",
39367 "Mauritania (موريتانيا)",
39372 "Mauritius (Moris)",
39393 "Moldova (Republica Moldova)",
39403 "Mongolia (Монгол)",
39408 "Montenegro (Crna Gora)",
39418 "Morocco (المغرب)",
39424 "Mozambique (Moçambique)",
39429 "Myanmar (Burma) (မြန်မာ)",
39434 "Namibia (Namibië)",
39449 "Netherlands (Nederland)",
39454 "New Caledonia (Nouvelle-Calédonie)",
39489 "North Korea (조선 민주주의 인민 공화국)",
39494 "Northern Mariana Islands",
39510 "Pakistan (پاکستان)",
39520 "Palestine (فلسطين)",
39530 "Papua New Guinea",
39572 "Réunion (La Réunion)",
39578 "Romania (România)",
39594 "Saint Barthélemy",
39605 "Saint Kitts and Nevis",
39615 "Saint Martin (Saint-Martin (partie française))",
39621 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39626 "Saint Vincent and the Grenadines",
39641 "São Tomé and Príncipe (São Tomé e Príncipe)",
39646 "Saudi Arabia (المملكة العربية السعودية)",
39651 "Senegal (Sénégal)",
39681 "Slovakia (Slovensko)",
39686 "Slovenia (Slovenija)",
39696 "Somalia (Soomaaliya)",
39706 "South Korea (대한민국)",
39711 "South Sudan (جنوب السودان)",
39721 "Sri Lanka (ශ්රී ලංකාව)",
39726 "Sudan (السودان)",
39736 "Svalbard and Jan Mayen",
39747 "Sweden (Sverige)",
39752 "Switzerland (Schweiz)",
39757 "Syria (سوريا)",
39802 "Trinidad and Tobago",
39807 "Tunisia (تونس)",
39812 "Turkey (Türkiye)",
39822 "Turks and Caicos Islands",
39832 "U.S. Virgin Islands",
39842 "Ukraine (Україна)",
39847 "United Arab Emirates (الإمارات العربية المتحدة)",
39869 "Uzbekistan (Oʻzbekiston)",
39879 "Vatican City (Città del Vaticano)",
39890 "Vietnam (Việt Nam)",
39895 "Wallis and Futuna (Wallis-et-Futuna)",
39900 "Western Sahara (الصحراء الغربية)",
39906 "Yemen (اليمن)",
39930 * This script refer to:
39931 * Title: International Telephone Input
39932 * Author: Jack O'Connor
39933 * Code version: v12.1.12
39934 * Availability: https://github.com/jackocnr/intl-tel-input.git
39938 * @class Roo.bootstrap.PhoneInput
39939 * @extends Roo.bootstrap.TriggerField
39940 * An input with International dial-code selection
39942 * @cfg {String} defaultDialCode default '+852'
39943 * @cfg {Array} preferedCountries default []
39946 * Create a new PhoneInput.
39947 * @param {Object} config Configuration options
39950 Roo.bootstrap.PhoneInput = function(config) {
39951 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39954 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39956 listWidth: undefined,
39958 selectedClass: 'active',
39960 invalidClass : "has-warning",
39962 validClass: 'has-success',
39964 allowed: '0123456789',
39969 * @cfg {String} defaultDialCode The default dial code when initializing the input
39971 defaultDialCode: '+852',
39974 * @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
39976 preferedCountries: false,
39978 getAutoCreate : function()
39980 var data = Roo.bootstrap.PhoneInputData();
39981 var align = this.labelAlign || this.parentLabelAlign();
39984 this.allCountries = [];
39985 this.dialCodeMapping = [];
39987 for (var i = 0; i < data.length; i++) {
39989 this.allCountries[i] = {
39993 priority: c[3] || 0,
39994 areaCodes: c[4] || null
39996 this.dialCodeMapping[c[2]] = {
39999 priority: c[3] || 0,
40000 areaCodes: c[4] || null
40012 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40013 maxlength: this.max_length,
40014 cls : 'form-control tel-input',
40015 autocomplete: 'new-password'
40018 var hiddenInput = {
40021 cls: 'hidden-tel-input'
40025 hiddenInput.name = this.name;
40028 if (this.disabled) {
40029 input.disabled = true;
40032 var flag_container = {
40049 cls: this.hasFeedback ? 'has-feedback' : '',
40055 cls: 'dial-code-holder',
40062 cls: 'roo-select2-container input-group',
40069 if (this.fieldLabel.length) {
40072 tooltip: 'This field is required'
40078 cls: 'control-label',
40084 html: this.fieldLabel
40087 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40093 if(this.indicatorpos == 'right') {
40094 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40101 if(align == 'left') {
40109 if(this.labelWidth > 12){
40110 label.style = "width: " + this.labelWidth + 'px';
40112 if(this.labelWidth < 13 && this.labelmd == 0){
40113 this.labelmd = this.labelWidth;
40115 if(this.labellg > 0){
40116 label.cls += ' col-lg-' + this.labellg;
40117 input.cls += ' col-lg-' + (12 - this.labellg);
40119 if(this.labelmd > 0){
40120 label.cls += ' col-md-' + this.labelmd;
40121 container.cls += ' col-md-' + (12 - this.labelmd);
40123 if(this.labelsm > 0){
40124 label.cls += ' col-sm-' + this.labelsm;
40125 container.cls += ' col-sm-' + (12 - this.labelsm);
40127 if(this.labelxs > 0){
40128 label.cls += ' col-xs-' + this.labelxs;
40129 container.cls += ' col-xs-' + (12 - this.labelxs);
40139 var settings = this;
40141 ['xs','sm','md','lg'].map(function(size){
40142 if (settings[size]) {
40143 cfg.cls += ' col-' + size + '-' + settings[size];
40147 this.store = new Roo.data.Store({
40148 proxy : new Roo.data.MemoryProxy({}),
40149 reader : new Roo.data.JsonReader({
40160 'name' : 'dialCode',
40164 'name' : 'priority',
40168 'name' : 'areaCodes',
40175 if(!this.preferedCountries) {
40176 this.preferedCountries = [
40183 var p = this.preferedCountries.reverse();
40186 for (var i = 0; i < p.length; i++) {
40187 for (var j = 0; j < this.allCountries.length; j++) {
40188 if(this.allCountries[j].iso2 == p[i]) {
40189 var t = this.allCountries[j];
40190 this.allCountries.splice(j,1);
40191 this.allCountries.unshift(t);
40197 this.store.proxy.data = {
40199 data: this.allCountries
40205 initEvents : function()
40208 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40210 this.indicator = this.indicatorEl();
40211 this.flag = this.flagEl();
40212 this.dialCodeHolder = this.dialCodeHolderEl();
40214 this.trigger = this.el.select('div.flag-box',true).first();
40215 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40220 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40221 _this.list.setWidth(lw);
40224 this.list.on('mouseover', this.onViewOver, this);
40225 this.list.on('mousemove', this.onViewMove, this);
40226 this.inputEl().on("keyup", this.onKeyUp, this);
40227 this.inputEl().on("keypress", this.onKeyPress, this);
40229 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40231 this.view = new Roo.View(this.list, this.tpl, {
40232 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40235 this.view.on('click', this.onViewClick, this);
40236 this.setValue(this.defaultDialCode);
40239 onTriggerClick : function(e)
40241 Roo.log('trigger click');
40246 if(this.isExpanded()){
40248 this.hasFocus = false;
40250 this.store.load({});
40251 this.hasFocus = true;
40256 isExpanded : function()
40258 return this.list.isVisible();
40261 collapse : function()
40263 if(!this.isExpanded()){
40267 Roo.get(document).un('mousedown', this.collapseIf, this);
40268 Roo.get(document).un('mousewheel', this.collapseIf, this);
40269 this.fireEvent('collapse', this);
40273 expand : function()
40277 if(this.isExpanded() || !this.hasFocus){
40281 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40282 this.list.setWidth(lw);
40285 this.restrictHeight();
40287 Roo.get(document).on('mousedown', this.collapseIf, this);
40288 Roo.get(document).on('mousewheel', this.collapseIf, this);
40290 this.fireEvent('expand', this);
40293 restrictHeight : function()
40295 this.list.alignTo(this.inputEl(), this.listAlign);
40296 this.list.alignTo(this.inputEl(), this.listAlign);
40299 onViewOver : function(e, t)
40301 if(this.inKeyMode){
40304 var item = this.view.findItemFromChild(t);
40307 var index = this.view.indexOf(item);
40308 this.select(index, false);
40313 onViewClick : function(view, doFocus, el, e)
40315 var index = this.view.getSelectedIndexes()[0];
40317 var r = this.store.getAt(index);
40320 this.onSelect(r, index);
40322 if(doFocus !== false && !this.blockFocus){
40323 this.inputEl().focus();
40327 onViewMove : function(e, t)
40329 this.inKeyMode = false;
40332 select : function(index, scrollIntoView)
40334 this.selectedIndex = index;
40335 this.view.select(index);
40336 if(scrollIntoView !== false){
40337 var el = this.view.getNode(index);
40339 this.list.scrollChildIntoView(el, false);
40344 createList : function()
40346 this.list = Roo.get(document.body).createChild({
40348 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40349 style: 'display:none'
40352 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40355 collapseIf : function(e)
40357 var in_combo = e.within(this.el);
40358 var in_list = e.within(this.list);
40359 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40361 if (in_combo || in_list || is_list) {
40367 onSelect : function(record, index)
40369 if(this.fireEvent('beforeselect', this, record, index) !== false){
40371 this.setFlagClass(record.data.iso2);
40372 this.setDialCode(record.data.dialCode);
40373 this.hasFocus = false;
40375 this.fireEvent('select', this, record, index);
40379 flagEl : function()
40381 var flag = this.el.select('div.flag',true).first();
40388 dialCodeHolderEl : function()
40390 var d = this.el.select('input.dial-code-holder',true).first();
40397 setDialCode : function(v)
40399 this.dialCodeHolder.dom.value = '+'+v;
40402 setFlagClass : function(n)
40404 this.flag.dom.className = 'flag '+n;
40407 getValue : function()
40409 var v = this.inputEl().getValue();
40410 if(this.dialCodeHolder) {
40411 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40416 setValue : function(v)
40418 var d = this.getDialCode(v);
40420 //invalid dial code
40421 if(v.length == 0 || !d || d.length == 0) {
40423 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40424 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40430 this.setFlagClass(this.dialCodeMapping[d].iso2);
40431 this.setDialCode(d);
40432 this.inputEl().dom.value = v.replace('+'+d,'');
40433 this.hiddenEl().dom.value = this.getValue();
40438 getDialCode : function(v)
40442 if (v.length == 0) {
40443 return this.dialCodeHolder.dom.value;
40447 if (v.charAt(0) != "+") {
40450 var numericChars = "";
40451 for (var i = 1; i < v.length; i++) {
40452 var c = v.charAt(i);
40455 if (this.dialCodeMapping[numericChars]) {
40456 dialCode = v.substr(1, i);
40458 if (numericChars.length == 4) {
40468 this.setValue(this.defaultDialCode);
40472 hiddenEl : function()
40474 return this.el.select('input.hidden-tel-input',true).first();
40477 // after setting val
40478 onKeyUp : function(e){
40479 this.setValue(this.getValue());
40482 onKeyPress : function(e){
40483 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40490 * @class Roo.bootstrap.MoneyField
40491 * @extends Roo.bootstrap.ComboBox
40492 * Bootstrap MoneyField class
40495 * Create a new MoneyField.
40496 * @param {Object} config Configuration options
40499 Roo.bootstrap.MoneyField = function(config) {
40501 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40505 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40508 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40510 allowDecimals : true,
40512 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40514 decimalSeparator : ".",
40516 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40518 decimalPrecision : 0,
40520 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40522 allowNegative : true,
40524 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40528 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40530 minValue : Number.NEGATIVE_INFINITY,
40532 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40534 maxValue : Number.MAX_VALUE,
40536 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40538 minText : "The minimum value for this field is {0}",
40540 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40542 maxText : "The maximum value for this field is {0}",
40544 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40545 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40547 nanText : "{0} is not a valid number",
40549 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40553 * @cfg {String} defaults currency of the MoneyField
40554 * value should be in lkey
40556 defaultCurrency : false,
40558 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40560 thousandsDelimiter : false,
40562 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40573 getAutoCreate : function()
40575 var align = this.labelAlign || this.parentLabelAlign();
40587 cls : 'form-control roo-money-amount-input',
40588 autocomplete: 'new-password'
40591 var hiddenInput = {
40595 cls: 'hidden-number-input'
40598 if(this.max_length) {
40599 input.maxlength = this.max_length;
40603 hiddenInput.name = this.name;
40606 if (this.disabled) {
40607 input.disabled = true;
40610 var clg = 12 - this.inputlg;
40611 var cmd = 12 - this.inputmd;
40612 var csm = 12 - this.inputsm;
40613 var cxs = 12 - this.inputxs;
40617 cls : 'row roo-money-field',
40621 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40625 cls: 'roo-select2-container input-group',
40629 cls : 'form-control roo-money-currency-input',
40630 autocomplete: 'new-password',
40632 name : this.currencyName
40636 cls : 'input-group-addon',
40650 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40654 cls: this.hasFeedback ? 'has-feedback' : '',
40665 if (this.fieldLabel.length) {
40668 tooltip: 'This field is required'
40674 cls: 'control-label',
40680 html: this.fieldLabel
40683 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40689 if(this.indicatorpos == 'right') {
40690 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40697 if(align == 'left') {
40705 if(this.labelWidth > 12){
40706 label.style = "width: " + this.labelWidth + 'px';
40708 if(this.labelWidth < 13 && this.labelmd == 0){
40709 this.labelmd = this.labelWidth;
40711 if(this.labellg > 0){
40712 label.cls += ' col-lg-' + this.labellg;
40713 input.cls += ' col-lg-' + (12 - this.labellg);
40715 if(this.labelmd > 0){
40716 label.cls += ' col-md-' + this.labelmd;
40717 container.cls += ' col-md-' + (12 - this.labelmd);
40719 if(this.labelsm > 0){
40720 label.cls += ' col-sm-' + this.labelsm;
40721 container.cls += ' col-sm-' + (12 - this.labelsm);
40723 if(this.labelxs > 0){
40724 label.cls += ' col-xs-' + this.labelxs;
40725 container.cls += ' col-xs-' + (12 - this.labelxs);
40736 var settings = this;
40738 ['xs','sm','md','lg'].map(function(size){
40739 if (settings[size]) {
40740 cfg.cls += ' col-' + size + '-' + settings[size];
40747 initEvents : function()
40749 this.indicator = this.indicatorEl();
40751 this.initCurrencyEvent();
40753 this.initNumberEvent();
40756 initCurrencyEvent : function()
40759 throw "can not find store for combo";
40762 this.store = Roo.factory(this.store, Roo.data);
40763 this.store.parent = this;
40767 this.triggerEl = this.el.select('.input-group-addon', true).first();
40769 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40774 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40775 _this.list.setWidth(lw);
40778 this.list.on('mouseover', this.onViewOver, this);
40779 this.list.on('mousemove', this.onViewMove, this);
40780 this.list.on('scroll', this.onViewScroll, this);
40783 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40786 this.view = new Roo.View(this.list, this.tpl, {
40787 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40790 this.view.on('click', this.onViewClick, this);
40792 this.store.on('beforeload', this.onBeforeLoad, this);
40793 this.store.on('load', this.onLoad, this);
40794 this.store.on('loadexception', this.onLoadException, this);
40796 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40797 "up" : function(e){
40798 this.inKeyMode = true;
40802 "down" : function(e){
40803 if(!this.isExpanded()){
40804 this.onTriggerClick();
40806 this.inKeyMode = true;
40811 "enter" : function(e){
40814 if(this.fireEvent("specialkey", this, e)){
40815 this.onViewClick(false);
40821 "esc" : function(e){
40825 "tab" : function(e){
40828 if(this.fireEvent("specialkey", this, e)){
40829 this.onViewClick(false);
40837 doRelay : function(foo, bar, hname){
40838 if(hname == 'down' || this.scope.isExpanded()){
40839 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40847 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40851 initNumberEvent : function(e)
40853 this.inputEl().on("keydown" , this.fireKey, this);
40854 this.inputEl().on("focus", this.onFocus, this);
40855 this.inputEl().on("blur", this.onBlur, this);
40857 this.inputEl().relayEvent('keyup', this);
40859 if(this.indicator){
40860 this.indicator.addClass('invisible');
40863 this.originalValue = this.getValue();
40865 if(this.validationEvent == 'keyup'){
40866 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40867 this.inputEl().on('keyup', this.filterValidation, this);
40869 else if(this.validationEvent !== false){
40870 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40873 if(this.selectOnFocus){
40874 this.on("focus", this.preFocus, this);
40877 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40878 this.inputEl().on("keypress", this.filterKeys, this);
40880 this.inputEl().relayEvent('keypress', this);
40883 var allowed = "0123456789";
40885 if(this.allowDecimals){
40886 allowed += this.decimalSeparator;
40889 if(this.allowNegative){
40893 if(this.thousandsDelimiter) {
40897 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40899 var keyPress = function(e){
40901 var k = e.getKey();
40903 var c = e.getCharCode();
40906 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40907 allowed.indexOf(String.fromCharCode(c)) === -1
40913 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40917 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40922 this.inputEl().on("keypress", keyPress, this);
40926 onTriggerClick : function(e)
40933 this.loadNext = false;
40935 if(this.isExpanded()){
40940 this.hasFocus = true;
40942 if(this.triggerAction == 'all') {
40943 this.doQuery(this.allQuery, true);
40947 this.doQuery(this.getRawValue());
40950 getCurrency : function()
40952 var v = this.currencyEl().getValue();
40957 restrictHeight : function()
40959 this.list.alignTo(this.currencyEl(), this.listAlign);
40960 this.list.alignTo(this.currencyEl(), this.listAlign);
40963 onViewClick : function(view, doFocus, el, e)
40965 var index = this.view.getSelectedIndexes()[0];
40967 var r = this.store.getAt(index);
40970 this.onSelect(r, index);
40974 onSelect : function(record, index){
40976 if(this.fireEvent('beforeselect', this, record, index) !== false){
40978 this.setFromCurrencyData(index > -1 ? record.data : false);
40982 this.fireEvent('select', this, record, index);
40986 setFromCurrencyData : function(o)
40990 this.lastCurrency = o;
40992 if (this.currencyField) {
40993 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40995 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40998 this.lastSelectionText = currency;
41000 //setting default currency
41001 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41002 this.setCurrency(this.defaultCurrency);
41006 this.setCurrency(currency);
41009 setFromData : function(o)
41013 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41015 this.setFromCurrencyData(c);
41020 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41022 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41025 this.setValue(value);
41029 setCurrency : function(v)
41031 this.currencyValue = v;
41034 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41039 setValue : function(v)
41041 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41047 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41049 this.inputEl().dom.value = (v == '') ? '' :
41050 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41052 if(!this.allowZero && v === '0') {
41053 this.hiddenEl().dom.value = '';
41054 this.inputEl().dom.value = '';
41061 getRawValue : function()
41063 var v = this.inputEl().getValue();
41068 getValue : function()
41070 return this.fixPrecision(this.parseValue(this.getRawValue()));
41073 parseValue : function(value)
41075 if(this.thousandsDelimiter) {
41077 r = new RegExp(",", "g");
41078 value = value.replace(r, "");
41081 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41082 return isNaN(value) ? '' : value;
41086 fixPrecision : function(value)
41088 if(this.thousandsDelimiter) {
41090 r = new RegExp(",", "g");
41091 value = value.replace(r, "");
41094 var nan = isNaN(value);
41096 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41097 return nan ? '' : value;
41099 return parseFloat(value).toFixed(this.decimalPrecision);
41102 decimalPrecisionFcn : function(v)
41104 return Math.floor(v);
41107 validateValue : function(value)
41109 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41113 var num = this.parseValue(value);
41116 this.markInvalid(String.format(this.nanText, value));
41120 if(num < this.minValue){
41121 this.markInvalid(String.format(this.minText, this.minValue));
41125 if(num > this.maxValue){
41126 this.markInvalid(String.format(this.maxText, this.maxValue));
41133 validate : function()
41135 if(this.disabled || this.allowBlank){
41140 var currency = this.getCurrency();
41142 if(this.validateValue(this.getRawValue()) && currency.length){
41147 this.markInvalid();
41151 getName: function()
41156 beforeBlur : function()
41162 var v = this.parseValue(this.getRawValue());
41169 onBlur : function()
41173 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41174 //this.el.removeClass(this.focusClass);
41177 this.hasFocus = false;
41179 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41183 var v = this.getValue();
41185 if(String(v) !== String(this.startValue)){
41186 this.fireEvent('change', this, v, this.startValue);
41189 this.fireEvent("blur", this);
41192 inputEl : function()
41194 return this.el.select('.roo-money-amount-input', true).first();
41197 currencyEl : function()
41199 return this.el.select('.roo-money-currency-input', true).first();
41202 hiddenEl : function()
41204 return this.el.select('input.hidden-number-input',true).first();