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 var ce = this.el.select('.navbar-collapse',true).first();
3865 ce.toggleClass('in'); // old...
3866 if (ce.hasClass('collapse')) {
3868 ce.removeClass('collapse');
3869 ce.addClass('show');
3870 var h = ce.getHeight();
3872 ce.removeClass('show');
3873 // at this point we should be able to see it..
3874 ce.addClass('collapsing');
3876 ce.setHeight(0); // resize it ...
3877 ce.on('transitionend', function() {
3878 Roo.log('done transition');
3879 ce.removeClass('collapsing');
3880 ce.addClass('show');
3881 ce.removeClass('collapse');
3883 ce.dom.style.height = '';
3884 }, this, { single: true} );
3888 ce.setHeight(ce.getHeight());
3889 ce.removeClass('show');
3890 ce.addClass('collapsing');
3892 ce.on('transitionend', function() {
3893 ce.dom.style.height = '';
3894 ce.removeClass('collapsing');
3895 ce.addClass('collapse');
3896 }, this, { single: true} );
3908 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3910 var size = this.el.getSize();
3911 this.maskEl.setSize(size.width, size.height);
3912 this.maskEl.enableDisplayMode("block");
3921 getChildContainer : function()
3923 if (this.el.select('.collapse').getCount()) {
3924 return this.el.select('.collapse',true).first();
3957 * @class Roo.bootstrap.NavSimplebar
3958 * @extends Roo.bootstrap.Navbar
3959 * Bootstrap Sidebar class
3961 * @cfg {Boolean} inverse is inverted color
3963 * @cfg {String} type (nav | pills | tabs)
3964 * @cfg {Boolean} arrangement stacked | justified
3965 * @cfg {String} align (left | right) alignment
3967 * @cfg {Boolean} main (true|false) main nav bar? default false
3968 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3970 * @cfg {String} tag (header|footer|nav|div) default is nav
3972 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3976 * Create a new Sidebar
3977 * @param {Object} config The config object
3981 Roo.bootstrap.NavSimplebar = function(config){
3982 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3985 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4001 getAutoCreate : function(){
4005 tag : this.tag || 'div',
4006 cls : 'navbar navbar-expand-lg'
4008 if (['light','white'].indexOf(this.weight) > -1) {
4009 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4011 cfg.cls += ' bg-' + this.weight;
4023 this.type = this.type || 'nav';
4024 if (['tabs','pills'].indexOf(this.type)!==-1) {
4025 cfg.cn[0].cls += ' nav-' + this.type
4029 if (this.type!=='nav') {
4030 Roo.log('nav type must be nav/tabs/pills')
4032 cfg.cn[0].cls += ' navbar-nav'
4038 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4039 cfg.cn[0].cls += ' nav-' + this.arrangement;
4043 if (this.align === 'right') {
4044 cfg.cn[0].cls += ' navbar-right';
4048 cfg.cls += ' navbar-inverse';
4072 * navbar-expand-md fixed-top
4076 * @class Roo.bootstrap.NavHeaderbar
4077 * @extends Roo.bootstrap.NavSimplebar
4078 * Bootstrap Sidebar class
4080 * @cfg {String} brand what is brand
4081 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4082 * @cfg {String} brand_href href of the brand
4083 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4084 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4085 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4086 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4089 * Create a new Sidebar
4090 * @param {Object} config The config object
4094 Roo.bootstrap.NavHeaderbar = function(config){
4095 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4099 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4106 desktopCenter : false,
4109 getAutoCreate : function(){
4112 tag: this.nav || 'nav',
4113 cls: 'navbar navbar-expand-md',
4119 if (this.desktopCenter) {
4120 cn.push({cls : 'container', cn : []});
4128 cls: 'navbar-toggle navbar-toggler',
4129 'data-toggle': 'collapse',
4134 html: 'Toggle navigation'
4138 cls: 'icon-bar navbar-toggler-icon'
4151 cn.push( Roo.bootstrap.version == 4 ? btn : {
4153 cls: 'navbar-header',
4162 cls: 'collapse navbar-collapse',
4166 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4168 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4169 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4171 // tag can override this..
4173 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4176 if (this.brand !== '') {
4177 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4178 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4180 href: this.brand_href ? this.brand_href : '#',
4181 cls: 'navbar-brand',
4189 cfg.cls += ' main-nav';
4197 getHeaderChildContainer : function()
4199 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4200 return this.el.select('.navbar-header',true).first();
4203 return this.getChildContainer();
4207 initEvents : function()
4209 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4211 if (this.autohide) {
4216 Roo.get(document).on('scroll',function(e) {
4217 var ns = Roo.get(document).getScroll().top;
4218 var os = prevScroll;
4222 ft.removeClass('slideDown');
4223 ft.addClass('slideUp');
4226 ft.removeClass('slideUp');
4227 ft.addClass('slideDown');
4248 * @class Roo.bootstrap.NavSidebar
4249 * @extends Roo.bootstrap.Navbar
4250 * Bootstrap Sidebar class
4253 * Create a new Sidebar
4254 * @param {Object} config The config object
4258 Roo.bootstrap.NavSidebar = function(config){
4259 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4262 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4264 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4266 getAutoCreate : function(){
4271 cls: 'sidebar sidebar-nav'
4293 * @class Roo.bootstrap.NavGroup
4294 * @extends Roo.bootstrap.Component
4295 * Bootstrap NavGroup class
4296 * @cfg {String} align (left|right)
4297 * @cfg {Boolean} inverse
4298 * @cfg {String} type (nav|pills|tab) default nav
4299 * @cfg {String} navId - reference Id for navbar.
4303 * Create a new nav group
4304 * @param {Object} config The config object
4307 Roo.bootstrap.NavGroup = function(config){
4308 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4311 Roo.bootstrap.NavGroup.register(this);
4315 * Fires when the active item changes
4316 * @param {Roo.bootstrap.NavGroup} this
4317 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4318 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4325 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4336 getAutoCreate : function()
4338 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4345 if (['tabs','pills'].indexOf(this.type)!==-1) {
4346 cfg.cls += ' nav-' + this.type
4348 if (this.type!=='nav') {
4349 Roo.log('nav type must be nav/tabs/pills')
4351 cfg.cls += ' navbar-nav'
4354 if (this.parent() && this.parent().sidebar) {
4357 cls: 'dashboard-menu sidebar-menu'
4363 if (this.form === true) {
4369 if (this.align === 'right') {
4370 cfg.cls += ' navbar-right ml-md-auto';
4372 cfg.cls += ' navbar-left';
4376 if (this.align === 'right') {
4377 cfg.cls += ' navbar-right ml-md-auto';
4379 cfg.cls += ' mr-auto';
4383 cfg.cls += ' navbar-inverse';
4391 * sets the active Navigation item
4392 * @param {Roo.bootstrap.NavItem} the new current navitem
4394 setActiveItem : function(item)
4397 Roo.each(this.navItems, function(v){
4402 v.setActive(false, true);
4409 item.setActive(true, true);
4410 this.fireEvent('changed', this, item, prev);
4415 * gets the active Navigation item
4416 * @return {Roo.bootstrap.NavItem} the current navitem
4418 getActive : function()
4422 Roo.each(this.navItems, function(v){
4433 indexOfNav : function()
4437 Roo.each(this.navItems, function(v,i){
4448 * adds a Navigation item
4449 * @param {Roo.bootstrap.NavItem} the navitem to add
4451 addItem : function(cfg)
4453 var cn = new Roo.bootstrap.NavItem(cfg);
4455 cn.parentId = this.id;
4456 cn.onRender(this.el, null);
4460 * register a Navigation item
4461 * @param {Roo.bootstrap.NavItem} the navitem to add
4463 register : function(item)
4465 this.navItems.push( item);
4466 item.navId = this.navId;
4471 * clear all the Navigation item
4474 clearAll : function()
4477 this.el.dom.innerHTML = '';
4480 getNavItem: function(tabId)
4483 Roo.each(this.navItems, function(e) {
4484 if (e.tabId == tabId) {
4494 setActiveNext : function()
4496 var i = this.indexOfNav(this.getActive());
4497 if (i > this.navItems.length) {
4500 this.setActiveItem(this.navItems[i+1]);
4502 setActivePrev : function()
4504 var i = this.indexOfNav(this.getActive());
4508 this.setActiveItem(this.navItems[i-1]);
4510 clearWasActive : function(except) {
4511 Roo.each(this.navItems, function(e) {
4512 if (e.tabId != except.tabId && e.was_active) {
4513 e.was_active = false;
4520 getWasActive : function ()
4523 Roo.each(this.navItems, function(e) {
4538 Roo.apply(Roo.bootstrap.NavGroup, {
4542 * register a Navigation Group
4543 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4545 register : function(navgrp)
4547 this.groups[navgrp.navId] = navgrp;
4551 * fetch a Navigation Group based on the navigation ID
4552 * @param {string} the navgroup to add
4553 * @returns {Roo.bootstrap.NavGroup} the navgroup
4555 get: function(navId) {
4556 if (typeof(this.groups[navId]) == 'undefined') {
4558 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4560 return this.groups[navId] ;
4575 * @class Roo.bootstrap.NavItem
4576 * @extends Roo.bootstrap.Component
4577 * Bootstrap Navbar.NavItem class
4578 * @cfg {String} href link to
4579 * @cfg {String} html content of button
4580 * @cfg {String} badge text inside badge
4581 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4582 * @cfg {String} glyphicon DEPRICATED - use fa
4583 * @cfg {String} icon DEPRICATED - use fa
4584 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4585 * @cfg {Boolean} active Is item active
4586 * @cfg {Boolean} disabled Is item disabled
4588 * @cfg {Boolean} preventDefault (true | false) default false
4589 * @cfg {String} tabId the tab that this item activates.
4590 * @cfg {String} tagtype (a|span) render as a href or span?
4591 * @cfg {Boolean} animateRef (true|false) link to element default false
4594 * Create a new Navbar Item
4595 * @param {Object} config The config object
4597 Roo.bootstrap.NavItem = function(config){
4598 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4603 * The raw click event for the entire grid.
4604 * @param {Roo.EventObject} e
4609 * Fires when the active item active state changes
4610 * @param {Roo.bootstrap.NavItem} this
4611 * @param {boolean} state the new state
4617 * Fires when scroll to element
4618 * @param {Roo.bootstrap.NavItem} this
4619 * @param {Object} options
4620 * @param {Roo.EventObject} e
4628 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4637 preventDefault : false,
4644 getAutoCreate : function(){
4653 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4655 if (this.disabled) {
4656 cfg.cls += ' disabled';
4659 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4663 href : this.href || "#",
4664 html: this.html || ''
4667 if (this.tagtype == 'a') {
4668 cfg.cn[0].cls = 'nav-link';
4671 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4674 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4676 if(this.glyphicon) {
4677 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4682 cfg.cn[0].html += " <span class='caret'></span>";
4686 if (this.badge !== '') {
4688 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4696 initEvents: function()
4698 if (typeof (this.menu) != 'undefined') {
4699 this.menu.parentType = this.xtype;
4700 this.menu.triggerEl = this.el;
4701 this.menu = this.addxtype(Roo.apply({}, this.menu));
4704 this.el.select('a',true).on('click', this.onClick, this);
4706 if(this.tagtype == 'span'){
4707 this.el.select('span',true).on('click', this.onClick, this);
4710 // at this point parent should be available..
4711 this.parent().register(this);
4714 onClick : function(e)
4716 if (e.getTarget('.dropdown-menu-item')) {
4717 // did you click on a menu itemm.... - then don't trigger onclick..
4722 this.preventDefault ||
4725 Roo.log("NavItem - prevent Default?");
4729 if (this.disabled) {
4733 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4734 if (tg && tg.transition) {
4735 Roo.log("waiting for the transitionend");
4741 //Roo.log("fire event clicked");
4742 if(this.fireEvent('click', this, e) === false){
4746 if(this.tagtype == 'span'){
4750 //Roo.log(this.href);
4751 var ael = this.el.select('a',true).first();
4754 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4755 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4756 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4757 return; // ignore... - it's a 'hash' to another page.
4759 Roo.log("NavItem - prevent Default?");
4761 this.scrollToElement(e);
4765 var p = this.parent();
4767 if (['tabs','pills'].indexOf(p.type)!==-1) {
4768 if (typeof(p.setActiveItem) !== 'undefined') {
4769 p.setActiveItem(this);
4773 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4774 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4775 // remove the collapsed menu expand...
4776 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4780 isActive: function () {
4783 setActive : function(state, fire, is_was_active)
4785 if (this.active && !state && this.navId) {
4786 this.was_active = true;
4787 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4789 nv.clearWasActive(this);
4793 this.active = state;
4796 this.el.removeClass('active');
4797 } else if (!this.el.hasClass('active')) {
4798 this.el.addClass('active');
4801 this.fireEvent('changed', this, state);
4804 // show a panel if it's registered and related..
4806 if (!this.navId || !this.tabId || !state || is_was_active) {
4810 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4814 var pan = tg.getPanelByName(this.tabId);
4818 // if we can not flip to new panel - go back to old nav highlight..
4819 if (false == tg.showPanel(pan)) {
4820 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4822 var onav = nv.getWasActive();
4824 onav.setActive(true, false, true);
4833 // this should not be here...
4834 setDisabled : function(state)
4836 this.disabled = state;
4838 this.el.removeClass('disabled');
4839 } else if (!this.el.hasClass('disabled')) {
4840 this.el.addClass('disabled');
4846 * Fetch the element to display the tooltip on.
4847 * @return {Roo.Element} defaults to this.el
4849 tooltipEl : function()
4851 return this.el.select('' + this.tagtype + '', true).first();
4854 scrollToElement : function(e)
4856 var c = document.body;
4859 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4861 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4862 c = document.documentElement;
4865 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4871 var o = target.calcOffsetsTo(c);
4878 this.fireEvent('scrollto', this, options, e);
4880 Roo.get(c).scrollTo('top', options.value, true);
4893 * <span> icon </span>
4894 * <span> text </span>
4895 * <span>badge </span>
4899 * @class Roo.bootstrap.NavSidebarItem
4900 * @extends Roo.bootstrap.NavItem
4901 * Bootstrap Navbar.NavSidebarItem class
4902 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4903 * {Boolean} open is the menu open
4904 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4905 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4906 * {String} buttonSize (sm|md|lg)the extra classes for the button
4907 * {Boolean} showArrow show arrow next to the text (default true)
4909 * Create a new Navbar Button
4910 * @param {Object} config The config object
4912 Roo.bootstrap.NavSidebarItem = function(config){
4913 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4918 * The raw click event for the entire grid.
4919 * @param {Roo.EventObject} e
4924 * Fires when the active item active state changes
4925 * @param {Roo.bootstrap.NavSidebarItem} this
4926 * @param {boolean} state the new state
4934 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4936 badgeWeight : 'default',
4942 buttonWeight : 'default',
4948 getAutoCreate : function(){
4953 href : this.href || '#',
4959 if(this.buttonView){
4962 href : this.href || '#',
4963 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4976 cfg.cls += ' active';
4979 if (this.disabled) {
4980 cfg.cls += ' disabled';
4983 cfg.cls += ' open x-open';
4986 if (this.glyphicon || this.icon) {
4987 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4988 a.cn.push({ tag : 'i', cls : c }) ;
4991 if(!this.buttonView){
4994 html : this.html || ''
5001 if (this.badge !== '') {
5002 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5008 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5011 a.cls += ' dropdown-toggle treeview' ;
5017 initEvents : function()
5019 if (typeof (this.menu) != 'undefined') {
5020 this.menu.parentType = this.xtype;
5021 this.menu.triggerEl = this.el;
5022 this.menu = this.addxtype(Roo.apply({}, this.menu));
5025 this.el.on('click', this.onClick, this);
5027 if(this.badge !== ''){
5028 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5033 onClick : function(e)
5040 if(this.preventDefault){
5044 this.fireEvent('click', this);
5047 disable : function()
5049 this.setDisabled(true);
5054 this.setDisabled(false);
5057 setDisabled : function(state)
5059 if(this.disabled == state){
5063 this.disabled = state;
5066 this.el.addClass('disabled');
5070 this.el.removeClass('disabled');
5075 setActive : function(state)
5077 if(this.active == state){
5081 this.active = state;
5084 this.el.addClass('active');
5088 this.el.removeClass('active');
5093 isActive: function ()
5098 setBadge : function(str)
5104 this.badgeEl.dom.innerHTML = str;
5121 * @class Roo.bootstrap.Row
5122 * @extends Roo.bootstrap.Component
5123 * Bootstrap Row class (contains columns...)
5127 * @param {Object} config The config object
5130 Roo.bootstrap.Row = function(config){
5131 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5134 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5136 getAutoCreate : function(){
5155 * @class Roo.bootstrap.Element
5156 * @extends Roo.bootstrap.Component
5157 * Bootstrap Element class
5158 * @cfg {String} html contents of the element
5159 * @cfg {String} tag tag of the element
5160 * @cfg {String} cls class of the element
5161 * @cfg {Boolean} preventDefault (true|false) default false
5162 * @cfg {Boolean} clickable (true|false) default false
5165 * Create a new Element
5166 * @param {Object} config The config object
5169 Roo.bootstrap.Element = function(config){
5170 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5176 * When a element is chick
5177 * @param {Roo.bootstrap.Element} this
5178 * @param {Roo.EventObject} e
5184 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5189 preventDefault: false,
5192 getAutoCreate : function(){
5196 // cls: this.cls, double assign in parent class Component.js :: onRender
5203 initEvents: function()
5205 Roo.bootstrap.Element.superclass.initEvents.call(this);
5208 this.el.on('click', this.onClick, this);
5213 onClick : function(e)
5215 if(this.preventDefault){
5219 this.fireEvent('click', this, e);
5222 getValue : function()
5224 return this.el.dom.innerHTML;
5227 setValue : function(value)
5229 this.el.dom.innerHTML = value;
5244 * @class Roo.bootstrap.Pagination
5245 * @extends Roo.bootstrap.Component
5246 * Bootstrap Pagination class
5247 * @cfg {String} size xs | sm | md | lg
5248 * @cfg {Boolean} inverse false | true
5251 * Create a new Pagination
5252 * @param {Object} config The config object
5255 Roo.bootstrap.Pagination = function(config){
5256 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5259 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5265 getAutoCreate : function(){
5271 cfg.cls += ' inverse';
5277 cfg.cls += " " + this.cls;
5295 * @class Roo.bootstrap.PaginationItem
5296 * @extends Roo.bootstrap.Component
5297 * Bootstrap PaginationItem class
5298 * @cfg {String} html text
5299 * @cfg {String} href the link
5300 * @cfg {Boolean} preventDefault (true | false) default true
5301 * @cfg {Boolean} active (true | false) default false
5302 * @cfg {Boolean} disabled default false
5306 * Create a new PaginationItem
5307 * @param {Object} config The config object
5311 Roo.bootstrap.PaginationItem = function(config){
5312 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5317 * The raw click event for the entire grid.
5318 * @param {Roo.EventObject} e
5324 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5328 preventDefault: true,
5333 getAutoCreate : function(){
5339 href : this.href ? this.href : '#',
5340 html : this.html ? this.html : ''
5350 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5354 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5360 initEvents: function() {
5362 this.el.on('click', this.onClick, this);
5365 onClick : function(e)
5367 Roo.log('PaginationItem on click ');
5368 if(this.preventDefault){
5376 this.fireEvent('click', this, e);
5392 * @class Roo.bootstrap.Slider
5393 * @extends Roo.bootstrap.Component
5394 * Bootstrap Slider class
5397 * Create a new Slider
5398 * @param {Object} config The config object
5401 Roo.bootstrap.Slider = function(config){
5402 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5405 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5407 getAutoCreate : function(){
5411 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5415 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5427 * Ext JS Library 1.1.1
5428 * Copyright(c) 2006-2007, Ext JS, LLC.
5430 * Originally Released Under LGPL - original licence link has changed is not relivant.
5433 * <script type="text/javascript">
5438 * @class Roo.grid.ColumnModel
5439 * @extends Roo.util.Observable
5440 * This is the default implementation of a ColumnModel used by the Grid. It defines
5441 * the columns in the grid.
5444 var colModel = new Roo.grid.ColumnModel([
5445 {header: "Ticker", width: 60, sortable: true, locked: true},
5446 {header: "Company Name", width: 150, sortable: true},
5447 {header: "Market Cap.", width: 100, sortable: true},
5448 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5449 {header: "Employees", width: 100, sortable: true, resizable: false}
5454 * The config options listed for this class are options which may appear in each
5455 * individual column definition.
5456 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5458 * @param {Object} config An Array of column config objects. See this class's
5459 * config objects for details.
5461 Roo.grid.ColumnModel = function(config){
5463 * The config passed into the constructor
5465 this.config = config;
5468 // if no id, create one
5469 // if the column does not have a dataIndex mapping,
5470 // map it to the order it is in the config
5471 for(var i = 0, len = config.length; i < len; i++){
5473 if(typeof c.dataIndex == "undefined"){
5476 if(typeof c.renderer == "string"){
5477 c.renderer = Roo.util.Format[c.renderer];
5479 if(typeof c.id == "undefined"){
5482 if(c.editor && c.editor.xtype){
5483 c.editor = Roo.factory(c.editor, Roo.grid);
5485 if(c.editor && c.editor.isFormField){
5486 c.editor = new Roo.grid.GridEditor(c.editor);
5488 this.lookup[c.id] = c;
5492 * The width of columns which have no width specified (defaults to 100)
5495 this.defaultWidth = 100;
5498 * Default sortable of columns which have no sortable specified (defaults to false)
5501 this.defaultSortable = false;
5505 * @event widthchange
5506 * Fires when the width of a column changes.
5507 * @param {ColumnModel} this
5508 * @param {Number} columnIndex The column index
5509 * @param {Number} newWidth The new width
5511 "widthchange": true,
5513 * @event headerchange
5514 * Fires when the text of a header changes.
5515 * @param {ColumnModel} this
5516 * @param {Number} columnIndex The column index
5517 * @param {Number} newText The new header text
5519 "headerchange": true,
5521 * @event hiddenchange
5522 * Fires when a column is hidden or "unhidden".
5523 * @param {ColumnModel} this
5524 * @param {Number} columnIndex The column index
5525 * @param {Boolean} hidden true if hidden, false otherwise
5527 "hiddenchange": true,
5529 * @event columnmoved
5530 * Fires when a column is moved.
5531 * @param {ColumnModel} this
5532 * @param {Number} oldIndex
5533 * @param {Number} newIndex
5535 "columnmoved" : true,
5537 * @event columlockchange
5538 * Fires when a column's locked state is changed
5539 * @param {ColumnModel} this
5540 * @param {Number} colIndex
5541 * @param {Boolean} locked true if locked
5543 "columnlockchange" : true
5545 Roo.grid.ColumnModel.superclass.constructor.call(this);
5547 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5549 * @cfg {String} header The header text to display in the Grid view.
5552 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5553 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5554 * specified, the column's index is used as an index into the Record's data Array.
5557 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5558 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5561 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5562 * Defaults to the value of the {@link #defaultSortable} property.
5563 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5566 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5569 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5572 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5575 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5578 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5579 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5580 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5581 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5584 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5587 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5590 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5593 * @cfg {String} cursor (Optional)
5596 * @cfg {String} tooltip (Optional)
5599 * @cfg {Number} xs (Optional)
5602 * @cfg {Number} sm (Optional)
5605 * @cfg {Number} md (Optional)
5608 * @cfg {Number} lg (Optional)
5611 * Returns the id of the column at the specified index.
5612 * @param {Number} index The column index
5613 * @return {String} the id
5615 getColumnId : function(index){
5616 return this.config[index].id;
5620 * Returns the column for a specified id.
5621 * @param {String} id The column id
5622 * @return {Object} the column
5624 getColumnById : function(id){
5625 return this.lookup[id];
5630 * Returns the column for a specified dataIndex.
5631 * @param {String} dataIndex The column dataIndex
5632 * @return {Object|Boolean} the column or false if not found
5634 getColumnByDataIndex: function(dataIndex){
5635 var index = this.findColumnIndex(dataIndex);
5636 return index > -1 ? this.config[index] : false;
5640 * Returns the index for a specified column id.
5641 * @param {String} id The column id
5642 * @return {Number} the index, or -1 if not found
5644 getIndexById : function(id){
5645 for(var i = 0, len = this.config.length; i < len; i++){
5646 if(this.config[i].id == id){
5654 * Returns the index for a specified column dataIndex.
5655 * @param {String} dataIndex The column dataIndex
5656 * @return {Number} the index, or -1 if not found
5659 findColumnIndex : function(dataIndex){
5660 for(var i = 0, len = this.config.length; i < len; i++){
5661 if(this.config[i].dataIndex == dataIndex){
5669 moveColumn : function(oldIndex, newIndex){
5670 var c = this.config[oldIndex];
5671 this.config.splice(oldIndex, 1);
5672 this.config.splice(newIndex, 0, c);
5673 this.dataMap = null;
5674 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5677 isLocked : function(colIndex){
5678 return this.config[colIndex].locked === true;
5681 setLocked : function(colIndex, value, suppressEvent){
5682 if(this.isLocked(colIndex) == value){
5685 this.config[colIndex].locked = value;
5687 this.fireEvent("columnlockchange", this, colIndex, value);
5691 getTotalLockedWidth : function(){
5693 for(var i = 0; i < this.config.length; i++){
5694 if(this.isLocked(i) && !this.isHidden(i)){
5695 this.totalWidth += this.getColumnWidth(i);
5701 getLockedCount : function(){
5702 for(var i = 0, len = this.config.length; i < len; i++){
5703 if(!this.isLocked(i)){
5708 return this.config.length;
5712 * Returns the number of columns.
5715 getColumnCount : function(visibleOnly){
5716 if(visibleOnly === true){
5718 for(var i = 0, len = this.config.length; i < len; i++){
5719 if(!this.isHidden(i)){
5725 return this.config.length;
5729 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5730 * @param {Function} fn
5731 * @param {Object} scope (optional)
5732 * @return {Array} result
5734 getColumnsBy : function(fn, scope){
5736 for(var i = 0, len = this.config.length; i < len; i++){
5737 var c = this.config[i];
5738 if(fn.call(scope||this, c, i) === true){
5746 * Returns true if the specified column is sortable.
5747 * @param {Number} col The column index
5750 isSortable : function(col){
5751 if(typeof this.config[col].sortable == "undefined"){
5752 return this.defaultSortable;
5754 return this.config[col].sortable;
5758 * Returns the rendering (formatting) function defined for the column.
5759 * @param {Number} col The column index.
5760 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5762 getRenderer : function(col){
5763 if(!this.config[col].renderer){
5764 return Roo.grid.ColumnModel.defaultRenderer;
5766 return this.config[col].renderer;
5770 * Sets the rendering (formatting) function for a column.
5771 * @param {Number} col The column index
5772 * @param {Function} fn The function to use to process the cell's raw data
5773 * to return HTML markup for the grid view. The render function is called with
5774 * the following parameters:<ul>
5775 * <li>Data value.</li>
5776 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5777 * <li>css A CSS style string to apply to the table cell.</li>
5778 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5779 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5780 * <li>Row index</li>
5781 * <li>Column index</li>
5782 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5784 setRenderer : function(col, fn){
5785 this.config[col].renderer = fn;
5789 * Returns the width for the specified column.
5790 * @param {Number} col The column index
5793 getColumnWidth : function(col){
5794 return this.config[col].width * 1 || this.defaultWidth;
5798 * Sets the width for a column.
5799 * @param {Number} col The column index
5800 * @param {Number} width The new width
5802 setColumnWidth : function(col, width, suppressEvent){
5803 this.config[col].width = width;
5804 this.totalWidth = null;
5806 this.fireEvent("widthchange", this, col, width);
5811 * Returns the total width of all columns.
5812 * @param {Boolean} includeHidden True to include hidden column widths
5815 getTotalWidth : function(includeHidden){
5816 if(!this.totalWidth){
5817 this.totalWidth = 0;
5818 for(var i = 0, len = this.config.length; i < len; i++){
5819 if(includeHidden || !this.isHidden(i)){
5820 this.totalWidth += this.getColumnWidth(i);
5824 return this.totalWidth;
5828 * Returns the header for the specified column.
5829 * @param {Number} col The column index
5832 getColumnHeader : function(col){
5833 return this.config[col].header;
5837 * Sets the header for a column.
5838 * @param {Number} col The column index
5839 * @param {String} header The new header
5841 setColumnHeader : function(col, header){
5842 this.config[col].header = header;
5843 this.fireEvent("headerchange", this, col, header);
5847 * Returns the tooltip for the specified column.
5848 * @param {Number} col The column index
5851 getColumnTooltip : function(col){
5852 return this.config[col].tooltip;
5855 * Sets the tooltip for a column.
5856 * @param {Number} col The column index
5857 * @param {String} tooltip The new tooltip
5859 setColumnTooltip : function(col, tooltip){
5860 this.config[col].tooltip = tooltip;
5864 * Returns the dataIndex for the specified column.
5865 * @param {Number} col The column index
5868 getDataIndex : function(col){
5869 return this.config[col].dataIndex;
5873 * Sets the dataIndex for a column.
5874 * @param {Number} col The column index
5875 * @param {Number} dataIndex The new dataIndex
5877 setDataIndex : function(col, dataIndex){
5878 this.config[col].dataIndex = dataIndex;
5884 * Returns true if the cell is editable.
5885 * @param {Number} colIndex The column index
5886 * @param {Number} rowIndex The row index - this is nto actually used..?
5889 isCellEditable : function(colIndex, rowIndex){
5890 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5894 * Returns the editor defined for the cell/column.
5895 * return false or null to disable editing.
5896 * @param {Number} colIndex The column index
5897 * @param {Number} rowIndex The row index
5900 getCellEditor : function(colIndex, rowIndex){
5901 return this.config[colIndex].editor;
5905 * Sets if a column is editable.
5906 * @param {Number} col The column index
5907 * @param {Boolean} editable True if the column is editable
5909 setEditable : function(col, editable){
5910 this.config[col].editable = editable;
5915 * Returns true if the column is hidden.
5916 * @param {Number} colIndex The column index
5919 isHidden : function(colIndex){
5920 return this.config[colIndex].hidden;
5925 * Returns true if the column width cannot be changed
5927 isFixed : function(colIndex){
5928 return this.config[colIndex].fixed;
5932 * Returns true if the column can be resized
5935 isResizable : function(colIndex){
5936 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5939 * Sets if a column is hidden.
5940 * @param {Number} colIndex The column index
5941 * @param {Boolean} hidden True if the column is hidden
5943 setHidden : function(colIndex, hidden){
5944 this.config[colIndex].hidden = hidden;
5945 this.totalWidth = null;
5946 this.fireEvent("hiddenchange", this, colIndex, hidden);
5950 * Sets the editor for a column.
5951 * @param {Number} col The column index
5952 * @param {Object} editor The editor object
5954 setEditor : function(col, editor){
5955 this.config[col].editor = editor;
5959 Roo.grid.ColumnModel.defaultRenderer = function(value)
5961 if(typeof value == "object") {
5964 if(typeof value == "string" && value.length < 1){
5968 return String.format("{0}", value);
5971 // Alias for backwards compatibility
5972 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5975 * Ext JS Library 1.1.1
5976 * Copyright(c) 2006-2007, Ext JS, LLC.
5978 * Originally Released Under LGPL - original licence link has changed is not relivant.
5981 * <script type="text/javascript">
5985 * @class Roo.LoadMask
5986 * A simple utility class for generically masking elements while loading data. If the element being masked has
5987 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5988 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5989 * element's UpdateManager load indicator and will be destroyed after the initial load.
5991 * Create a new LoadMask
5992 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5993 * @param {Object} config The config object
5995 Roo.LoadMask = function(el, config){
5996 this.el = Roo.get(el);
5997 Roo.apply(this, config);
5999 this.store.on('beforeload', this.onBeforeLoad, this);
6000 this.store.on('load', this.onLoad, this);
6001 this.store.on('loadexception', this.onLoadException, this);
6002 this.removeMask = false;
6004 var um = this.el.getUpdateManager();
6005 um.showLoadIndicator = false; // disable the default indicator
6006 um.on('beforeupdate', this.onBeforeLoad, this);
6007 um.on('update', this.onLoad, this);
6008 um.on('failure', this.onLoad, this);
6009 this.removeMask = true;
6013 Roo.LoadMask.prototype = {
6015 * @cfg {Boolean} removeMask
6016 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6017 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6021 * The text to display in a centered loading message box (defaults to 'Loading...')
6025 * @cfg {String} msgCls
6026 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6028 msgCls : 'x-mask-loading',
6031 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6037 * Disables the mask to prevent it from being displayed
6039 disable : function(){
6040 this.disabled = true;
6044 * Enables the mask so that it can be displayed
6046 enable : function(){
6047 this.disabled = false;
6050 onLoadException : function()
6054 if (typeof(arguments[3]) != 'undefined') {
6055 Roo.MessageBox.alert("Error loading",arguments[3]);
6059 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6060 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6067 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6072 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6076 onBeforeLoad : function(){
6078 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6083 destroy : function(){
6085 this.store.un('beforeload', this.onBeforeLoad, this);
6086 this.store.un('load', this.onLoad, this);
6087 this.store.un('loadexception', this.onLoadException, this);
6089 var um = this.el.getUpdateManager();
6090 um.un('beforeupdate', this.onBeforeLoad, this);
6091 um.un('update', this.onLoad, this);
6092 um.un('failure', this.onLoad, this);
6103 * @class Roo.bootstrap.Table
6104 * @extends Roo.bootstrap.Component
6105 * Bootstrap Table class
6106 * @cfg {String} cls table class
6107 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6108 * @cfg {String} bgcolor Specifies the background color for a table
6109 * @cfg {Number} border Specifies whether the table cells should have borders or not
6110 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6111 * @cfg {Number} cellspacing Specifies the space between cells
6112 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6113 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6114 * @cfg {String} sortable Specifies that the table should be sortable
6115 * @cfg {String} summary Specifies a summary of the content of a table
6116 * @cfg {Number} width Specifies the width of a table
6117 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6119 * @cfg {boolean} striped Should the rows be alternative striped
6120 * @cfg {boolean} bordered Add borders to the table
6121 * @cfg {boolean} hover Add hover highlighting
6122 * @cfg {boolean} condensed Format condensed
6123 * @cfg {boolean} responsive Format condensed
6124 * @cfg {Boolean} loadMask (true|false) default false
6125 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6126 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6127 * @cfg {Boolean} rowSelection (true|false) default false
6128 * @cfg {Boolean} cellSelection (true|false) default false
6129 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6130 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6131 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6132 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6136 * Create a new Table
6137 * @param {Object} config The config object
6140 Roo.bootstrap.Table = function(config){
6141 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6146 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6147 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6148 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6149 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6151 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6153 this.sm.grid = this;
6154 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6155 this.sm = this.selModel;
6156 this.sm.xmodule = this.xmodule || false;
6159 if (this.cm && typeof(this.cm.config) == 'undefined') {
6160 this.colModel = new Roo.grid.ColumnModel(this.cm);
6161 this.cm = this.colModel;
6162 this.cm.xmodule = this.xmodule || false;
6165 this.store= Roo.factory(this.store, Roo.data);
6166 this.ds = this.store;
6167 this.ds.xmodule = this.xmodule || false;
6170 if (this.footer && this.store) {
6171 this.footer.dataSource = this.ds;
6172 this.footer = Roo.factory(this.footer);
6179 * Fires when a cell is clicked
6180 * @param {Roo.bootstrap.Table} this
6181 * @param {Roo.Element} el
6182 * @param {Number} rowIndex
6183 * @param {Number} columnIndex
6184 * @param {Roo.EventObject} e
6188 * @event celldblclick
6189 * Fires when a cell is double clicked
6190 * @param {Roo.bootstrap.Table} this
6191 * @param {Roo.Element} el
6192 * @param {Number} rowIndex
6193 * @param {Number} columnIndex
6194 * @param {Roo.EventObject} e
6196 "celldblclick" : true,
6199 * Fires when a row is clicked
6200 * @param {Roo.bootstrap.Table} this
6201 * @param {Roo.Element} el
6202 * @param {Number} rowIndex
6203 * @param {Roo.EventObject} e
6207 * @event rowdblclick
6208 * Fires when a row is double clicked
6209 * @param {Roo.bootstrap.Table} this
6210 * @param {Roo.Element} el
6211 * @param {Number} rowIndex
6212 * @param {Roo.EventObject} e
6214 "rowdblclick" : true,
6217 * Fires when a mouseover occur
6218 * @param {Roo.bootstrap.Table} this
6219 * @param {Roo.Element} el
6220 * @param {Number} rowIndex
6221 * @param {Number} columnIndex
6222 * @param {Roo.EventObject} e
6227 * Fires when a mouseout occur
6228 * @param {Roo.bootstrap.Table} this
6229 * @param {Roo.Element} el
6230 * @param {Number} rowIndex
6231 * @param {Number} columnIndex
6232 * @param {Roo.EventObject} e
6237 * Fires when a row is rendered, so you can change add a style to it.
6238 * @param {Roo.bootstrap.Table} this
6239 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6243 * @event rowsrendered
6244 * Fires when all the rows have been rendered
6245 * @param {Roo.bootstrap.Table} this
6247 'rowsrendered' : true,
6249 * @event contextmenu
6250 * The raw contextmenu event for the entire grid.
6251 * @param {Roo.EventObject} e
6253 "contextmenu" : true,
6255 * @event rowcontextmenu
6256 * Fires when a row is right clicked
6257 * @param {Roo.bootstrap.Table} this
6258 * @param {Number} rowIndex
6259 * @param {Roo.EventObject} e
6261 "rowcontextmenu" : true,
6263 * @event cellcontextmenu
6264 * Fires when a cell is right clicked
6265 * @param {Roo.bootstrap.Table} this
6266 * @param {Number} rowIndex
6267 * @param {Number} cellIndex
6268 * @param {Roo.EventObject} e
6270 "cellcontextmenu" : true,
6272 * @event headercontextmenu
6273 * Fires when a header is right clicked
6274 * @param {Roo.bootstrap.Table} this
6275 * @param {Number} columnIndex
6276 * @param {Roo.EventObject} e
6278 "headercontextmenu" : true
6282 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6308 rowSelection : false,
6309 cellSelection : false,
6312 // Roo.Element - the tbody
6314 // Roo.Element - thead element
6317 container: false, // used by gridpanel...
6323 auto_hide_footer : false,
6325 getAutoCreate : function()
6327 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6334 if (this.scrollBody) {
6335 cfg.cls += ' table-body-fixed';
6338 cfg.cls += ' table-striped';
6342 cfg.cls += ' table-hover';
6344 if (this.bordered) {
6345 cfg.cls += ' table-bordered';
6347 if (this.condensed) {
6348 cfg.cls += ' table-condensed';
6350 if (this.responsive) {
6351 cfg.cls += ' table-responsive';
6355 cfg.cls+= ' ' +this.cls;
6358 // this lot should be simplifed...
6371 ].forEach(function(k) {
6379 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6382 if(this.store || this.cm){
6383 if(this.headerShow){
6384 cfg.cn.push(this.renderHeader());
6387 cfg.cn.push(this.renderBody());
6389 if(this.footerShow){
6390 cfg.cn.push(this.renderFooter());
6392 // where does this come from?
6393 //cfg.cls+= ' TableGrid';
6396 return { cn : [ cfg ] };
6399 initEvents : function()
6401 if(!this.store || !this.cm){
6404 if (this.selModel) {
6405 this.selModel.initEvents();
6409 //Roo.log('initEvents with ds!!!!');
6411 this.mainBody = this.el.select('tbody', true).first();
6412 this.mainHead = this.el.select('thead', true).first();
6413 this.mainFoot = this.el.select('tfoot', true).first();
6419 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6420 e.on('click', _this.sort, _this);
6423 this.mainBody.on("click", this.onClick, this);
6424 this.mainBody.on("dblclick", this.onDblClick, this);
6426 // why is this done????? = it breaks dialogs??
6427 //this.parent().el.setStyle('position', 'relative');
6431 this.footer.parentId = this.id;
6432 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6435 this.el.select('tfoot tr td').first().addClass('hide');
6440 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6443 this.store.on('load', this.onLoad, this);
6444 this.store.on('beforeload', this.onBeforeLoad, this);
6445 this.store.on('update', this.onUpdate, this);
6446 this.store.on('add', this.onAdd, this);
6447 this.store.on("clear", this.clear, this);
6449 this.el.on("contextmenu", this.onContextMenu, this);
6451 this.mainBody.on('scroll', this.onBodyScroll, this);
6453 this.cm.on("headerchange", this.onHeaderChange, this);
6455 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6459 onContextMenu : function(e, t)
6461 this.processEvent("contextmenu", e);
6464 processEvent : function(name, e)
6466 if (name != 'touchstart' ) {
6467 this.fireEvent(name, e);
6470 var t = e.getTarget();
6472 var cell = Roo.get(t);
6478 if(cell.findParent('tfoot', false, true)){
6482 if(cell.findParent('thead', false, true)){
6484 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6485 cell = Roo.get(t).findParent('th', false, true);
6487 Roo.log("failed to find th in thead?");
6488 Roo.log(e.getTarget());
6493 var cellIndex = cell.dom.cellIndex;
6495 var ename = name == 'touchstart' ? 'click' : name;
6496 this.fireEvent("header" + ename, this, cellIndex, e);
6501 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6502 cell = Roo.get(t).findParent('td', false, true);
6504 Roo.log("failed to find th in tbody?");
6505 Roo.log(e.getTarget());
6510 var row = cell.findParent('tr', false, true);
6511 var cellIndex = cell.dom.cellIndex;
6512 var rowIndex = row.dom.rowIndex - 1;
6516 this.fireEvent("row" + name, this, rowIndex, e);
6520 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6526 onMouseover : function(e, el)
6528 var cell = Roo.get(el);
6534 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6535 cell = cell.findParent('td', false, true);
6538 var row = cell.findParent('tr', false, true);
6539 var cellIndex = cell.dom.cellIndex;
6540 var rowIndex = row.dom.rowIndex - 1; // start from 0
6542 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6546 onMouseout : function(e, el)
6548 var cell = Roo.get(el);
6554 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6555 cell = cell.findParent('td', false, true);
6558 var row = cell.findParent('tr', false, true);
6559 var cellIndex = cell.dom.cellIndex;
6560 var rowIndex = row.dom.rowIndex - 1; // start from 0
6562 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6566 onClick : function(e, el)
6568 var cell = Roo.get(el);
6570 if(!cell || (!this.cellSelection && !this.rowSelection)){
6574 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6575 cell = cell.findParent('td', false, true);
6578 if(!cell || typeof(cell) == 'undefined'){
6582 var row = cell.findParent('tr', false, true);
6584 if(!row || typeof(row) == 'undefined'){
6588 var cellIndex = cell.dom.cellIndex;
6589 var rowIndex = this.getRowIndex(row);
6591 // why??? - should these not be based on SelectionModel?
6592 if(this.cellSelection){
6593 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6596 if(this.rowSelection){
6597 this.fireEvent('rowclick', this, row, rowIndex, e);
6603 onDblClick : function(e,el)
6605 var cell = Roo.get(el);
6607 if(!cell || (!this.cellSelection && !this.rowSelection)){
6611 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6612 cell = cell.findParent('td', false, true);
6615 if(!cell || typeof(cell) == 'undefined'){
6619 var row = cell.findParent('tr', false, true);
6621 if(!row || typeof(row) == 'undefined'){
6625 var cellIndex = cell.dom.cellIndex;
6626 var rowIndex = this.getRowIndex(row);
6628 if(this.cellSelection){
6629 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6632 if(this.rowSelection){
6633 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6637 sort : function(e,el)
6639 var col = Roo.get(el);
6641 if(!col.hasClass('sortable')){
6645 var sort = col.attr('sort');
6648 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6652 this.store.sortInfo = {field : sort, direction : dir};
6655 Roo.log("calling footer first");
6656 this.footer.onClick('first');
6659 this.store.load({ params : { start : 0 } });
6663 renderHeader : function()
6671 this.totalWidth = 0;
6673 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6675 var config = cm.config[i];
6679 cls : 'x-hcol-' + i,
6681 html: cm.getColumnHeader(i)
6686 if(typeof(config.sortable) != 'undefined' && config.sortable){
6688 c.html = '<i class="glyphicon"></i>' + c.html;
6691 if(typeof(config.lgHeader) != 'undefined'){
6692 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6695 if(typeof(config.mdHeader) != 'undefined'){
6696 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6699 if(typeof(config.smHeader) != 'undefined'){
6700 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6703 if(typeof(config.xsHeader) != 'undefined'){
6704 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6711 if(typeof(config.tooltip) != 'undefined'){
6712 c.tooltip = config.tooltip;
6715 if(typeof(config.colspan) != 'undefined'){
6716 c.colspan = config.colspan;
6719 if(typeof(config.hidden) != 'undefined' && config.hidden){
6720 c.style += ' display:none;';
6723 if(typeof(config.dataIndex) != 'undefined'){
6724 c.sort = config.dataIndex;
6729 if(typeof(config.align) != 'undefined' && config.align.length){
6730 c.style += ' text-align:' + config.align + ';';
6733 if(typeof(config.width) != 'undefined'){
6734 c.style += ' width:' + config.width + 'px;';
6735 this.totalWidth += config.width;
6737 this.totalWidth += 100; // assume minimum of 100 per column?
6740 if(typeof(config.cls) != 'undefined'){
6741 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6744 ['xs','sm','md','lg'].map(function(size){
6746 if(typeof(config[size]) == 'undefined'){
6750 if (!config[size]) { // 0 = hidden
6751 c.cls += ' hidden-' + size;
6755 c.cls += ' col-' + size + '-' + config[size];
6765 renderBody : function()
6775 colspan : this.cm.getColumnCount()
6785 renderFooter : function()
6795 colspan : this.cm.getColumnCount()
6809 // Roo.log('ds onload');
6814 var ds = this.store;
6816 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6817 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6818 if (_this.store.sortInfo) {
6820 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6821 e.select('i', true).addClass(['glyphicon-arrow-up']);
6824 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6825 e.select('i', true).addClass(['glyphicon-arrow-down']);
6830 var tbody = this.mainBody;
6832 if(ds.getCount() > 0){
6833 ds.data.each(function(d,rowIndex){
6834 var row = this.renderRow(cm, ds, rowIndex);
6836 tbody.createChild(row);
6840 if(row.cellObjects.length){
6841 Roo.each(row.cellObjects, function(r){
6842 _this.renderCellObject(r);
6849 var tfoot = this.el.select('tfoot', true).first();
6851 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6853 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6855 var total = this.ds.getTotalCount();
6857 if(this.footer.pageSize < total){
6858 this.mainFoot.show();
6862 Roo.each(this.el.select('tbody td', true).elements, function(e){
6863 e.on('mouseover', _this.onMouseover, _this);
6866 Roo.each(this.el.select('tbody td', true).elements, function(e){
6867 e.on('mouseout', _this.onMouseout, _this);
6869 this.fireEvent('rowsrendered', this);
6875 onUpdate : function(ds,record)
6877 this.refreshRow(record);
6881 onRemove : function(ds, record, index, isUpdate){
6882 if(isUpdate !== true){
6883 this.fireEvent("beforerowremoved", this, index, record);
6885 var bt = this.mainBody.dom;
6887 var rows = this.el.select('tbody > tr', true).elements;
6889 if(typeof(rows[index]) != 'undefined'){
6890 bt.removeChild(rows[index].dom);
6893 // if(bt.rows[index]){
6894 // bt.removeChild(bt.rows[index]);
6897 if(isUpdate !== true){
6898 //this.stripeRows(index);
6899 //this.syncRowHeights(index, index);
6901 this.fireEvent("rowremoved", this, index, record);
6905 onAdd : function(ds, records, rowIndex)
6907 //Roo.log('on Add called');
6908 // - note this does not handle multiple adding very well..
6909 var bt = this.mainBody.dom;
6910 for (var i =0 ; i < records.length;i++) {
6911 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6912 //Roo.log(records[i]);
6913 //Roo.log(this.store.getAt(rowIndex+i));
6914 this.insertRow(this.store, rowIndex + i, false);
6921 refreshRow : function(record){
6922 var ds = this.store, index;
6923 if(typeof record == 'number'){
6925 record = ds.getAt(index);
6927 index = ds.indexOf(record);
6929 this.insertRow(ds, index, true);
6931 this.onRemove(ds, record, index+1, true);
6933 //this.syncRowHeights(index, index);
6935 this.fireEvent("rowupdated", this, index, record);
6938 insertRow : function(dm, rowIndex, isUpdate){
6941 this.fireEvent("beforerowsinserted", this, rowIndex);
6943 //var s = this.getScrollState();
6944 var row = this.renderRow(this.cm, this.store, rowIndex);
6945 // insert before rowIndex..
6946 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6950 if(row.cellObjects.length){
6951 Roo.each(row.cellObjects, function(r){
6952 _this.renderCellObject(r);
6957 this.fireEvent("rowsinserted", this, rowIndex);
6958 //this.syncRowHeights(firstRow, lastRow);
6959 //this.stripeRows(firstRow);
6966 getRowDom : function(rowIndex)
6968 var rows = this.el.select('tbody > tr', true).elements;
6970 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6973 // returns the object tree for a tr..
6976 renderRow : function(cm, ds, rowIndex)
6978 var d = ds.getAt(rowIndex);
6982 cls : 'x-row-' + rowIndex,
6986 var cellObjects = [];
6988 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6989 var config = cm.config[i];
6991 var renderer = cm.getRenderer(i);
6995 if(typeof(renderer) !== 'undefined'){
6996 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6998 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6999 // and are rendered into the cells after the row is rendered - using the id for the element.
7001 if(typeof(value) === 'object'){
7011 rowIndex : rowIndex,
7016 this.fireEvent('rowclass', this, rowcfg);
7020 cls : rowcfg.rowClass + ' x-col-' + i,
7022 html: (typeof(value) === 'object') ? '' : value
7029 if(typeof(config.colspan) != 'undefined'){
7030 td.colspan = config.colspan;
7033 if(typeof(config.hidden) != 'undefined' && config.hidden){
7034 td.style += ' display:none;';
7037 if(typeof(config.align) != 'undefined' && config.align.length){
7038 td.style += ' text-align:' + config.align + ';';
7040 if(typeof(config.valign) != 'undefined' && config.valign.length){
7041 td.style += ' vertical-align:' + config.valign + ';';
7044 if(typeof(config.width) != 'undefined'){
7045 td.style += ' width:' + config.width + 'px;';
7048 if(typeof(config.cursor) != 'undefined'){
7049 td.style += ' cursor:' + config.cursor + ';';
7052 if(typeof(config.cls) != 'undefined'){
7053 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7056 ['xs','sm','md','lg'].map(function(size){
7058 if(typeof(config[size]) == 'undefined'){
7062 if (!config[size]) { // 0 = hidden
7063 td.cls += ' hidden-' + size;
7067 td.cls += ' col-' + size + '-' + config[size];
7075 row.cellObjects = cellObjects;
7083 onBeforeLoad : function()
7092 this.el.select('tbody', true).first().dom.innerHTML = '';
7095 * Show or hide a row.
7096 * @param {Number} rowIndex to show or hide
7097 * @param {Boolean} state hide
7099 setRowVisibility : function(rowIndex, state)
7101 var bt = this.mainBody.dom;
7103 var rows = this.el.select('tbody > tr', true).elements;
7105 if(typeof(rows[rowIndex]) == 'undefined'){
7108 rows[rowIndex].dom.style.display = state ? '' : 'none';
7112 getSelectionModel : function(){
7114 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7116 return this.selModel;
7119 * Render the Roo.bootstrap object from renderder
7121 renderCellObject : function(r)
7125 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7127 var t = r.cfg.render(r.container);
7130 Roo.each(r.cfg.cn, function(c){
7132 container: t.getChildContainer(),
7135 _this.renderCellObject(child);
7140 getRowIndex : function(row)
7144 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7155 * Returns the grid's underlying element = used by panel.Grid
7156 * @return {Element} The element
7158 getGridEl : function(){
7162 * Forces a resize - used by panel.Grid
7163 * @return {Element} The element
7165 autoSize : function()
7167 //var ctr = Roo.get(this.container.dom.parentElement);
7168 var ctr = Roo.get(this.el.dom);
7170 var thd = this.getGridEl().select('thead',true).first();
7171 var tbd = this.getGridEl().select('tbody', true).first();
7172 var tfd = this.getGridEl().select('tfoot', true).first();
7174 var cw = ctr.getWidth();
7178 tbd.setSize(ctr.getWidth(),
7179 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7181 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7184 cw = Math.max(cw, this.totalWidth);
7185 this.getGridEl().select('tr',true).setWidth(cw);
7186 // resize 'expandable coloumn?
7188 return; // we doe not have a view in this design..
7191 onBodyScroll: function()
7193 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7195 this.mainHead.setStyle({
7196 'position' : 'relative',
7197 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7203 var scrollHeight = this.mainBody.dom.scrollHeight;
7205 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7207 var height = this.mainBody.getHeight();
7209 if(scrollHeight - height == scrollTop) {
7211 var total = this.ds.getTotalCount();
7213 if(this.footer.cursor + this.footer.pageSize < total){
7215 this.footer.ds.load({
7217 start : this.footer.cursor + this.footer.pageSize,
7218 limit : this.footer.pageSize
7228 onHeaderChange : function()
7230 var header = this.renderHeader();
7231 var table = this.el.select('table', true).first();
7233 this.mainHead.remove();
7234 this.mainHead = table.createChild(header, this.mainBody, false);
7237 onHiddenChange : function(colModel, colIndex, hidden)
7239 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7240 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7242 this.CSS.updateRule(thSelector, "display", "");
7243 this.CSS.updateRule(tdSelector, "display", "");
7246 this.CSS.updateRule(thSelector, "display", "none");
7247 this.CSS.updateRule(tdSelector, "display", "none");
7250 this.onHeaderChange();
7254 setColumnWidth: function(col_index, width)
7256 // width = "md-2 xs-2..."
7257 if(!this.colModel.config[col_index]) {
7261 var w = width.split(" ");
7263 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7265 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7268 for(var j = 0; j < w.length; j++) {
7274 var size_cls = w[j].split("-");
7276 if(!Number.isInteger(size_cls[1] * 1)) {
7280 if(!this.colModel.config[col_index][size_cls[0]]) {
7284 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7288 h_row[0].classList.replace(
7289 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7290 "col-"+size_cls[0]+"-"+size_cls[1]
7293 for(var i = 0; i < rows.length; i++) {
7295 var size_cls = w[j].split("-");
7297 if(!Number.isInteger(size_cls[1] * 1)) {
7301 if(!this.colModel.config[col_index][size_cls[0]]) {
7305 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7309 rows[i].classList.replace(
7310 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7311 "col-"+size_cls[0]+"-"+size_cls[1]
7315 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7330 * @class Roo.bootstrap.TableCell
7331 * @extends Roo.bootstrap.Component
7332 * Bootstrap TableCell class
7333 * @cfg {String} html cell contain text
7334 * @cfg {String} cls cell class
7335 * @cfg {String} tag cell tag (td|th) default td
7336 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7337 * @cfg {String} align Aligns the content in a cell
7338 * @cfg {String} axis Categorizes cells
7339 * @cfg {String} bgcolor Specifies the background color of a cell
7340 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7341 * @cfg {Number} colspan Specifies the number of columns a cell should span
7342 * @cfg {String} headers Specifies one or more header cells a cell is related to
7343 * @cfg {Number} height Sets the height of a cell
7344 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7345 * @cfg {Number} rowspan Sets the number of rows a cell should span
7346 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7347 * @cfg {String} valign Vertical aligns the content in a cell
7348 * @cfg {Number} width Specifies the width of a cell
7351 * Create a new TableCell
7352 * @param {Object} config The config object
7355 Roo.bootstrap.TableCell = function(config){
7356 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7359 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7379 getAutoCreate : function(){
7380 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7400 cfg.align=this.align
7406 cfg.bgcolor=this.bgcolor
7409 cfg.charoff=this.charoff
7412 cfg.colspan=this.colspan
7415 cfg.headers=this.headers
7418 cfg.height=this.height
7421 cfg.nowrap=this.nowrap
7424 cfg.rowspan=this.rowspan
7427 cfg.scope=this.scope
7430 cfg.valign=this.valign
7433 cfg.width=this.width
7452 * @class Roo.bootstrap.TableRow
7453 * @extends Roo.bootstrap.Component
7454 * Bootstrap TableRow class
7455 * @cfg {String} cls row class
7456 * @cfg {String} align Aligns the content in a table row
7457 * @cfg {String} bgcolor Specifies a background color for a table row
7458 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7459 * @cfg {String} valign Vertical aligns the content in a table row
7462 * Create a new TableRow
7463 * @param {Object} config The config object
7466 Roo.bootstrap.TableRow = function(config){
7467 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7470 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7478 getAutoCreate : function(){
7479 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7489 cfg.align = this.align;
7492 cfg.bgcolor = this.bgcolor;
7495 cfg.charoff = this.charoff;
7498 cfg.valign = this.valign;
7516 * @class Roo.bootstrap.TableBody
7517 * @extends Roo.bootstrap.Component
7518 * Bootstrap TableBody class
7519 * @cfg {String} cls element class
7520 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7521 * @cfg {String} align Aligns the content inside the element
7522 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7523 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7526 * Create a new TableBody
7527 * @param {Object} config The config object
7530 Roo.bootstrap.TableBody = function(config){
7531 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7534 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7542 getAutoCreate : function(){
7543 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7557 cfg.align = this.align;
7560 cfg.charoff = this.charoff;
7563 cfg.valign = this.valign;
7570 // initEvents : function()
7577 // this.store = Roo.factory(this.store, Roo.data);
7578 // this.store.on('load', this.onLoad, this);
7580 // this.store.load();
7584 // onLoad: function ()
7586 // this.fireEvent('load', this);
7596 * Ext JS Library 1.1.1
7597 * Copyright(c) 2006-2007, Ext JS, LLC.
7599 * Originally Released Under LGPL - original licence link has changed is not relivant.
7602 * <script type="text/javascript">
7605 // as we use this in bootstrap.
7606 Roo.namespace('Roo.form');
7608 * @class Roo.form.Action
7609 * Internal Class used to handle form actions
7611 * @param {Roo.form.BasicForm} el The form element or its id
7612 * @param {Object} config Configuration options
7617 // define the action interface
7618 Roo.form.Action = function(form, options){
7620 this.options = options || {};
7623 * Client Validation Failed
7626 Roo.form.Action.CLIENT_INVALID = 'client';
7628 * Server Validation Failed
7631 Roo.form.Action.SERVER_INVALID = 'server';
7633 * Connect to Server Failed
7636 Roo.form.Action.CONNECT_FAILURE = 'connect';
7638 * Reading Data from Server Failed
7641 Roo.form.Action.LOAD_FAILURE = 'load';
7643 Roo.form.Action.prototype = {
7645 failureType : undefined,
7646 response : undefined,
7650 run : function(options){
7655 success : function(response){
7660 handleResponse : function(response){
7664 // default connection failure
7665 failure : function(response){
7667 this.response = response;
7668 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7669 this.form.afterAction(this, false);
7672 processResponse : function(response){
7673 this.response = response;
7674 if(!response.responseText){
7677 this.result = this.handleResponse(response);
7681 // utility functions used internally
7682 getUrl : function(appendParams){
7683 var url = this.options.url || this.form.url || this.form.el.dom.action;
7685 var p = this.getParams();
7687 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7693 getMethod : function(){
7694 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7697 getParams : function(){
7698 var bp = this.form.baseParams;
7699 var p = this.options.params;
7701 if(typeof p == "object"){
7702 p = Roo.urlEncode(Roo.applyIf(p, bp));
7703 }else if(typeof p == 'string' && bp){
7704 p += '&' + Roo.urlEncode(bp);
7707 p = Roo.urlEncode(bp);
7712 createCallback : function(){
7714 success: this.success,
7715 failure: this.failure,
7717 timeout: (this.form.timeout*1000),
7718 upload: this.form.fileUpload ? this.success : undefined
7723 Roo.form.Action.Submit = function(form, options){
7724 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7727 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7730 haveProgress : false,
7731 uploadComplete : false,
7733 // uploadProgress indicator.
7734 uploadProgress : function()
7736 if (!this.form.progressUrl) {
7740 if (!this.haveProgress) {
7741 Roo.MessageBox.progress("Uploading", "Uploading");
7743 if (this.uploadComplete) {
7744 Roo.MessageBox.hide();
7748 this.haveProgress = true;
7750 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7752 var c = new Roo.data.Connection();
7754 url : this.form.progressUrl,
7759 success : function(req){
7760 //console.log(data);
7764 rdata = Roo.decode(req.responseText)
7766 Roo.log("Invalid data from server..");
7770 if (!rdata || !rdata.success) {
7772 Roo.MessageBox.alert(Roo.encode(rdata));
7775 var data = rdata.data;
7777 if (this.uploadComplete) {
7778 Roo.MessageBox.hide();
7783 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7784 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7787 this.uploadProgress.defer(2000,this);
7790 failure: function(data) {
7791 Roo.log('progress url failed ');
7802 // run get Values on the form, so it syncs any secondary forms.
7803 this.form.getValues();
7805 var o = this.options;
7806 var method = this.getMethod();
7807 var isPost = method == 'POST';
7808 if(o.clientValidation === false || this.form.isValid()){
7810 if (this.form.progressUrl) {
7811 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7812 (new Date() * 1) + '' + Math.random());
7817 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7818 form:this.form.el.dom,
7819 url:this.getUrl(!isPost),
7821 params:isPost ? this.getParams() : null,
7822 isUpload: this.form.fileUpload
7825 this.uploadProgress();
7827 }else if (o.clientValidation !== false){ // client validation failed
7828 this.failureType = Roo.form.Action.CLIENT_INVALID;
7829 this.form.afterAction(this, false);
7833 success : function(response)
7835 this.uploadComplete= true;
7836 if (this.haveProgress) {
7837 Roo.MessageBox.hide();
7841 var result = this.processResponse(response);
7842 if(result === true || result.success){
7843 this.form.afterAction(this, true);
7847 this.form.markInvalid(result.errors);
7848 this.failureType = Roo.form.Action.SERVER_INVALID;
7850 this.form.afterAction(this, false);
7852 failure : function(response)
7854 this.uploadComplete= true;
7855 if (this.haveProgress) {
7856 Roo.MessageBox.hide();
7859 this.response = response;
7860 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7861 this.form.afterAction(this, false);
7864 handleResponse : function(response){
7865 if(this.form.errorReader){
7866 var rs = this.form.errorReader.read(response);
7869 for(var i = 0, len = rs.records.length; i < len; i++) {
7870 var r = rs.records[i];
7874 if(errors.length < 1){
7878 success : rs.success,
7884 ret = Roo.decode(response.responseText);
7888 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7898 Roo.form.Action.Load = function(form, options){
7899 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7900 this.reader = this.form.reader;
7903 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7908 Roo.Ajax.request(Roo.apply(
7909 this.createCallback(), {
7910 method:this.getMethod(),
7911 url:this.getUrl(false),
7912 params:this.getParams()
7916 success : function(response){
7918 var result = this.processResponse(response);
7919 if(result === true || !result.success || !result.data){
7920 this.failureType = Roo.form.Action.LOAD_FAILURE;
7921 this.form.afterAction(this, false);
7924 this.form.clearInvalid();
7925 this.form.setValues(result.data);
7926 this.form.afterAction(this, true);
7929 handleResponse : function(response){
7930 if(this.form.reader){
7931 var rs = this.form.reader.read(response);
7932 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7934 success : rs.success,
7938 return Roo.decode(response.responseText);
7942 Roo.form.Action.ACTION_TYPES = {
7943 'load' : Roo.form.Action.Load,
7944 'submit' : Roo.form.Action.Submit
7953 * @class Roo.bootstrap.Form
7954 * @extends Roo.bootstrap.Component
7955 * Bootstrap Form class
7956 * @cfg {String} method GET | POST (default POST)
7957 * @cfg {String} labelAlign top | left (default top)
7958 * @cfg {String} align left | right - for navbars
7959 * @cfg {Boolean} loadMask load mask when submit (default true)
7964 * @param {Object} config The config object
7968 Roo.bootstrap.Form = function(config){
7970 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7972 Roo.bootstrap.Form.popover.apply();
7976 * @event clientvalidation
7977 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7978 * @param {Form} this
7979 * @param {Boolean} valid true if the form has passed client-side validation
7981 clientvalidation: true,
7983 * @event beforeaction
7984 * Fires before any action is performed. Return false to cancel the action.
7985 * @param {Form} this
7986 * @param {Action} action The action to be performed
7990 * @event actionfailed
7991 * Fires when an action fails.
7992 * @param {Form} this
7993 * @param {Action} action The action that failed
7995 actionfailed : true,
7997 * @event actioncomplete
7998 * Fires when an action is completed.
7999 * @param {Form} this
8000 * @param {Action} action The action that completed
8002 actioncomplete : true
8006 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8009 * @cfg {String} method
8010 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8015 * The URL to use for form actions if one isn't supplied in the action options.
8018 * @cfg {Boolean} fileUpload
8019 * Set to true if this form is a file upload.
8023 * @cfg {Object} baseParams
8024 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8028 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8032 * @cfg {Sting} align (left|right) for navbar forms
8037 activeAction : null,
8040 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8041 * element by passing it or its id or mask the form itself by passing in true.
8044 waitMsgTarget : false,
8049 * @cfg {Boolean} errorMask (true|false) default false
8054 * @cfg {Number} maskOffset Default 100
8059 * @cfg {Boolean} maskBody
8063 getAutoCreate : function(){
8067 method : this.method || 'POST',
8068 id : this.id || Roo.id(),
8071 if (this.parent().xtype.match(/^Nav/)) {
8072 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8076 if (this.labelAlign == 'left' ) {
8077 cfg.cls += ' form-horizontal';
8083 initEvents : function()
8085 this.el.on('submit', this.onSubmit, this);
8086 // this was added as random key presses on the form where triggering form submit.
8087 this.el.on('keypress', function(e) {
8088 if (e.getCharCode() != 13) {
8091 // we might need to allow it for textareas.. and some other items.
8092 // check e.getTarget().
8094 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8098 Roo.log("keypress blocked");
8106 onSubmit : function(e){
8111 * Returns true if client-side validation on the form is successful.
8114 isValid : function(){
8115 var items = this.getItems();
8119 items.each(function(f){
8125 Roo.log('invalid field: ' + f.name);
8129 if(!target && f.el.isVisible(true)){
8135 if(this.errorMask && !valid){
8136 Roo.bootstrap.Form.popover.mask(this, target);
8143 * Returns true if any fields in this form have changed since their original load.
8146 isDirty : function(){
8148 var items = this.getItems();
8149 items.each(function(f){
8159 * Performs a predefined action (submit or load) or custom actions you define on this form.
8160 * @param {String} actionName The name of the action type
8161 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8162 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8163 * accept other config options):
8165 Property Type Description
8166 ---------------- --------------- ----------------------------------------------------------------------------------
8167 url String The url for the action (defaults to the form's url)
8168 method String The form method to use (defaults to the form's method, or POST if not defined)
8169 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8170 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8171 validate the form on the client (defaults to false)
8173 * @return {BasicForm} this
8175 doAction : function(action, options){
8176 if(typeof action == 'string'){
8177 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8179 if(this.fireEvent('beforeaction', this, action) !== false){
8180 this.beforeAction(action);
8181 action.run.defer(100, action);
8187 beforeAction : function(action){
8188 var o = action.options;
8193 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8195 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8198 // not really supported yet.. ??
8200 //if(this.waitMsgTarget === true){
8201 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8202 //}else if(this.waitMsgTarget){
8203 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8204 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8206 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8212 afterAction : function(action, success){
8213 this.activeAction = null;
8214 var o = action.options;
8219 Roo.get(document.body).unmask();
8225 //if(this.waitMsgTarget === true){
8226 // this.el.unmask();
8227 //}else if(this.waitMsgTarget){
8228 // this.waitMsgTarget.unmask();
8230 // Roo.MessageBox.updateProgress(1);
8231 // Roo.MessageBox.hide();
8238 Roo.callback(o.success, o.scope, [this, action]);
8239 this.fireEvent('actioncomplete', this, action);
8243 // failure condition..
8244 // we have a scenario where updates need confirming.
8245 // eg. if a locking scenario exists..
8246 // we look for { errors : { needs_confirm : true }} in the response.
8248 (typeof(action.result) != 'undefined') &&
8249 (typeof(action.result.errors) != 'undefined') &&
8250 (typeof(action.result.errors.needs_confirm) != 'undefined')
8253 Roo.log("not supported yet");
8256 Roo.MessageBox.confirm(
8257 "Change requires confirmation",
8258 action.result.errorMsg,
8263 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8273 Roo.callback(o.failure, o.scope, [this, action]);
8274 // show an error message if no failed handler is set..
8275 if (!this.hasListener('actionfailed')) {
8276 Roo.log("need to add dialog support");
8278 Roo.MessageBox.alert("Error",
8279 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8280 action.result.errorMsg :
8281 "Saving Failed, please check your entries or try again"
8286 this.fireEvent('actionfailed', this, action);
8291 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8292 * @param {String} id The value to search for
8295 findField : function(id){
8296 var items = this.getItems();
8297 var field = items.get(id);
8299 items.each(function(f){
8300 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8307 return field || null;
8310 * Mark fields in this form invalid in bulk.
8311 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8312 * @return {BasicForm} this
8314 markInvalid : function(errors){
8315 if(errors instanceof Array){
8316 for(var i = 0, len = errors.length; i < len; i++){
8317 var fieldError = errors[i];
8318 var f = this.findField(fieldError.id);
8320 f.markInvalid(fieldError.msg);
8326 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8327 field.markInvalid(errors[id]);
8331 //Roo.each(this.childForms || [], function (f) {
8332 // f.markInvalid(errors);
8339 * Set values for fields in this form in bulk.
8340 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8341 * @return {BasicForm} this
8343 setValues : function(values){
8344 if(values instanceof Array){ // array of objects
8345 for(var i = 0, len = values.length; i < len; i++){
8347 var f = this.findField(v.id);
8349 f.setValue(v.value);
8350 if(this.trackResetOnLoad){
8351 f.originalValue = f.getValue();
8355 }else{ // object hash
8358 if(typeof values[id] != 'function' && (field = this.findField(id))){
8360 if (field.setFromData &&
8362 field.displayField &&
8363 // combos' with local stores can
8364 // be queried via setValue()
8365 // to set their value..
8366 (field.store && !field.store.isLocal)
8370 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8371 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8372 field.setFromData(sd);
8374 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8376 field.setFromData(values);
8379 field.setValue(values[id]);
8383 if(this.trackResetOnLoad){
8384 field.originalValue = field.getValue();
8390 //Roo.each(this.childForms || [], function (f) {
8391 // f.setValues(values);
8398 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8399 * they are returned as an array.
8400 * @param {Boolean} asString
8403 getValues : function(asString){
8404 //if (this.childForms) {
8405 // copy values from the child forms
8406 // Roo.each(this.childForms, function (f) {
8407 // this.setValues(f.getValues());
8413 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8414 if(asString === true){
8417 return Roo.urlDecode(fs);
8421 * Returns the fields in this form as an object with key/value pairs.
8422 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8425 getFieldValues : function(with_hidden)
8427 var items = this.getItems();
8429 items.each(function(f){
8435 var v = f.getValue();
8437 if (f.inputType =='radio') {
8438 if (typeof(ret[f.getName()]) == 'undefined') {
8439 ret[f.getName()] = ''; // empty..
8442 if (!f.el.dom.checked) {
8450 if(f.xtype == 'MoneyField'){
8451 ret[f.currencyName] = f.getCurrency();
8454 // not sure if this supported any more..
8455 if ((typeof(v) == 'object') && f.getRawValue) {
8456 v = f.getRawValue() ; // dates..
8458 // combo boxes where name != hiddenName...
8459 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8460 ret[f.name] = f.getRawValue();
8462 ret[f.getName()] = v;
8469 * Clears all invalid messages in this form.
8470 * @return {BasicForm} this
8472 clearInvalid : function(){
8473 var items = this.getItems();
8475 items.each(function(f){
8484 * @return {BasicForm} this
8487 var items = this.getItems();
8488 items.each(function(f){
8492 Roo.each(this.childForms || [], function (f) {
8500 getItems : function()
8502 var r=new Roo.util.MixedCollection(false, function(o){
8503 return o.id || (o.id = Roo.id());
8505 var iter = function(el) {
8512 Roo.each(el.items,function(e) {
8521 hideFields : function(items)
8523 Roo.each(items, function(i){
8525 var f = this.findField(i);
8536 showFields : function(items)
8538 Roo.each(items, function(i){
8540 var f = this.findField(i);
8553 Roo.apply(Roo.bootstrap.Form, {
8580 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8581 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8582 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8583 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8586 this.maskEl.top.enableDisplayMode("block");
8587 this.maskEl.left.enableDisplayMode("block");
8588 this.maskEl.bottom.enableDisplayMode("block");
8589 this.maskEl.right.enableDisplayMode("block");
8591 this.toolTip = new Roo.bootstrap.Tooltip({
8592 cls : 'roo-form-error-popover',
8594 'left' : ['r-l', [-2,0], 'right'],
8595 'right' : ['l-r', [2,0], 'left'],
8596 'bottom' : ['tl-bl', [0,2], 'top'],
8597 'top' : [ 'bl-tl', [0,-2], 'bottom']
8601 this.toolTip.render(Roo.get(document.body));
8603 this.toolTip.el.enableDisplayMode("block");
8605 Roo.get(document.body).on('click', function(){
8609 Roo.get(document.body).on('touchstart', function(){
8613 this.isApplied = true
8616 mask : function(form, target)
8620 this.target = target;
8622 if(!this.form.errorMask || !target.el){
8626 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8628 Roo.log(scrollable);
8630 var ot = this.target.el.calcOffsetsTo(scrollable);
8632 var scrollTo = ot[1] - this.form.maskOffset;
8634 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8636 scrollable.scrollTo('top', scrollTo);
8638 var box = this.target.el.getBox();
8640 var zIndex = Roo.bootstrap.Modal.zIndex++;
8643 this.maskEl.top.setStyle('position', 'absolute');
8644 this.maskEl.top.setStyle('z-index', zIndex);
8645 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8646 this.maskEl.top.setLeft(0);
8647 this.maskEl.top.setTop(0);
8648 this.maskEl.top.show();
8650 this.maskEl.left.setStyle('position', 'absolute');
8651 this.maskEl.left.setStyle('z-index', zIndex);
8652 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8653 this.maskEl.left.setLeft(0);
8654 this.maskEl.left.setTop(box.y - this.padding);
8655 this.maskEl.left.show();
8657 this.maskEl.bottom.setStyle('position', 'absolute');
8658 this.maskEl.bottom.setStyle('z-index', zIndex);
8659 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8660 this.maskEl.bottom.setLeft(0);
8661 this.maskEl.bottom.setTop(box.bottom + this.padding);
8662 this.maskEl.bottom.show();
8664 this.maskEl.right.setStyle('position', 'absolute');
8665 this.maskEl.right.setStyle('z-index', zIndex);
8666 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8667 this.maskEl.right.setLeft(box.right + this.padding);
8668 this.maskEl.right.setTop(box.y - this.padding);
8669 this.maskEl.right.show();
8671 this.toolTip.bindEl = this.target.el;
8673 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8675 var tip = this.target.blankText;
8677 if(this.target.getValue() !== '' ) {
8679 if (this.target.invalidText.length) {
8680 tip = this.target.invalidText;
8681 } else if (this.target.regexText.length){
8682 tip = this.target.regexText;
8686 this.toolTip.show(tip);
8688 this.intervalID = window.setInterval(function() {
8689 Roo.bootstrap.Form.popover.unmask();
8692 window.onwheel = function(){ return false;};
8694 (function(){ this.isMasked = true; }).defer(500, this);
8700 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8704 this.maskEl.top.setStyle('position', 'absolute');
8705 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8706 this.maskEl.top.hide();
8708 this.maskEl.left.setStyle('position', 'absolute');
8709 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8710 this.maskEl.left.hide();
8712 this.maskEl.bottom.setStyle('position', 'absolute');
8713 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8714 this.maskEl.bottom.hide();
8716 this.maskEl.right.setStyle('position', 'absolute');
8717 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8718 this.maskEl.right.hide();
8720 this.toolTip.hide();
8722 this.toolTip.el.hide();
8724 window.onwheel = function(){ return true;};
8726 if(this.intervalID){
8727 window.clearInterval(this.intervalID);
8728 this.intervalID = false;
8731 this.isMasked = false;
8741 * Ext JS Library 1.1.1
8742 * Copyright(c) 2006-2007, Ext JS, LLC.
8744 * Originally Released Under LGPL - original licence link has changed is not relivant.
8747 * <script type="text/javascript">
8750 * @class Roo.form.VTypes
8751 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8754 Roo.form.VTypes = function(){
8755 // closure these in so they are only created once.
8756 var alpha = /^[a-zA-Z_]+$/;
8757 var alphanum = /^[a-zA-Z0-9_]+$/;
8758 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8759 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8761 // All these messages and functions are configurable
8764 * The function used to validate email addresses
8765 * @param {String} value The email address
8767 'email' : function(v){
8768 return email.test(v);
8771 * The error text to display when the email validation function returns false
8774 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8776 * The keystroke filter mask to be applied on email input
8779 'emailMask' : /[a-z0-9_\.\-@]/i,
8782 * The function used to validate URLs
8783 * @param {String} value The URL
8785 'url' : function(v){
8789 * The error text to display when the url validation function returns false
8792 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8795 * The function used to validate alpha values
8796 * @param {String} value The value
8798 'alpha' : function(v){
8799 return alpha.test(v);
8802 * The error text to display when the alpha validation function returns false
8805 'alphaText' : 'This field should only contain letters and _',
8807 * The keystroke filter mask to be applied on alpha input
8810 'alphaMask' : /[a-z_]/i,
8813 * The function used to validate alphanumeric values
8814 * @param {String} value The value
8816 'alphanum' : function(v){
8817 return alphanum.test(v);
8820 * The error text to display when the alphanumeric validation function returns false
8823 'alphanumText' : 'This field should only contain letters, numbers and _',
8825 * The keystroke filter mask to be applied on alphanumeric input
8828 'alphanumMask' : /[a-z0-9_]/i
8838 * @class Roo.bootstrap.Input
8839 * @extends Roo.bootstrap.Component
8840 * Bootstrap Input class
8841 * @cfg {Boolean} disabled is it disabled
8842 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8843 * @cfg {String} name name of the input
8844 * @cfg {string} fieldLabel - the label associated
8845 * @cfg {string} placeholder - placeholder to put in text.
8846 * @cfg {string} before - input group add on before
8847 * @cfg {string} after - input group add on after
8848 * @cfg {string} size - (lg|sm) or leave empty..
8849 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8850 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8851 * @cfg {Number} md colspan out of 12 for computer-sized screens
8852 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8853 * @cfg {string} value default value of the input
8854 * @cfg {Number} labelWidth set the width of label
8855 * @cfg {Number} labellg set the width of label (1-12)
8856 * @cfg {Number} labelmd set the width of label (1-12)
8857 * @cfg {Number} labelsm set the width of label (1-12)
8858 * @cfg {Number} labelxs set the width of label (1-12)
8859 * @cfg {String} labelAlign (top|left)
8860 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8861 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8862 * @cfg {String} indicatorpos (left|right) default left
8863 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8864 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8866 * @cfg {String} align (left|center|right) Default left
8867 * @cfg {Boolean} forceFeedback (true|false) Default false
8870 * Create a new Input
8871 * @param {Object} config The config object
8874 Roo.bootstrap.Input = function(config){
8876 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8881 * Fires when this field receives input focus.
8882 * @param {Roo.form.Field} this
8887 * Fires when this field loses input focus.
8888 * @param {Roo.form.Field} this
8893 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8894 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8895 * @param {Roo.form.Field} this
8896 * @param {Roo.EventObject} e The event object
8901 * Fires just before the field blurs if the field value has changed.
8902 * @param {Roo.form.Field} this
8903 * @param {Mixed} newValue The new value
8904 * @param {Mixed} oldValue The original value
8909 * Fires after the field has been marked as invalid.
8910 * @param {Roo.form.Field} this
8911 * @param {String} msg The validation message
8916 * Fires after the field has been validated with no errors.
8917 * @param {Roo.form.Field} this
8922 * Fires after the key up
8923 * @param {Roo.form.Field} this
8924 * @param {Roo.EventObject} e The event Object
8930 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8932 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8933 automatic validation (defaults to "keyup").
8935 validationEvent : "keyup",
8937 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8939 validateOnBlur : true,
8941 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8943 validationDelay : 250,
8945 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8947 focusClass : "x-form-focus", // not needed???
8951 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8953 invalidClass : "has-warning",
8956 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8958 validClass : "has-success",
8961 * @cfg {Boolean} hasFeedback (true|false) default true
8966 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8968 invalidFeedbackClass : "glyphicon-warning-sign",
8971 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8973 validFeedbackClass : "glyphicon-ok",
8976 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8978 selectOnFocus : false,
8981 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8985 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8990 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8992 disableKeyFilter : false,
8995 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8999 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9003 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9005 blankText : "Please complete this mandatory field",
9008 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9012 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9014 maxLength : Number.MAX_VALUE,
9016 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9018 minLengthText : "The minimum length for this field is {0}",
9020 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9022 maxLengthText : "The maximum length for this field is {0}",
9026 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9027 * If available, this function will be called only after the basic validators all return true, and will be passed the
9028 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9032 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9033 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9034 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9038 * @cfg {String} regexText -- Depricated - use Invalid Text
9043 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9049 autocomplete: false,
9068 formatedValue : false,
9069 forceFeedback : false,
9071 indicatorpos : 'left',
9081 parentLabelAlign : function()
9084 while (parent.parent()) {
9085 parent = parent.parent();
9086 if (typeof(parent.labelAlign) !='undefined') {
9087 return parent.labelAlign;
9094 getAutoCreate : function()
9096 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9102 if(this.inputType != 'hidden'){
9103 cfg.cls = 'form-group' //input-group
9109 type : this.inputType,
9111 cls : 'form-control',
9112 placeholder : this.placeholder || '',
9113 autocomplete : this.autocomplete || 'new-password'
9116 if(this.capture.length){
9117 input.capture = this.capture;
9120 if(this.accept.length){
9121 input.accept = this.accept + "/*";
9125 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9128 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9129 input.maxLength = this.maxLength;
9132 if (this.disabled) {
9133 input.disabled=true;
9136 if (this.readOnly) {
9137 input.readonly=true;
9141 input.name = this.name;
9145 input.cls += ' input-' + this.size;
9149 ['xs','sm','md','lg'].map(function(size){
9150 if (settings[size]) {
9151 cfg.cls += ' col-' + size + '-' + settings[size];
9155 var inputblock = input;
9159 cls: 'glyphicon form-control-feedback'
9162 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9165 cls : 'has-feedback',
9173 if (this.before || this.after) {
9176 cls : 'input-group',
9180 if (this.before && typeof(this.before) == 'string') {
9182 inputblock.cn.push({
9184 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9188 if (this.before && typeof(this.before) == 'object') {
9189 this.before = Roo.factory(this.before);
9191 inputblock.cn.push({
9193 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9194 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9198 inputblock.cn.push(input);
9200 if (this.after && typeof(this.after) == 'string') {
9201 inputblock.cn.push({
9203 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9207 if (this.after && typeof(this.after) == 'object') {
9208 this.after = Roo.factory(this.after);
9210 inputblock.cn.push({
9212 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9213 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9217 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9218 inputblock.cls += ' has-feedback';
9219 inputblock.cn.push(feedback);
9224 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9225 tooltip : 'This field is required'
9227 if (Roo.bootstrap.version == 4) {
9230 style : 'display-none'
9233 if (align ==='left' && this.fieldLabel.length) {
9235 cfg.cls += ' roo-form-group-label-left row';
9242 cls : 'control-label col-form-label',
9243 html : this.fieldLabel
9254 var labelCfg = cfg.cn[1];
9255 var contentCfg = cfg.cn[2];
9257 if(this.indicatorpos == 'right'){
9262 cls : 'control-label col-form-label',
9266 html : this.fieldLabel
9280 labelCfg = cfg.cn[0];
9281 contentCfg = cfg.cn[1];
9285 if(this.labelWidth > 12){
9286 labelCfg.style = "width: " + this.labelWidth + 'px';
9289 if(this.labelWidth < 13 && this.labelmd == 0){
9290 this.labelmd = this.labelWidth;
9293 if(this.labellg > 0){
9294 labelCfg.cls += ' col-lg-' + this.labellg;
9295 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9298 if(this.labelmd > 0){
9299 labelCfg.cls += ' col-md-' + this.labelmd;
9300 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9303 if(this.labelsm > 0){
9304 labelCfg.cls += ' col-sm-' + this.labelsm;
9305 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9308 if(this.labelxs > 0){
9309 labelCfg.cls += ' col-xs-' + this.labelxs;
9310 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9314 } else if ( this.fieldLabel.length) {
9319 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9320 tooltip : 'This field is required'
9324 //cls : 'input-group-addon',
9325 html : this.fieldLabel
9333 if(this.indicatorpos == 'right'){
9338 //cls : 'input-group-addon',
9339 html : this.fieldLabel
9344 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9345 tooltip : 'This field is required'
9365 if (this.parentType === 'Navbar' && this.parent().bar) {
9366 cfg.cls += ' navbar-form';
9369 if (this.parentType === 'NavGroup') {
9370 cfg.cls += ' navbar-form';
9378 * return the real input element.
9380 inputEl: function ()
9382 return this.el.select('input.form-control',true).first();
9385 tooltipEl : function()
9387 return this.inputEl();
9390 indicatorEl : function()
9392 if (Roo.bootstrap.version == 4) {
9393 return false; // not enabled in v4 yet.
9396 var indicator = this.el.select('i.roo-required-indicator',true).first();
9406 setDisabled : function(v)
9408 var i = this.inputEl().dom;
9410 i.removeAttribute('disabled');
9414 i.setAttribute('disabled','true');
9416 initEvents : function()
9419 this.inputEl().on("keydown" , this.fireKey, this);
9420 this.inputEl().on("focus", this.onFocus, this);
9421 this.inputEl().on("blur", this.onBlur, this);
9423 this.inputEl().relayEvent('keyup', this);
9425 this.indicator = this.indicatorEl();
9428 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9431 // reference to original value for reset
9432 this.originalValue = this.getValue();
9433 //Roo.form.TextField.superclass.initEvents.call(this);
9434 if(this.validationEvent == 'keyup'){
9435 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9436 this.inputEl().on('keyup', this.filterValidation, this);
9438 else if(this.validationEvent !== false){
9439 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9442 if(this.selectOnFocus){
9443 this.on("focus", this.preFocus, this);
9446 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9447 this.inputEl().on("keypress", this.filterKeys, this);
9449 this.inputEl().relayEvent('keypress', this);
9452 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9453 this.el.on("click", this.autoSize, this);
9456 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9457 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9460 if (typeof(this.before) == 'object') {
9461 this.before.render(this.el.select('.roo-input-before',true).first());
9463 if (typeof(this.after) == 'object') {
9464 this.after.render(this.el.select('.roo-input-after',true).first());
9467 this.inputEl().on('change', this.onChange, this);
9470 filterValidation : function(e){
9471 if(!e.isNavKeyPress()){
9472 this.validationTask.delay(this.validationDelay);
9476 * Validates the field value
9477 * @return {Boolean} True if the value is valid, else false
9479 validate : function(){
9480 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9481 if(this.disabled || this.validateValue(this.getRawValue())){
9492 * Validates a value according to the field's validation rules and marks the field as invalid
9493 * if the validation fails
9494 * @param {Mixed} value The value to validate
9495 * @return {Boolean} True if the value is valid, else false
9497 validateValue : function(value)
9499 if(this.getVisibilityEl().hasClass('hidden')){
9503 if(value.length < 1) { // if it's blank
9504 if(this.allowBlank){
9510 if(value.length < this.minLength){
9513 if(value.length > this.maxLength){
9517 var vt = Roo.form.VTypes;
9518 if(!vt[this.vtype](value, this)){
9522 if(typeof this.validator == "function"){
9523 var msg = this.validator(value);
9527 if (typeof(msg) == 'string') {
9528 this.invalidText = msg;
9532 if(this.regex && !this.regex.test(value)){
9540 fireKey : function(e){
9541 //Roo.log('field ' + e.getKey());
9542 if(e.isNavKeyPress()){
9543 this.fireEvent("specialkey", this, e);
9546 focus : function (selectText){
9548 this.inputEl().focus();
9549 if(selectText === true){
9550 this.inputEl().dom.select();
9556 onFocus : function(){
9557 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9558 // this.el.addClass(this.focusClass);
9561 this.hasFocus = true;
9562 this.startValue = this.getValue();
9563 this.fireEvent("focus", this);
9567 beforeBlur : Roo.emptyFn,
9571 onBlur : function(){
9573 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9574 //this.el.removeClass(this.focusClass);
9576 this.hasFocus = false;
9577 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9580 var v = this.getValue();
9581 if(String(v) !== String(this.startValue)){
9582 this.fireEvent('change', this, v, this.startValue);
9584 this.fireEvent("blur", this);
9587 onChange : function(e)
9589 var v = this.getValue();
9590 if(String(v) !== String(this.startValue)){
9591 this.fireEvent('change', this, v, this.startValue);
9597 * Resets the current field value to the originally loaded value and clears any validation messages
9600 this.setValue(this.originalValue);
9604 * Returns the name of the field
9605 * @return {Mixed} name The name field
9607 getName: function(){
9611 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9612 * @return {Mixed} value The field value
9614 getValue : function(){
9616 var v = this.inputEl().getValue();
9621 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9622 * @return {Mixed} value The field value
9624 getRawValue : function(){
9625 var v = this.inputEl().getValue();
9631 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9632 * @param {Mixed} value The value to set
9634 setRawValue : function(v){
9635 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9638 selectText : function(start, end){
9639 var v = this.getRawValue();
9641 start = start === undefined ? 0 : start;
9642 end = end === undefined ? v.length : end;
9643 var d = this.inputEl().dom;
9644 if(d.setSelectionRange){
9645 d.setSelectionRange(start, end);
9646 }else if(d.createTextRange){
9647 var range = d.createTextRange();
9648 range.moveStart("character", start);
9649 range.moveEnd("character", v.length-end);
9656 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9657 * @param {Mixed} value The value to set
9659 setValue : function(v){
9662 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9668 processValue : function(value){
9669 if(this.stripCharsRe){
9670 var newValue = value.replace(this.stripCharsRe, '');
9671 if(newValue !== value){
9672 this.setRawValue(newValue);
9679 preFocus : function(){
9681 if(this.selectOnFocus){
9682 this.inputEl().dom.select();
9685 filterKeys : function(e){
9687 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9690 var c = e.getCharCode(), cc = String.fromCharCode(c);
9691 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9694 if(!this.maskRe.test(cc)){
9699 * Clear any invalid styles/messages for this field
9701 clearInvalid : function(){
9703 if(!this.el || this.preventMark){ // not rendered
9708 this.el.removeClass(this.invalidClass);
9710 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9712 var feedback = this.el.select('.form-control-feedback', true).first();
9715 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9721 this.indicator.removeClass('visible');
9722 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9725 this.fireEvent('valid', this);
9729 * Mark this field as valid
9731 markValid : function()
9733 if(!this.el || this.preventMark){ // not rendered...
9737 this.el.removeClass([this.invalidClass, this.validClass]);
9739 var feedback = this.el.select('.form-control-feedback', true).first();
9742 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9746 this.indicator.removeClass('visible');
9747 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9754 if(this.allowBlank && !this.getRawValue().length){
9758 this.el.addClass(this.validClass);
9760 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9762 var feedback = this.el.select('.form-control-feedback', true).first();
9765 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9766 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9771 this.fireEvent('valid', this);
9775 * Mark this field as invalid
9776 * @param {String} msg The validation message
9778 markInvalid : function(msg)
9780 if(!this.el || this.preventMark){ // not rendered
9784 this.el.removeClass([this.invalidClass, this.validClass]);
9786 var feedback = this.el.select('.form-control-feedback', true).first();
9789 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9796 if(this.allowBlank && !this.getRawValue().length){
9801 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9802 this.indicator.addClass('visible');
9805 this.el.addClass(this.invalidClass);
9807 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9809 var feedback = this.el.select('.form-control-feedback', true).first();
9812 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814 if(this.getValue().length || this.forceFeedback){
9815 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9822 this.fireEvent('invalid', this, msg);
9825 SafariOnKeyDown : function(event)
9827 // this is a workaround for a password hang bug on chrome/ webkit.
9828 if (this.inputEl().dom.type != 'password') {
9832 var isSelectAll = false;
9834 if(this.inputEl().dom.selectionEnd > 0){
9835 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9837 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9838 event.preventDefault();
9843 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9845 event.preventDefault();
9846 // this is very hacky as keydown always get's upper case.
9848 var cc = String.fromCharCode(event.getCharCode());
9849 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9853 adjustWidth : function(tag, w){
9854 tag = tag.toLowerCase();
9855 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9856 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9860 if(tag == 'textarea'){
9863 }else if(Roo.isOpera){
9867 if(tag == 'textarea'){
9875 setFieldLabel : function(v)
9881 if(this.indicatorEl()){
9882 var ar = this.el.select('label > span',true);
9884 if (ar.elements.length) {
9885 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9886 this.fieldLabel = v;
9890 var br = this.el.select('label',true);
9892 if(br.elements.length) {
9893 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9894 this.fieldLabel = v;
9898 Roo.log('Cannot Found any of label > span || label in input');
9902 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9903 this.fieldLabel = v;
9918 * @class Roo.bootstrap.TextArea
9919 * @extends Roo.bootstrap.Input
9920 * Bootstrap TextArea class
9921 * @cfg {Number} cols Specifies the visible width of a text area
9922 * @cfg {Number} rows Specifies the visible number of lines in a text area
9923 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9924 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9925 * @cfg {string} html text
9928 * Create a new TextArea
9929 * @param {Object} config The config object
9932 Roo.bootstrap.TextArea = function(config){
9933 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9937 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9947 getAutoCreate : function(){
9949 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9955 if(this.inputType != 'hidden'){
9956 cfg.cls = 'form-group' //input-group
9964 value : this.value || '',
9965 html: this.html || '',
9966 cls : 'form-control',
9967 placeholder : this.placeholder || ''
9971 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9972 input.maxLength = this.maxLength;
9976 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9980 input.cols = this.cols;
9983 if (this.readOnly) {
9984 input.readonly = true;
9988 input.name = this.name;
9992 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9996 ['xs','sm','md','lg'].map(function(size){
9997 if (settings[size]) {
9998 cfg.cls += ' col-' + size + '-' + settings[size];
10002 var inputblock = input;
10004 if(this.hasFeedback && !this.allowBlank){
10008 cls: 'glyphicon form-control-feedback'
10012 cls : 'has-feedback',
10021 if (this.before || this.after) {
10024 cls : 'input-group',
10028 inputblock.cn.push({
10030 cls : 'input-group-addon',
10035 inputblock.cn.push(input);
10037 if(this.hasFeedback && !this.allowBlank){
10038 inputblock.cls += ' has-feedback';
10039 inputblock.cn.push(feedback);
10043 inputblock.cn.push({
10045 cls : 'input-group-addon',
10052 if (align ==='left' && this.fieldLabel.length) {
10057 cls : 'control-label',
10058 html : this.fieldLabel
10069 if(this.labelWidth > 12){
10070 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10073 if(this.labelWidth < 13 && this.labelmd == 0){
10074 this.labelmd = this.labelWidth;
10077 if(this.labellg > 0){
10078 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10079 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10082 if(this.labelmd > 0){
10083 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10084 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10087 if(this.labelsm > 0){
10088 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10089 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10092 if(this.labelxs > 0){
10093 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10094 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10097 } else if ( this.fieldLabel.length) {
10102 //cls : 'input-group-addon',
10103 html : this.fieldLabel
10121 if (this.disabled) {
10122 input.disabled=true;
10129 * return the real textarea element.
10131 inputEl: function ()
10133 return this.el.select('textarea.form-control',true).first();
10137 * Clear any invalid styles/messages for this field
10139 clearInvalid : function()
10142 if(!this.el || this.preventMark){ // not rendered
10146 var label = this.el.select('label', true).first();
10147 var icon = this.el.select('i.fa-star', true).first();
10153 this.el.removeClass(this.invalidClass);
10155 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10157 var feedback = this.el.select('.form-control-feedback', true).first();
10160 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10165 this.fireEvent('valid', this);
10169 * Mark this field as valid
10171 markValid : function()
10173 if(!this.el || this.preventMark){ // not rendered
10177 this.el.removeClass([this.invalidClass, this.validClass]);
10179 var feedback = this.el.select('.form-control-feedback', true).first();
10182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10185 if(this.disabled || this.allowBlank){
10189 var label = this.el.select('label', true).first();
10190 var icon = this.el.select('i.fa-star', true).first();
10196 this.el.addClass(this.validClass);
10198 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10200 var feedback = this.el.select('.form-control-feedback', true).first();
10203 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10204 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10209 this.fireEvent('valid', this);
10213 * Mark this field as invalid
10214 * @param {String} msg The validation message
10216 markInvalid : function(msg)
10218 if(!this.el || this.preventMark){ // not rendered
10222 this.el.removeClass([this.invalidClass, this.validClass]);
10224 var feedback = this.el.select('.form-control-feedback', true).first();
10227 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10230 if(this.disabled || this.allowBlank){
10234 var label = this.el.select('label', true).first();
10235 var icon = this.el.select('i.fa-star', true).first();
10237 if(!this.getValue().length && label && !icon){
10238 this.el.createChild({
10240 cls : 'text-danger fa fa-lg fa-star',
10241 tooltip : 'This field is required',
10242 style : 'margin-right:5px;'
10246 this.el.addClass(this.invalidClass);
10248 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10250 var feedback = this.el.select('.form-control-feedback', true).first();
10253 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10255 if(this.getValue().length || this.forceFeedback){
10256 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10263 this.fireEvent('invalid', this, msg);
10271 * trigger field - base class for combo..
10276 * @class Roo.bootstrap.TriggerField
10277 * @extends Roo.bootstrap.Input
10278 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10279 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10280 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10281 * for which you can provide a custom implementation. For example:
10283 var trigger = new Roo.bootstrap.TriggerField();
10284 trigger.onTriggerClick = myTriggerFn;
10285 trigger.applyTo('my-field');
10288 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10289 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10290 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10291 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10292 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10295 * Create a new TriggerField.
10296 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10297 * to the base TextField)
10299 Roo.bootstrap.TriggerField = function(config){
10300 this.mimicing = false;
10301 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10304 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10306 * @cfg {String} triggerClass A CSS class to apply to the trigger
10309 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10314 * @cfg {Boolean} removable (true|false) special filter default false
10318 /** @cfg {Boolean} grow @hide */
10319 /** @cfg {Number} growMin @hide */
10320 /** @cfg {Number} growMax @hide */
10326 autoSize: Roo.emptyFn,
10330 deferHeight : true,
10333 actionMode : 'wrap',
10338 getAutoCreate : function(){
10340 var align = this.labelAlign || this.parentLabelAlign();
10345 cls: 'form-group' //input-group
10352 type : this.inputType,
10353 cls : 'form-control',
10354 autocomplete: 'new-password',
10355 placeholder : this.placeholder || ''
10359 input.name = this.name;
10362 input.cls += ' input-' + this.size;
10365 if (this.disabled) {
10366 input.disabled=true;
10369 var inputblock = input;
10371 if(this.hasFeedback && !this.allowBlank){
10375 cls: 'glyphicon form-control-feedback'
10378 if(this.removable && !this.editable && !this.tickable){
10380 cls : 'has-feedback',
10386 cls : 'roo-combo-removable-btn close'
10393 cls : 'has-feedback',
10402 if(this.removable && !this.editable && !this.tickable){
10404 cls : 'roo-removable',
10410 cls : 'roo-combo-removable-btn close'
10417 if (this.before || this.after) {
10420 cls : 'input-group',
10424 inputblock.cn.push({
10426 cls : 'input-group-addon input-group-prepend input-group-text',
10431 inputblock.cn.push(input);
10433 if(this.hasFeedback && !this.allowBlank){
10434 inputblock.cls += ' has-feedback';
10435 inputblock.cn.push(feedback);
10439 inputblock.cn.push({
10441 cls : 'input-group-addon input-group-append input-group-text',
10450 var ibwrap = inputblock;
10455 cls: 'roo-select2-choices',
10459 cls: 'roo-select2-search-field',
10471 cls: 'roo-select2-container input-group',
10476 cls: 'form-hidden-field'
10482 if(!this.multiple && this.showToggleBtn){
10488 if (this.caret != false) {
10491 cls: 'fa fa-' + this.caret
10498 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10503 cls: 'combobox-clear',
10517 combobox.cls += ' roo-select2-container-multi';
10521 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10522 tooltip : 'This field is required'
10524 if (Roo.bootstrap.version == 4) {
10527 style : 'display:none'
10532 if (align ==='left' && this.fieldLabel.length) {
10534 cfg.cls += ' roo-form-group-label-left row';
10541 cls : 'control-label',
10542 html : this.fieldLabel
10554 var labelCfg = cfg.cn[1];
10555 var contentCfg = cfg.cn[2];
10557 if(this.indicatorpos == 'right'){
10562 cls : 'control-label',
10566 html : this.fieldLabel
10580 labelCfg = cfg.cn[0];
10581 contentCfg = cfg.cn[1];
10584 if(this.labelWidth > 12){
10585 labelCfg.style = "width: " + this.labelWidth + 'px';
10588 if(this.labelWidth < 13 && this.labelmd == 0){
10589 this.labelmd = this.labelWidth;
10592 if(this.labellg > 0){
10593 labelCfg.cls += ' col-lg-' + this.labellg;
10594 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10597 if(this.labelmd > 0){
10598 labelCfg.cls += ' col-md-' + this.labelmd;
10599 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10602 if(this.labelsm > 0){
10603 labelCfg.cls += ' col-sm-' + this.labelsm;
10604 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10607 if(this.labelxs > 0){
10608 labelCfg.cls += ' col-xs-' + this.labelxs;
10609 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10612 } else if ( this.fieldLabel.length) {
10613 // Roo.log(" label");
10618 //cls : 'input-group-addon',
10619 html : this.fieldLabel
10627 if(this.indicatorpos == 'right'){
10635 html : this.fieldLabel
10649 // Roo.log(" no label && no align");
10656 ['xs','sm','md','lg'].map(function(size){
10657 if (settings[size]) {
10658 cfg.cls += ' col-' + size + '-' + settings[size];
10669 onResize : function(w, h){
10670 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10671 // if(typeof w == 'number'){
10672 // var x = w - this.trigger.getWidth();
10673 // this.inputEl().setWidth(this.adjustWidth('input', x));
10674 // this.trigger.setStyle('left', x+'px');
10679 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10682 getResizeEl : function(){
10683 return this.inputEl();
10687 getPositionEl : function(){
10688 return this.inputEl();
10692 alignErrorIcon : function(){
10693 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10697 initEvents : function(){
10701 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10702 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10703 if(!this.multiple && this.showToggleBtn){
10704 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10705 if(this.hideTrigger){
10706 this.trigger.setDisplayed(false);
10708 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10712 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10715 if(this.removable && !this.editable && !this.tickable){
10716 var close = this.closeTriggerEl();
10719 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10720 close.on('click', this.removeBtnClick, this, close);
10724 //this.trigger.addClassOnOver('x-form-trigger-over');
10725 //this.trigger.addClassOnClick('x-form-trigger-click');
10728 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10732 closeTriggerEl : function()
10734 var close = this.el.select('.roo-combo-removable-btn', true).first();
10735 return close ? close : false;
10738 removeBtnClick : function(e, h, el)
10740 e.preventDefault();
10742 if(this.fireEvent("remove", this) !== false){
10744 this.fireEvent("afterremove", this)
10748 createList : function()
10750 this.list = Roo.get(document.body).createChild({
10751 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10752 cls: 'typeahead typeahead-long dropdown-menu',
10753 style: 'display:none'
10756 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10761 initTrigger : function(){
10766 onDestroy : function(){
10768 this.trigger.removeAllListeners();
10769 // this.trigger.remove();
10772 // this.wrap.remove();
10774 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10778 onFocus : function(){
10779 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10781 if(!this.mimicing){
10782 this.wrap.addClass('x-trigger-wrap-focus');
10783 this.mimicing = true;
10784 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10785 if(this.monitorTab){
10786 this.el.on("keydown", this.checkTab, this);
10793 checkTab : function(e){
10794 if(e.getKey() == e.TAB){
10795 this.triggerBlur();
10800 onBlur : function(){
10805 mimicBlur : function(e, t){
10807 if(!this.wrap.contains(t) && this.validateBlur()){
10808 this.triggerBlur();
10814 triggerBlur : function(){
10815 this.mimicing = false;
10816 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10817 if(this.monitorTab){
10818 this.el.un("keydown", this.checkTab, this);
10820 //this.wrap.removeClass('x-trigger-wrap-focus');
10821 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10825 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10826 validateBlur : function(e, t){
10831 onDisable : function(){
10832 this.inputEl().dom.disabled = true;
10833 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10835 // this.wrap.addClass('x-item-disabled');
10840 onEnable : function(){
10841 this.inputEl().dom.disabled = false;
10842 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10844 // this.el.removeClass('x-item-disabled');
10849 onShow : function(){
10850 var ae = this.getActionEl();
10853 ae.dom.style.display = '';
10854 ae.dom.style.visibility = 'visible';
10860 onHide : function(){
10861 var ae = this.getActionEl();
10862 ae.dom.style.display = 'none';
10866 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10867 * by an implementing function.
10869 * @param {EventObject} e
10871 onTriggerClick : Roo.emptyFn
10875 * Ext JS Library 1.1.1
10876 * Copyright(c) 2006-2007, Ext JS, LLC.
10878 * Originally Released Under LGPL - original licence link has changed is not relivant.
10881 * <script type="text/javascript">
10886 * @class Roo.data.SortTypes
10888 * Defines the default sorting (casting?) comparison functions used when sorting data.
10890 Roo.data.SortTypes = {
10892 * Default sort that does nothing
10893 * @param {Mixed} s The value being converted
10894 * @return {Mixed} The comparison value
10896 none : function(s){
10901 * The regular expression used to strip tags
10905 stripTagsRE : /<\/?[^>]+>/gi,
10908 * Strips all HTML tags to sort on text only
10909 * @param {Mixed} s The value being converted
10910 * @return {String} The comparison value
10912 asText : function(s){
10913 return String(s).replace(this.stripTagsRE, "");
10917 * Strips all HTML tags to sort on text only - Case insensitive
10918 * @param {Mixed} s The value being converted
10919 * @return {String} The comparison value
10921 asUCText : function(s){
10922 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10926 * Case insensitive string
10927 * @param {Mixed} s The value being converted
10928 * @return {String} The comparison value
10930 asUCString : function(s) {
10931 return String(s).toUpperCase();
10936 * @param {Mixed} s The value being converted
10937 * @return {Number} The comparison value
10939 asDate : function(s) {
10943 if(s instanceof Date){
10944 return s.getTime();
10946 return Date.parse(String(s));
10951 * @param {Mixed} s The value being converted
10952 * @return {Float} The comparison value
10954 asFloat : function(s) {
10955 var val = parseFloat(String(s).replace(/,/g, ""));
10964 * @param {Mixed} s The value being converted
10965 * @return {Number} The comparison value
10967 asInt : function(s) {
10968 var val = parseInt(String(s).replace(/,/g, ""));
10976 * Ext JS Library 1.1.1
10977 * Copyright(c) 2006-2007, Ext JS, LLC.
10979 * Originally Released Under LGPL - original licence link has changed is not relivant.
10982 * <script type="text/javascript">
10986 * @class Roo.data.Record
10987 * Instances of this class encapsulate both record <em>definition</em> information, and record
10988 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10989 * to access Records cached in an {@link Roo.data.Store} object.<br>
10991 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10992 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10995 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10997 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10998 * {@link #create}. The parameters are the same.
10999 * @param {Array} data An associative Array of data values keyed by the field name.
11000 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11001 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11002 * not specified an integer id is generated.
11004 Roo.data.Record = function(data, id){
11005 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11010 * Generate a constructor for a specific record layout.
11011 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11012 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11013 * Each field definition object may contain the following properties: <ul>
11014 * <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,
11015 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11016 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11017 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11018 * is being used, then this is a string containing the javascript expression to reference the data relative to
11019 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11020 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11021 * this may be omitted.</p></li>
11022 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11023 * <ul><li>auto (Default, implies no conversion)</li>
11028 * <li>date</li></ul></p></li>
11029 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11030 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11031 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11032 * by the Reader into an object that will be stored in the Record. It is passed the
11033 * following parameters:<ul>
11034 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11036 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11038 * <br>usage:<br><pre><code>
11039 var TopicRecord = Roo.data.Record.create(
11040 {name: 'title', mapping: 'topic_title'},
11041 {name: 'author', mapping: 'username'},
11042 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11043 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11044 {name: 'lastPoster', mapping: 'user2'},
11045 {name: 'excerpt', mapping: 'post_text'}
11048 var myNewRecord = new TopicRecord({
11049 title: 'Do my job please',
11052 lastPost: new Date(),
11053 lastPoster: 'Animal',
11054 excerpt: 'No way dude!'
11056 myStore.add(myNewRecord);
11061 Roo.data.Record.create = function(o){
11062 var f = function(){
11063 f.superclass.constructor.apply(this, arguments);
11065 Roo.extend(f, Roo.data.Record);
11066 var p = f.prototype;
11067 p.fields = new Roo.util.MixedCollection(false, function(field){
11070 for(var i = 0, len = o.length; i < len; i++){
11071 p.fields.add(new Roo.data.Field(o[i]));
11073 f.getField = function(name){
11074 return p.fields.get(name);
11079 Roo.data.Record.AUTO_ID = 1000;
11080 Roo.data.Record.EDIT = 'edit';
11081 Roo.data.Record.REJECT = 'reject';
11082 Roo.data.Record.COMMIT = 'commit';
11084 Roo.data.Record.prototype = {
11086 * Readonly flag - true if this record has been modified.
11095 join : function(store){
11096 this.store = store;
11100 * Set the named field to the specified value.
11101 * @param {String} name The name of the field to set.
11102 * @param {Object} value The value to set the field to.
11104 set : function(name, value){
11105 if(this.data[name] == value){
11109 if(!this.modified){
11110 this.modified = {};
11112 if(typeof this.modified[name] == 'undefined'){
11113 this.modified[name] = this.data[name];
11115 this.data[name] = value;
11116 if(!this.editing && this.store){
11117 this.store.afterEdit(this);
11122 * Get the value of the named field.
11123 * @param {String} name The name of the field to get the value of.
11124 * @return {Object} The value of the field.
11126 get : function(name){
11127 return this.data[name];
11131 beginEdit : function(){
11132 this.editing = true;
11133 this.modified = {};
11137 cancelEdit : function(){
11138 this.editing = false;
11139 delete this.modified;
11143 endEdit : function(){
11144 this.editing = false;
11145 if(this.dirty && this.store){
11146 this.store.afterEdit(this);
11151 * Usually called by the {@link Roo.data.Store} which owns the Record.
11152 * Rejects all changes made to the Record since either creation, or the last commit operation.
11153 * Modified fields are reverted to their original values.
11155 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11156 * of reject operations.
11158 reject : function(){
11159 var m = this.modified;
11161 if(typeof m[n] != "function"){
11162 this.data[n] = m[n];
11165 this.dirty = false;
11166 delete this.modified;
11167 this.editing = false;
11169 this.store.afterReject(this);
11174 * Usually called by the {@link Roo.data.Store} which owns the Record.
11175 * Commits all changes made to the Record since either creation, or the last commit operation.
11177 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11178 * of commit operations.
11180 commit : function(){
11181 this.dirty = false;
11182 delete this.modified;
11183 this.editing = false;
11185 this.store.afterCommit(this);
11190 hasError : function(){
11191 return this.error != null;
11195 clearError : function(){
11200 * Creates a copy of this record.
11201 * @param {String} id (optional) A new record id if you don't want to use this record's id
11204 copy : function(newId) {
11205 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11209 * Ext JS Library 1.1.1
11210 * Copyright(c) 2006-2007, Ext JS, LLC.
11212 * Originally Released Under LGPL - original licence link has changed is not relivant.
11215 * <script type="text/javascript">
11221 * @class Roo.data.Store
11222 * @extends Roo.util.Observable
11223 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11224 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11226 * 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
11227 * has no knowledge of the format of the data returned by the Proxy.<br>
11229 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11230 * instances from the data object. These records are cached and made available through accessor functions.
11232 * Creates a new Store.
11233 * @param {Object} config A config object containing the objects needed for the Store to access data,
11234 * and read the data into Records.
11236 Roo.data.Store = function(config){
11237 this.data = new Roo.util.MixedCollection(false);
11238 this.data.getKey = function(o){
11241 this.baseParams = {};
11243 this.paramNames = {
11248 "multisort" : "_multisort"
11251 if(config && config.data){
11252 this.inlineData = config.data;
11253 delete config.data;
11256 Roo.apply(this, config);
11258 if(this.reader){ // reader passed
11259 this.reader = Roo.factory(this.reader, Roo.data);
11260 this.reader.xmodule = this.xmodule || false;
11261 if(!this.recordType){
11262 this.recordType = this.reader.recordType;
11264 if(this.reader.onMetaChange){
11265 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11269 if(this.recordType){
11270 this.fields = this.recordType.prototype.fields;
11272 this.modified = [];
11276 * @event datachanged
11277 * Fires when the data cache has changed, and a widget which is using this Store
11278 * as a Record cache should refresh its view.
11279 * @param {Store} this
11281 datachanged : true,
11283 * @event metachange
11284 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11285 * @param {Store} this
11286 * @param {Object} meta The JSON metadata
11291 * Fires when Records have been added to the Store
11292 * @param {Store} this
11293 * @param {Roo.data.Record[]} records The array of Records added
11294 * @param {Number} index The index at which the record(s) were added
11299 * Fires when a Record has been removed from the Store
11300 * @param {Store} this
11301 * @param {Roo.data.Record} record The Record that was removed
11302 * @param {Number} index The index at which the record was removed
11307 * Fires when a Record has been updated
11308 * @param {Store} this
11309 * @param {Roo.data.Record} record The Record that was updated
11310 * @param {String} operation The update operation being performed. Value may be one of:
11312 Roo.data.Record.EDIT
11313 Roo.data.Record.REJECT
11314 Roo.data.Record.COMMIT
11320 * Fires when the data cache has been cleared.
11321 * @param {Store} this
11325 * @event beforeload
11326 * Fires before a request is made for a new data object. If the beforeload handler returns false
11327 * the load action will be canceled.
11328 * @param {Store} this
11329 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11333 * @event beforeloadadd
11334 * Fires after a new set of Records has been loaded.
11335 * @param {Store} this
11336 * @param {Roo.data.Record[]} records The Records that were loaded
11337 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11339 beforeloadadd : true,
11342 * Fires after a new set of Records has been loaded, before they are added to the store.
11343 * @param {Store} this
11344 * @param {Roo.data.Record[]} records The Records that were loaded
11345 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11346 * @params {Object} return from reader
11350 * @event loadexception
11351 * Fires if an exception occurs in the Proxy during loading.
11352 * Called with the signature of the Proxy's "loadexception" event.
11353 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11356 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11357 * @param {Object} load options
11358 * @param {Object} jsonData from your request (normally this contains the Exception)
11360 loadexception : true
11364 this.proxy = Roo.factory(this.proxy, Roo.data);
11365 this.proxy.xmodule = this.xmodule || false;
11366 this.relayEvents(this.proxy, ["loadexception"]);
11368 this.sortToggle = {};
11369 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11371 Roo.data.Store.superclass.constructor.call(this);
11373 if(this.inlineData){
11374 this.loadData(this.inlineData);
11375 delete this.inlineData;
11379 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11381 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11382 * without a remote query - used by combo/forms at present.
11386 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11389 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11392 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11393 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11396 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11397 * on any HTTP request
11400 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11403 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11407 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11408 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11410 remoteSort : false,
11413 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11414 * loaded or when a record is removed. (defaults to false).
11416 pruneModifiedRecords : false,
11419 lastOptions : null,
11422 * Add Records to the Store and fires the add event.
11423 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11425 add : function(records){
11426 records = [].concat(records);
11427 for(var i = 0, len = records.length; i < len; i++){
11428 records[i].join(this);
11430 var index = this.data.length;
11431 this.data.addAll(records);
11432 this.fireEvent("add", this, records, index);
11436 * Remove a Record from the Store and fires the remove event.
11437 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11439 remove : function(record){
11440 var index = this.data.indexOf(record);
11441 this.data.removeAt(index);
11443 if(this.pruneModifiedRecords){
11444 this.modified.remove(record);
11446 this.fireEvent("remove", this, record, index);
11450 * Remove all Records from the Store and fires the clear event.
11452 removeAll : function(){
11454 if(this.pruneModifiedRecords){
11455 this.modified = [];
11457 this.fireEvent("clear", this);
11461 * Inserts Records to the Store at the given index and fires the add event.
11462 * @param {Number} index The start index at which to insert the passed Records.
11463 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11465 insert : function(index, records){
11466 records = [].concat(records);
11467 for(var i = 0, len = records.length; i < len; i++){
11468 this.data.insert(index, records[i]);
11469 records[i].join(this);
11471 this.fireEvent("add", this, records, index);
11475 * Get the index within the cache of the passed Record.
11476 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11477 * @return {Number} The index of the passed Record. Returns -1 if not found.
11479 indexOf : function(record){
11480 return this.data.indexOf(record);
11484 * Get the index within the cache of the Record with the passed id.
11485 * @param {String} id The id of the Record to find.
11486 * @return {Number} The index of the Record. Returns -1 if not found.
11488 indexOfId : function(id){
11489 return this.data.indexOfKey(id);
11493 * Get the Record with the specified id.
11494 * @param {String} id The id of the Record to find.
11495 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11497 getById : function(id){
11498 return this.data.key(id);
11502 * Get the Record at the specified index.
11503 * @param {Number} index The index of the Record to find.
11504 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11506 getAt : function(index){
11507 return this.data.itemAt(index);
11511 * Returns a range of Records between specified indices.
11512 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11513 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11514 * @return {Roo.data.Record[]} An array of Records
11516 getRange : function(start, end){
11517 return this.data.getRange(start, end);
11521 storeOptions : function(o){
11522 o = Roo.apply({}, o);
11525 this.lastOptions = o;
11529 * Loads the Record cache from the configured Proxy using the configured Reader.
11531 * If using remote paging, then the first load call must specify the <em>start</em>
11532 * and <em>limit</em> properties in the options.params property to establish the initial
11533 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11535 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11536 * and this call will return before the new data has been loaded. Perform any post-processing
11537 * in a callback function, or in a "load" event handler.</strong>
11539 * @param {Object} options An object containing properties which control loading options:<ul>
11540 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11541 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11542 * passed the following arguments:<ul>
11543 * <li>r : Roo.data.Record[]</li>
11544 * <li>options: Options object from the load call</li>
11545 * <li>success: Boolean success indicator</li></ul></li>
11546 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11547 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11550 load : function(options){
11551 options = options || {};
11552 if(this.fireEvent("beforeload", this, options) !== false){
11553 this.storeOptions(options);
11554 var p = Roo.apply(options.params || {}, this.baseParams);
11555 // if meta was not loaded from remote source.. try requesting it.
11556 if (!this.reader.metaFromRemote) {
11557 p._requestMeta = 1;
11559 if(this.sortInfo && this.remoteSort){
11560 var pn = this.paramNames;
11561 p[pn["sort"]] = this.sortInfo.field;
11562 p[pn["dir"]] = this.sortInfo.direction;
11564 if (this.multiSort) {
11565 var pn = this.paramNames;
11566 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11569 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11574 * Reloads the Record cache from the configured Proxy using the configured Reader and
11575 * the options from the last load operation performed.
11576 * @param {Object} options (optional) An object containing properties which may override the options
11577 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11578 * the most recently used options are reused).
11580 reload : function(options){
11581 this.load(Roo.applyIf(options||{}, this.lastOptions));
11585 // Called as a callback by the Reader during a load operation.
11586 loadRecords : function(o, options, success){
11587 if(!o || success === false){
11588 if(success !== false){
11589 this.fireEvent("load", this, [], options, o);
11591 if(options.callback){
11592 options.callback.call(options.scope || this, [], options, false);
11596 // if data returned failure - throw an exception.
11597 if (o.success === false) {
11598 // show a message if no listener is registered.
11599 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11600 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11602 // loadmask wil be hooked into this..
11603 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11606 var r = o.records, t = o.totalRecords || r.length;
11608 this.fireEvent("beforeloadadd", this, r, options, o);
11610 if(!options || options.add !== true){
11611 if(this.pruneModifiedRecords){
11612 this.modified = [];
11614 for(var i = 0, len = r.length; i < len; i++){
11618 this.data = this.snapshot;
11619 delete this.snapshot;
11622 this.data.addAll(r);
11623 this.totalLength = t;
11625 this.fireEvent("datachanged", this);
11627 this.totalLength = Math.max(t, this.data.length+r.length);
11631 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11633 var e = new Roo.data.Record({});
11635 e.set(this.parent.displayField, this.parent.emptyTitle);
11636 e.set(this.parent.valueField, '');
11641 this.fireEvent("load", this, r, options, o);
11642 if(options.callback){
11643 options.callback.call(options.scope || this, r, options, true);
11649 * Loads data from a passed data block. A Reader which understands the format of the data
11650 * must have been configured in the constructor.
11651 * @param {Object} data The data block from which to read the Records. The format of the data expected
11652 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11653 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11655 loadData : function(o, append){
11656 var r = this.reader.readRecords(o);
11657 this.loadRecords(r, {add: append}, true);
11661 * Gets the number of cached records.
11663 * <em>If using paging, this may not be the total size of the dataset. If the data object
11664 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11665 * the data set size</em>
11667 getCount : function(){
11668 return this.data.length || 0;
11672 * Gets the total number of records in the dataset as returned by the server.
11674 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11675 * the dataset size</em>
11677 getTotalCount : function(){
11678 return this.totalLength || 0;
11682 * Returns the sort state of the Store as an object with two properties:
11684 field {String} The name of the field by which the Records are sorted
11685 direction {String} The sort order, "ASC" or "DESC"
11688 getSortState : function(){
11689 return this.sortInfo;
11693 applySort : function(){
11694 if(this.sortInfo && !this.remoteSort){
11695 var s = this.sortInfo, f = s.field;
11696 var st = this.fields.get(f).sortType;
11697 var fn = function(r1, r2){
11698 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11699 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11701 this.data.sort(s.direction, fn);
11702 if(this.snapshot && this.snapshot != this.data){
11703 this.snapshot.sort(s.direction, fn);
11709 * Sets the default sort column and order to be used by the next load operation.
11710 * @param {String} fieldName The name of the field to sort by.
11711 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11713 setDefaultSort : function(field, dir){
11714 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11718 * Sort the Records.
11719 * If remote sorting is used, the sort is performed on the server, and the cache is
11720 * reloaded. If local sorting is used, the cache is sorted internally.
11721 * @param {String} fieldName The name of the field to sort by.
11722 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11724 sort : function(fieldName, dir){
11725 var f = this.fields.get(fieldName);
11727 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11729 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11730 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11735 this.sortToggle[f.name] = dir;
11736 this.sortInfo = {field: f.name, direction: dir};
11737 if(!this.remoteSort){
11739 this.fireEvent("datachanged", this);
11741 this.load(this.lastOptions);
11746 * Calls the specified function for each of the Records in the cache.
11747 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11748 * Returning <em>false</em> aborts and exits the iteration.
11749 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11751 each : function(fn, scope){
11752 this.data.each(fn, scope);
11756 * Gets all records modified since the last commit. Modified records are persisted across load operations
11757 * (e.g., during paging).
11758 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11760 getModifiedRecords : function(){
11761 return this.modified;
11765 createFilterFn : function(property, value, anyMatch){
11766 if(!value.exec){ // not a regex
11767 value = String(value);
11768 if(value.length == 0){
11771 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11773 return function(r){
11774 return value.test(r.data[property]);
11779 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11780 * @param {String} property A field on your records
11781 * @param {Number} start The record index to start at (defaults to 0)
11782 * @param {Number} end The last record index to include (defaults to length - 1)
11783 * @return {Number} The sum
11785 sum : function(property, start, end){
11786 var rs = this.data.items, v = 0;
11787 start = start || 0;
11788 end = (end || end === 0) ? end : rs.length-1;
11790 for(var i = start; i <= end; i++){
11791 v += (rs[i].data[property] || 0);
11797 * Filter the records by a specified property.
11798 * @param {String} field A field on your records
11799 * @param {String/RegExp} value Either a string that the field
11800 * should start with or a RegExp to test against the field
11801 * @param {Boolean} anyMatch True to match any part not just the beginning
11803 filter : function(property, value, anyMatch){
11804 var fn = this.createFilterFn(property, value, anyMatch);
11805 return fn ? this.filterBy(fn) : this.clearFilter();
11809 * Filter by a function. The specified function will be called with each
11810 * record in this data source. If the function returns true the record is included,
11811 * otherwise it is filtered.
11812 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11813 * @param {Object} scope (optional) The scope of the function (defaults to this)
11815 filterBy : function(fn, scope){
11816 this.snapshot = this.snapshot || this.data;
11817 this.data = this.queryBy(fn, scope||this);
11818 this.fireEvent("datachanged", this);
11822 * Query the records by a specified property.
11823 * @param {String} field A field on your records
11824 * @param {String/RegExp} value Either a string that the field
11825 * should start with or a RegExp to test against the field
11826 * @param {Boolean} anyMatch True to match any part not just the beginning
11827 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11829 query : function(property, value, anyMatch){
11830 var fn = this.createFilterFn(property, value, anyMatch);
11831 return fn ? this.queryBy(fn) : this.data.clone();
11835 * Query by a function. The specified function will be called with each
11836 * record in this data source. If the function returns true the record is included
11838 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11839 * @param {Object} scope (optional) The scope of the function (defaults to this)
11840 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11842 queryBy : function(fn, scope){
11843 var data = this.snapshot || this.data;
11844 return data.filterBy(fn, scope||this);
11848 * Collects unique values for a particular dataIndex from this store.
11849 * @param {String} dataIndex The property to collect
11850 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11851 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11852 * @return {Array} An array of the unique values
11854 collect : function(dataIndex, allowNull, bypassFilter){
11855 var d = (bypassFilter === true && this.snapshot) ?
11856 this.snapshot.items : this.data.items;
11857 var v, sv, r = [], l = {};
11858 for(var i = 0, len = d.length; i < len; i++){
11859 v = d[i].data[dataIndex];
11861 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11870 * Revert to a view of the Record cache with no filtering applied.
11871 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11873 clearFilter : function(suppressEvent){
11874 if(this.snapshot && this.snapshot != this.data){
11875 this.data = this.snapshot;
11876 delete this.snapshot;
11877 if(suppressEvent !== true){
11878 this.fireEvent("datachanged", this);
11884 afterEdit : function(record){
11885 if(this.modified.indexOf(record) == -1){
11886 this.modified.push(record);
11888 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11892 afterReject : function(record){
11893 this.modified.remove(record);
11894 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11898 afterCommit : function(record){
11899 this.modified.remove(record);
11900 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11904 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11905 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11907 commitChanges : function(){
11908 var m = this.modified.slice(0);
11909 this.modified = [];
11910 for(var i = 0, len = m.length; i < len; i++){
11916 * Cancel outstanding changes on all changed records.
11918 rejectChanges : function(){
11919 var m = this.modified.slice(0);
11920 this.modified = [];
11921 for(var i = 0, len = m.length; i < len; i++){
11926 onMetaChange : function(meta, rtype, o){
11927 this.recordType = rtype;
11928 this.fields = rtype.prototype.fields;
11929 delete this.snapshot;
11930 this.sortInfo = meta.sortInfo || this.sortInfo;
11931 this.modified = [];
11932 this.fireEvent('metachange', this, this.reader.meta);
11935 moveIndex : function(data, type)
11937 var index = this.indexOf(data);
11939 var newIndex = index + type;
11943 this.insert(newIndex, data);
11948 * Ext JS Library 1.1.1
11949 * Copyright(c) 2006-2007, Ext JS, LLC.
11951 * Originally Released Under LGPL - original licence link has changed is not relivant.
11954 * <script type="text/javascript">
11958 * @class Roo.data.SimpleStore
11959 * @extends Roo.data.Store
11960 * Small helper class to make creating Stores from Array data easier.
11961 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11962 * @cfg {Array} fields An array of field definition objects, or field name strings.
11963 * @cfg {Array} data The multi-dimensional array of data
11965 * @param {Object} config
11967 Roo.data.SimpleStore = function(config){
11968 Roo.data.SimpleStore.superclass.constructor.call(this, {
11970 reader: new Roo.data.ArrayReader({
11973 Roo.data.Record.create(config.fields)
11975 proxy : new Roo.data.MemoryProxy(config.data)
11979 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11981 * Ext JS Library 1.1.1
11982 * Copyright(c) 2006-2007, Ext JS, LLC.
11984 * Originally Released Under LGPL - original licence link has changed is not relivant.
11987 * <script type="text/javascript">
11992 * @extends Roo.data.Store
11993 * @class Roo.data.JsonStore
11994 * Small helper class to make creating Stores for JSON data easier. <br/>
11996 var store = new Roo.data.JsonStore({
11997 url: 'get-images.php',
11999 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12002 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12003 * JsonReader and HttpProxy (unless inline data is provided).</b>
12004 * @cfg {Array} fields An array of field definition objects, or field name strings.
12006 * @param {Object} config
12008 Roo.data.JsonStore = function(c){
12009 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12010 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12011 reader: new Roo.data.JsonReader(c, c.fields)
12014 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12016 * Ext JS Library 1.1.1
12017 * Copyright(c) 2006-2007, Ext JS, LLC.
12019 * Originally Released Under LGPL - original licence link has changed is not relivant.
12022 * <script type="text/javascript">
12026 Roo.data.Field = function(config){
12027 if(typeof config == "string"){
12028 config = {name: config};
12030 Roo.apply(this, config);
12033 this.type = "auto";
12036 var st = Roo.data.SortTypes;
12037 // named sortTypes are supported, here we look them up
12038 if(typeof this.sortType == "string"){
12039 this.sortType = st[this.sortType];
12042 // set default sortType for strings and dates
12043 if(!this.sortType){
12046 this.sortType = st.asUCString;
12049 this.sortType = st.asDate;
12052 this.sortType = st.none;
12057 var stripRe = /[\$,%]/g;
12059 // prebuilt conversion function for this field, instead of
12060 // switching every time we're reading a value
12062 var cv, dateFormat = this.dateFormat;
12067 cv = function(v){ return v; };
12070 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12074 return v !== undefined && v !== null && v !== '' ?
12075 parseInt(String(v).replace(stripRe, ""), 10) : '';
12080 return v !== undefined && v !== null && v !== '' ?
12081 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12086 cv = function(v){ return v === true || v === "true" || v == 1; };
12093 if(v instanceof Date){
12097 if(dateFormat == "timestamp"){
12098 return new Date(v*1000);
12100 return Date.parseDate(v, dateFormat);
12102 var parsed = Date.parse(v);
12103 return parsed ? new Date(parsed) : null;
12112 Roo.data.Field.prototype = {
12120 * Ext JS Library 1.1.1
12121 * Copyright(c) 2006-2007, Ext JS, LLC.
12123 * Originally Released Under LGPL - original licence link has changed is not relivant.
12126 * <script type="text/javascript">
12129 // Base class for reading structured data from a data source. This class is intended to be
12130 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12133 * @class Roo.data.DataReader
12134 * Base class for reading structured data from a data source. This class is intended to be
12135 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12138 Roo.data.DataReader = function(meta, recordType){
12142 this.recordType = recordType instanceof Array ?
12143 Roo.data.Record.create(recordType) : recordType;
12146 Roo.data.DataReader.prototype = {
12148 * Create an empty record
12149 * @param {Object} data (optional) - overlay some values
12150 * @return {Roo.data.Record} record created.
12152 newRow : function(d) {
12154 this.recordType.prototype.fields.each(function(c) {
12156 case 'int' : da[c.name] = 0; break;
12157 case 'date' : da[c.name] = new Date(); break;
12158 case 'float' : da[c.name] = 0.0; break;
12159 case 'boolean' : da[c.name] = false; break;
12160 default : da[c.name] = ""; break;
12164 return new this.recordType(Roo.apply(da, d));
12169 * Ext JS Library 1.1.1
12170 * Copyright(c) 2006-2007, Ext JS, LLC.
12172 * Originally Released Under LGPL - original licence link has changed is not relivant.
12175 * <script type="text/javascript">
12179 * @class Roo.data.DataProxy
12180 * @extends Roo.data.Observable
12181 * This class is an abstract base class for implementations which provide retrieval of
12182 * unformatted data objects.<br>
12184 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12185 * (of the appropriate type which knows how to parse the data object) to provide a block of
12186 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12188 * Custom implementations must implement the load method as described in
12189 * {@link Roo.data.HttpProxy#load}.
12191 Roo.data.DataProxy = function(){
12194 * @event beforeload
12195 * Fires before a network request is made to retrieve a data object.
12196 * @param {Object} This DataProxy object.
12197 * @param {Object} params The params parameter to the load function.
12202 * Fires before the load method's callback is called.
12203 * @param {Object} This DataProxy object.
12204 * @param {Object} o The data object.
12205 * @param {Object} arg The callback argument object passed to the load function.
12209 * @event loadexception
12210 * Fires if an Exception occurs during data retrieval.
12211 * @param {Object} This DataProxy object.
12212 * @param {Object} o The data object.
12213 * @param {Object} arg The callback argument object passed to the load function.
12214 * @param {Object} e The Exception.
12216 loadexception : true
12218 Roo.data.DataProxy.superclass.constructor.call(this);
12221 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12224 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12228 * Ext JS Library 1.1.1
12229 * Copyright(c) 2006-2007, Ext JS, LLC.
12231 * Originally Released Under LGPL - original licence link has changed is not relivant.
12234 * <script type="text/javascript">
12237 * @class Roo.data.MemoryProxy
12238 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12239 * to the Reader when its load method is called.
12241 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12243 Roo.data.MemoryProxy = function(data){
12247 Roo.data.MemoryProxy.superclass.constructor.call(this);
12251 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12254 * Load data from the requested source (in this case an in-memory
12255 * data object passed to the constructor), read the data object into
12256 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12257 * process that block using the passed callback.
12258 * @param {Object} params This parameter is not used by the MemoryProxy class.
12259 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12260 * object into a block of Roo.data.Records.
12261 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12262 * The function must be passed <ul>
12263 * <li>The Record block object</li>
12264 * <li>The "arg" argument from the load function</li>
12265 * <li>A boolean success indicator</li>
12267 * @param {Object} scope The scope in which to call the callback
12268 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270 load : function(params, reader, callback, scope, arg){
12271 params = params || {};
12274 result = reader.readRecords(this.data);
12276 this.fireEvent("loadexception", this, arg, null, e);
12277 callback.call(scope, null, arg, false);
12280 callback.call(scope, result, arg, true);
12284 update : function(params, records){
12289 * Ext JS Library 1.1.1
12290 * Copyright(c) 2006-2007, Ext JS, LLC.
12292 * Originally Released Under LGPL - original licence link has changed is not relivant.
12295 * <script type="text/javascript">
12298 * @class Roo.data.HttpProxy
12299 * @extends Roo.data.DataProxy
12300 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12301 * configured to reference a certain URL.<br><br>
12303 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12304 * from which the running page was served.<br><br>
12306 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12308 * Be aware that to enable the browser to parse an XML document, the server must set
12309 * the Content-Type header in the HTTP response to "text/xml".
12311 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12312 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12313 * will be used to make the request.
12315 Roo.data.HttpProxy = function(conn){
12316 Roo.data.HttpProxy.superclass.constructor.call(this);
12317 // is conn a conn config or a real conn?
12319 this.useAjax = !conn || !conn.events;
12323 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12324 // thse are take from connection...
12327 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12330 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12331 * extra parameters to each request made by this object. (defaults to undefined)
12334 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12335 * to each request made by this object. (defaults to undefined)
12338 * @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)
12341 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12344 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12350 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12354 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12355 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12356 * a finer-grained basis than the DataProxy events.
12358 getConnection : function(){
12359 return this.useAjax ? Roo.Ajax : this.conn;
12363 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12364 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12365 * process that block using the passed callback.
12366 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12367 * for the request to the remote server.
12368 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12369 * object into a block of Roo.data.Records.
12370 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12371 * The function must be passed <ul>
12372 * <li>The Record block object</li>
12373 * <li>The "arg" argument from the load function</li>
12374 * <li>A boolean success indicator</li>
12376 * @param {Object} scope The scope in which to call the callback
12377 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12379 load : function(params, reader, callback, scope, arg){
12380 if(this.fireEvent("beforeload", this, params) !== false){
12382 params : params || {},
12384 callback : callback,
12389 callback : this.loadResponse,
12393 Roo.applyIf(o, this.conn);
12394 if(this.activeRequest){
12395 Roo.Ajax.abort(this.activeRequest);
12397 this.activeRequest = Roo.Ajax.request(o);
12399 this.conn.request(o);
12402 callback.call(scope||this, null, arg, false);
12407 loadResponse : function(o, success, response){
12408 delete this.activeRequest;
12410 this.fireEvent("loadexception", this, o, response);
12411 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12416 result = o.reader.read(response);
12418 this.fireEvent("loadexception", this, o, response, e);
12419 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12423 this.fireEvent("load", this, o, o.request.arg);
12424 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12428 update : function(dataSet){
12433 updateResponse : function(dataSet){
12438 * Ext JS Library 1.1.1
12439 * Copyright(c) 2006-2007, Ext JS, LLC.
12441 * Originally Released Under LGPL - original licence link has changed is not relivant.
12444 * <script type="text/javascript">
12448 * @class Roo.data.ScriptTagProxy
12449 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12450 * other than the originating domain of the running page.<br><br>
12452 * <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
12453 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12455 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12456 * source code that is used as the source inside a <script> tag.<br><br>
12458 * In order for the browser to process the returned data, the server must wrap the data object
12459 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12460 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12461 * depending on whether the callback name was passed:
12464 boolean scriptTag = false;
12465 String cb = request.getParameter("callback");
12468 response.setContentType("text/javascript");
12470 response.setContentType("application/x-json");
12472 Writer out = response.getWriter();
12474 out.write(cb + "(");
12476 out.print(dataBlock.toJsonString());
12483 * @param {Object} config A configuration object.
12485 Roo.data.ScriptTagProxy = function(config){
12486 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12487 Roo.apply(this, config);
12488 this.head = document.getElementsByTagName("head")[0];
12491 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12493 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12495 * @cfg {String} url The URL from which to request the data object.
12498 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12502 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12503 * the server the name of the callback function set up by the load call to process the returned data object.
12504 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12505 * javascript output which calls this named function passing the data object as its only parameter.
12507 callbackParam : "callback",
12509 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12510 * name to the request.
12515 * Load data from the configured URL, read the data object into
12516 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12517 * process that block using the passed callback.
12518 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12519 * for the request to the remote server.
12520 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12521 * object into a block of Roo.data.Records.
12522 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12523 * The function must be passed <ul>
12524 * <li>The Record block object</li>
12525 * <li>The "arg" argument from the load function</li>
12526 * <li>A boolean success indicator</li>
12528 * @param {Object} scope The scope in which to call the callback
12529 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12531 load : function(params, reader, callback, scope, arg){
12532 if(this.fireEvent("beforeload", this, params) !== false){
12534 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12536 var url = this.url;
12537 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12539 url += "&_dc=" + (new Date().getTime());
12541 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12544 cb : "stcCallback"+transId,
12545 scriptId : "stcScript"+transId,
12549 callback : callback,
12555 window[trans.cb] = function(o){
12556 conn.handleResponse(o, trans);
12559 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12561 if(this.autoAbort !== false){
12565 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12567 var script = document.createElement("script");
12568 script.setAttribute("src", url);
12569 script.setAttribute("type", "text/javascript");
12570 script.setAttribute("id", trans.scriptId);
12571 this.head.appendChild(script);
12573 this.trans = trans;
12575 callback.call(scope||this, null, arg, false);
12580 isLoading : function(){
12581 return this.trans ? true : false;
12585 * Abort the current server request.
12587 abort : function(){
12588 if(this.isLoading()){
12589 this.destroyTrans(this.trans);
12594 destroyTrans : function(trans, isLoaded){
12595 this.head.removeChild(document.getElementById(trans.scriptId));
12596 clearTimeout(trans.timeoutId);
12598 window[trans.cb] = undefined;
12600 delete window[trans.cb];
12603 // if hasn't been loaded, wait for load to remove it to prevent script error
12604 window[trans.cb] = function(){
12605 window[trans.cb] = undefined;
12607 delete window[trans.cb];
12614 handleResponse : function(o, trans){
12615 this.trans = false;
12616 this.destroyTrans(trans, true);
12619 result = trans.reader.readRecords(o);
12621 this.fireEvent("loadexception", this, o, trans.arg, e);
12622 trans.callback.call(trans.scope||window, null, trans.arg, false);
12625 this.fireEvent("load", this, o, trans.arg);
12626 trans.callback.call(trans.scope||window, result, trans.arg, true);
12630 handleFailure : function(trans){
12631 this.trans = false;
12632 this.destroyTrans(trans, false);
12633 this.fireEvent("loadexception", this, null, trans.arg);
12634 trans.callback.call(trans.scope||window, null, trans.arg, false);
12638 * Ext JS Library 1.1.1
12639 * Copyright(c) 2006-2007, Ext JS, LLC.
12641 * Originally Released Under LGPL - original licence link has changed is not relivant.
12644 * <script type="text/javascript">
12648 * @class Roo.data.JsonReader
12649 * @extends Roo.data.DataReader
12650 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12651 * based on mappings in a provided Roo.data.Record constructor.
12653 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12654 * in the reply previously.
12659 var RecordDef = Roo.data.Record.create([
12660 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12661 {name: 'occupation'} // This field will use "occupation" as the mapping.
12663 var myReader = new Roo.data.JsonReader({
12664 totalProperty: "results", // The property which contains the total dataset size (optional)
12665 root: "rows", // The property which contains an Array of row objects
12666 id: "id" // The property within each row object that provides an ID for the record (optional)
12670 * This would consume a JSON file like this:
12672 { 'results': 2, 'rows': [
12673 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12674 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12677 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12678 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12679 * paged from the remote server.
12680 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12681 * @cfg {String} root name of the property which contains the Array of row objects.
12682 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12683 * @cfg {Array} fields Array of field definition objects
12685 * Create a new JsonReader
12686 * @param {Object} meta Metadata configuration options
12687 * @param {Object} recordType Either an Array of field definition objects,
12688 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12690 Roo.data.JsonReader = function(meta, recordType){
12693 // set some defaults:
12694 Roo.applyIf(meta, {
12695 totalProperty: 'total',
12696 successProperty : 'success',
12701 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12703 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12706 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12707 * Used by Store query builder to append _requestMeta to params.
12710 metaFromRemote : false,
12712 * This method is only used by a DataProxy which has retrieved data from a remote server.
12713 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12714 * @return {Object} data A data block which is used by an Roo.data.Store object as
12715 * a cache of Roo.data.Records.
12717 read : function(response){
12718 var json = response.responseText;
12720 var o = /* eval:var:o */ eval("("+json+")");
12722 throw {message: "JsonReader.read: Json object not found"};
12728 this.metaFromRemote = true;
12729 this.meta = o.metaData;
12730 this.recordType = Roo.data.Record.create(o.metaData.fields);
12731 this.onMetaChange(this.meta, this.recordType, o);
12733 return this.readRecords(o);
12736 // private function a store will implement
12737 onMetaChange : function(meta, recordType, o){
12744 simpleAccess: function(obj, subsc) {
12751 getJsonAccessor: function(){
12753 return function(expr) {
12755 return(re.test(expr))
12756 ? new Function("obj", "return obj." + expr)
12761 return Roo.emptyFn;
12766 * Create a data block containing Roo.data.Records from an XML document.
12767 * @param {Object} o An object which contains an Array of row objects in the property specified
12768 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12769 * which contains the total size of the dataset.
12770 * @return {Object} data A data block which is used by an Roo.data.Store object as
12771 * a cache of Roo.data.Records.
12773 readRecords : function(o){
12775 * After any data loads, the raw JSON data is available for further custom processing.
12779 var s = this.meta, Record = this.recordType,
12780 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12782 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12784 if(s.totalProperty) {
12785 this.getTotal = this.getJsonAccessor(s.totalProperty);
12787 if(s.successProperty) {
12788 this.getSuccess = this.getJsonAccessor(s.successProperty);
12790 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12792 var g = this.getJsonAccessor(s.id);
12793 this.getId = function(rec) {
12795 return (r === undefined || r === "") ? null : r;
12798 this.getId = function(){return null;};
12801 for(var jj = 0; jj < fl; jj++){
12803 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12804 this.ef[jj] = this.getJsonAccessor(map);
12808 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12809 if(s.totalProperty){
12810 var vt = parseInt(this.getTotal(o), 10);
12815 if(s.successProperty){
12816 var vs = this.getSuccess(o);
12817 if(vs === false || vs === 'false'){
12822 for(var i = 0; i < c; i++){
12825 var id = this.getId(n);
12826 for(var j = 0; j < fl; j++){
12828 var v = this.ef[j](n);
12830 Roo.log('missing convert for ' + f.name);
12834 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12836 var record = new Record(values, id);
12838 records[i] = record;
12844 totalRecords : totalRecords
12849 * Ext JS Library 1.1.1
12850 * Copyright(c) 2006-2007, Ext JS, LLC.
12852 * Originally Released Under LGPL - original licence link has changed is not relivant.
12855 * <script type="text/javascript">
12859 * @class Roo.data.ArrayReader
12860 * @extends Roo.data.DataReader
12861 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12862 * Each element of that Array represents a row of data fields. The
12863 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12864 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12868 var RecordDef = Roo.data.Record.create([
12869 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12870 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12872 var myReader = new Roo.data.ArrayReader({
12873 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12877 * This would consume an Array like this:
12879 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12881 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12883 * Create a new JsonReader
12884 * @param {Object} meta Metadata configuration options.
12885 * @param {Object} recordType Either an Array of field definition objects
12886 * as specified to {@link Roo.data.Record#create},
12887 * or an {@link Roo.data.Record} object
12888 * created using {@link Roo.data.Record#create}.
12890 Roo.data.ArrayReader = function(meta, recordType){
12891 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12894 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12896 * Create a data block containing Roo.data.Records from an XML document.
12897 * @param {Object} o An Array of row objects which represents the dataset.
12898 * @return {Object} data A data block which is used by an Roo.data.Store object as
12899 * a cache of Roo.data.Records.
12901 readRecords : function(o){
12902 var sid = this.meta ? this.meta.id : null;
12903 var recordType = this.recordType, fields = recordType.prototype.fields;
12906 for(var i = 0; i < root.length; i++){
12909 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12910 for(var j = 0, jlen = fields.length; j < jlen; j++){
12911 var f = fields.items[j];
12912 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12913 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12915 values[f.name] = v;
12917 var record = new recordType(values, id);
12919 records[records.length] = record;
12923 totalRecords : records.length
12932 * @class Roo.bootstrap.ComboBox
12933 * @extends Roo.bootstrap.TriggerField
12934 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12935 * @cfg {Boolean} append (true|false) default false
12936 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12937 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12938 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12939 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12940 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12941 * @cfg {Boolean} animate default true
12942 * @cfg {Boolean} emptyResultText only for touch device
12943 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12944 * @cfg {String} emptyTitle default ''
12946 * Create a new ComboBox.
12947 * @param {Object} config Configuration options
12949 Roo.bootstrap.ComboBox = function(config){
12950 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12954 * Fires when the dropdown list is expanded
12955 * @param {Roo.bootstrap.ComboBox} combo This combo box
12960 * Fires when the dropdown list is collapsed
12961 * @param {Roo.bootstrap.ComboBox} combo This combo box
12965 * @event beforeselect
12966 * Fires before a list item is selected. Return false to cancel the selection.
12967 * @param {Roo.bootstrap.ComboBox} combo This combo box
12968 * @param {Roo.data.Record} record The data record returned from the underlying store
12969 * @param {Number} index The index of the selected item in the dropdown list
12971 'beforeselect' : true,
12974 * Fires when a list item is selected
12975 * @param {Roo.bootstrap.ComboBox} combo This combo box
12976 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12977 * @param {Number} index The index of the selected item in the dropdown list
12981 * @event beforequery
12982 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12983 * The event object passed has these properties:
12984 * @param {Roo.bootstrap.ComboBox} combo This combo box
12985 * @param {String} query The query
12986 * @param {Boolean} forceAll true to force "all" query
12987 * @param {Boolean} cancel true to cancel the query
12988 * @param {Object} e The query event object
12990 'beforequery': true,
12993 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12994 * @param {Roo.bootstrap.ComboBox} combo This combo box
12999 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13000 * @param {Roo.bootstrap.ComboBox} combo This combo box
13001 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13006 * Fires when the remove value from the combobox array
13007 * @param {Roo.bootstrap.ComboBox} combo This combo box
13011 * @event afterremove
13012 * Fires when the remove value from the combobox array
13013 * @param {Roo.bootstrap.ComboBox} combo This combo box
13015 'afterremove' : true,
13017 * @event specialfilter
13018 * Fires when specialfilter
13019 * @param {Roo.bootstrap.ComboBox} combo This combo box
13021 'specialfilter' : true,
13024 * Fires when tick the element
13025 * @param {Roo.bootstrap.ComboBox} combo This combo box
13029 * @event touchviewdisplay
13030 * Fires when touch view require special display (default is using displayField)
13031 * @param {Roo.bootstrap.ComboBox} combo This combo box
13032 * @param {Object} cfg set html .
13034 'touchviewdisplay' : true
13039 this.tickItems = [];
13041 this.selectedIndex = -1;
13042 if(this.mode == 'local'){
13043 if(config.queryDelay === undefined){
13044 this.queryDelay = 10;
13046 if(config.minChars === undefined){
13052 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13055 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13056 * rendering into an Roo.Editor, defaults to false)
13059 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13060 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13063 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13066 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13067 * the dropdown list (defaults to undefined, with no header element)
13071 * @cfg {String/Roo.Template} tpl The template to use to render the output
13075 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13077 listWidth: undefined,
13079 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13080 * mode = 'remote' or 'text' if mode = 'local')
13082 displayField: undefined,
13085 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13086 * mode = 'remote' or 'value' if mode = 'local').
13087 * Note: use of a valueField requires the user make a selection
13088 * in order for a value to be mapped.
13090 valueField: undefined,
13092 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13097 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13098 * field's data value (defaults to the underlying DOM element's name)
13100 hiddenName: undefined,
13102 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13106 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13108 selectedClass: 'active',
13111 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13115 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13116 * anchor positions (defaults to 'tl-bl')
13118 listAlign: 'tl-bl?',
13120 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13124 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13125 * query specified by the allQuery config option (defaults to 'query')
13127 triggerAction: 'query',
13129 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13130 * (defaults to 4, does not apply if editable = false)
13134 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13135 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13139 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13140 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13144 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13145 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13149 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13150 * when editable = true (defaults to false)
13152 selectOnFocus:false,
13154 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13156 queryParam: 'query',
13158 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13159 * when mode = 'remote' (defaults to 'Loading...')
13161 loadingText: 'Loading...',
13163 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13167 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13171 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13172 * traditional select (defaults to true)
13176 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13180 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13184 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13185 * listWidth has a higher value)
13189 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13190 * allow the user to set arbitrary text into the field (defaults to false)
13192 forceSelection:false,
13194 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13195 * if typeAhead = true (defaults to 250)
13197 typeAheadDelay : 250,
13199 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13200 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13202 valueNotFoundText : undefined,
13204 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13206 blockFocus : false,
13209 * @cfg {Boolean} disableClear Disable showing of clear button.
13211 disableClear : false,
13213 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13215 alwaysQuery : false,
13218 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13223 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13225 invalidClass : "has-warning",
13228 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13230 validClass : "has-success",
13233 * @cfg {Boolean} specialFilter (true|false) special filter default false
13235 specialFilter : false,
13238 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13240 mobileTouchView : true,
13243 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13245 useNativeIOS : false,
13248 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13250 mobile_restrict_height : false,
13252 ios_options : false,
13264 btnPosition : 'right',
13265 triggerList : true,
13266 showToggleBtn : true,
13268 emptyResultText: 'Empty',
13269 triggerText : 'Select',
13272 // element that contains real text value.. (when hidden is used..)
13274 getAutoCreate : function()
13279 * Render classic select for iso
13282 if(Roo.isIOS && this.useNativeIOS){
13283 cfg = this.getAutoCreateNativeIOS();
13291 if(Roo.isTouch && this.mobileTouchView){
13292 cfg = this.getAutoCreateTouchView();
13299 if(!this.tickable){
13300 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13305 * ComboBox with tickable selections
13308 var align = this.labelAlign || this.parentLabelAlign();
13311 cls : 'form-group roo-combobox-tickable' //input-group
13314 var btn_text_select = '';
13315 var btn_text_done = '';
13316 var btn_text_cancel = '';
13318 if (this.btn_text_show) {
13319 btn_text_select = 'Select';
13320 btn_text_done = 'Done';
13321 btn_text_cancel = 'Cancel';
13326 cls : 'tickable-buttons',
13331 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13332 //html : this.triggerText
13333 html: btn_text_select
13339 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13341 html: btn_text_done
13347 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13349 html: btn_text_cancel
13355 buttons.cn.unshift({
13357 cls: 'roo-select2-search-field-input'
13363 Roo.each(buttons.cn, function(c){
13365 c.cls += ' btn-' + _this.size;
13368 if (_this.disabled) {
13379 cls: 'form-hidden-field'
13383 cls: 'roo-select2-choices',
13387 cls: 'roo-select2-search-field',
13398 cls: 'roo-select2-container input-group roo-select2-container-multi',
13404 // cls: 'typeahead typeahead-long dropdown-menu',
13405 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13410 if(this.hasFeedback && !this.allowBlank){
13414 cls: 'glyphicon form-control-feedback'
13417 combobox.cn.push(feedback);
13422 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13423 tooltip : 'This field is required'
13425 if (Roo.bootstrap.version == 4) {
13428 style : 'display:none'
13431 if (align ==='left' && this.fieldLabel.length) {
13433 cfg.cls += ' roo-form-group-label-left row';
13440 cls : 'control-label col-form-label',
13441 html : this.fieldLabel
13453 var labelCfg = cfg.cn[1];
13454 var contentCfg = cfg.cn[2];
13457 if(this.indicatorpos == 'right'){
13463 cls : 'control-label col-form-label',
13467 html : this.fieldLabel
13483 labelCfg = cfg.cn[0];
13484 contentCfg = cfg.cn[1];
13488 if(this.labelWidth > 12){
13489 labelCfg.style = "width: " + this.labelWidth + 'px';
13492 if(this.labelWidth < 13 && this.labelmd == 0){
13493 this.labelmd = this.labelWidth;
13496 if(this.labellg > 0){
13497 labelCfg.cls += ' col-lg-' + this.labellg;
13498 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13501 if(this.labelmd > 0){
13502 labelCfg.cls += ' col-md-' + this.labelmd;
13503 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13506 if(this.labelsm > 0){
13507 labelCfg.cls += ' col-sm-' + this.labelsm;
13508 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13511 if(this.labelxs > 0){
13512 labelCfg.cls += ' col-xs-' + this.labelxs;
13513 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13517 } else if ( this.fieldLabel.length) {
13518 // Roo.log(" label");
13523 //cls : 'input-group-addon',
13524 html : this.fieldLabel
13529 if(this.indicatorpos == 'right'){
13533 //cls : 'input-group-addon',
13534 html : this.fieldLabel
13544 // Roo.log(" no label && no align");
13551 ['xs','sm','md','lg'].map(function(size){
13552 if (settings[size]) {
13553 cfg.cls += ' col-' + size + '-' + settings[size];
13561 _initEventsCalled : false,
13564 initEvents: function()
13566 if (this._initEventsCalled) { // as we call render... prevent looping...
13569 this._initEventsCalled = true;
13572 throw "can not find store for combo";
13575 this.indicator = this.indicatorEl();
13577 this.store = Roo.factory(this.store, Roo.data);
13578 this.store.parent = this;
13580 // if we are building from html. then this element is so complex, that we can not really
13581 // use the rendered HTML.
13582 // so we have to trash and replace the previous code.
13583 if (Roo.XComponent.build_from_html) {
13584 // remove this element....
13585 var e = this.el.dom, k=0;
13586 while (e ) { e = e.previousSibling; ++k;}
13591 this.rendered = false;
13593 this.render(this.parent().getChildContainer(true), k);
13596 if(Roo.isIOS && this.useNativeIOS){
13597 this.initIOSView();
13605 if(Roo.isTouch && this.mobileTouchView){
13606 this.initTouchView();
13611 this.initTickableEvents();
13615 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13617 if(this.hiddenName){
13619 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13621 this.hiddenField.dom.value =
13622 this.hiddenValue !== undefined ? this.hiddenValue :
13623 this.value !== undefined ? this.value : '';
13625 // prevent input submission
13626 this.el.dom.removeAttribute('name');
13627 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13632 // this.el.dom.setAttribute('autocomplete', 'off');
13635 var cls = 'x-combo-list';
13637 //this.list = new Roo.Layer({
13638 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13644 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13645 _this.list.setWidth(lw);
13648 this.list.on('mouseover', this.onViewOver, this);
13649 this.list.on('mousemove', this.onViewMove, this);
13650 this.list.on('scroll', this.onViewScroll, this);
13653 this.list.swallowEvent('mousewheel');
13654 this.assetHeight = 0;
13657 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13658 this.assetHeight += this.header.getHeight();
13661 this.innerList = this.list.createChild({cls:cls+'-inner'});
13662 this.innerList.on('mouseover', this.onViewOver, this);
13663 this.innerList.on('mousemove', this.onViewMove, this);
13664 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13666 if(this.allowBlank && !this.pageSize && !this.disableClear){
13667 this.footer = this.list.createChild({cls:cls+'-ft'});
13668 this.pageTb = new Roo.Toolbar(this.footer);
13672 this.footer = this.list.createChild({cls:cls+'-ft'});
13673 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13674 {pageSize: this.pageSize});
13678 if (this.pageTb && this.allowBlank && !this.disableClear) {
13680 this.pageTb.add(new Roo.Toolbar.Fill(), {
13681 cls: 'x-btn-icon x-btn-clear',
13683 handler: function()
13686 _this.clearValue();
13687 _this.onSelect(false, -1);
13692 this.assetHeight += this.footer.getHeight();
13697 this.tpl = Roo.bootstrap.version == 4 ?
13698 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13699 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13702 this.view = new Roo.View(this.list, this.tpl, {
13703 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13705 //this.view.wrapEl.setDisplayed(false);
13706 this.view.on('click', this.onViewClick, this);
13709 this.store.on('beforeload', this.onBeforeLoad, this);
13710 this.store.on('load', this.onLoad, this);
13711 this.store.on('loadexception', this.onLoadException, this);
13713 if(this.resizable){
13714 this.resizer = new Roo.Resizable(this.list, {
13715 pinned:true, handles:'se'
13717 this.resizer.on('resize', function(r, w, h){
13718 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13719 this.listWidth = w;
13720 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13721 this.restrictHeight();
13723 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13726 if(!this.editable){
13727 this.editable = true;
13728 this.setEditable(false);
13733 if (typeof(this.events.add.listeners) != 'undefined') {
13735 this.addicon = this.wrap.createChild(
13736 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13738 this.addicon.on('click', function(e) {
13739 this.fireEvent('add', this);
13742 if (typeof(this.events.edit.listeners) != 'undefined') {
13744 this.editicon = this.wrap.createChild(
13745 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13746 if (this.addicon) {
13747 this.editicon.setStyle('margin-left', '40px');
13749 this.editicon.on('click', function(e) {
13751 // we fire even if inothing is selected..
13752 this.fireEvent('edit', this, this.lastData );
13758 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13759 "up" : function(e){
13760 this.inKeyMode = true;
13764 "down" : function(e){
13765 if(!this.isExpanded()){
13766 this.onTriggerClick();
13768 this.inKeyMode = true;
13773 "enter" : function(e){
13774 // this.onViewClick();
13778 if(this.fireEvent("specialkey", this, e)){
13779 this.onViewClick(false);
13785 "esc" : function(e){
13789 "tab" : function(e){
13792 if(this.fireEvent("specialkey", this, e)){
13793 this.onViewClick(false);
13801 doRelay : function(foo, bar, hname){
13802 if(hname == 'down' || this.scope.isExpanded()){
13803 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13812 this.queryDelay = Math.max(this.queryDelay || 10,
13813 this.mode == 'local' ? 10 : 250);
13816 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13818 if(this.typeAhead){
13819 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13821 if(this.editable !== false){
13822 this.inputEl().on("keyup", this.onKeyUp, this);
13824 if(this.forceSelection){
13825 this.inputEl().on('blur', this.doForce, this);
13829 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13830 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13834 initTickableEvents: function()
13838 if(this.hiddenName){
13840 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13842 this.hiddenField.dom.value =
13843 this.hiddenValue !== undefined ? this.hiddenValue :
13844 this.value !== undefined ? this.value : '';
13846 // prevent input submission
13847 this.el.dom.removeAttribute('name');
13848 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13853 // this.list = this.el.select('ul.dropdown-menu',true).first();
13855 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13856 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13857 if(this.triggerList){
13858 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13861 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13862 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13864 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13865 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13867 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13868 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13870 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13871 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13872 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13875 this.cancelBtn.hide();
13880 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13881 _this.list.setWidth(lw);
13884 this.list.on('mouseover', this.onViewOver, this);
13885 this.list.on('mousemove', this.onViewMove, this);
13887 this.list.on('scroll', this.onViewScroll, this);
13890 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13891 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13894 this.view = new Roo.View(this.list, this.tpl, {
13899 selectedClass: this.selectedClass
13902 //this.view.wrapEl.setDisplayed(false);
13903 this.view.on('click', this.onViewClick, this);
13907 this.store.on('beforeload', this.onBeforeLoad, this);
13908 this.store.on('load', this.onLoad, this);
13909 this.store.on('loadexception', this.onLoadException, this);
13912 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13913 "up" : function(e){
13914 this.inKeyMode = true;
13918 "down" : function(e){
13919 this.inKeyMode = true;
13923 "enter" : function(e){
13924 if(this.fireEvent("specialkey", this, e)){
13925 this.onViewClick(false);
13931 "esc" : function(e){
13932 this.onTickableFooterButtonClick(e, false, false);
13935 "tab" : function(e){
13936 this.fireEvent("specialkey", this, e);
13938 this.onTickableFooterButtonClick(e, false, false);
13945 doRelay : function(e, fn, key){
13946 if(this.scope.isExpanded()){
13947 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13956 this.queryDelay = Math.max(this.queryDelay || 10,
13957 this.mode == 'local' ? 10 : 250);
13960 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13962 if(this.typeAhead){
13963 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13966 if(this.editable !== false){
13967 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13970 this.indicator = this.indicatorEl();
13972 if(this.indicator){
13973 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13974 this.indicator.hide();
13979 onDestroy : function(){
13981 this.view.setStore(null);
13982 this.view.el.removeAllListeners();
13983 this.view.el.remove();
13984 this.view.purgeListeners();
13987 this.list.dom.innerHTML = '';
13991 this.store.un('beforeload', this.onBeforeLoad, this);
13992 this.store.un('load', this.onLoad, this);
13993 this.store.un('loadexception', this.onLoadException, this);
13995 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13999 fireKey : function(e){
14000 if(e.isNavKeyPress() && !this.list.isVisible()){
14001 this.fireEvent("specialkey", this, e);
14006 onResize: function(w, h){
14007 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14009 // if(typeof w != 'number'){
14010 // // we do not handle it!?!?
14013 // var tw = this.trigger.getWidth();
14014 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14015 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14017 // this.inputEl().setWidth( this.adjustWidth('input', x));
14019 // //this.trigger.setStyle('left', x+'px');
14021 // if(this.list && this.listWidth === undefined){
14022 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14023 // this.list.setWidth(lw);
14024 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14032 * Allow or prevent the user from directly editing the field text. If false is passed,
14033 * the user will only be able to select from the items defined in the dropdown list. This method
14034 * is the runtime equivalent of setting the 'editable' config option at config time.
14035 * @param {Boolean} value True to allow the user to directly edit the field text
14037 setEditable : function(value){
14038 if(value == this.editable){
14041 this.editable = value;
14043 this.inputEl().dom.setAttribute('readOnly', true);
14044 this.inputEl().on('mousedown', this.onTriggerClick, this);
14045 this.inputEl().addClass('x-combo-noedit');
14047 this.inputEl().dom.setAttribute('readOnly', false);
14048 this.inputEl().un('mousedown', this.onTriggerClick, this);
14049 this.inputEl().removeClass('x-combo-noedit');
14055 onBeforeLoad : function(combo,opts){
14056 if(!this.hasFocus){
14060 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14062 this.restrictHeight();
14063 this.selectedIndex = -1;
14067 onLoad : function(){
14069 this.hasQuery = false;
14071 if(!this.hasFocus){
14075 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14076 this.loading.hide();
14079 if(this.store.getCount() > 0){
14082 this.restrictHeight();
14083 if(this.lastQuery == this.allQuery){
14084 if(this.editable && !this.tickable){
14085 this.inputEl().dom.select();
14089 !this.selectByValue(this.value, true) &&
14092 !this.store.lastOptions ||
14093 typeof(this.store.lastOptions.add) == 'undefined' ||
14094 this.store.lastOptions.add != true
14097 this.select(0, true);
14100 if(this.autoFocus){
14103 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14104 this.taTask.delay(this.typeAheadDelay);
14108 this.onEmptyResults();
14114 onLoadException : function()
14116 this.hasQuery = false;
14118 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14119 this.loading.hide();
14122 if(this.tickable && this.editable){
14127 // only causes errors at present
14128 //Roo.log(this.store.reader.jsonData);
14129 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14131 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14137 onTypeAhead : function(){
14138 if(this.store.getCount() > 0){
14139 var r = this.store.getAt(0);
14140 var newValue = r.data[this.displayField];
14141 var len = newValue.length;
14142 var selStart = this.getRawValue().length;
14144 if(selStart != len){
14145 this.setRawValue(newValue);
14146 this.selectText(selStart, newValue.length);
14152 onSelect : function(record, index){
14154 if(this.fireEvent('beforeselect', this, record, index) !== false){
14156 this.setFromData(index > -1 ? record.data : false);
14159 this.fireEvent('select', this, record, index);
14164 * Returns the currently selected field value or empty string if no value is set.
14165 * @return {String} value The selected value
14167 getValue : function()
14169 if(Roo.isIOS && this.useNativeIOS){
14170 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14174 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14177 if(this.valueField){
14178 return typeof this.value != 'undefined' ? this.value : '';
14180 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14184 getRawValue : function()
14186 if(Roo.isIOS && this.useNativeIOS){
14187 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14190 var v = this.inputEl().getValue();
14196 * Clears any text/value currently set in the field
14198 clearValue : function(){
14200 if(this.hiddenField){
14201 this.hiddenField.dom.value = '';
14204 this.setRawValue('');
14205 this.lastSelectionText = '';
14206 this.lastData = false;
14208 var close = this.closeTriggerEl();
14219 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14220 * will be displayed in the field. If the value does not match the data value of an existing item,
14221 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14222 * Otherwise the field will be blank (although the value will still be set).
14223 * @param {String} value The value to match
14225 setValue : function(v)
14227 if(Roo.isIOS && this.useNativeIOS){
14228 this.setIOSValue(v);
14238 if(this.valueField){
14239 var r = this.findRecord(this.valueField, v);
14241 text = r.data[this.displayField];
14242 }else if(this.valueNotFoundText !== undefined){
14243 text = this.valueNotFoundText;
14246 this.lastSelectionText = text;
14247 if(this.hiddenField){
14248 this.hiddenField.dom.value = v;
14250 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14253 var close = this.closeTriggerEl();
14256 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14262 * @property {Object} the last set data for the element
14267 * Sets the value of the field based on a object which is related to the record format for the store.
14268 * @param {Object} value the value to set as. or false on reset?
14270 setFromData : function(o){
14277 var dv = ''; // display value
14278 var vv = ''; // value value..
14280 if (this.displayField) {
14281 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14283 // this is an error condition!!!
14284 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14287 if(this.valueField){
14288 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14291 var close = this.closeTriggerEl();
14294 if(dv.length || vv * 1 > 0){
14296 this.blockFocus=true;
14302 if(this.hiddenField){
14303 this.hiddenField.dom.value = vv;
14305 this.lastSelectionText = dv;
14306 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14310 // no hidden field.. - we store the value in 'value', but still display
14311 // display field!!!!
14312 this.lastSelectionText = dv;
14313 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14320 reset : function(){
14321 // overridden so that last data is reset..
14328 this.setValue(this.originalValue);
14329 //this.clearInvalid();
14330 this.lastData = false;
14332 this.view.clearSelections();
14338 findRecord : function(prop, value){
14340 if(this.store.getCount() > 0){
14341 this.store.each(function(r){
14342 if(r.data[prop] == value){
14352 getName: function()
14354 // returns hidden if it's set..
14355 if (!this.rendered) {return ''};
14356 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14360 onViewMove : function(e, t){
14361 this.inKeyMode = false;
14365 onViewOver : function(e, t){
14366 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14369 var item = this.view.findItemFromChild(t);
14372 var index = this.view.indexOf(item);
14373 this.select(index, false);
14378 onViewClick : function(view, doFocus, el, e)
14380 var index = this.view.getSelectedIndexes()[0];
14382 var r = this.store.getAt(index);
14386 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14393 Roo.each(this.tickItems, function(v,k){
14395 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14397 _this.tickItems.splice(k, 1);
14399 if(typeof(e) == 'undefined' && view == false){
14400 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14412 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14413 this.tickItems.push(r.data);
14416 if(typeof(e) == 'undefined' && view == false){
14417 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14424 this.onSelect(r, index);
14426 if(doFocus !== false && !this.blockFocus){
14427 this.inputEl().focus();
14432 restrictHeight : function(){
14433 //this.innerList.dom.style.height = '';
14434 //var inner = this.innerList.dom;
14435 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14436 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14437 //this.list.beginUpdate();
14438 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14439 this.list.alignTo(this.inputEl(), this.listAlign);
14440 this.list.alignTo(this.inputEl(), this.listAlign);
14441 //this.list.endUpdate();
14445 onEmptyResults : function(){
14447 if(this.tickable && this.editable){
14448 this.hasFocus = false;
14449 this.restrictHeight();
14457 * Returns true if the dropdown list is expanded, else false.
14459 isExpanded : function(){
14460 return this.list.isVisible();
14464 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14465 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14466 * @param {String} value The data value of the item to select
14467 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14468 * selected item if it is not currently in view (defaults to true)
14469 * @return {Boolean} True if the value matched an item in the list, else false
14471 selectByValue : function(v, scrollIntoView){
14472 if(v !== undefined && v !== null){
14473 var r = this.findRecord(this.valueField || this.displayField, v);
14475 this.select(this.store.indexOf(r), scrollIntoView);
14483 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14484 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14485 * @param {Number} index The zero-based index of the list item to select
14486 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14487 * selected item if it is not currently in view (defaults to true)
14489 select : function(index, scrollIntoView){
14490 this.selectedIndex = index;
14491 this.view.select(index);
14492 if(scrollIntoView !== false){
14493 var el = this.view.getNode(index);
14495 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14498 this.list.scrollChildIntoView(el, false);
14504 selectNext : function(){
14505 var ct = this.store.getCount();
14507 if(this.selectedIndex == -1){
14509 }else if(this.selectedIndex < ct-1){
14510 this.select(this.selectedIndex+1);
14516 selectPrev : function(){
14517 var ct = this.store.getCount();
14519 if(this.selectedIndex == -1){
14521 }else if(this.selectedIndex != 0){
14522 this.select(this.selectedIndex-1);
14528 onKeyUp : function(e){
14529 if(this.editable !== false && !e.isSpecialKey()){
14530 this.lastKey = e.getKey();
14531 this.dqTask.delay(this.queryDelay);
14536 validateBlur : function(){
14537 return !this.list || !this.list.isVisible();
14541 initQuery : function(){
14543 var v = this.getRawValue();
14545 if(this.tickable && this.editable){
14546 v = this.tickableInputEl().getValue();
14553 doForce : function(){
14554 if(this.inputEl().dom.value.length > 0){
14555 this.inputEl().dom.value =
14556 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14562 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14563 * query allowing the query action to be canceled if needed.
14564 * @param {String} query The SQL query to execute
14565 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14566 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14567 * saved in the current store (defaults to false)
14569 doQuery : function(q, forceAll){
14571 if(q === undefined || q === null){
14576 forceAll: forceAll,
14580 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14585 forceAll = qe.forceAll;
14586 if(forceAll === true || (q.length >= this.minChars)){
14588 this.hasQuery = true;
14590 if(this.lastQuery != q || this.alwaysQuery){
14591 this.lastQuery = q;
14592 if(this.mode == 'local'){
14593 this.selectedIndex = -1;
14595 this.store.clearFilter();
14598 if(this.specialFilter){
14599 this.fireEvent('specialfilter', this);
14604 this.store.filter(this.displayField, q);
14607 this.store.fireEvent("datachanged", this.store);
14614 this.store.baseParams[this.queryParam] = q;
14616 var options = {params : this.getParams(q)};
14619 options.add = true;
14620 options.params.start = this.page * this.pageSize;
14623 this.store.load(options);
14626 * this code will make the page width larger, at the beginning, the list not align correctly,
14627 * we should expand the list on onLoad
14628 * so command out it
14633 this.selectedIndex = -1;
14638 this.loadNext = false;
14642 getParams : function(q){
14644 //p[this.queryParam] = q;
14648 p.limit = this.pageSize;
14654 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14656 collapse : function(){
14657 if(!this.isExpanded()){
14663 this.hasFocus = false;
14667 this.cancelBtn.hide();
14668 this.trigger.show();
14671 this.tickableInputEl().dom.value = '';
14672 this.tickableInputEl().blur();
14677 Roo.get(document).un('mousedown', this.collapseIf, this);
14678 Roo.get(document).un('mousewheel', this.collapseIf, this);
14679 if (!this.editable) {
14680 Roo.get(document).un('keydown', this.listKeyPress, this);
14682 this.fireEvent('collapse', this);
14688 collapseIf : function(e){
14689 var in_combo = e.within(this.el);
14690 var in_list = e.within(this.list);
14691 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14693 if (in_combo || in_list || is_list) {
14694 //e.stopPropagation();
14699 this.onTickableFooterButtonClick(e, false, false);
14707 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14709 expand : function(){
14711 if(this.isExpanded() || !this.hasFocus){
14715 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14716 this.list.setWidth(lw);
14722 this.restrictHeight();
14726 this.tickItems = Roo.apply([], this.item);
14729 this.cancelBtn.show();
14730 this.trigger.hide();
14733 this.tickableInputEl().focus();
14738 Roo.get(document).on('mousedown', this.collapseIf, this);
14739 Roo.get(document).on('mousewheel', this.collapseIf, this);
14740 if (!this.editable) {
14741 Roo.get(document).on('keydown', this.listKeyPress, this);
14744 this.fireEvent('expand', this);
14748 // Implements the default empty TriggerField.onTriggerClick function
14749 onTriggerClick : function(e)
14751 Roo.log('trigger click');
14753 if(this.disabled || !this.triggerList){
14758 this.loadNext = false;
14760 if(this.isExpanded()){
14762 if (!this.blockFocus) {
14763 this.inputEl().focus();
14767 this.hasFocus = true;
14768 if(this.triggerAction == 'all') {
14769 this.doQuery(this.allQuery, true);
14771 this.doQuery(this.getRawValue());
14773 if (!this.blockFocus) {
14774 this.inputEl().focus();
14779 onTickableTriggerClick : function(e)
14786 this.loadNext = false;
14787 this.hasFocus = true;
14789 if(this.triggerAction == 'all') {
14790 this.doQuery(this.allQuery, true);
14792 this.doQuery(this.getRawValue());
14796 onSearchFieldClick : function(e)
14798 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14799 this.onTickableFooterButtonClick(e, false, false);
14803 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14808 this.loadNext = false;
14809 this.hasFocus = true;
14811 if(this.triggerAction == 'all') {
14812 this.doQuery(this.allQuery, true);
14814 this.doQuery(this.getRawValue());
14818 listKeyPress : function(e)
14820 //Roo.log('listkeypress');
14821 // scroll to first matching element based on key pres..
14822 if (e.isSpecialKey()) {
14825 var k = String.fromCharCode(e.getKey()).toUpperCase();
14828 var csel = this.view.getSelectedNodes();
14829 var cselitem = false;
14831 var ix = this.view.indexOf(csel[0]);
14832 cselitem = this.store.getAt(ix);
14833 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14839 this.store.each(function(v) {
14841 // start at existing selection.
14842 if (cselitem.id == v.id) {
14848 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14849 match = this.store.indexOf(v);
14855 if (match === false) {
14856 return true; // no more action?
14859 this.view.select(match);
14860 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14861 sn.scrollIntoView(sn.dom.parentNode, false);
14864 onViewScroll : function(e, t){
14866 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){
14870 this.hasQuery = true;
14872 this.loading = this.list.select('.loading', true).first();
14874 if(this.loading === null){
14875 this.list.createChild({
14877 cls: 'loading roo-select2-more-results roo-select2-active',
14878 html: 'Loading more results...'
14881 this.loading = this.list.select('.loading', true).first();
14883 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14885 this.loading.hide();
14888 this.loading.show();
14893 this.loadNext = true;
14895 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14900 addItem : function(o)
14902 var dv = ''; // display value
14904 if (this.displayField) {
14905 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14907 // this is an error condition!!!
14908 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14915 var choice = this.choices.createChild({
14917 cls: 'roo-select2-search-choice',
14926 cls: 'roo-select2-search-choice-close fa fa-times',
14931 }, this.searchField);
14933 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14935 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14943 this.inputEl().dom.value = '';
14948 onRemoveItem : function(e, _self, o)
14950 e.preventDefault();
14952 this.lastItem = Roo.apply([], this.item);
14954 var index = this.item.indexOf(o.data) * 1;
14957 Roo.log('not this item?!');
14961 this.item.splice(index, 1);
14966 this.fireEvent('remove', this, e);
14972 syncValue : function()
14974 if(!this.item.length){
14981 Roo.each(this.item, function(i){
14982 if(_this.valueField){
14983 value.push(i[_this.valueField]);
14990 this.value = value.join(',');
14992 if(this.hiddenField){
14993 this.hiddenField.dom.value = this.value;
14996 this.store.fireEvent("datachanged", this.store);
15001 clearItem : function()
15003 if(!this.multiple){
15009 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15017 if(this.tickable && !Roo.isTouch){
15018 this.view.refresh();
15022 inputEl: function ()
15024 if(Roo.isIOS && this.useNativeIOS){
15025 return this.el.select('select.roo-ios-select', true).first();
15028 if(Roo.isTouch && this.mobileTouchView){
15029 return this.el.select('input.form-control',true).first();
15033 return this.searchField;
15036 return this.el.select('input.form-control',true).first();
15039 onTickableFooterButtonClick : function(e, btn, el)
15041 e.preventDefault();
15043 this.lastItem = Roo.apply([], this.item);
15045 if(btn && btn.name == 'cancel'){
15046 this.tickItems = Roo.apply([], this.item);
15055 Roo.each(this.tickItems, function(o){
15063 validate : function()
15065 if(this.getVisibilityEl().hasClass('hidden')){
15069 var v = this.getRawValue();
15072 v = this.getValue();
15075 if(this.disabled || this.allowBlank || v.length){
15080 this.markInvalid();
15084 tickableInputEl : function()
15086 if(!this.tickable || !this.editable){
15087 return this.inputEl();
15090 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15094 getAutoCreateTouchView : function()
15099 cls: 'form-group' //input-group
15105 type : this.inputType,
15106 cls : 'form-control x-combo-noedit',
15107 autocomplete: 'new-password',
15108 placeholder : this.placeholder || '',
15113 input.name = this.name;
15117 input.cls += ' input-' + this.size;
15120 if (this.disabled) {
15121 input.disabled = true;
15132 inputblock.cls += ' input-group';
15134 inputblock.cn.unshift({
15136 cls : 'input-group-addon input-group-prepend input-group-text',
15141 if(this.removable && !this.multiple){
15142 inputblock.cls += ' roo-removable';
15144 inputblock.cn.push({
15147 cls : 'roo-combo-removable-btn close'
15151 if(this.hasFeedback && !this.allowBlank){
15153 inputblock.cls += ' has-feedback';
15155 inputblock.cn.push({
15157 cls: 'glyphicon form-control-feedback'
15164 inputblock.cls += (this.before) ? '' : ' input-group';
15166 inputblock.cn.push({
15168 cls : 'input-group-addon input-group-append input-group-text',
15174 var ibwrap = inputblock;
15179 cls: 'roo-select2-choices',
15183 cls: 'roo-select2-search-field',
15196 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15201 cls: 'form-hidden-field'
15207 if(!this.multiple && this.showToggleBtn){
15214 if (this.caret != false) {
15217 cls: 'fa fa-' + this.caret
15224 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15229 cls: 'combobox-clear',
15243 combobox.cls += ' roo-select2-container-multi';
15246 var align = this.labelAlign || this.parentLabelAlign();
15248 if (align ==='left' && this.fieldLabel.length) {
15253 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15254 tooltip : 'This field is required'
15258 cls : 'control-label col-form-label',
15259 html : this.fieldLabel
15270 var labelCfg = cfg.cn[1];
15271 var contentCfg = cfg.cn[2];
15274 if(this.indicatorpos == 'right'){
15279 cls : 'control-label col-form-label',
15283 html : this.fieldLabel
15287 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15288 tooltip : 'This field is required'
15301 labelCfg = cfg.cn[0];
15302 contentCfg = cfg.cn[1];
15307 if(this.labelWidth > 12){
15308 labelCfg.style = "width: " + this.labelWidth + 'px';
15311 if(this.labelWidth < 13 && this.labelmd == 0){
15312 this.labelmd = this.labelWidth;
15315 if(this.labellg > 0){
15316 labelCfg.cls += ' col-lg-' + this.labellg;
15317 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15320 if(this.labelmd > 0){
15321 labelCfg.cls += ' col-md-' + this.labelmd;
15322 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15325 if(this.labelsm > 0){
15326 labelCfg.cls += ' col-sm-' + this.labelsm;
15327 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15330 if(this.labelxs > 0){
15331 labelCfg.cls += ' col-xs-' + this.labelxs;
15332 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15336 } else if ( this.fieldLabel.length) {
15340 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15341 tooltip : 'This field is required'
15345 cls : 'control-label',
15346 html : this.fieldLabel
15357 if(this.indicatorpos == 'right'){
15361 cls : 'control-label',
15362 html : this.fieldLabel,
15366 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15367 tooltip : 'This field is required'
15384 var settings = this;
15386 ['xs','sm','md','lg'].map(function(size){
15387 if (settings[size]) {
15388 cfg.cls += ' col-' + size + '-' + settings[size];
15395 initTouchView : function()
15397 this.renderTouchView();
15399 this.touchViewEl.on('scroll', function(){
15400 this.el.dom.scrollTop = 0;
15403 this.originalValue = this.getValue();
15405 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15407 this.inputEl().on("click", this.showTouchView, this);
15408 if (this.triggerEl) {
15409 this.triggerEl.on("click", this.showTouchView, this);
15413 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15414 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15416 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15418 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15419 this.store.on('load', this.onTouchViewLoad, this);
15420 this.store.on('loadexception', this.onTouchViewLoadException, this);
15422 if(this.hiddenName){
15424 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15426 this.hiddenField.dom.value =
15427 this.hiddenValue !== undefined ? this.hiddenValue :
15428 this.value !== undefined ? this.value : '';
15430 this.el.dom.removeAttribute('name');
15431 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15435 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15436 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15439 if(this.removable && !this.multiple){
15440 var close = this.closeTriggerEl();
15442 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15443 close.on('click', this.removeBtnClick, this, close);
15447 * fix the bug in Safari iOS8
15449 this.inputEl().on("focus", function(e){
15450 document.activeElement.blur();
15453 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15460 renderTouchView : function()
15462 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15463 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15465 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15466 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15469 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15470 this.touchViewBodyEl.setStyle('overflow', 'auto');
15472 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15473 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15475 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15476 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15480 showTouchView : function()
15486 this.touchViewHeaderEl.hide();
15488 if(this.modalTitle.length){
15489 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15490 this.touchViewHeaderEl.show();
15493 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15494 this.touchViewEl.show();
15496 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15498 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15499 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15501 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15503 if(this.modalTitle.length){
15504 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15507 this.touchViewBodyEl.setHeight(bodyHeight);
15511 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15513 this.touchViewEl.addClass('in');
15516 if(this._touchViewMask){
15517 Roo.get(document.body).addClass("x-body-masked");
15518 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15519 this._touchViewMask.setStyle('z-index', 10000);
15520 this._touchViewMask.addClass('show');
15523 this.doTouchViewQuery();
15527 hideTouchView : function()
15529 this.touchViewEl.removeClass('in');
15533 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15535 this.touchViewEl.setStyle('display', 'none');
15538 if(this._touchViewMask){
15539 this._touchViewMask.removeClass('show');
15540 Roo.get(document.body).removeClass("x-body-masked");
15544 setTouchViewValue : function()
15551 Roo.each(this.tickItems, function(o){
15556 this.hideTouchView();
15559 doTouchViewQuery : function()
15568 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15572 if(!this.alwaysQuery || this.mode == 'local'){
15573 this.onTouchViewLoad();
15580 onTouchViewBeforeLoad : function(combo,opts)
15586 onTouchViewLoad : function()
15588 if(this.store.getCount() < 1){
15589 this.onTouchViewEmptyResults();
15593 this.clearTouchView();
15595 var rawValue = this.getRawValue();
15597 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15599 this.tickItems = [];
15601 this.store.data.each(function(d, rowIndex){
15602 var row = this.touchViewListGroup.createChild(template);
15604 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15605 row.addClass(d.data.cls);
15608 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15611 html : d.data[this.displayField]
15614 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15615 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15618 row.removeClass('selected');
15619 if(!this.multiple && this.valueField &&
15620 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15623 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15624 row.addClass('selected');
15627 if(this.multiple && this.valueField &&
15628 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15632 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15633 this.tickItems.push(d.data);
15636 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15640 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15642 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15644 if(this.modalTitle.length){
15645 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15648 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15650 if(this.mobile_restrict_height && listHeight < bodyHeight){
15651 this.touchViewBodyEl.setHeight(listHeight);
15656 if(firstChecked && listHeight > bodyHeight){
15657 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15662 onTouchViewLoadException : function()
15664 this.hideTouchView();
15667 onTouchViewEmptyResults : function()
15669 this.clearTouchView();
15671 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15673 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15677 clearTouchView : function()
15679 this.touchViewListGroup.dom.innerHTML = '';
15682 onTouchViewClick : function(e, el, o)
15684 e.preventDefault();
15687 var rowIndex = o.rowIndex;
15689 var r = this.store.getAt(rowIndex);
15691 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15693 if(!this.multiple){
15694 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15695 c.dom.removeAttribute('checked');
15698 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15700 this.setFromData(r.data);
15702 var close = this.closeTriggerEl();
15708 this.hideTouchView();
15710 this.fireEvent('select', this, r, rowIndex);
15715 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15716 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15717 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15721 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15722 this.addItem(r.data);
15723 this.tickItems.push(r.data);
15727 getAutoCreateNativeIOS : function()
15730 cls: 'form-group' //input-group,
15735 cls : 'roo-ios-select'
15739 combobox.name = this.name;
15742 if (this.disabled) {
15743 combobox.disabled = true;
15746 var settings = this;
15748 ['xs','sm','md','lg'].map(function(size){
15749 if (settings[size]) {
15750 cfg.cls += ' col-' + size + '-' + settings[size];
15760 initIOSView : function()
15762 this.store.on('load', this.onIOSViewLoad, this);
15767 onIOSViewLoad : function()
15769 if(this.store.getCount() < 1){
15773 this.clearIOSView();
15775 if(this.allowBlank) {
15777 var default_text = '-- SELECT --';
15779 if(this.placeholder.length){
15780 default_text = this.placeholder;
15783 if(this.emptyTitle.length){
15784 default_text += ' - ' + this.emptyTitle + ' -';
15787 var opt = this.inputEl().createChild({
15790 html : default_text
15794 o[this.valueField] = 0;
15795 o[this.displayField] = default_text;
15797 this.ios_options.push({
15804 this.store.data.each(function(d, rowIndex){
15808 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15809 html = d.data[this.displayField];
15814 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15815 value = d.data[this.valueField];
15824 if(this.value == d.data[this.valueField]){
15825 option['selected'] = true;
15828 var opt = this.inputEl().createChild(option);
15830 this.ios_options.push({
15837 this.inputEl().on('change', function(){
15838 this.fireEvent('select', this);
15843 clearIOSView: function()
15845 this.inputEl().dom.innerHTML = '';
15847 this.ios_options = [];
15850 setIOSValue: function(v)
15854 if(!this.ios_options){
15858 Roo.each(this.ios_options, function(opts){
15860 opts.el.dom.removeAttribute('selected');
15862 if(opts.data[this.valueField] != v){
15866 opts.el.dom.setAttribute('selected', true);
15872 * @cfg {Boolean} grow
15876 * @cfg {Number} growMin
15880 * @cfg {Number} growMax
15889 Roo.apply(Roo.bootstrap.ComboBox, {
15893 cls: 'modal-header',
15915 cls: 'list-group-item',
15919 cls: 'roo-combobox-list-group-item-value'
15923 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15937 listItemCheckbox : {
15939 cls: 'list-group-item',
15943 cls: 'roo-combobox-list-group-item-value'
15947 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15963 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15968 cls: 'modal-footer',
15976 cls: 'col-xs-6 text-left',
15979 cls: 'btn btn-danger roo-touch-view-cancel',
15985 cls: 'col-xs-6 text-right',
15988 cls: 'btn btn-success roo-touch-view-ok',
15999 Roo.apply(Roo.bootstrap.ComboBox, {
16001 touchViewTemplate : {
16003 cls: 'modal fade roo-combobox-touch-view',
16007 cls: 'modal-dialog',
16008 style : 'position:fixed', // we have to fix position....
16012 cls: 'modal-content',
16014 Roo.bootstrap.ComboBox.header,
16015 Roo.bootstrap.ComboBox.body,
16016 Roo.bootstrap.ComboBox.footer
16025 * Ext JS Library 1.1.1
16026 * Copyright(c) 2006-2007, Ext JS, LLC.
16028 * Originally Released Under LGPL - original licence link has changed is not relivant.
16031 * <script type="text/javascript">
16036 * @extends Roo.util.Observable
16037 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16038 * This class also supports single and multi selection modes. <br>
16039 * Create a data model bound view:
16041 var store = new Roo.data.Store(...);
16043 var view = new Roo.View({
16045 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16047 singleSelect: true,
16048 selectedClass: "ydataview-selected",
16052 // listen for node click?
16053 view.on("click", function(vw, index, node, e){
16054 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16058 dataModel.load("foobar.xml");
16060 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16062 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16063 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16065 * Note: old style constructor is still suported (container, template, config)
16068 * Create a new View
16069 * @param {Object} config The config object
16072 Roo.View = function(config, depreciated_tpl, depreciated_config){
16074 this.parent = false;
16076 if (typeof(depreciated_tpl) == 'undefined') {
16077 // new way.. - universal constructor.
16078 Roo.apply(this, config);
16079 this.el = Roo.get(this.el);
16082 this.el = Roo.get(config);
16083 this.tpl = depreciated_tpl;
16084 Roo.apply(this, depreciated_config);
16086 this.wrapEl = this.el.wrap().wrap();
16087 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16090 if(typeof(this.tpl) == "string"){
16091 this.tpl = new Roo.Template(this.tpl);
16093 // support xtype ctors..
16094 this.tpl = new Roo.factory(this.tpl, Roo);
16098 this.tpl.compile();
16103 * @event beforeclick
16104 * Fires before a click is processed. Returns false to cancel the default action.
16105 * @param {Roo.View} this
16106 * @param {Number} index The index of the target node
16107 * @param {HTMLElement} node The target node
16108 * @param {Roo.EventObject} e The raw event object
16110 "beforeclick" : true,
16113 * Fires when a template node is clicked.
16114 * @param {Roo.View} this
16115 * @param {Number} index The index of the target node
16116 * @param {HTMLElement} node The target node
16117 * @param {Roo.EventObject} e The raw event object
16122 * Fires when a template node is double clicked.
16123 * @param {Roo.View} this
16124 * @param {Number} index The index of the target node
16125 * @param {HTMLElement} node The target node
16126 * @param {Roo.EventObject} e The raw event object
16130 * @event contextmenu
16131 * Fires when a template node is right clicked.
16132 * @param {Roo.View} this
16133 * @param {Number} index The index of the target node
16134 * @param {HTMLElement} node The target node
16135 * @param {Roo.EventObject} e The raw event object
16137 "contextmenu" : true,
16139 * @event selectionchange
16140 * Fires when the selected nodes change.
16141 * @param {Roo.View} this
16142 * @param {Array} selections Array of the selected nodes
16144 "selectionchange" : true,
16147 * @event beforeselect
16148 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16149 * @param {Roo.View} this
16150 * @param {HTMLElement} node The node to be selected
16151 * @param {Array} selections Array of currently selected nodes
16153 "beforeselect" : true,
16155 * @event preparedata
16156 * Fires on every row to render, to allow you to change the data.
16157 * @param {Roo.View} this
16158 * @param {Object} data to be rendered (change this)
16160 "preparedata" : true
16168 "click": this.onClick,
16169 "dblclick": this.onDblClick,
16170 "contextmenu": this.onContextMenu,
16174 this.selections = [];
16176 this.cmp = new Roo.CompositeElementLite([]);
16178 this.store = Roo.factory(this.store, Roo.data);
16179 this.setStore(this.store, true);
16182 if ( this.footer && this.footer.xtype) {
16184 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16186 this.footer.dataSource = this.store;
16187 this.footer.container = fctr;
16188 this.footer = Roo.factory(this.footer, Roo);
16189 fctr.insertFirst(this.el);
16191 // this is a bit insane - as the paging toolbar seems to detach the el..
16192 // dom.parentNode.parentNode.parentNode
16193 // they get detached?
16197 Roo.View.superclass.constructor.call(this);
16202 Roo.extend(Roo.View, Roo.util.Observable, {
16205 * @cfg {Roo.data.Store} store Data store to load data from.
16210 * @cfg {String|Roo.Element} el The container element.
16215 * @cfg {String|Roo.Template} tpl The template used by this View
16219 * @cfg {String} dataName the named area of the template to use as the data area
16220 * Works with domtemplates roo-name="name"
16224 * @cfg {String} selectedClass The css class to add to selected nodes
16226 selectedClass : "x-view-selected",
16228 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16233 * @cfg {String} text to display on mask (default Loading)
16237 * @cfg {Boolean} multiSelect Allow multiple selection
16239 multiSelect : false,
16241 * @cfg {Boolean} singleSelect Allow single selection
16243 singleSelect: false,
16246 * @cfg {Boolean} toggleSelect - selecting
16248 toggleSelect : false,
16251 * @cfg {Boolean} tickable - selecting
16256 * Returns the element this view is bound to.
16257 * @return {Roo.Element}
16259 getEl : function(){
16260 return this.wrapEl;
16266 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16268 refresh : function(){
16269 //Roo.log('refresh');
16272 // if we are using something like 'domtemplate', then
16273 // the what gets used is:
16274 // t.applySubtemplate(NAME, data, wrapping data..)
16275 // the outer template then get' applied with
16276 // the store 'extra data'
16277 // and the body get's added to the
16278 // roo-name="data" node?
16279 // <span class='roo-tpl-{name}'></span> ?????
16283 this.clearSelections();
16284 this.el.update("");
16286 var records = this.store.getRange();
16287 if(records.length < 1) {
16289 // is this valid?? = should it render a template??
16291 this.el.update(this.emptyText);
16295 if (this.dataName) {
16296 this.el.update(t.apply(this.store.meta)); //????
16297 el = this.el.child('.roo-tpl-' + this.dataName);
16300 for(var i = 0, len = records.length; i < len; i++){
16301 var data = this.prepareData(records[i].data, i, records[i]);
16302 this.fireEvent("preparedata", this, data, i, records[i]);
16304 var d = Roo.apply({}, data);
16307 Roo.apply(d, {'roo-id' : Roo.id()});
16311 Roo.each(this.parent.item, function(item){
16312 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16315 Roo.apply(d, {'roo-data-checked' : 'checked'});
16319 html[html.length] = Roo.util.Format.trim(
16321 t.applySubtemplate(this.dataName, d, this.store.meta) :
16328 el.update(html.join(""));
16329 this.nodes = el.dom.childNodes;
16330 this.updateIndexes(0);
16335 * Function to override to reformat the data that is sent to
16336 * the template for each node.
16337 * DEPRICATED - use the preparedata event handler.
16338 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16339 * a JSON object for an UpdateManager bound view).
16341 prepareData : function(data, index, record)
16343 this.fireEvent("preparedata", this, data, index, record);
16347 onUpdate : function(ds, record){
16348 // Roo.log('on update');
16349 this.clearSelections();
16350 var index = this.store.indexOf(record);
16351 var n = this.nodes[index];
16352 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16353 n.parentNode.removeChild(n);
16354 this.updateIndexes(index, index);
16360 onAdd : function(ds, records, index)
16362 //Roo.log(['on Add', ds, records, index] );
16363 this.clearSelections();
16364 if(this.nodes.length == 0){
16368 var n = this.nodes[index];
16369 for(var i = 0, len = records.length; i < len; i++){
16370 var d = this.prepareData(records[i].data, i, records[i]);
16372 this.tpl.insertBefore(n, d);
16375 this.tpl.append(this.el, d);
16378 this.updateIndexes(index);
16381 onRemove : function(ds, record, index){
16382 // Roo.log('onRemove');
16383 this.clearSelections();
16384 var el = this.dataName ?
16385 this.el.child('.roo-tpl-' + this.dataName) :
16388 el.dom.removeChild(this.nodes[index]);
16389 this.updateIndexes(index);
16393 * Refresh an individual node.
16394 * @param {Number} index
16396 refreshNode : function(index){
16397 this.onUpdate(this.store, this.store.getAt(index));
16400 updateIndexes : function(startIndex, endIndex){
16401 var ns = this.nodes;
16402 startIndex = startIndex || 0;
16403 endIndex = endIndex || ns.length - 1;
16404 for(var i = startIndex; i <= endIndex; i++){
16405 ns[i].nodeIndex = i;
16410 * Changes the data store this view uses and refresh the view.
16411 * @param {Store} store
16413 setStore : function(store, initial){
16414 if(!initial && this.store){
16415 this.store.un("datachanged", this.refresh);
16416 this.store.un("add", this.onAdd);
16417 this.store.un("remove", this.onRemove);
16418 this.store.un("update", this.onUpdate);
16419 this.store.un("clear", this.refresh);
16420 this.store.un("beforeload", this.onBeforeLoad);
16421 this.store.un("load", this.onLoad);
16422 this.store.un("loadexception", this.onLoad);
16426 store.on("datachanged", this.refresh, this);
16427 store.on("add", this.onAdd, this);
16428 store.on("remove", this.onRemove, this);
16429 store.on("update", this.onUpdate, this);
16430 store.on("clear", this.refresh, this);
16431 store.on("beforeload", this.onBeforeLoad, this);
16432 store.on("load", this.onLoad, this);
16433 store.on("loadexception", this.onLoad, this);
16441 * onbeforeLoad - masks the loading area.
16444 onBeforeLoad : function(store,opts)
16446 //Roo.log('onBeforeLoad');
16448 this.el.update("");
16450 this.el.mask(this.mask ? this.mask : "Loading" );
16452 onLoad : function ()
16459 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16460 * @param {HTMLElement} node
16461 * @return {HTMLElement} The template node
16463 findItemFromChild : function(node){
16464 var el = this.dataName ?
16465 this.el.child('.roo-tpl-' + this.dataName,true) :
16468 if(!node || node.parentNode == el){
16471 var p = node.parentNode;
16472 while(p && p != el){
16473 if(p.parentNode == el){
16482 onClick : function(e){
16483 var item = this.findItemFromChild(e.getTarget());
16485 var index = this.indexOf(item);
16486 if(this.onItemClick(item, index, e) !== false){
16487 this.fireEvent("click", this, index, item, e);
16490 this.clearSelections();
16495 onContextMenu : function(e){
16496 var item = this.findItemFromChild(e.getTarget());
16498 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16503 onDblClick : function(e){
16504 var item = this.findItemFromChild(e.getTarget());
16506 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16510 onItemClick : function(item, index, e)
16512 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16515 if (this.toggleSelect) {
16516 var m = this.isSelected(item) ? 'unselect' : 'select';
16519 _t[m](item, true, false);
16522 if(this.multiSelect || this.singleSelect){
16523 if(this.multiSelect && e.shiftKey && this.lastSelection){
16524 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16526 this.select(item, this.multiSelect && e.ctrlKey);
16527 this.lastSelection = item;
16530 if(!this.tickable){
16531 e.preventDefault();
16539 * Get the number of selected nodes.
16542 getSelectionCount : function(){
16543 return this.selections.length;
16547 * Get the currently selected nodes.
16548 * @return {Array} An array of HTMLElements
16550 getSelectedNodes : function(){
16551 return this.selections;
16555 * Get the indexes of the selected nodes.
16558 getSelectedIndexes : function(){
16559 var indexes = [], s = this.selections;
16560 for(var i = 0, len = s.length; i < len; i++){
16561 indexes.push(s[i].nodeIndex);
16567 * Clear all selections
16568 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16570 clearSelections : function(suppressEvent){
16571 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16572 this.cmp.elements = this.selections;
16573 this.cmp.removeClass(this.selectedClass);
16574 this.selections = [];
16575 if(!suppressEvent){
16576 this.fireEvent("selectionchange", this, this.selections);
16582 * Returns true if the passed node is selected
16583 * @param {HTMLElement/Number} node The node or node index
16584 * @return {Boolean}
16586 isSelected : function(node){
16587 var s = this.selections;
16591 node = this.getNode(node);
16592 return s.indexOf(node) !== -1;
16597 * @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
16598 * @param {Boolean} keepExisting (optional) true to keep existing selections
16599 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16601 select : function(nodeInfo, keepExisting, suppressEvent){
16602 if(nodeInfo instanceof Array){
16604 this.clearSelections(true);
16606 for(var i = 0, len = nodeInfo.length; i < len; i++){
16607 this.select(nodeInfo[i], true, true);
16611 var node = this.getNode(nodeInfo);
16612 if(!node || this.isSelected(node)){
16613 return; // already selected.
16616 this.clearSelections(true);
16619 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16620 Roo.fly(node).addClass(this.selectedClass);
16621 this.selections.push(node);
16622 if(!suppressEvent){
16623 this.fireEvent("selectionchange", this, this.selections);
16631 * @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
16632 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16633 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16635 unselect : function(nodeInfo, keepExisting, suppressEvent)
16637 if(nodeInfo instanceof Array){
16638 Roo.each(this.selections, function(s) {
16639 this.unselect(s, nodeInfo);
16643 var node = this.getNode(nodeInfo);
16644 if(!node || !this.isSelected(node)){
16645 //Roo.log("not selected");
16646 return; // not selected.
16650 Roo.each(this.selections, function(s) {
16652 Roo.fly(node).removeClass(this.selectedClass);
16659 this.selections= ns;
16660 this.fireEvent("selectionchange", this, this.selections);
16664 * Gets a template node.
16665 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16666 * @return {HTMLElement} The node or null if it wasn't found
16668 getNode : function(nodeInfo){
16669 if(typeof nodeInfo == "string"){
16670 return document.getElementById(nodeInfo);
16671 }else if(typeof nodeInfo == "number"){
16672 return this.nodes[nodeInfo];
16678 * Gets a range template nodes.
16679 * @param {Number} startIndex
16680 * @param {Number} endIndex
16681 * @return {Array} An array of nodes
16683 getNodes : function(start, end){
16684 var ns = this.nodes;
16685 start = start || 0;
16686 end = typeof end == "undefined" ? ns.length - 1 : end;
16689 for(var i = start; i <= end; i++){
16693 for(var i = start; i >= end; i--){
16701 * Finds the index of the passed node
16702 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16703 * @return {Number} The index of the node or -1
16705 indexOf : function(node){
16706 node = this.getNode(node);
16707 if(typeof node.nodeIndex == "number"){
16708 return node.nodeIndex;
16710 var ns = this.nodes;
16711 for(var i = 0, len = ns.length; i < len; i++){
16722 * based on jquery fullcalendar
16726 Roo.bootstrap = Roo.bootstrap || {};
16728 * @class Roo.bootstrap.Calendar
16729 * @extends Roo.bootstrap.Component
16730 * Bootstrap Calendar class
16731 * @cfg {Boolean} loadMask (true|false) default false
16732 * @cfg {Object} header generate the user specific header of the calendar, default false
16735 * Create a new Container
16736 * @param {Object} config The config object
16741 Roo.bootstrap.Calendar = function(config){
16742 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16746 * Fires when a date is selected
16747 * @param {DatePicker} this
16748 * @param {Date} date The selected date
16752 * @event monthchange
16753 * Fires when the displayed month changes
16754 * @param {DatePicker} this
16755 * @param {Date} date The selected month
16757 'monthchange': true,
16759 * @event evententer
16760 * Fires when mouse over an event
16761 * @param {Calendar} this
16762 * @param {event} Event
16764 'evententer': true,
16766 * @event eventleave
16767 * Fires when the mouse leaves an
16768 * @param {Calendar} this
16771 'eventleave': true,
16773 * @event eventclick
16774 * Fires when the mouse click an
16775 * @param {Calendar} this
16784 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16787 * @cfg {Number} startDay
16788 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16796 getAutoCreate : function(){
16799 var fc_button = function(name, corner, style, content ) {
16800 return Roo.apply({},{
16802 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16804 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16807 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16818 style : 'width:100%',
16825 cls : 'fc-header-left',
16827 fc_button('prev', 'left', 'arrow', '‹' ),
16828 fc_button('next', 'right', 'arrow', '›' ),
16829 { tag: 'span', cls: 'fc-header-space' },
16830 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16838 cls : 'fc-header-center',
16842 cls: 'fc-header-title',
16845 html : 'month / year'
16853 cls : 'fc-header-right',
16855 /* fc_button('month', 'left', '', 'month' ),
16856 fc_button('week', '', '', 'week' ),
16857 fc_button('day', 'right', '', 'day' )
16869 header = this.header;
16872 var cal_heads = function() {
16874 // fixme - handle this.
16876 for (var i =0; i < Date.dayNames.length; i++) {
16877 var d = Date.dayNames[i];
16880 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16881 html : d.substring(0,3)
16885 ret[0].cls += ' fc-first';
16886 ret[6].cls += ' fc-last';
16889 var cal_cell = function(n) {
16892 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16897 cls: 'fc-day-number',
16901 cls: 'fc-day-content',
16905 style: 'position: relative;' // height: 17px;
16917 var cal_rows = function() {
16920 for (var r = 0; r < 6; r++) {
16927 for (var i =0; i < Date.dayNames.length; i++) {
16928 var d = Date.dayNames[i];
16929 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16932 row.cn[0].cls+=' fc-first';
16933 row.cn[0].cn[0].style = 'min-height:90px';
16934 row.cn[6].cls+=' fc-last';
16938 ret[0].cls += ' fc-first';
16939 ret[4].cls += ' fc-prev-last';
16940 ret[5].cls += ' fc-last';
16947 cls: 'fc-border-separate',
16948 style : 'width:100%',
16956 cls : 'fc-first fc-last',
16974 cls : 'fc-content',
16975 style : "position: relative;",
16978 cls : 'fc-view fc-view-month fc-grid',
16979 style : 'position: relative',
16980 unselectable : 'on',
16983 cls : 'fc-event-container',
16984 style : 'position:absolute;z-index:8;top:0;left:0;'
17002 initEvents : function()
17005 throw "can not find store for calendar";
17011 style: "text-align:center",
17015 style: "background-color:white;width:50%;margin:250 auto",
17019 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17030 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17032 var size = this.el.select('.fc-content', true).first().getSize();
17033 this.maskEl.setSize(size.width, size.height);
17034 this.maskEl.enableDisplayMode("block");
17035 if(!this.loadMask){
17036 this.maskEl.hide();
17039 this.store = Roo.factory(this.store, Roo.data);
17040 this.store.on('load', this.onLoad, this);
17041 this.store.on('beforeload', this.onBeforeLoad, this);
17045 this.cells = this.el.select('.fc-day',true);
17046 //Roo.log(this.cells);
17047 this.textNodes = this.el.query('.fc-day-number');
17048 this.cells.addClassOnOver('fc-state-hover');
17050 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17051 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17052 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17053 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17055 this.on('monthchange', this.onMonthChange, this);
17057 this.update(new Date().clearTime());
17060 resize : function() {
17061 var sz = this.el.getSize();
17063 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17064 this.el.select('.fc-day-content div',true).setHeight(34);
17069 showPrevMonth : function(e){
17070 this.update(this.activeDate.add("mo", -1));
17072 showToday : function(e){
17073 this.update(new Date().clearTime());
17076 showNextMonth : function(e){
17077 this.update(this.activeDate.add("mo", 1));
17081 showPrevYear : function(){
17082 this.update(this.activeDate.add("y", -1));
17086 showNextYear : function(){
17087 this.update(this.activeDate.add("y", 1));
17092 update : function(date)
17094 var vd = this.activeDate;
17095 this.activeDate = date;
17096 // if(vd && this.el){
17097 // var t = date.getTime();
17098 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17099 // Roo.log('using add remove');
17101 // this.fireEvent('monthchange', this, date);
17103 // this.cells.removeClass("fc-state-highlight");
17104 // this.cells.each(function(c){
17105 // if(c.dateValue == t){
17106 // c.addClass("fc-state-highlight");
17107 // setTimeout(function(){
17108 // try{c.dom.firstChild.focus();}catch(e){}
17118 var days = date.getDaysInMonth();
17120 var firstOfMonth = date.getFirstDateOfMonth();
17121 var startingPos = firstOfMonth.getDay()-this.startDay;
17123 if(startingPos < this.startDay){
17127 var pm = date.add(Date.MONTH, -1);
17128 var prevStart = pm.getDaysInMonth()-startingPos;
17130 this.cells = this.el.select('.fc-day',true);
17131 this.textNodes = this.el.query('.fc-day-number');
17132 this.cells.addClassOnOver('fc-state-hover');
17134 var cells = this.cells.elements;
17135 var textEls = this.textNodes;
17137 Roo.each(cells, function(cell){
17138 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17141 days += startingPos;
17143 // convert everything to numbers so it's fast
17144 var day = 86400000;
17145 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17148 //Roo.log(prevStart);
17150 var today = new Date().clearTime().getTime();
17151 var sel = date.clearTime().getTime();
17152 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17153 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17154 var ddMatch = this.disabledDatesRE;
17155 var ddText = this.disabledDatesText;
17156 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17157 var ddaysText = this.disabledDaysText;
17158 var format = this.format;
17160 var setCellClass = function(cal, cell){
17164 //Roo.log('set Cell Class');
17166 var t = d.getTime();
17170 cell.dateValue = t;
17172 cell.className += " fc-today";
17173 cell.className += " fc-state-highlight";
17174 cell.title = cal.todayText;
17177 // disable highlight in other month..
17178 //cell.className += " fc-state-highlight";
17183 cell.className = " fc-state-disabled";
17184 cell.title = cal.minText;
17188 cell.className = " fc-state-disabled";
17189 cell.title = cal.maxText;
17193 if(ddays.indexOf(d.getDay()) != -1){
17194 cell.title = ddaysText;
17195 cell.className = " fc-state-disabled";
17198 if(ddMatch && format){
17199 var fvalue = d.dateFormat(format);
17200 if(ddMatch.test(fvalue)){
17201 cell.title = ddText.replace("%0", fvalue);
17202 cell.className = " fc-state-disabled";
17206 if (!cell.initialClassName) {
17207 cell.initialClassName = cell.dom.className;
17210 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17215 for(; i < startingPos; i++) {
17216 textEls[i].innerHTML = (++prevStart);
17217 d.setDate(d.getDate()+1);
17219 cells[i].className = "fc-past fc-other-month";
17220 setCellClass(this, cells[i]);
17225 for(; i < days; i++){
17226 intDay = i - startingPos + 1;
17227 textEls[i].innerHTML = (intDay);
17228 d.setDate(d.getDate()+1);
17230 cells[i].className = ''; // "x-date-active";
17231 setCellClass(this, cells[i]);
17235 for(; i < 42; i++) {
17236 textEls[i].innerHTML = (++extraDays);
17237 d.setDate(d.getDate()+1);
17239 cells[i].className = "fc-future fc-other-month";
17240 setCellClass(this, cells[i]);
17243 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17245 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17247 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17248 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17250 if(totalRows != 6){
17251 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17252 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17255 this.fireEvent('monthchange', this, date);
17259 if(!this.internalRender){
17260 var main = this.el.dom.firstChild;
17261 var w = main.offsetWidth;
17262 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17263 Roo.fly(main).setWidth(w);
17264 this.internalRender = true;
17265 // opera does not respect the auto grow header center column
17266 // then, after it gets a width opera refuses to recalculate
17267 // without a second pass
17268 if(Roo.isOpera && !this.secondPass){
17269 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17270 this.secondPass = true;
17271 this.update.defer(10, this, [date]);
17278 findCell : function(dt) {
17279 dt = dt.clearTime().getTime();
17281 this.cells.each(function(c){
17282 //Roo.log("check " +c.dateValue + '?=' + dt);
17283 if(c.dateValue == dt){
17293 findCells : function(ev) {
17294 var s = ev.start.clone().clearTime().getTime();
17296 var e= ev.end.clone().clearTime().getTime();
17299 this.cells.each(function(c){
17300 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17302 if(c.dateValue > e){
17305 if(c.dateValue < s){
17314 // findBestRow: function(cells)
17318 // for (var i =0 ; i < cells.length;i++) {
17319 // ret = Math.max(cells[i].rows || 0,ret);
17326 addItem : function(ev)
17328 // look for vertical location slot in
17329 var cells = this.findCells(ev);
17331 // ev.row = this.findBestRow(cells);
17333 // work out the location.
17337 for(var i =0; i < cells.length; i++) {
17339 cells[i].row = cells[0].row;
17342 cells[i].row = cells[i].row + 1;
17352 if (crow.start.getY() == cells[i].getY()) {
17354 crow.end = cells[i];
17371 cells[0].events.push(ev);
17373 this.calevents.push(ev);
17376 clearEvents: function() {
17378 if(!this.calevents){
17382 Roo.each(this.cells.elements, function(c){
17388 Roo.each(this.calevents, function(e) {
17389 Roo.each(e.els, function(el) {
17390 el.un('mouseenter' ,this.onEventEnter, this);
17391 el.un('mouseleave' ,this.onEventLeave, this);
17396 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17402 renderEvents: function()
17406 this.cells.each(function(c) {
17415 if(c.row != c.events.length){
17416 r = 4 - (4 - (c.row - c.events.length));
17419 c.events = ev.slice(0, r);
17420 c.more = ev.slice(r);
17422 if(c.more.length && c.more.length == 1){
17423 c.events.push(c.more.pop());
17426 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17430 this.cells.each(function(c) {
17432 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17435 for (var e = 0; e < c.events.length; e++){
17436 var ev = c.events[e];
17437 var rows = ev.rows;
17439 for(var i = 0; i < rows.length; i++) {
17441 // how many rows should it span..
17444 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17445 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17447 unselectable : "on",
17450 cls: 'fc-event-inner',
17454 // cls: 'fc-event-time',
17455 // html : cells.length > 1 ? '' : ev.time
17459 cls: 'fc-event-title',
17460 html : String.format('{0}', ev.title)
17467 cls: 'ui-resizable-handle ui-resizable-e',
17468 html : '  '
17475 cfg.cls += ' fc-event-start';
17477 if ((i+1) == rows.length) {
17478 cfg.cls += ' fc-event-end';
17481 var ctr = _this.el.select('.fc-event-container',true).first();
17482 var cg = ctr.createChild(cfg);
17484 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17485 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17487 var r = (c.more.length) ? 1 : 0;
17488 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17489 cg.setWidth(ebox.right - sbox.x -2);
17491 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17492 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17493 cg.on('click', _this.onEventClick, _this, ev);
17504 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17505 style : 'position: absolute',
17506 unselectable : "on",
17509 cls: 'fc-event-inner',
17513 cls: 'fc-event-title',
17521 cls: 'ui-resizable-handle ui-resizable-e',
17522 html : '  '
17528 var ctr = _this.el.select('.fc-event-container',true).first();
17529 var cg = ctr.createChild(cfg);
17531 var sbox = c.select('.fc-day-content',true).first().getBox();
17532 var ebox = c.select('.fc-day-content',true).first().getBox();
17534 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17535 cg.setWidth(ebox.right - sbox.x -2);
17537 cg.on('click', _this.onMoreEventClick, _this, c.more);
17547 onEventEnter: function (e, el,event,d) {
17548 this.fireEvent('evententer', this, el, event);
17551 onEventLeave: function (e, el,event,d) {
17552 this.fireEvent('eventleave', this, el, event);
17555 onEventClick: function (e, el,event,d) {
17556 this.fireEvent('eventclick', this, el, event);
17559 onMonthChange: function () {
17563 onMoreEventClick: function(e, el, more)
17567 this.calpopover.placement = 'right';
17568 this.calpopover.setTitle('More');
17570 this.calpopover.setContent('');
17572 var ctr = this.calpopover.el.select('.popover-content', true).first();
17574 Roo.each(more, function(m){
17576 cls : 'fc-event-hori fc-event-draggable',
17579 var cg = ctr.createChild(cfg);
17581 cg.on('click', _this.onEventClick, _this, m);
17584 this.calpopover.show(el);
17589 onLoad: function ()
17591 this.calevents = [];
17594 if(this.store.getCount() > 0){
17595 this.store.data.each(function(d){
17598 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17599 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17600 time : d.data.start_time,
17601 title : d.data.title,
17602 description : d.data.description,
17603 venue : d.data.venue
17608 this.renderEvents();
17610 if(this.calevents.length && this.loadMask){
17611 this.maskEl.hide();
17615 onBeforeLoad: function()
17617 this.clearEvents();
17619 this.maskEl.show();
17633 * @class Roo.bootstrap.Popover
17634 * @extends Roo.bootstrap.Component
17635 * Bootstrap Popover class
17636 * @cfg {String} html contents of the popover (or false to use children..)
17637 * @cfg {String} title of popover (or false to hide)
17638 * @cfg {String} placement how it is placed
17639 * @cfg {String} trigger click || hover (or false to trigger manually)
17640 * @cfg {String} over what (parent or false to trigger manually.)
17641 * @cfg {Number} delay - delay before showing
17644 * Create a new Popover
17645 * @param {Object} config The config object
17648 Roo.bootstrap.Popover = function(config){
17649 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17655 * After the popover show
17657 * @param {Roo.bootstrap.Popover} this
17662 * After the popover hide
17664 * @param {Roo.bootstrap.Popover} this
17670 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17672 title: 'Fill in a title',
17675 placement : 'right',
17676 trigger : 'hover', // hover
17682 can_build_overlaid : false,
17684 getChildContainer : function()
17686 return this.el.select('.popover-content',true).first();
17689 getAutoCreate : function(){
17692 cls : 'popover roo-dynamic',
17693 style: 'display:block',
17699 cls : 'popover-inner',
17703 cls: 'popover-title popover-header',
17707 cls : 'popover-content popover-body',
17718 setTitle: function(str)
17721 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17723 setContent: function(str)
17726 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17728 // as it get's added to the bottom of the page.
17729 onRender : function(ct, position)
17731 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17733 var cfg = Roo.apply({}, this.getAutoCreate());
17737 cfg.cls += ' ' + this.cls;
17740 cfg.style = this.style;
17742 //Roo.log("adding to ");
17743 this.el = Roo.get(document.body).createChild(cfg, position);
17744 // Roo.log(this.el);
17749 initEvents : function()
17751 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17752 this.el.enableDisplayMode('block');
17754 if (this.over === false) {
17757 if (this.triggers === false) {
17760 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17761 var triggers = this.trigger ? this.trigger.split(' ') : [];
17762 Roo.each(triggers, function(trigger) {
17764 if (trigger == 'click') {
17765 on_el.on('click', this.toggle, this);
17766 } else if (trigger != 'manual') {
17767 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17768 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17770 on_el.on(eventIn ,this.enter, this);
17771 on_el.on(eventOut, this.leave, this);
17782 toggle : function () {
17783 this.hoverState == 'in' ? this.leave() : this.enter();
17786 enter : function () {
17788 clearTimeout(this.timeout);
17790 this.hoverState = 'in';
17792 if (!this.delay || !this.delay.show) {
17797 this.timeout = setTimeout(function () {
17798 if (_t.hoverState == 'in') {
17801 }, this.delay.show)
17804 leave : function() {
17805 clearTimeout(this.timeout);
17807 this.hoverState = 'out';
17809 if (!this.delay || !this.delay.hide) {
17814 this.timeout = setTimeout(function () {
17815 if (_t.hoverState == 'out') {
17818 }, this.delay.hide)
17821 show : function (on_el)
17824 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17828 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17829 if (this.html !== false) {
17830 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17832 this.el.removeClass([
17833 'fade','top','bottom', 'left', 'right','in',
17834 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17836 if (!this.title.length) {
17837 this.el.select('.popover-title',true).hide();
17840 var placement = typeof this.placement == 'function' ?
17841 this.placement.call(this, this.el, on_el) :
17844 var autoToken = /\s?auto?\s?/i;
17845 var autoPlace = autoToken.test(placement);
17847 placement = placement.replace(autoToken, '') || 'top';
17851 //this.el.setXY([0,0]);
17853 this.el.dom.style.display='block';
17854 this.el.addClass(placement);
17856 //this.el.appendTo(on_el);
17858 var p = this.getPosition();
17859 var box = this.el.getBox();
17864 var align = Roo.bootstrap.Popover.alignment[placement];
17867 this.el.alignTo(on_el, align[0],align[1]);
17868 //var arrow = this.el.select('.arrow',true).first();
17869 //arrow.set(align[2],
17871 this.el.addClass('in');
17874 if (this.el.hasClass('fade')) {
17878 this.hoverState = 'in';
17880 this.fireEvent('show', this);
17885 this.el.setXY([0,0]);
17886 this.el.removeClass('in');
17888 this.hoverState = null;
17890 this.fireEvent('hide', this);
17895 Roo.bootstrap.Popover.alignment = {
17896 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17897 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17898 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17899 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17910 * @class Roo.bootstrap.Progress
17911 * @extends Roo.bootstrap.Component
17912 * Bootstrap Progress class
17913 * @cfg {Boolean} striped striped of the progress bar
17914 * @cfg {Boolean} active animated of the progress bar
17918 * Create a new Progress
17919 * @param {Object} config The config object
17922 Roo.bootstrap.Progress = function(config){
17923 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17926 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17931 getAutoCreate : function(){
17939 cfg.cls += ' progress-striped';
17943 cfg.cls += ' active';
17962 * @class Roo.bootstrap.ProgressBar
17963 * @extends Roo.bootstrap.Component
17964 * Bootstrap ProgressBar class
17965 * @cfg {Number} aria_valuenow aria-value now
17966 * @cfg {Number} aria_valuemin aria-value min
17967 * @cfg {Number} aria_valuemax aria-value max
17968 * @cfg {String} label label for the progress bar
17969 * @cfg {String} panel (success | info | warning | danger )
17970 * @cfg {String} role role of the progress bar
17971 * @cfg {String} sr_only text
17975 * Create a new ProgressBar
17976 * @param {Object} config The config object
17979 Roo.bootstrap.ProgressBar = function(config){
17980 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17983 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17987 aria_valuemax : 100,
17993 getAutoCreate : function()
17998 cls: 'progress-bar',
17999 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18011 cfg.role = this.role;
18014 if(this.aria_valuenow){
18015 cfg['aria-valuenow'] = this.aria_valuenow;
18018 if(this.aria_valuemin){
18019 cfg['aria-valuemin'] = this.aria_valuemin;
18022 if(this.aria_valuemax){
18023 cfg['aria-valuemax'] = this.aria_valuemax;
18026 if(this.label && !this.sr_only){
18027 cfg.html = this.label;
18031 cfg.cls += ' progress-bar-' + this.panel;
18037 update : function(aria_valuenow)
18039 this.aria_valuenow = aria_valuenow;
18041 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18056 * @class Roo.bootstrap.TabGroup
18057 * @extends Roo.bootstrap.Column
18058 * Bootstrap Column class
18059 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18060 * @cfg {Boolean} carousel true to make the group behave like a carousel
18061 * @cfg {Boolean} bullets show bullets for the panels
18062 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18063 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18064 * @cfg {Boolean} showarrow (true|false) show arrow default true
18067 * Create a new TabGroup
18068 * @param {Object} config The config object
18071 Roo.bootstrap.TabGroup = function(config){
18072 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18074 this.navId = Roo.id();
18077 Roo.bootstrap.TabGroup.register(this);
18081 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18084 transition : false,
18089 slideOnTouch : false,
18092 getAutoCreate : function()
18094 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18096 cfg.cls += ' tab-content';
18098 if (this.carousel) {
18099 cfg.cls += ' carousel slide';
18102 cls : 'carousel-inner',
18106 if(this.bullets && !Roo.isTouch){
18109 cls : 'carousel-bullets',
18113 if(this.bullets_cls){
18114 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18121 cfg.cn[0].cn.push(bullets);
18124 if(this.showarrow){
18125 cfg.cn[0].cn.push({
18127 class : 'carousel-arrow',
18131 class : 'carousel-prev',
18135 class : 'fa fa-chevron-left'
18141 class : 'carousel-next',
18145 class : 'fa fa-chevron-right'
18158 initEvents: function()
18160 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18161 // this.el.on("touchstart", this.onTouchStart, this);
18164 if(this.autoslide){
18167 this.slideFn = window.setInterval(function() {
18168 _this.showPanelNext();
18172 if(this.showarrow){
18173 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18174 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18180 // onTouchStart : function(e, el, o)
18182 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18186 // this.showPanelNext();
18190 getChildContainer : function()
18192 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18196 * register a Navigation item
18197 * @param {Roo.bootstrap.NavItem} the navitem to add
18199 register : function(item)
18201 this.tabs.push( item);
18202 item.navId = this.navId; // not really needed..
18207 getActivePanel : function()
18210 Roo.each(this.tabs, function(t) {
18220 getPanelByName : function(n)
18223 Roo.each(this.tabs, function(t) {
18224 if (t.tabId == n) {
18232 indexOfPanel : function(p)
18235 Roo.each(this.tabs, function(t,i) {
18236 if (t.tabId == p.tabId) {
18245 * show a specific panel
18246 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18247 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18249 showPanel : function (pan)
18251 if(this.transition || typeof(pan) == 'undefined'){
18252 Roo.log("waiting for the transitionend");
18256 if (typeof(pan) == 'number') {
18257 pan = this.tabs[pan];
18260 if (typeof(pan) == 'string') {
18261 pan = this.getPanelByName(pan);
18264 var cur = this.getActivePanel();
18267 Roo.log('pan or acitve pan is undefined');
18271 if (pan.tabId == this.getActivePanel().tabId) {
18275 if (false === cur.fireEvent('beforedeactivate')) {
18279 if(this.bullets > 0 && !Roo.isTouch){
18280 this.setActiveBullet(this.indexOfPanel(pan));
18283 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18285 this.transition = true;
18286 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18287 var lr = dir == 'next' ? 'left' : 'right';
18288 pan.el.addClass(dir); // or prev
18289 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18290 cur.el.addClass(lr); // or right
18291 pan.el.addClass(lr);
18294 cur.el.on('transitionend', function() {
18295 Roo.log("trans end?");
18297 pan.el.removeClass([lr,dir]);
18298 pan.setActive(true);
18300 cur.el.removeClass([lr]);
18301 cur.setActive(false);
18303 _this.transition = false;
18305 }, this, { single: true } );
18310 cur.setActive(false);
18311 pan.setActive(true);
18316 showPanelNext : function()
18318 var i = this.indexOfPanel(this.getActivePanel());
18320 if (i >= this.tabs.length - 1 && !this.autoslide) {
18324 if (i >= this.tabs.length - 1 && this.autoslide) {
18328 this.showPanel(this.tabs[i+1]);
18331 showPanelPrev : function()
18333 var i = this.indexOfPanel(this.getActivePanel());
18335 if (i < 1 && !this.autoslide) {
18339 if (i < 1 && this.autoslide) {
18340 i = this.tabs.length;
18343 this.showPanel(this.tabs[i-1]);
18347 addBullet: function()
18349 if(!this.bullets || Roo.isTouch){
18352 var ctr = this.el.select('.carousel-bullets',true).first();
18353 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18354 var bullet = ctr.createChild({
18355 cls : 'bullet bullet-' + i
18356 },ctr.dom.lastChild);
18361 bullet.on('click', (function(e, el, o, ii, t){
18363 e.preventDefault();
18365 this.showPanel(ii);
18367 if(this.autoslide && this.slideFn){
18368 clearInterval(this.slideFn);
18369 this.slideFn = window.setInterval(function() {
18370 _this.showPanelNext();
18374 }).createDelegate(this, [i, bullet], true));
18379 setActiveBullet : function(i)
18385 Roo.each(this.el.select('.bullet', true).elements, function(el){
18386 el.removeClass('selected');
18389 var bullet = this.el.select('.bullet-' + i, true).first();
18395 bullet.addClass('selected');
18406 Roo.apply(Roo.bootstrap.TabGroup, {
18410 * register a Navigation Group
18411 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18413 register : function(navgrp)
18415 this.groups[navgrp.navId] = navgrp;
18419 * fetch a Navigation Group based on the navigation ID
18420 * if one does not exist , it will get created.
18421 * @param {string} the navgroup to add
18422 * @returns {Roo.bootstrap.NavGroup} the navgroup
18424 get: function(navId) {
18425 if (typeof(this.groups[navId]) == 'undefined') {
18426 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18428 return this.groups[navId] ;
18443 * @class Roo.bootstrap.TabPanel
18444 * @extends Roo.bootstrap.Component
18445 * Bootstrap TabPanel class
18446 * @cfg {Boolean} active panel active
18447 * @cfg {String} html panel content
18448 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18449 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18450 * @cfg {String} href click to link..
18454 * Create a new TabPanel
18455 * @param {Object} config The config object
18458 Roo.bootstrap.TabPanel = function(config){
18459 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18463 * Fires when the active status changes
18464 * @param {Roo.bootstrap.TabPanel} this
18465 * @param {Boolean} state the new state
18470 * @event beforedeactivate
18471 * Fires before a tab is de-activated - can be used to do validation on a form.
18472 * @param {Roo.bootstrap.TabPanel} this
18473 * @return {Boolean} false if there is an error
18476 'beforedeactivate': true
18479 this.tabId = this.tabId || Roo.id();
18483 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18491 getAutoCreate : function(){
18494 // item is needed for carousel - not sure if it has any effect otherwise
18495 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18496 html: this.html || ''
18500 cfg.cls += ' active';
18504 cfg.tabId = this.tabId;
18511 initEvents: function()
18513 var p = this.parent();
18515 this.navId = this.navId || p.navId;
18517 if (typeof(this.navId) != 'undefined') {
18518 // not really needed.. but just in case.. parent should be a NavGroup.
18519 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18523 var i = tg.tabs.length - 1;
18525 if(this.active && tg.bullets > 0 && i < tg.bullets){
18526 tg.setActiveBullet(i);
18530 this.el.on('click', this.onClick, this);
18533 this.el.on("touchstart", this.onTouchStart, this);
18534 this.el.on("touchmove", this.onTouchMove, this);
18535 this.el.on("touchend", this.onTouchEnd, this);
18540 onRender : function(ct, position)
18542 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18545 setActive : function(state)
18547 Roo.log("panel - set active " + this.tabId + "=" + state);
18549 this.active = state;
18551 this.el.removeClass('active');
18553 } else if (!this.el.hasClass('active')) {
18554 this.el.addClass('active');
18557 this.fireEvent('changed', this, state);
18560 onClick : function(e)
18562 e.preventDefault();
18564 if(!this.href.length){
18568 window.location.href = this.href;
18577 onTouchStart : function(e)
18579 this.swiping = false;
18581 this.startX = e.browserEvent.touches[0].clientX;
18582 this.startY = e.browserEvent.touches[0].clientY;
18585 onTouchMove : function(e)
18587 this.swiping = true;
18589 this.endX = e.browserEvent.touches[0].clientX;
18590 this.endY = e.browserEvent.touches[0].clientY;
18593 onTouchEnd : function(e)
18600 var tabGroup = this.parent();
18602 if(this.endX > this.startX){ // swiping right
18603 tabGroup.showPanelPrev();
18607 if(this.startX > this.endX){ // swiping left
18608 tabGroup.showPanelNext();
18627 * @class Roo.bootstrap.DateField
18628 * @extends Roo.bootstrap.Input
18629 * Bootstrap DateField class
18630 * @cfg {Number} weekStart default 0
18631 * @cfg {String} viewMode default empty, (months|years)
18632 * @cfg {String} minViewMode default empty, (months|years)
18633 * @cfg {Number} startDate default -Infinity
18634 * @cfg {Number} endDate default Infinity
18635 * @cfg {Boolean} todayHighlight default false
18636 * @cfg {Boolean} todayBtn default false
18637 * @cfg {Boolean} calendarWeeks default false
18638 * @cfg {Object} daysOfWeekDisabled default empty
18639 * @cfg {Boolean} singleMode default false (true | false)
18641 * @cfg {Boolean} keyboardNavigation default true
18642 * @cfg {String} language default en
18645 * Create a new DateField
18646 * @param {Object} config The config object
18649 Roo.bootstrap.DateField = function(config){
18650 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18654 * Fires when this field show.
18655 * @param {Roo.bootstrap.DateField} this
18656 * @param {Mixed} date The date value
18661 * Fires when this field hide.
18662 * @param {Roo.bootstrap.DateField} this
18663 * @param {Mixed} date The date value
18668 * Fires when select a date.
18669 * @param {Roo.bootstrap.DateField} this
18670 * @param {Mixed} date The date value
18674 * @event beforeselect
18675 * Fires when before select a date.
18676 * @param {Roo.bootstrap.DateField} this
18677 * @param {Mixed} date The date value
18679 beforeselect : true
18683 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18686 * @cfg {String} format
18687 * The default date format string which can be overriden for localization support. The format must be
18688 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18692 * @cfg {String} altFormats
18693 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18694 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18696 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18704 todayHighlight : false,
18710 keyboardNavigation: true,
18712 calendarWeeks: false,
18714 startDate: -Infinity,
18718 daysOfWeekDisabled: [],
18722 singleMode : false,
18724 UTCDate: function()
18726 return new Date(Date.UTC.apply(Date, arguments));
18729 UTCToday: function()
18731 var today = new Date();
18732 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18735 getDate: function() {
18736 var d = this.getUTCDate();
18737 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18740 getUTCDate: function() {
18744 setDate: function(d) {
18745 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18748 setUTCDate: function(d) {
18750 this.setValue(this.formatDate(this.date));
18753 onRender: function(ct, position)
18756 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18758 this.language = this.language || 'en';
18759 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18760 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18762 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18763 this.format = this.format || 'm/d/y';
18764 this.isInline = false;
18765 this.isInput = true;
18766 this.component = this.el.select('.add-on', true).first() || false;
18767 this.component = (this.component && this.component.length === 0) ? false : this.component;
18768 this.hasInput = this.component && this.inputEl().length;
18770 if (typeof(this.minViewMode === 'string')) {
18771 switch (this.minViewMode) {
18773 this.minViewMode = 1;
18776 this.minViewMode = 2;
18779 this.minViewMode = 0;
18784 if (typeof(this.viewMode === 'string')) {
18785 switch (this.viewMode) {
18798 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18800 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18802 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18804 this.picker().on('mousedown', this.onMousedown, this);
18805 this.picker().on('click', this.onClick, this);
18807 this.picker().addClass('datepicker-dropdown');
18809 this.startViewMode = this.viewMode;
18811 if(this.singleMode){
18812 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18813 v.setVisibilityMode(Roo.Element.DISPLAY);
18817 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18818 v.setStyle('width', '189px');
18822 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18823 if(!this.calendarWeeks){
18828 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18829 v.attr('colspan', function(i, val){
18830 return parseInt(val) + 1;
18835 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18837 this.setStartDate(this.startDate);
18838 this.setEndDate(this.endDate);
18840 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18847 if(this.isInline) {
18852 picker : function()
18854 return this.pickerEl;
18855 // return this.el.select('.datepicker', true).first();
18858 fillDow: function()
18860 var dowCnt = this.weekStart;
18869 if(this.calendarWeeks){
18877 while (dowCnt < this.weekStart + 7) {
18881 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18885 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18888 fillMonths: function()
18891 var months = this.picker().select('>.datepicker-months td', true).first();
18893 months.dom.innerHTML = '';
18899 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18902 months.createChild(month);
18909 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;
18911 if (this.date < this.startDate) {
18912 this.viewDate = new Date(this.startDate);
18913 } else if (this.date > this.endDate) {
18914 this.viewDate = new Date(this.endDate);
18916 this.viewDate = new Date(this.date);
18924 var d = new Date(this.viewDate),
18925 year = d.getUTCFullYear(),
18926 month = d.getUTCMonth(),
18927 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18928 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18929 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18930 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18931 currentDate = this.date && this.date.valueOf(),
18932 today = this.UTCToday();
18934 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18936 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18938 // this.picker.select('>tfoot th.today').
18939 // .text(dates[this.language].today)
18940 // .toggle(this.todayBtn !== false);
18942 this.updateNavArrows();
18945 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18947 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18949 prevMonth.setUTCDate(day);
18951 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18953 var nextMonth = new Date(prevMonth);
18955 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18957 nextMonth = nextMonth.valueOf();
18959 var fillMonths = false;
18961 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18963 while(prevMonth.valueOf() <= nextMonth) {
18966 if (prevMonth.getUTCDay() === this.weekStart) {
18968 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18976 if(this.calendarWeeks){
18977 // ISO 8601: First week contains first thursday.
18978 // ISO also states week starts on Monday, but we can be more abstract here.
18980 // Start of current week: based on weekstart/current date
18981 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18982 // Thursday of this week
18983 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18984 // First Thursday of year, year from thursday
18985 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18986 // Calendar week: ms between thursdays, div ms per day, div 7 days
18987 calWeek = (th - yth) / 864e5 / 7 + 1;
18989 fillMonths.cn.push({
18997 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18999 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19002 if (this.todayHighlight &&
19003 prevMonth.getUTCFullYear() == today.getFullYear() &&
19004 prevMonth.getUTCMonth() == today.getMonth() &&
19005 prevMonth.getUTCDate() == today.getDate()) {
19006 clsName += ' today';
19009 if (currentDate && prevMonth.valueOf() === currentDate) {
19010 clsName += ' active';
19013 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19014 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19015 clsName += ' disabled';
19018 fillMonths.cn.push({
19020 cls: 'day ' + clsName,
19021 html: prevMonth.getDate()
19024 prevMonth.setDate(prevMonth.getDate()+1);
19027 var currentYear = this.date && this.date.getUTCFullYear();
19028 var currentMonth = this.date && this.date.getUTCMonth();
19030 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19032 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19033 v.removeClass('active');
19035 if(currentYear === year && k === currentMonth){
19036 v.addClass('active');
19039 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19040 v.addClass('disabled');
19046 year = parseInt(year/10, 10) * 10;
19048 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19050 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19053 for (var i = -1; i < 11; i++) {
19054 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19056 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19064 showMode: function(dir)
19067 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19070 Roo.each(this.picker().select('>div',true).elements, function(v){
19071 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19074 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19079 if(this.isInline) {
19083 this.picker().removeClass(['bottom', 'top']);
19085 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19087 * place to the top of element!
19091 this.picker().addClass('top');
19092 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19097 this.picker().addClass('bottom');
19099 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19102 parseDate : function(value)
19104 if(!value || value instanceof Date){
19107 var v = Date.parseDate(value, this.format);
19108 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19109 v = Date.parseDate(value, 'Y-m-d');
19111 if(!v && this.altFormats){
19112 if(!this.altFormatsArray){
19113 this.altFormatsArray = this.altFormats.split("|");
19115 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19116 v = Date.parseDate(value, this.altFormatsArray[i]);
19122 formatDate : function(date, fmt)
19124 return (!date || !(date instanceof Date)) ?
19125 date : date.dateFormat(fmt || this.format);
19128 onFocus : function()
19130 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19134 onBlur : function()
19136 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19138 var d = this.inputEl().getValue();
19145 showPopup : function()
19147 this.picker().show();
19151 this.fireEvent('showpopup', this, this.date);
19154 hidePopup : function()
19156 if(this.isInline) {
19159 this.picker().hide();
19160 this.viewMode = this.startViewMode;
19163 this.fireEvent('hidepopup', this, this.date);
19167 onMousedown: function(e)
19169 e.stopPropagation();
19170 e.preventDefault();
19175 Roo.bootstrap.DateField.superclass.keyup.call(this);
19179 setValue: function(v)
19181 if(this.fireEvent('beforeselect', this, v) !== false){
19182 var d = new Date(this.parseDate(v) ).clearTime();
19184 if(isNaN(d.getTime())){
19185 this.date = this.viewDate = '';
19186 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19190 v = this.formatDate(d);
19192 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19194 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19198 this.fireEvent('select', this, this.date);
19202 getValue: function()
19204 return this.formatDate(this.date);
19207 fireKey: function(e)
19209 if (!this.picker().isVisible()){
19210 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19216 var dateChanged = false,
19218 newDate, newViewDate;
19223 e.preventDefault();
19227 if (!this.keyboardNavigation) {
19230 dir = e.keyCode == 37 ? -1 : 1;
19233 newDate = this.moveYear(this.date, dir);
19234 newViewDate = this.moveYear(this.viewDate, dir);
19235 } else if (e.shiftKey){
19236 newDate = this.moveMonth(this.date, dir);
19237 newViewDate = this.moveMonth(this.viewDate, dir);
19239 newDate = new Date(this.date);
19240 newDate.setUTCDate(this.date.getUTCDate() + dir);
19241 newViewDate = new Date(this.viewDate);
19242 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19244 if (this.dateWithinRange(newDate)){
19245 this.date = newDate;
19246 this.viewDate = newViewDate;
19247 this.setValue(this.formatDate(this.date));
19249 e.preventDefault();
19250 dateChanged = true;
19255 if (!this.keyboardNavigation) {
19258 dir = e.keyCode == 38 ? -1 : 1;
19260 newDate = this.moveYear(this.date, dir);
19261 newViewDate = this.moveYear(this.viewDate, dir);
19262 } else if (e.shiftKey){
19263 newDate = this.moveMonth(this.date, dir);
19264 newViewDate = this.moveMonth(this.viewDate, dir);
19266 newDate = new Date(this.date);
19267 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19268 newViewDate = new Date(this.viewDate);
19269 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19271 if (this.dateWithinRange(newDate)){
19272 this.date = newDate;
19273 this.viewDate = newViewDate;
19274 this.setValue(this.formatDate(this.date));
19276 e.preventDefault();
19277 dateChanged = true;
19281 this.setValue(this.formatDate(this.date));
19283 e.preventDefault();
19286 this.setValue(this.formatDate(this.date));
19300 onClick: function(e)
19302 e.stopPropagation();
19303 e.preventDefault();
19305 var target = e.getTarget();
19307 if(target.nodeName.toLowerCase() === 'i'){
19308 target = Roo.get(target).dom.parentNode;
19311 var nodeName = target.nodeName;
19312 var className = target.className;
19313 var html = target.innerHTML;
19314 //Roo.log(nodeName);
19316 switch(nodeName.toLowerCase()) {
19318 switch(className) {
19324 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19325 switch(this.viewMode){
19327 this.viewDate = this.moveMonth(this.viewDate, dir);
19331 this.viewDate = this.moveYear(this.viewDate, dir);
19337 var date = new Date();
19338 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19340 this.setValue(this.formatDate(this.date));
19347 if (className.indexOf('disabled') < 0) {
19348 this.viewDate.setUTCDate(1);
19349 if (className.indexOf('month') > -1) {
19350 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19352 var year = parseInt(html, 10) || 0;
19353 this.viewDate.setUTCFullYear(year);
19357 if(this.singleMode){
19358 this.setValue(this.formatDate(this.viewDate));
19369 //Roo.log(className);
19370 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19371 var day = parseInt(html, 10) || 1;
19372 var year = this.viewDate.getUTCFullYear(),
19373 month = this.viewDate.getUTCMonth();
19375 if (className.indexOf('old') > -1) {
19382 } else if (className.indexOf('new') > -1) {
19390 //Roo.log([year,month,day]);
19391 this.date = this.UTCDate(year, month, day,0,0,0,0);
19392 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19394 //Roo.log(this.formatDate(this.date));
19395 this.setValue(this.formatDate(this.date));
19402 setStartDate: function(startDate)
19404 this.startDate = startDate || -Infinity;
19405 if (this.startDate !== -Infinity) {
19406 this.startDate = this.parseDate(this.startDate);
19409 this.updateNavArrows();
19412 setEndDate: function(endDate)
19414 this.endDate = endDate || Infinity;
19415 if (this.endDate !== Infinity) {
19416 this.endDate = this.parseDate(this.endDate);
19419 this.updateNavArrows();
19422 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19424 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19425 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19426 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19428 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19429 return parseInt(d, 10);
19432 this.updateNavArrows();
19435 updateNavArrows: function()
19437 if(this.singleMode){
19441 var d = new Date(this.viewDate),
19442 year = d.getUTCFullYear(),
19443 month = d.getUTCMonth();
19445 Roo.each(this.picker().select('.prev', true).elements, function(v){
19447 switch (this.viewMode) {
19450 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19456 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19463 Roo.each(this.picker().select('.next', true).elements, function(v){
19465 switch (this.viewMode) {
19468 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19474 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19482 moveMonth: function(date, dir)
19487 var new_date = new Date(date.valueOf()),
19488 day = new_date.getUTCDate(),
19489 month = new_date.getUTCMonth(),
19490 mag = Math.abs(dir),
19492 dir = dir > 0 ? 1 : -1;
19495 // If going back one month, make sure month is not current month
19496 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19498 return new_date.getUTCMonth() == month;
19500 // If going forward one month, make sure month is as expected
19501 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19503 return new_date.getUTCMonth() != new_month;
19505 new_month = month + dir;
19506 new_date.setUTCMonth(new_month);
19507 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19508 if (new_month < 0 || new_month > 11) {
19509 new_month = (new_month + 12) % 12;
19512 // For magnitudes >1, move one month at a time...
19513 for (var i=0; i<mag; i++) {
19514 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19515 new_date = this.moveMonth(new_date, dir);
19517 // ...then reset the day, keeping it in the new month
19518 new_month = new_date.getUTCMonth();
19519 new_date.setUTCDate(day);
19521 return new_month != new_date.getUTCMonth();
19524 // Common date-resetting loop -- if date is beyond end of month, make it
19527 new_date.setUTCDate(--day);
19528 new_date.setUTCMonth(new_month);
19533 moveYear: function(date, dir)
19535 return this.moveMonth(date, dir*12);
19538 dateWithinRange: function(date)
19540 return date >= this.startDate && date <= this.endDate;
19546 this.picker().remove();
19549 validateValue : function(value)
19551 if(this.getVisibilityEl().hasClass('hidden')){
19555 if(value.length < 1) {
19556 if(this.allowBlank){
19562 if(value.length < this.minLength){
19565 if(value.length > this.maxLength){
19569 var vt = Roo.form.VTypes;
19570 if(!vt[this.vtype](value, this)){
19574 if(typeof this.validator == "function"){
19575 var msg = this.validator(value);
19581 if(this.regex && !this.regex.test(value)){
19585 if(typeof(this.parseDate(value)) == 'undefined'){
19589 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19593 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19603 this.date = this.viewDate = '';
19605 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19610 Roo.apply(Roo.bootstrap.DateField, {
19621 html: '<i class="fa fa-arrow-left"/>'
19631 html: '<i class="fa fa-arrow-right"/>'
19673 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19674 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19675 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19676 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19677 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19690 navFnc: 'FullYear',
19695 navFnc: 'FullYear',
19700 Roo.apply(Roo.bootstrap.DateField, {
19704 cls: 'datepicker dropdown-menu roo-dynamic',
19708 cls: 'datepicker-days',
19712 cls: 'table-condensed',
19714 Roo.bootstrap.DateField.head,
19718 Roo.bootstrap.DateField.footer
19725 cls: 'datepicker-months',
19729 cls: 'table-condensed',
19731 Roo.bootstrap.DateField.head,
19732 Roo.bootstrap.DateField.content,
19733 Roo.bootstrap.DateField.footer
19740 cls: 'datepicker-years',
19744 cls: 'table-condensed',
19746 Roo.bootstrap.DateField.head,
19747 Roo.bootstrap.DateField.content,
19748 Roo.bootstrap.DateField.footer
19767 * @class Roo.bootstrap.TimeField
19768 * @extends Roo.bootstrap.Input
19769 * Bootstrap DateField class
19773 * Create a new TimeField
19774 * @param {Object} config The config object
19777 Roo.bootstrap.TimeField = function(config){
19778 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19782 * Fires when this field show.
19783 * @param {Roo.bootstrap.DateField} thisthis
19784 * @param {Mixed} date The date value
19789 * Fires when this field hide.
19790 * @param {Roo.bootstrap.DateField} this
19791 * @param {Mixed} date The date value
19796 * Fires when select a date.
19797 * @param {Roo.bootstrap.DateField} this
19798 * @param {Mixed} date The date value
19804 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19807 * @cfg {String} format
19808 * The default time format string which can be overriden for localization support. The format must be
19809 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19813 onRender: function(ct, position)
19816 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19818 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19820 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19822 this.pop = this.picker().select('>.datepicker-time',true).first();
19823 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19825 this.picker().on('mousedown', this.onMousedown, this);
19826 this.picker().on('click', this.onClick, this);
19828 this.picker().addClass('datepicker-dropdown');
19833 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19834 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19835 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19836 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19837 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19838 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19842 fireKey: function(e){
19843 if (!this.picker().isVisible()){
19844 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19850 e.preventDefault();
19858 this.onTogglePeriod();
19861 this.onIncrementMinutes();
19864 this.onDecrementMinutes();
19873 onClick: function(e) {
19874 e.stopPropagation();
19875 e.preventDefault();
19878 picker : function()
19880 return this.el.select('.datepicker', true).first();
19883 fillTime: function()
19885 var time = this.pop.select('tbody', true).first();
19887 time.dom.innerHTML = '';
19902 cls: 'hours-up glyphicon glyphicon-chevron-up'
19922 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19943 cls: 'timepicker-hour',
19958 cls: 'timepicker-minute',
19973 cls: 'btn btn-primary period',
19995 cls: 'hours-down glyphicon glyphicon-chevron-down'
20015 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20033 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20040 var hours = this.time.getHours();
20041 var minutes = this.time.getMinutes();
20054 hours = hours - 12;
20058 hours = '0' + hours;
20062 minutes = '0' + minutes;
20065 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20066 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20067 this.pop.select('button', true).first().dom.innerHTML = period;
20073 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20075 var cls = ['bottom'];
20077 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20084 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20089 this.picker().addClass(cls.join('-'));
20093 Roo.each(cls, function(c){
20095 _this.picker().setTop(_this.inputEl().getHeight());
20099 _this.picker().setTop(0 - _this.picker().getHeight());
20104 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20108 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20115 onFocus : function()
20117 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20121 onBlur : function()
20123 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20129 this.picker().show();
20134 this.fireEvent('show', this, this.date);
20139 this.picker().hide();
20142 this.fireEvent('hide', this, this.date);
20145 setTime : function()
20148 this.setValue(this.time.format(this.format));
20150 this.fireEvent('select', this, this.date);
20155 onMousedown: function(e){
20156 e.stopPropagation();
20157 e.preventDefault();
20160 onIncrementHours: function()
20162 Roo.log('onIncrementHours');
20163 this.time = this.time.add(Date.HOUR, 1);
20168 onDecrementHours: function()
20170 Roo.log('onDecrementHours');
20171 this.time = this.time.add(Date.HOUR, -1);
20175 onIncrementMinutes: function()
20177 Roo.log('onIncrementMinutes');
20178 this.time = this.time.add(Date.MINUTE, 1);
20182 onDecrementMinutes: function()
20184 Roo.log('onDecrementMinutes');
20185 this.time = this.time.add(Date.MINUTE, -1);
20189 onTogglePeriod: function()
20191 Roo.log('onTogglePeriod');
20192 this.time = this.time.add(Date.HOUR, 12);
20199 Roo.apply(Roo.bootstrap.TimeField, {
20229 cls: 'btn btn-info ok',
20241 Roo.apply(Roo.bootstrap.TimeField, {
20245 cls: 'datepicker dropdown-menu',
20249 cls: 'datepicker-time',
20253 cls: 'table-condensed',
20255 Roo.bootstrap.TimeField.content,
20256 Roo.bootstrap.TimeField.footer
20275 * @class Roo.bootstrap.MonthField
20276 * @extends Roo.bootstrap.Input
20277 * Bootstrap MonthField class
20279 * @cfg {String} language default en
20282 * Create a new MonthField
20283 * @param {Object} config The config object
20286 Roo.bootstrap.MonthField = function(config){
20287 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20292 * Fires when this field show.
20293 * @param {Roo.bootstrap.MonthField} this
20294 * @param {Mixed} date The date value
20299 * Fires when this field hide.
20300 * @param {Roo.bootstrap.MonthField} this
20301 * @param {Mixed} date The date value
20306 * Fires when select a date.
20307 * @param {Roo.bootstrap.MonthField} this
20308 * @param {String} oldvalue The old value
20309 * @param {String} newvalue The new value
20315 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20317 onRender: function(ct, position)
20320 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20322 this.language = this.language || 'en';
20323 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20324 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20326 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20327 this.isInline = false;
20328 this.isInput = true;
20329 this.component = this.el.select('.add-on', true).first() || false;
20330 this.component = (this.component && this.component.length === 0) ? false : this.component;
20331 this.hasInput = this.component && this.inputEL().length;
20333 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20335 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20337 this.picker().on('mousedown', this.onMousedown, this);
20338 this.picker().on('click', this.onClick, this);
20340 this.picker().addClass('datepicker-dropdown');
20342 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20343 v.setStyle('width', '189px');
20350 if(this.isInline) {
20356 setValue: function(v, suppressEvent)
20358 var o = this.getValue();
20360 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20364 if(suppressEvent !== true){
20365 this.fireEvent('select', this, o, v);
20370 getValue: function()
20375 onClick: function(e)
20377 e.stopPropagation();
20378 e.preventDefault();
20380 var target = e.getTarget();
20382 if(target.nodeName.toLowerCase() === 'i'){
20383 target = Roo.get(target).dom.parentNode;
20386 var nodeName = target.nodeName;
20387 var className = target.className;
20388 var html = target.innerHTML;
20390 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20394 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20396 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20402 picker : function()
20404 return this.pickerEl;
20407 fillMonths: function()
20410 var months = this.picker().select('>.datepicker-months td', true).first();
20412 months.dom.innerHTML = '';
20418 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20421 months.createChild(month);
20430 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20431 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20434 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20435 e.removeClass('active');
20437 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20438 e.addClass('active');
20445 if(this.isInline) {
20449 this.picker().removeClass(['bottom', 'top']);
20451 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20453 * place to the top of element!
20457 this.picker().addClass('top');
20458 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20463 this.picker().addClass('bottom');
20465 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20468 onFocus : function()
20470 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20474 onBlur : function()
20476 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20478 var d = this.inputEl().getValue();
20487 this.picker().show();
20488 this.picker().select('>.datepicker-months', true).first().show();
20492 this.fireEvent('show', this, this.date);
20497 if(this.isInline) {
20500 this.picker().hide();
20501 this.fireEvent('hide', this, this.date);
20505 onMousedown: function(e)
20507 e.stopPropagation();
20508 e.preventDefault();
20513 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20517 fireKey: function(e)
20519 if (!this.picker().isVisible()){
20520 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20531 e.preventDefault();
20535 dir = e.keyCode == 37 ? -1 : 1;
20537 this.vIndex = this.vIndex + dir;
20539 if(this.vIndex < 0){
20543 if(this.vIndex > 11){
20547 if(isNaN(this.vIndex)){
20551 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20557 dir = e.keyCode == 38 ? -1 : 1;
20559 this.vIndex = this.vIndex + dir * 4;
20561 if(this.vIndex < 0){
20565 if(this.vIndex > 11){
20569 if(isNaN(this.vIndex)){
20573 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20578 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20579 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20583 e.preventDefault();
20586 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20587 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20603 this.picker().remove();
20608 Roo.apply(Roo.bootstrap.MonthField, {
20627 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20628 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20633 Roo.apply(Roo.bootstrap.MonthField, {
20637 cls: 'datepicker dropdown-menu roo-dynamic',
20641 cls: 'datepicker-months',
20645 cls: 'table-condensed',
20647 Roo.bootstrap.DateField.content
20667 * @class Roo.bootstrap.CheckBox
20668 * @extends Roo.bootstrap.Input
20669 * Bootstrap CheckBox class
20671 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20672 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20673 * @cfg {String} boxLabel The text that appears beside the checkbox
20674 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20675 * @cfg {Boolean} checked initnal the element
20676 * @cfg {Boolean} inline inline the element (default false)
20677 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20678 * @cfg {String} tooltip label tooltip
20681 * Create a new CheckBox
20682 * @param {Object} config The config object
20685 Roo.bootstrap.CheckBox = function(config){
20686 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20691 * Fires when the element is checked or unchecked.
20692 * @param {Roo.bootstrap.CheckBox} this This input
20693 * @param {Boolean} checked The new checked value
20698 * Fires when the element is click.
20699 * @param {Roo.bootstrap.CheckBox} this This input
20706 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20708 inputType: 'checkbox',
20717 getAutoCreate : function()
20719 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20725 cfg.cls = 'form-group ' + this.inputType; //input-group
20728 cfg.cls += ' ' + this.inputType + '-inline';
20734 type : this.inputType,
20735 value : this.inputValue,
20736 cls : 'roo-' + this.inputType, //'form-box',
20737 placeholder : this.placeholder || ''
20741 if(this.inputType != 'radio'){
20745 cls : 'roo-hidden-value',
20746 value : this.checked ? this.inputValue : this.valueOff
20751 if (this.weight) { // Validity check?
20752 cfg.cls += " " + this.inputType + "-" + this.weight;
20755 if (this.disabled) {
20756 input.disabled=true;
20760 input.checked = this.checked;
20765 input.name = this.name;
20767 if(this.inputType != 'radio'){
20768 hidden.name = this.name;
20769 input.name = '_hidden_' + this.name;
20774 input.cls += ' input-' + this.size;
20779 ['xs','sm','md','lg'].map(function(size){
20780 if (settings[size]) {
20781 cfg.cls += ' col-' + size + '-' + settings[size];
20785 var inputblock = input;
20787 if (this.before || this.after) {
20790 cls : 'input-group',
20795 inputblock.cn.push({
20797 cls : 'input-group-addon',
20802 inputblock.cn.push(input);
20804 if(this.inputType != 'radio'){
20805 inputblock.cn.push(hidden);
20809 inputblock.cn.push({
20811 cls : 'input-group-addon',
20818 if (align ==='left' && this.fieldLabel.length) {
20819 // Roo.log("left and has label");
20824 cls : 'control-label',
20825 html : this.fieldLabel
20835 if(this.labelWidth > 12){
20836 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20839 if(this.labelWidth < 13 && this.labelmd == 0){
20840 this.labelmd = this.labelWidth;
20843 if(this.labellg > 0){
20844 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20845 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20848 if(this.labelmd > 0){
20849 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20850 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20853 if(this.labelsm > 0){
20854 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20855 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20858 if(this.labelxs > 0){
20859 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20860 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20863 } else if ( this.fieldLabel.length) {
20864 // Roo.log(" label");
20868 tag: this.boxLabel ? 'span' : 'label',
20870 cls: 'control-label box-input-label',
20871 //cls : 'input-group-addon',
20872 html : this.fieldLabel
20881 // Roo.log(" no label && no align");
20882 cfg.cn = [ inputblock ] ;
20888 var boxLabelCfg = {
20890 //'for': id, // box label is handled by onclick - so no for...
20892 html: this.boxLabel
20896 boxLabelCfg.tooltip = this.tooltip;
20899 cfg.cn.push(boxLabelCfg);
20902 if(this.inputType != 'radio'){
20903 cfg.cn.push(hidden);
20911 * return the real input element.
20913 inputEl: function ()
20915 return this.el.select('input.roo-' + this.inputType,true).first();
20917 hiddenEl: function ()
20919 return this.el.select('input.roo-hidden-value',true).first();
20922 labelEl: function()
20924 return this.el.select('label.control-label',true).first();
20926 /* depricated... */
20930 return this.labelEl();
20933 boxLabelEl: function()
20935 return this.el.select('label.box-label',true).first();
20938 initEvents : function()
20940 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20942 this.inputEl().on('click', this.onClick, this);
20944 if (this.boxLabel) {
20945 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20948 this.startValue = this.getValue();
20951 Roo.bootstrap.CheckBox.register(this);
20955 onClick : function(e)
20957 if(this.fireEvent('click', this, e) !== false){
20958 this.setChecked(!this.checked);
20963 setChecked : function(state,suppressEvent)
20965 this.startValue = this.getValue();
20967 if(this.inputType == 'radio'){
20969 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970 e.dom.checked = false;
20973 this.inputEl().dom.checked = true;
20975 this.inputEl().dom.value = this.inputValue;
20977 if(suppressEvent !== true){
20978 this.fireEvent('check', this, true);
20986 this.checked = state;
20988 this.inputEl().dom.checked = state;
20991 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20993 if(suppressEvent !== true){
20994 this.fireEvent('check', this, state);
21000 getValue : function()
21002 if(this.inputType == 'radio'){
21003 return this.getGroupValue();
21006 return this.hiddenEl().dom.value;
21010 getGroupValue : function()
21012 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21016 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21019 setValue : function(v,suppressEvent)
21021 if(this.inputType == 'radio'){
21022 this.setGroupValue(v, suppressEvent);
21026 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21031 setGroupValue : function(v, suppressEvent)
21033 this.startValue = this.getValue();
21035 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21036 e.dom.checked = false;
21038 if(e.dom.value == v){
21039 e.dom.checked = true;
21043 if(suppressEvent !== true){
21044 this.fireEvent('check', this, true);
21052 validate : function()
21054 if(this.getVisibilityEl().hasClass('hidden')){
21060 (this.inputType == 'radio' && this.validateRadio()) ||
21061 (this.inputType == 'checkbox' && this.validateCheckbox())
21067 this.markInvalid();
21071 validateRadio : function()
21073 if(this.getVisibilityEl().hasClass('hidden')){
21077 if(this.allowBlank){
21083 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21084 if(!e.dom.checked){
21096 validateCheckbox : function()
21099 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21100 //return (this.getValue() == this.inputValue) ? true : false;
21103 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21111 for(var i in group){
21112 if(group[i].el.isVisible(true)){
21120 for(var i in group){
21125 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21132 * Mark this field as valid
21134 markValid : function()
21138 this.fireEvent('valid', this);
21140 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21143 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21150 if(this.inputType == 'radio'){
21151 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21152 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21153 e.findParent('.form-group', false, true).addClass(_this.validClass);
21160 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21161 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21165 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21171 for(var i in group){
21172 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21173 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21178 * Mark this field as invalid
21179 * @param {String} msg The validation message
21181 markInvalid : function(msg)
21183 if(this.allowBlank){
21189 this.fireEvent('invalid', this, msg);
21191 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21194 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21198 label.markInvalid();
21201 if(this.inputType == 'radio'){
21202 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21203 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21204 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21211 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21212 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21216 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21222 for(var i in group){
21223 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21224 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21229 clearInvalid : function()
21231 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21233 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21235 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21237 if (label && label.iconEl) {
21238 label.iconEl.removeClass(label.validClass);
21239 label.iconEl.removeClass(label.invalidClass);
21243 disable : function()
21245 if(this.inputType != 'radio'){
21246 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21253 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21254 _this.getActionEl().addClass(this.disabledClass);
21255 e.dom.disabled = true;
21259 this.disabled = true;
21260 this.fireEvent("disable", this);
21264 enable : function()
21266 if(this.inputType != 'radio'){
21267 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21274 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21275 _this.getActionEl().removeClass(this.disabledClass);
21276 e.dom.disabled = false;
21280 this.disabled = false;
21281 this.fireEvent("enable", this);
21285 setBoxLabel : function(v)
21290 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21296 Roo.apply(Roo.bootstrap.CheckBox, {
21301 * register a CheckBox Group
21302 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21304 register : function(checkbox)
21306 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21307 this.groups[checkbox.groupId] = {};
21310 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21314 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21318 * fetch a CheckBox Group based on the group ID
21319 * @param {string} the group ID
21320 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21322 get: function(groupId) {
21323 if (typeof(this.groups[groupId]) == 'undefined') {
21327 return this.groups[groupId] ;
21340 * @class Roo.bootstrap.Radio
21341 * @extends Roo.bootstrap.Component
21342 * Bootstrap Radio class
21343 * @cfg {String} boxLabel - the label associated
21344 * @cfg {String} value - the value of radio
21347 * Create a new Radio
21348 * @param {Object} config The config object
21350 Roo.bootstrap.Radio = function(config){
21351 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21355 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21361 getAutoCreate : function()
21365 cls : 'form-group radio',
21370 html : this.boxLabel
21378 initEvents : function()
21380 this.parent().register(this);
21382 this.el.on('click', this.onClick, this);
21386 onClick : function(e)
21388 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21389 this.setChecked(true);
21393 setChecked : function(state, suppressEvent)
21395 this.parent().setValue(this.value, suppressEvent);
21399 setBoxLabel : function(v)
21404 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21419 * @class Roo.bootstrap.SecurePass
21420 * @extends Roo.bootstrap.Input
21421 * Bootstrap SecurePass class
21425 * Create a new SecurePass
21426 * @param {Object} config The config object
21429 Roo.bootstrap.SecurePass = function (config) {
21430 // these go here, so the translation tool can replace them..
21432 PwdEmpty: "Please type a password, and then retype it to confirm.",
21433 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21434 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21435 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21436 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21437 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21438 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21439 TooWeak: "Your password is Too Weak."
21441 this.meterLabel = "Password strength:";
21442 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21443 this.meterClass = [
21444 "roo-password-meter-tooweak",
21445 "roo-password-meter-weak",
21446 "roo-password-meter-medium",
21447 "roo-password-meter-strong",
21448 "roo-password-meter-grey"
21453 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21456 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21458 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21460 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21461 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21462 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21463 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21464 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21465 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21466 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21476 * @cfg {String/Object} Label for the strength meter (defaults to
21477 * 'Password strength:')
21482 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21483 * ['Weak', 'Medium', 'Strong'])
21486 pwdStrengths: false,
21499 initEvents: function ()
21501 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21503 if (this.el.is('input[type=password]') && Roo.isSafari) {
21504 this.el.on('keydown', this.SafariOnKeyDown, this);
21507 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21510 onRender: function (ct, position)
21512 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21513 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21514 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21516 this.trigger.createChild({
21521 cls: 'roo-password-meter-grey col-xs-12',
21524 //width: this.meterWidth + 'px'
21528 cls: 'roo-password-meter-text'
21534 if (this.hideTrigger) {
21535 this.trigger.setDisplayed(false);
21537 this.setSize(this.width || '', this.height || '');
21540 onDestroy: function ()
21542 if (this.trigger) {
21543 this.trigger.removeAllListeners();
21544 this.trigger.remove();
21547 this.wrap.remove();
21549 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21552 checkStrength: function ()
21554 var pwd = this.inputEl().getValue();
21555 if (pwd == this._lastPwd) {
21560 if (this.ClientSideStrongPassword(pwd)) {
21562 } else if (this.ClientSideMediumPassword(pwd)) {
21564 } else if (this.ClientSideWeakPassword(pwd)) {
21570 Roo.log('strength1: ' + strength);
21572 //var pm = this.trigger.child('div/div/div').dom;
21573 var pm = this.trigger.child('div/div');
21574 pm.removeClass(this.meterClass);
21575 pm.addClass(this.meterClass[strength]);
21578 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21580 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21582 this._lastPwd = pwd;
21586 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21588 this._lastPwd = '';
21590 var pm = this.trigger.child('div/div');
21591 pm.removeClass(this.meterClass);
21592 pm.addClass('roo-password-meter-grey');
21595 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21598 this.inputEl().dom.type='password';
21601 validateValue: function (value)
21604 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21607 if (value.length == 0) {
21608 if (this.allowBlank) {
21609 this.clearInvalid();
21613 this.markInvalid(this.errors.PwdEmpty);
21614 this.errorMsg = this.errors.PwdEmpty;
21622 if ('[\x21-\x7e]*'.match(value)) {
21623 this.markInvalid(this.errors.PwdBadChar);
21624 this.errorMsg = this.errors.PwdBadChar;
21627 if (value.length < 6) {
21628 this.markInvalid(this.errors.PwdShort);
21629 this.errorMsg = this.errors.PwdShort;
21632 if (value.length > 16) {
21633 this.markInvalid(this.errors.PwdLong);
21634 this.errorMsg = this.errors.PwdLong;
21638 if (this.ClientSideStrongPassword(value)) {
21640 } else if (this.ClientSideMediumPassword(value)) {
21642 } else if (this.ClientSideWeakPassword(value)) {
21649 if (strength < 2) {
21650 //this.markInvalid(this.errors.TooWeak);
21651 this.errorMsg = this.errors.TooWeak;
21656 console.log('strength2: ' + strength);
21658 //var pm = this.trigger.child('div/div/div').dom;
21660 var pm = this.trigger.child('div/div');
21661 pm.removeClass(this.meterClass);
21662 pm.addClass(this.meterClass[strength]);
21664 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21666 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21668 this.errorMsg = '';
21672 CharacterSetChecks: function (type)
21675 this.fResult = false;
21678 isctype: function (character, type)
21681 case this.kCapitalLetter:
21682 if (character >= 'A' && character <= 'Z') {
21687 case this.kSmallLetter:
21688 if (character >= 'a' && character <= 'z') {
21694 if (character >= '0' && character <= '9') {
21699 case this.kPunctuation:
21700 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21711 IsLongEnough: function (pwd, size)
21713 return !(pwd == null || isNaN(size) || pwd.length < size);
21716 SpansEnoughCharacterSets: function (word, nb)
21718 if (!this.IsLongEnough(word, nb))
21723 var characterSetChecks = new Array(
21724 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21725 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21728 for (var index = 0; index < word.length; ++index) {
21729 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21730 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21731 characterSetChecks[nCharSet].fResult = true;
21738 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21739 if (characterSetChecks[nCharSet].fResult) {
21744 if (nCharSets < nb) {
21750 ClientSideStrongPassword: function (pwd)
21752 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21755 ClientSideMediumPassword: function (pwd)
21757 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21760 ClientSideWeakPassword: function (pwd)
21762 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21765 })//<script type="text/javascript">
21768 * Based Ext JS Library 1.1.1
21769 * Copyright(c) 2006-2007, Ext JS, LLC.
21775 * @class Roo.HtmlEditorCore
21776 * @extends Roo.Component
21777 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21779 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21782 Roo.HtmlEditorCore = function(config){
21785 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21790 * @event initialize
21791 * Fires when the editor is fully initialized (including the iframe)
21792 * @param {Roo.HtmlEditorCore} this
21797 * Fires when the editor is first receives the focus. Any insertion must wait
21798 * until after this event.
21799 * @param {Roo.HtmlEditorCore} this
21803 * @event beforesync
21804 * Fires before the textarea is updated with content from the editor iframe. Return false
21805 * to cancel the sync.
21806 * @param {Roo.HtmlEditorCore} this
21807 * @param {String} html
21811 * @event beforepush
21812 * Fires before the iframe editor is updated with content from the textarea. Return false
21813 * to cancel the push.
21814 * @param {Roo.HtmlEditorCore} this
21815 * @param {String} html
21820 * Fires when the textarea is updated with content from the editor iframe.
21821 * @param {Roo.HtmlEditorCore} this
21822 * @param {String} html
21827 * Fires when the iframe editor is updated with content from the textarea.
21828 * @param {Roo.HtmlEditorCore} this
21829 * @param {String} html
21834 * @event editorevent
21835 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21836 * @param {Roo.HtmlEditorCore} this
21842 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21844 // defaults : white / black...
21845 this.applyBlacklists();
21852 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21856 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21862 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21867 * @cfg {Number} height (in pixels)
21871 * @cfg {Number} width (in pixels)
21876 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21879 stylesheets: false,
21884 // private properties
21885 validationEvent : false,
21887 initialized : false,
21889 sourceEditMode : false,
21890 onFocus : Roo.emptyFn,
21892 hideMode:'offsets',
21896 // blacklist + whitelisted elements..
21903 * Protected method that will not generally be called directly. It
21904 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21905 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21907 getDocMarkup : function(){
21911 // inherit styels from page...??
21912 if (this.stylesheets === false) {
21914 Roo.get(document.head).select('style').each(function(node) {
21915 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21918 Roo.get(document.head).select('link').each(function(node) {
21919 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21922 } else if (!this.stylesheets.length) {
21924 st = '<style type="text/css">' +
21925 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21928 st = '<style type="text/css">' +
21933 st += '<style type="text/css">' +
21934 'IMG { cursor: pointer } ' +
21937 var cls = 'roo-htmleditor-body';
21939 if(this.bodyCls.length){
21940 cls += ' ' + this.bodyCls;
21943 return '<html><head>' + st +
21944 //<style type="text/css">' +
21945 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21947 ' </head><body class="' + cls + '"></body></html>';
21951 onRender : function(ct, position)
21954 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21955 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21958 this.el.dom.style.border = '0 none';
21959 this.el.dom.setAttribute('tabIndex', -1);
21960 this.el.addClass('x-hidden hide');
21964 if(Roo.isIE){ // fix IE 1px bogus margin
21965 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21969 this.frameId = Roo.id();
21973 var iframe = this.owner.wrap.createChild({
21975 cls: 'form-control', // bootstrap..
21977 name: this.frameId,
21978 frameBorder : 'no',
21979 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21984 this.iframe = iframe.dom;
21986 this.assignDocWin();
21988 this.doc.designMode = 'on';
21991 this.doc.write(this.getDocMarkup());
21995 var task = { // must defer to wait for browser to be ready
21997 //console.log("run task?" + this.doc.readyState);
21998 this.assignDocWin();
21999 if(this.doc.body || this.doc.readyState == 'complete'){
22001 this.doc.designMode="on";
22005 Roo.TaskMgr.stop(task);
22006 this.initEditor.defer(10, this);
22013 Roo.TaskMgr.start(task);
22018 onResize : function(w, h)
22020 Roo.log('resize: ' +w + ',' + h );
22021 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22025 if(typeof w == 'number'){
22027 this.iframe.style.width = w + 'px';
22029 if(typeof h == 'number'){
22031 this.iframe.style.height = h + 'px';
22033 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22040 * Toggles the editor between standard and source edit mode.
22041 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22043 toggleSourceEdit : function(sourceEditMode){
22045 this.sourceEditMode = sourceEditMode === true;
22047 if(this.sourceEditMode){
22049 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22052 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22053 //this.iframe.className = '';
22056 //this.setSize(this.owner.wrap.getSize());
22057 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22064 * Protected method that will not generally be called directly. If you need/want
22065 * custom HTML cleanup, this is the method you should override.
22066 * @param {String} html The HTML to be cleaned
22067 * return {String} The cleaned HTML
22069 cleanHtml : function(html){
22070 html = String(html);
22071 if(html.length > 5){
22072 if(Roo.isSafari){ // strip safari nonsense
22073 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22076 if(html == ' '){
22083 * HTML Editor -> Textarea
22084 * Protected method that will not generally be called directly. Syncs the contents
22085 * of the editor iframe with the textarea.
22087 syncValue : function(){
22088 if(this.initialized){
22089 var bd = (this.doc.body || this.doc.documentElement);
22090 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22091 var html = bd.innerHTML;
22093 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22094 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22096 html = '<div style="'+m[0]+'">' + html + '</div>';
22099 html = this.cleanHtml(html);
22100 // fix up the special chars.. normaly like back quotes in word...
22101 // however we do not want to do this with chinese..
22102 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22103 var cc = b.charCodeAt();
22105 (cc >= 0x4E00 && cc < 0xA000 ) ||
22106 (cc >= 0x3400 && cc < 0x4E00 ) ||
22107 (cc >= 0xf900 && cc < 0xfb00 )
22113 if(this.owner.fireEvent('beforesync', this, html) !== false){
22114 this.el.dom.value = html;
22115 this.owner.fireEvent('sync', this, html);
22121 * Protected method that will not generally be called directly. Pushes the value of the textarea
22122 * into the iframe editor.
22124 pushValue : function(){
22125 if(this.initialized){
22126 var v = this.el.dom.value.trim();
22128 // if(v.length < 1){
22132 if(this.owner.fireEvent('beforepush', this, v) !== false){
22133 var d = (this.doc.body || this.doc.documentElement);
22135 this.cleanUpPaste();
22136 this.el.dom.value = d.innerHTML;
22137 this.owner.fireEvent('push', this, v);
22143 deferFocus : function(){
22144 this.focus.defer(10, this);
22148 focus : function(){
22149 if(this.win && !this.sourceEditMode){
22156 assignDocWin: function()
22158 var iframe = this.iframe;
22161 this.doc = iframe.contentWindow.document;
22162 this.win = iframe.contentWindow;
22164 // if (!Roo.get(this.frameId)) {
22167 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22168 // this.win = Roo.get(this.frameId).dom.contentWindow;
22170 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22174 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22175 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22180 initEditor : function(){
22181 //console.log("INIT EDITOR");
22182 this.assignDocWin();
22186 this.doc.designMode="on";
22188 this.doc.write(this.getDocMarkup());
22191 var dbody = (this.doc.body || this.doc.documentElement);
22192 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22193 // this copies styles from the containing element into thsi one..
22194 // not sure why we need all of this..
22195 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22197 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22198 //ss['background-attachment'] = 'fixed'; // w3c
22199 dbody.bgProperties = 'fixed'; // ie
22200 //Roo.DomHelper.applyStyles(dbody, ss);
22201 Roo.EventManager.on(this.doc, {
22202 //'mousedown': this.onEditorEvent,
22203 'mouseup': this.onEditorEvent,
22204 'dblclick': this.onEditorEvent,
22205 'click': this.onEditorEvent,
22206 'keyup': this.onEditorEvent,
22211 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22213 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22214 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22216 this.initialized = true;
22218 this.owner.fireEvent('initialize', this);
22223 onDestroy : function(){
22229 //for (var i =0; i < this.toolbars.length;i++) {
22230 // // fixme - ask toolbars for heights?
22231 // this.toolbars[i].onDestroy();
22234 //this.wrap.dom.innerHTML = '';
22235 //this.wrap.remove();
22240 onFirstFocus : function(){
22242 this.assignDocWin();
22245 this.activated = true;
22248 if(Roo.isGecko){ // prevent silly gecko errors
22250 var s = this.win.getSelection();
22251 if(!s.focusNode || s.focusNode.nodeType != 3){
22252 var r = s.getRangeAt(0);
22253 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22258 this.execCmd('useCSS', true);
22259 this.execCmd('styleWithCSS', false);
22262 this.owner.fireEvent('activate', this);
22266 adjustFont: function(btn){
22267 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22268 //if(Roo.isSafari){ // safari
22271 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22272 if(Roo.isSafari){ // safari
22273 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22274 v = (v < 10) ? 10 : v;
22275 v = (v > 48) ? 48 : v;
22276 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22281 v = Math.max(1, v+adjust);
22283 this.execCmd('FontSize', v );
22286 onEditorEvent : function(e)
22288 this.owner.fireEvent('editorevent', this, e);
22289 // this.updateToolbar();
22290 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22293 insertTag : function(tg)
22295 // could be a bit smarter... -> wrap the current selected tRoo..
22296 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22298 range = this.createRange(this.getSelection());
22299 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22300 wrappingNode.appendChild(range.extractContents());
22301 range.insertNode(wrappingNode);
22308 this.execCmd("formatblock", tg);
22312 insertText : function(txt)
22316 var range = this.createRange();
22317 range.deleteContents();
22318 //alert(Sender.getAttribute('label'));
22320 range.insertNode(this.doc.createTextNode(txt));
22326 * Executes a Midas editor command on the editor document and performs necessary focus and
22327 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22328 * @param {String} cmd The Midas command
22329 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22331 relayCmd : function(cmd, value){
22333 this.execCmd(cmd, value);
22334 this.owner.fireEvent('editorevent', this);
22335 //this.updateToolbar();
22336 this.owner.deferFocus();
22340 * Executes a Midas editor command directly on the editor document.
22341 * For visual commands, you should use {@link #relayCmd} instead.
22342 * <b>This should only be called after the editor is initialized.</b>
22343 * @param {String} cmd The Midas command
22344 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22346 execCmd : function(cmd, value){
22347 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22354 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22356 * @param {String} text | dom node..
22358 insertAtCursor : function(text)
22361 if(!this.activated){
22367 var r = this.doc.selection.createRange();
22378 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22382 // from jquery ui (MIT licenced)
22384 var win = this.win;
22386 if (win.getSelection && win.getSelection().getRangeAt) {
22387 range = win.getSelection().getRangeAt(0);
22388 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22389 range.insertNode(node);
22390 } else if (win.document.selection && win.document.selection.createRange) {
22391 // no firefox support
22392 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22393 win.document.selection.createRange().pasteHTML(txt);
22395 // no firefox support
22396 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22397 this.execCmd('InsertHTML', txt);
22406 mozKeyPress : function(e){
22408 var c = e.getCharCode(), cmd;
22411 c = String.fromCharCode(c).toLowerCase();
22425 this.cleanUpPaste.defer(100, this);
22433 e.preventDefault();
22441 fixKeys : function(){ // load time branching for fastest keydown performance
22443 return function(e){
22444 var k = e.getKey(), r;
22447 r = this.doc.selection.createRange();
22450 r.pasteHTML('    ');
22457 r = this.doc.selection.createRange();
22459 var target = r.parentElement();
22460 if(!target || target.tagName.toLowerCase() != 'li'){
22462 r.pasteHTML('<br />');
22468 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22469 this.cleanUpPaste.defer(100, this);
22475 }else if(Roo.isOpera){
22476 return function(e){
22477 var k = e.getKey();
22481 this.execCmd('InsertHTML','    ');
22484 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22485 this.cleanUpPaste.defer(100, this);
22490 }else if(Roo.isSafari){
22491 return function(e){
22492 var k = e.getKey();
22496 this.execCmd('InsertText','\t');
22500 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22501 this.cleanUpPaste.defer(100, this);
22509 getAllAncestors: function()
22511 var p = this.getSelectedNode();
22514 a.push(p); // push blank onto stack..
22515 p = this.getParentElement();
22519 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22523 a.push(this.doc.body);
22527 lastSelNode : false,
22530 getSelection : function()
22532 this.assignDocWin();
22533 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22536 getSelectedNode: function()
22538 // this may only work on Gecko!!!
22540 // should we cache this!!!!
22545 var range = this.createRange(this.getSelection()).cloneRange();
22548 var parent = range.parentElement();
22550 var testRange = range.duplicate();
22551 testRange.moveToElementText(parent);
22552 if (testRange.inRange(range)) {
22555 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22558 parent = parent.parentElement;
22563 // is ancestor a text element.
22564 var ac = range.commonAncestorContainer;
22565 if (ac.nodeType == 3) {
22566 ac = ac.parentNode;
22569 var ar = ac.childNodes;
22572 var other_nodes = [];
22573 var has_other_nodes = false;
22574 for (var i=0;i<ar.length;i++) {
22575 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22578 // fullly contained node.
22580 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22585 // probably selected..
22586 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22587 other_nodes.push(ar[i]);
22591 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22596 has_other_nodes = true;
22598 if (!nodes.length && other_nodes.length) {
22599 nodes= other_nodes;
22601 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22607 createRange: function(sel)
22609 // this has strange effects when using with
22610 // top toolbar - not sure if it's a great idea.
22611 //this.editor.contentWindow.focus();
22612 if (typeof sel != "undefined") {
22614 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22616 return this.doc.createRange();
22619 return this.doc.createRange();
22622 getParentElement: function()
22625 this.assignDocWin();
22626 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22628 var range = this.createRange(sel);
22631 var p = range.commonAncestorContainer;
22632 while (p.nodeType == 3) { // text node
22643 * Range intersection.. the hard stuff...
22647 * [ -- selected range --- ]
22651 * if end is before start or hits it. fail.
22652 * if start is after end or hits it fail.
22654 * if either hits (but other is outside. - then it's not
22660 // @see http://www.thismuchiknow.co.uk/?p=64.
22661 rangeIntersectsNode : function(range, node)
22663 var nodeRange = node.ownerDocument.createRange();
22665 nodeRange.selectNode(node);
22667 nodeRange.selectNodeContents(node);
22670 var rangeStartRange = range.cloneRange();
22671 rangeStartRange.collapse(true);
22673 var rangeEndRange = range.cloneRange();
22674 rangeEndRange.collapse(false);
22676 var nodeStartRange = nodeRange.cloneRange();
22677 nodeStartRange.collapse(true);
22679 var nodeEndRange = nodeRange.cloneRange();
22680 nodeEndRange.collapse(false);
22682 return rangeStartRange.compareBoundaryPoints(
22683 Range.START_TO_START, nodeEndRange) == -1 &&
22684 rangeEndRange.compareBoundaryPoints(
22685 Range.START_TO_START, nodeStartRange) == 1;
22689 rangeCompareNode : function(range, node)
22691 var nodeRange = node.ownerDocument.createRange();
22693 nodeRange.selectNode(node);
22695 nodeRange.selectNodeContents(node);
22699 range.collapse(true);
22701 nodeRange.collapse(true);
22703 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22704 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22706 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22708 var nodeIsBefore = ss == 1;
22709 var nodeIsAfter = ee == -1;
22711 if (nodeIsBefore && nodeIsAfter) {
22714 if (!nodeIsBefore && nodeIsAfter) {
22715 return 1; //right trailed.
22718 if (nodeIsBefore && !nodeIsAfter) {
22719 return 2; // left trailed.
22725 // private? - in a new class?
22726 cleanUpPaste : function()
22728 // cleans up the whole document..
22729 Roo.log('cleanuppaste');
22731 this.cleanUpChildren(this.doc.body);
22732 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22733 if (clean != this.doc.body.innerHTML) {
22734 this.doc.body.innerHTML = clean;
22739 cleanWordChars : function(input) {// change the chars to hex code
22740 var he = Roo.HtmlEditorCore;
22742 var output = input;
22743 Roo.each(he.swapCodes, function(sw) {
22744 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22746 output = output.replace(swapper, sw[1]);
22753 cleanUpChildren : function (n)
22755 if (!n.childNodes.length) {
22758 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22759 this.cleanUpChild(n.childNodes[i]);
22766 cleanUpChild : function (node)
22769 //console.log(node);
22770 if (node.nodeName == "#text") {
22771 // clean up silly Windows -- stuff?
22774 if (node.nodeName == "#comment") {
22775 node.parentNode.removeChild(node);
22776 // clean up silly Windows -- stuff?
22779 var lcname = node.tagName.toLowerCase();
22780 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22781 // whitelist of tags..
22783 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22785 node.parentNode.removeChild(node);
22790 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22792 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22793 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22795 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22796 // remove_keep_children = true;
22799 if (remove_keep_children) {
22800 this.cleanUpChildren(node);
22801 // inserts everything just before this node...
22802 while (node.childNodes.length) {
22803 var cn = node.childNodes[0];
22804 node.removeChild(cn);
22805 node.parentNode.insertBefore(cn, node);
22807 node.parentNode.removeChild(node);
22811 if (!node.attributes || !node.attributes.length) {
22812 this.cleanUpChildren(node);
22816 function cleanAttr(n,v)
22819 if (v.match(/^\./) || v.match(/^\//)) {
22822 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22825 if (v.match(/^#/)) {
22828 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22829 node.removeAttribute(n);
22833 var cwhite = this.cwhite;
22834 var cblack = this.cblack;
22836 function cleanStyle(n,v)
22838 if (v.match(/expression/)) { //XSS?? should we even bother..
22839 node.removeAttribute(n);
22843 var parts = v.split(/;/);
22846 Roo.each(parts, function(p) {
22847 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22851 var l = p.split(':').shift().replace(/\s+/g,'');
22852 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22854 if ( cwhite.length && cblack.indexOf(l) > -1) {
22855 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22856 //node.removeAttribute(n);
22860 // only allow 'c whitelisted system attributes'
22861 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22862 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22863 //node.removeAttribute(n);
22873 if (clean.length) {
22874 node.setAttribute(n, clean.join(';'));
22876 node.removeAttribute(n);
22882 for (var i = node.attributes.length-1; i > -1 ; i--) {
22883 var a = node.attributes[i];
22886 if (a.name.toLowerCase().substr(0,2)=='on') {
22887 node.removeAttribute(a.name);
22890 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22891 node.removeAttribute(a.name);
22894 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22895 cleanAttr(a.name,a.value); // fixme..
22898 if (a.name == 'style') {
22899 cleanStyle(a.name,a.value);
22902 /// clean up MS crap..
22903 // tecnically this should be a list of valid class'es..
22906 if (a.name == 'class') {
22907 if (a.value.match(/^Mso/)) {
22908 node.className = '';
22911 if (a.value.match(/^body$/)) {
22912 node.className = '';
22923 this.cleanUpChildren(node);
22929 * Clean up MS wordisms...
22931 cleanWord : function(node)
22936 this.cleanWord(this.doc.body);
22939 if (node.nodeName == "#text") {
22940 // clean up silly Windows -- stuff?
22943 if (node.nodeName == "#comment") {
22944 node.parentNode.removeChild(node);
22945 // clean up silly Windows -- stuff?
22949 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22950 node.parentNode.removeChild(node);
22954 // remove - but keep children..
22955 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22956 while (node.childNodes.length) {
22957 var cn = node.childNodes[0];
22958 node.removeChild(cn);
22959 node.parentNode.insertBefore(cn, node);
22961 node.parentNode.removeChild(node);
22962 this.iterateChildren(node, this.cleanWord);
22966 if (node.className.length) {
22968 var cn = node.className.split(/\W+/);
22970 Roo.each(cn, function(cls) {
22971 if (cls.match(/Mso[a-zA-Z]+/)) {
22976 node.className = cna.length ? cna.join(' ') : '';
22978 node.removeAttribute("class");
22982 if (node.hasAttribute("lang")) {
22983 node.removeAttribute("lang");
22986 if (node.hasAttribute("style")) {
22988 var styles = node.getAttribute("style").split(";");
22990 Roo.each(styles, function(s) {
22991 if (!s.match(/:/)) {
22994 var kv = s.split(":");
22995 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22998 // what ever is left... we allow.
23001 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23002 if (!nstyle.length) {
23003 node.removeAttribute('style');
23006 this.iterateChildren(node, this.cleanWord);
23012 * iterateChildren of a Node, calling fn each time, using this as the scole..
23013 * @param {DomNode} node node to iterate children of.
23014 * @param {Function} fn method of this class to call on each item.
23016 iterateChildren : function(node, fn)
23018 if (!node.childNodes.length) {
23021 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23022 fn.call(this, node.childNodes[i])
23028 * cleanTableWidths.
23030 * Quite often pasting from word etc.. results in tables with column and widths.
23031 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23034 cleanTableWidths : function(node)
23039 this.cleanTableWidths(this.doc.body);
23044 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23047 Roo.log(node.tagName);
23048 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23049 this.iterateChildren(node, this.cleanTableWidths);
23052 if (node.hasAttribute('width')) {
23053 node.removeAttribute('width');
23057 if (node.hasAttribute("style")) {
23060 var styles = node.getAttribute("style").split(";");
23062 Roo.each(styles, function(s) {
23063 if (!s.match(/:/)) {
23066 var kv = s.split(":");
23067 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23070 // what ever is left... we allow.
23073 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23074 if (!nstyle.length) {
23075 node.removeAttribute('style');
23079 this.iterateChildren(node, this.cleanTableWidths);
23087 domToHTML : function(currentElement, depth, nopadtext) {
23089 depth = depth || 0;
23090 nopadtext = nopadtext || false;
23092 if (!currentElement) {
23093 return this.domToHTML(this.doc.body);
23096 //Roo.log(currentElement);
23098 var allText = false;
23099 var nodeName = currentElement.nodeName;
23100 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23102 if (nodeName == '#text') {
23104 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23109 if (nodeName != 'BODY') {
23112 // Prints the node tagName, such as <A>, <IMG>, etc
23115 for(i = 0; i < currentElement.attributes.length;i++) {
23117 var aname = currentElement.attributes.item(i).name;
23118 if (!currentElement.attributes.item(i).value.length) {
23121 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23124 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23133 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23136 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23141 // Traverse the tree
23143 var currentElementChild = currentElement.childNodes.item(i);
23144 var allText = true;
23145 var innerHTML = '';
23147 while (currentElementChild) {
23148 // Formatting code (indent the tree so it looks nice on the screen)
23149 var nopad = nopadtext;
23150 if (lastnode == 'SPAN') {
23154 if (currentElementChild.nodeName == '#text') {
23155 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23156 toadd = nopadtext ? toadd : toadd.trim();
23157 if (!nopad && toadd.length > 80) {
23158 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23160 innerHTML += toadd;
23163 currentElementChild = currentElement.childNodes.item(i);
23169 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23171 // Recursively traverse the tree structure of the child node
23172 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23173 lastnode = currentElementChild.nodeName;
23175 currentElementChild=currentElement.childNodes.item(i);
23181 // The remaining code is mostly for formatting the tree
23182 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23187 ret+= "</"+tagName+">";
23193 applyBlacklists : function()
23195 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23196 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23200 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23201 if (b.indexOf(tag) > -1) {
23204 this.white.push(tag);
23208 Roo.each(w, function(tag) {
23209 if (b.indexOf(tag) > -1) {
23212 if (this.white.indexOf(tag) > -1) {
23215 this.white.push(tag);
23220 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23221 if (w.indexOf(tag) > -1) {
23224 this.black.push(tag);
23228 Roo.each(b, function(tag) {
23229 if (w.indexOf(tag) > -1) {
23232 if (this.black.indexOf(tag) > -1) {
23235 this.black.push(tag);
23240 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23241 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23245 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23246 if (b.indexOf(tag) > -1) {
23249 this.cwhite.push(tag);
23253 Roo.each(w, function(tag) {
23254 if (b.indexOf(tag) > -1) {
23257 if (this.cwhite.indexOf(tag) > -1) {
23260 this.cwhite.push(tag);
23265 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23266 if (w.indexOf(tag) > -1) {
23269 this.cblack.push(tag);
23273 Roo.each(b, function(tag) {
23274 if (w.indexOf(tag) > -1) {
23277 if (this.cblack.indexOf(tag) > -1) {
23280 this.cblack.push(tag);
23285 setStylesheets : function(stylesheets)
23287 if(typeof(stylesheets) == 'string'){
23288 Roo.get(this.iframe.contentDocument.head).createChild({
23290 rel : 'stylesheet',
23299 Roo.each(stylesheets, function(s) {
23304 Roo.get(_this.iframe.contentDocument.head).createChild({
23306 rel : 'stylesheet',
23315 removeStylesheets : function()
23319 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23324 setStyle : function(style)
23326 Roo.get(this.iframe.contentDocument.head).createChild({
23335 // hide stuff that is not compatible
23349 * @event specialkey
23353 * @cfg {String} fieldClass @hide
23356 * @cfg {String} focusClass @hide
23359 * @cfg {String} autoCreate @hide
23362 * @cfg {String} inputType @hide
23365 * @cfg {String} invalidClass @hide
23368 * @cfg {String} invalidText @hide
23371 * @cfg {String} msgFx @hide
23374 * @cfg {String} validateOnBlur @hide
23378 Roo.HtmlEditorCore.white = [
23379 'area', 'br', 'img', 'input', 'hr', 'wbr',
23381 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23382 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23383 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23384 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23385 'table', 'ul', 'xmp',
23387 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23390 'dir', 'menu', 'ol', 'ul', 'dl',
23396 Roo.HtmlEditorCore.black = [
23397 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23399 'base', 'basefont', 'bgsound', 'blink', 'body',
23400 'frame', 'frameset', 'head', 'html', 'ilayer',
23401 'iframe', 'layer', 'link', 'meta', 'object',
23402 'script', 'style' ,'title', 'xml' // clean later..
23404 Roo.HtmlEditorCore.clean = [
23405 'script', 'style', 'title', 'xml'
23407 Roo.HtmlEditorCore.remove = [
23412 Roo.HtmlEditorCore.ablack = [
23416 Roo.HtmlEditorCore.aclean = [
23417 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23421 Roo.HtmlEditorCore.pwhite= [
23422 'http', 'https', 'mailto'
23425 // white listed style attributes.
23426 Roo.HtmlEditorCore.cwhite= [
23427 // 'text-align', /// default is to allow most things..
23433 // black listed style attributes.
23434 Roo.HtmlEditorCore.cblack= [
23435 // 'font-size' -- this can be set by the project
23439 Roo.HtmlEditorCore.swapCodes =[
23458 * @class Roo.bootstrap.HtmlEditor
23459 * @extends Roo.bootstrap.TextArea
23460 * Bootstrap HtmlEditor class
23463 * Create a new HtmlEditor
23464 * @param {Object} config The config object
23467 Roo.bootstrap.HtmlEditor = function(config){
23468 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23469 if (!this.toolbars) {
23470 this.toolbars = [];
23473 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23476 * @event initialize
23477 * Fires when the editor is fully initialized (including the iframe)
23478 * @param {HtmlEditor} this
23483 * Fires when the editor is first receives the focus. Any insertion must wait
23484 * until after this event.
23485 * @param {HtmlEditor} this
23489 * @event beforesync
23490 * Fires before the textarea is updated with content from the editor iframe. Return false
23491 * to cancel the sync.
23492 * @param {HtmlEditor} this
23493 * @param {String} html
23497 * @event beforepush
23498 * Fires before the iframe editor is updated with content from the textarea. Return false
23499 * to cancel the push.
23500 * @param {HtmlEditor} this
23501 * @param {String} html
23506 * Fires when the textarea is updated with content from the editor iframe.
23507 * @param {HtmlEditor} this
23508 * @param {String} html
23513 * Fires when the iframe editor is updated with content from the textarea.
23514 * @param {HtmlEditor} this
23515 * @param {String} html
23519 * @event editmodechange
23520 * Fires when the editor switches edit modes
23521 * @param {HtmlEditor} this
23522 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23524 editmodechange: true,
23526 * @event editorevent
23527 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23528 * @param {HtmlEditor} this
23532 * @event firstfocus
23533 * Fires when on first focus - needed by toolbars..
23534 * @param {HtmlEditor} this
23539 * Auto save the htmlEditor value as a file into Events
23540 * @param {HtmlEditor} this
23544 * @event savedpreview
23545 * preview the saved version of htmlEditor
23546 * @param {HtmlEditor} this
23553 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23557 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23562 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23567 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23572 * @cfg {Number} height (in pixels)
23576 * @cfg {Number} width (in pixels)
23581 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23584 stylesheets: false,
23589 // private properties
23590 validationEvent : false,
23592 initialized : false,
23595 onFocus : Roo.emptyFn,
23597 hideMode:'offsets',
23599 tbContainer : false,
23603 toolbarContainer :function() {
23604 return this.wrap.select('.x-html-editor-tb',true).first();
23608 * Protected method that will not generally be called directly. It
23609 * is called when the editor creates its toolbar. Override this method if you need to
23610 * add custom toolbar buttons.
23611 * @param {HtmlEditor} editor
23613 createToolbar : function(){
23614 Roo.log('renewing');
23615 Roo.log("create toolbars");
23617 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23618 this.toolbars[0].render(this.toolbarContainer());
23622 // if (!editor.toolbars || !editor.toolbars.length) {
23623 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23626 // for (var i =0 ; i < editor.toolbars.length;i++) {
23627 // editor.toolbars[i] = Roo.factory(
23628 // typeof(editor.toolbars[i]) == 'string' ?
23629 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23630 // Roo.bootstrap.HtmlEditor);
23631 // editor.toolbars[i].init(editor);
23637 onRender : function(ct, position)
23639 // Roo.log("Call onRender: " + this.xtype);
23641 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23643 this.wrap = this.inputEl().wrap({
23644 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23647 this.editorcore.onRender(ct, position);
23649 if (this.resizable) {
23650 this.resizeEl = new Roo.Resizable(this.wrap, {
23654 minHeight : this.height,
23655 height: this.height,
23656 handles : this.resizable,
23659 resize : function(r, w, h) {
23660 _t.onResize(w,h); // -something
23666 this.createToolbar(this);
23669 if(!this.width && this.resizable){
23670 this.setSize(this.wrap.getSize());
23672 if (this.resizeEl) {
23673 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23674 // should trigger onReize..
23680 onResize : function(w, h)
23682 Roo.log('resize: ' +w + ',' + h );
23683 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23687 if(this.inputEl() ){
23688 if(typeof w == 'number'){
23689 var aw = w - this.wrap.getFrameWidth('lr');
23690 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23693 if(typeof h == 'number'){
23694 var tbh = -11; // fixme it needs to tool bar size!
23695 for (var i =0; i < this.toolbars.length;i++) {
23696 // fixme - ask toolbars for heights?
23697 tbh += this.toolbars[i].el.getHeight();
23698 //if (this.toolbars[i].footer) {
23699 // tbh += this.toolbars[i].footer.el.getHeight();
23707 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23708 ah -= 5; // knock a few pixes off for look..
23709 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23713 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23714 this.editorcore.onResize(ew,eh);
23719 * Toggles the editor between standard and source edit mode.
23720 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23722 toggleSourceEdit : function(sourceEditMode)
23724 this.editorcore.toggleSourceEdit(sourceEditMode);
23726 if(this.editorcore.sourceEditMode){
23727 Roo.log('editor - showing textarea');
23730 // Roo.log(this.syncValue());
23732 this.inputEl().removeClass(['hide', 'x-hidden']);
23733 this.inputEl().dom.removeAttribute('tabIndex');
23734 this.inputEl().focus();
23736 Roo.log('editor - hiding textarea');
23738 // Roo.log(this.pushValue());
23741 this.inputEl().addClass(['hide', 'x-hidden']);
23742 this.inputEl().dom.setAttribute('tabIndex', -1);
23743 //this.deferFocus();
23746 if(this.resizable){
23747 this.setSize(this.wrap.getSize());
23750 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23753 // private (for BoxComponent)
23754 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23756 // private (for BoxComponent)
23757 getResizeEl : function(){
23761 // private (for BoxComponent)
23762 getPositionEl : function(){
23767 initEvents : function(){
23768 this.originalValue = this.getValue();
23772 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23775 // markInvalid : Roo.emptyFn,
23777 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23780 // clearInvalid : Roo.emptyFn,
23782 setValue : function(v){
23783 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23784 this.editorcore.pushValue();
23789 deferFocus : function(){
23790 this.focus.defer(10, this);
23794 focus : function(){
23795 this.editorcore.focus();
23801 onDestroy : function(){
23807 for (var i =0; i < this.toolbars.length;i++) {
23808 // fixme - ask toolbars for heights?
23809 this.toolbars[i].onDestroy();
23812 this.wrap.dom.innerHTML = '';
23813 this.wrap.remove();
23818 onFirstFocus : function(){
23819 //Roo.log("onFirstFocus");
23820 this.editorcore.onFirstFocus();
23821 for (var i =0; i < this.toolbars.length;i++) {
23822 this.toolbars[i].onFirstFocus();
23828 syncValue : function()
23830 this.editorcore.syncValue();
23833 pushValue : function()
23835 this.editorcore.pushValue();
23839 // hide stuff that is not compatible
23853 * @event specialkey
23857 * @cfg {String} fieldClass @hide
23860 * @cfg {String} focusClass @hide
23863 * @cfg {String} autoCreate @hide
23866 * @cfg {String} inputType @hide
23869 * @cfg {String} invalidClass @hide
23872 * @cfg {String} invalidText @hide
23875 * @cfg {String} msgFx @hide
23878 * @cfg {String} validateOnBlur @hide
23887 Roo.namespace('Roo.bootstrap.htmleditor');
23889 * @class Roo.bootstrap.HtmlEditorToolbar1
23894 new Roo.bootstrap.HtmlEditor({
23897 new Roo.bootstrap.HtmlEditorToolbar1({
23898 disable : { fonts: 1 , format: 1, ..., ... , ...],
23904 * @cfg {Object} disable List of elements to disable..
23905 * @cfg {Array} btns List of additional buttons.
23909 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23912 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23915 Roo.apply(this, config);
23917 // default disabled, based on 'good practice'..
23918 this.disable = this.disable || {};
23919 Roo.applyIf(this.disable, {
23922 specialElements : true
23924 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23926 this.editor = config.editor;
23927 this.editorcore = config.editor.editorcore;
23929 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23931 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23932 // dont call parent... till later.
23934 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23939 editorcore : false,
23944 "h1","h2","h3","h4","h5","h6",
23946 "abbr", "acronym", "address", "cite", "samp", "var",
23950 onRender : function(ct, position)
23952 // Roo.log("Call onRender: " + this.xtype);
23954 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23956 this.el.dom.style.marginBottom = '0';
23958 var editorcore = this.editorcore;
23959 var editor= this.editor;
23962 var btn = function(id,cmd , toggle, handler, html){
23964 var event = toggle ? 'toggle' : 'click';
23969 xns: Roo.bootstrap,
23972 enableToggle:toggle !== false,
23974 pressed : toggle ? false : null,
23977 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23978 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23984 // var cb_box = function...
23989 xns: Roo.bootstrap,
23990 glyphicon : 'font',
23994 xns: Roo.bootstrap,
23998 Roo.each(this.formats, function(f) {
23999 style.menu.items.push({
24001 xns: Roo.bootstrap,
24002 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24007 editorcore.insertTag(this.tagname);
24014 children.push(style);
24016 btn('bold',false,true);
24017 btn('italic',false,true);
24018 btn('align-left', 'justifyleft',true);
24019 btn('align-center', 'justifycenter',true);
24020 btn('align-right' , 'justifyright',true);
24021 btn('link', false, false, function(btn) {
24022 //Roo.log("create link?");
24023 var url = prompt(this.createLinkText, this.defaultLinkValue);
24024 if(url && url != 'http:/'+'/'){
24025 this.editorcore.relayCmd('createlink', url);
24028 btn('list','insertunorderedlist',true);
24029 btn('pencil', false,true, function(btn){
24031 this.toggleSourceEdit(btn.pressed);
24034 if (this.editor.btns.length > 0) {
24035 for (var i = 0; i<this.editor.btns.length; i++) {
24036 children.push(this.editor.btns[i]);
24044 xns: Roo.bootstrap,
24049 xns: Roo.bootstrap,
24054 cog.menu.items.push({
24056 xns: Roo.bootstrap,
24057 html : Clean styles,
24062 editorcore.insertTag(this.tagname);
24071 this.xtype = 'NavSimplebar';
24073 for(var i=0;i< children.length;i++) {
24075 this.buttons.add(this.addxtypeChild(children[i]));
24079 editor.on('editorevent', this.updateToolbar, this);
24081 onBtnClick : function(id)
24083 this.editorcore.relayCmd(id);
24084 this.editorcore.focus();
24088 * Protected method that will not generally be called directly. It triggers
24089 * a toolbar update by reading the markup state of the current selection in the editor.
24091 updateToolbar: function(){
24093 if(!this.editorcore.activated){
24094 this.editor.onFirstFocus(); // is this neeed?
24098 var btns = this.buttons;
24099 var doc = this.editorcore.doc;
24100 btns.get('bold').setActive(doc.queryCommandState('bold'));
24101 btns.get('italic').setActive(doc.queryCommandState('italic'));
24102 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24104 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24105 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24106 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24108 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24109 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24112 var ans = this.editorcore.getAllAncestors();
24113 if (this.formatCombo) {
24116 var store = this.formatCombo.store;
24117 this.formatCombo.setValue("");
24118 for (var i =0; i < ans.length;i++) {
24119 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24121 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24129 // hides menus... - so this cant be on a menu...
24130 Roo.bootstrap.MenuMgr.hideAll();
24132 Roo.bootstrap.MenuMgr.hideAll();
24133 //this.editorsyncValue();
24135 onFirstFocus: function() {
24136 this.buttons.each(function(item){
24140 toggleSourceEdit : function(sourceEditMode){
24143 if(sourceEditMode){
24144 Roo.log("disabling buttons");
24145 this.buttons.each( function(item){
24146 if(item.cmd != 'pencil'){
24152 Roo.log("enabling buttons");
24153 if(this.editorcore.initialized){
24154 this.buttons.each( function(item){
24160 Roo.log("calling toggole on editor");
24161 // tell the editor that it's been pressed..
24162 this.editor.toggleSourceEdit(sourceEditMode);
24172 * @class Roo.bootstrap.Table.AbstractSelectionModel
24173 * @extends Roo.util.Observable
24174 * Abstract base class for grid SelectionModels. It provides the interface that should be
24175 * implemented by descendant classes. This class should not be directly instantiated.
24178 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24179 this.locked = false;
24180 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24184 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24185 /** @ignore Called by the grid automatically. Do not call directly. */
24186 init : function(grid){
24192 * Locks the selections.
24195 this.locked = true;
24199 * Unlocks the selections.
24201 unlock : function(){
24202 this.locked = false;
24206 * Returns true if the selections are locked.
24207 * @return {Boolean}
24209 isLocked : function(){
24210 return this.locked;
24214 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24215 * @class Roo.bootstrap.Table.RowSelectionModel
24216 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24217 * It supports multiple selections and keyboard selection/navigation.
24219 * @param {Object} config
24222 Roo.bootstrap.Table.RowSelectionModel = function(config){
24223 Roo.apply(this, config);
24224 this.selections = new Roo.util.MixedCollection(false, function(o){
24229 this.lastActive = false;
24233 * @event selectionchange
24234 * Fires when the selection changes
24235 * @param {SelectionModel} this
24237 "selectionchange" : true,
24239 * @event afterselectionchange
24240 * Fires after the selection changes (eg. by key press or clicking)
24241 * @param {SelectionModel} this
24243 "afterselectionchange" : true,
24245 * @event beforerowselect
24246 * Fires when a row is selected being selected, return false to cancel.
24247 * @param {SelectionModel} this
24248 * @param {Number} rowIndex The selected index
24249 * @param {Boolean} keepExisting False if other selections will be cleared
24251 "beforerowselect" : true,
24254 * Fires when a row is selected.
24255 * @param {SelectionModel} this
24256 * @param {Number} rowIndex The selected index
24257 * @param {Roo.data.Record} r The record
24259 "rowselect" : true,
24261 * @event rowdeselect
24262 * Fires when a row is deselected.
24263 * @param {SelectionModel} this
24264 * @param {Number} rowIndex The selected index
24266 "rowdeselect" : true
24268 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24269 this.locked = false;
24272 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24274 * @cfg {Boolean} singleSelect
24275 * True to allow selection of only one row at a time (defaults to false)
24277 singleSelect : false,
24280 initEvents : function()
24283 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24284 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24285 //}else{ // allow click to work like normal
24286 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24288 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24289 this.grid.on("rowclick", this.handleMouseDown, this);
24291 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24292 "up" : function(e){
24294 this.selectPrevious(e.shiftKey);
24295 }else if(this.last !== false && this.lastActive !== false){
24296 var last = this.last;
24297 this.selectRange(this.last, this.lastActive-1);
24298 this.grid.getView().focusRow(this.lastActive);
24299 if(last !== false){
24303 this.selectFirstRow();
24305 this.fireEvent("afterselectionchange", this);
24307 "down" : function(e){
24309 this.selectNext(e.shiftKey);
24310 }else if(this.last !== false && this.lastActive !== false){
24311 var last = this.last;
24312 this.selectRange(this.last, this.lastActive+1);
24313 this.grid.getView().focusRow(this.lastActive);
24314 if(last !== false){
24318 this.selectFirstRow();
24320 this.fireEvent("afterselectionchange", this);
24324 this.grid.store.on('load', function(){
24325 this.selections.clear();
24328 var view = this.grid.view;
24329 view.on("refresh", this.onRefresh, this);
24330 view.on("rowupdated", this.onRowUpdated, this);
24331 view.on("rowremoved", this.onRemove, this);
24336 onRefresh : function()
24338 var ds = this.grid.store, i, v = this.grid.view;
24339 var s = this.selections;
24340 s.each(function(r){
24341 if((i = ds.indexOfId(r.id)) != -1){
24350 onRemove : function(v, index, r){
24351 this.selections.remove(r);
24355 onRowUpdated : function(v, index, r){
24356 if(this.isSelected(r)){
24357 v.onRowSelect(index);
24363 * @param {Array} records The records to select
24364 * @param {Boolean} keepExisting (optional) True to keep existing selections
24366 selectRecords : function(records, keepExisting)
24369 this.clearSelections();
24371 var ds = this.grid.store;
24372 for(var i = 0, len = records.length; i < len; i++){
24373 this.selectRow(ds.indexOf(records[i]), true);
24378 * Gets the number of selected rows.
24381 getCount : function(){
24382 return this.selections.length;
24386 * Selects the first row in the grid.
24388 selectFirstRow : function(){
24393 * Select the last row.
24394 * @param {Boolean} keepExisting (optional) True to keep existing selections
24396 selectLastRow : function(keepExisting){
24397 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24398 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24402 * Selects the row immediately following the last selected row.
24403 * @param {Boolean} keepExisting (optional) True to keep existing selections
24405 selectNext : function(keepExisting)
24407 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24408 this.selectRow(this.last+1, keepExisting);
24409 this.grid.getView().focusRow(this.last);
24414 * Selects the row that precedes the last selected row.
24415 * @param {Boolean} keepExisting (optional) True to keep existing selections
24417 selectPrevious : function(keepExisting){
24419 this.selectRow(this.last-1, keepExisting);
24420 this.grid.getView().focusRow(this.last);
24425 * Returns the selected records
24426 * @return {Array} Array of selected records
24428 getSelections : function(){
24429 return [].concat(this.selections.items);
24433 * Returns the first selected record.
24436 getSelected : function(){
24437 return this.selections.itemAt(0);
24442 * Clears all selections.
24444 clearSelections : function(fast)
24450 var ds = this.grid.store;
24451 var s = this.selections;
24452 s.each(function(r){
24453 this.deselectRow(ds.indexOfId(r.id));
24457 this.selections.clear();
24464 * Selects all rows.
24466 selectAll : function(){
24470 this.selections.clear();
24471 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24472 this.selectRow(i, true);
24477 * Returns True if there is a selection.
24478 * @return {Boolean}
24480 hasSelection : function(){
24481 return this.selections.length > 0;
24485 * Returns True if the specified row is selected.
24486 * @param {Number/Record} record The record or index of the record to check
24487 * @return {Boolean}
24489 isSelected : function(index){
24490 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24491 return (r && this.selections.key(r.id) ? true : false);
24495 * Returns True if the specified record id is selected.
24496 * @param {String} id The id of record to check
24497 * @return {Boolean}
24499 isIdSelected : function(id){
24500 return (this.selections.key(id) ? true : false);
24505 handleMouseDBClick : function(e, t){
24509 handleMouseDown : function(e, t)
24511 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24512 if(this.isLocked() || rowIndex < 0 ){
24515 if(e.shiftKey && this.last !== false){
24516 var last = this.last;
24517 this.selectRange(last, rowIndex, e.ctrlKey);
24518 this.last = last; // reset the last
24522 var isSelected = this.isSelected(rowIndex);
24523 //Roo.log("select row:" + rowIndex);
24525 this.deselectRow(rowIndex);
24527 this.selectRow(rowIndex, true);
24531 if(e.button !== 0 && isSelected){
24532 alert('rowIndex 2: ' + rowIndex);
24533 view.focusRow(rowIndex);
24534 }else if(e.ctrlKey && isSelected){
24535 this.deselectRow(rowIndex);
24536 }else if(!isSelected){
24537 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24538 view.focusRow(rowIndex);
24542 this.fireEvent("afterselectionchange", this);
24545 handleDragableRowClick : function(grid, rowIndex, e)
24547 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24548 this.selectRow(rowIndex, false);
24549 grid.view.focusRow(rowIndex);
24550 this.fireEvent("afterselectionchange", this);
24555 * Selects multiple rows.
24556 * @param {Array} rows Array of the indexes of the row to select
24557 * @param {Boolean} keepExisting (optional) True to keep existing selections
24559 selectRows : function(rows, keepExisting){
24561 this.clearSelections();
24563 for(var i = 0, len = rows.length; i < len; i++){
24564 this.selectRow(rows[i], true);
24569 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24570 * @param {Number} startRow The index of the first row in the range
24571 * @param {Number} endRow The index of the last row in the range
24572 * @param {Boolean} keepExisting (optional) True to retain existing selections
24574 selectRange : function(startRow, endRow, keepExisting){
24579 this.clearSelections();
24581 if(startRow <= endRow){
24582 for(var i = startRow; i <= endRow; i++){
24583 this.selectRow(i, true);
24586 for(var i = startRow; i >= endRow; i--){
24587 this.selectRow(i, true);
24593 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24594 * @param {Number} startRow The index of the first row in the range
24595 * @param {Number} endRow The index of the last row in the range
24597 deselectRange : function(startRow, endRow, preventViewNotify){
24601 for(var i = startRow; i <= endRow; i++){
24602 this.deselectRow(i, preventViewNotify);
24608 * @param {Number} row The index of the row to select
24609 * @param {Boolean} keepExisting (optional) True to keep existing selections
24611 selectRow : function(index, keepExisting, preventViewNotify)
24613 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24616 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24617 if(!keepExisting || this.singleSelect){
24618 this.clearSelections();
24621 var r = this.grid.store.getAt(index);
24622 //console.log('selectRow - record id :' + r.id);
24624 this.selections.add(r);
24625 this.last = this.lastActive = index;
24626 if(!preventViewNotify){
24627 var proxy = new Roo.Element(
24628 this.grid.getRowDom(index)
24630 proxy.addClass('bg-info info');
24632 this.fireEvent("rowselect", this, index, r);
24633 this.fireEvent("selectionchange", this);
24639 * @param {Number} row The index of the row to deselect
24641 deselectRow : function(index, preventViewNotify)
24646 if(this.last == index){
24649 if(this.lastActive == index){
24650 this.lastActive = false;
24653 var r = this.grid.store.getAt(index);
24658 this.selections.remove(r);
24659 //.console.log('deselectRow - record id :' + r.id);
24660 if(!preventViewNotify){
24662 var proxy = new Roo.Element(
24663 this.grid.getRowDom(index)
24665 proxy.removeClass('bg-info info');
24667 this.fireEvent("rowdeselect", this, index);
24668 this.fireEvent("selectionchange", this);
24672 restoreLast : function(){
24674 this.last = this._last;
24679 acceptsNav : function(row, col, cm){
24680 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24684 onEditorKey : function(field, e){
24685 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24690 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24692 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24694 }else if(k == e.ENTER && !e.ctrlKey){
24698 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24700 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24702 }else if(k == e.ESC){
24706 g.startEditing(newCell[0], newCell[1]);
24712 * Ext JS Library 1.1.1
24713 * Copyright(c) 2006-2007, Ext JS, LLC.
24715 * Originally Released Under LGPL - original licence link has changed is not relivant.
24718 * <script type="text/javascript">
24722 * @class Roo.bootstrap.PagingToolbar
24723 * @extends Roo.bootstrap.NavSimplebar
24724 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24726 * Create a new PagingToolbar
24727 * @param {Object} config The config object
24728 * @param {Roo.data.Store} store
24730 Roo.bootstrap.PagingToolbar = function(config)
24732 // old args format still supported... - xtype is prefered..
24733 // created from xtype...
24735 this.ds = config.dataSource;
24737 if (config.store && !this.ds) {
24738 this.store= Roo.factory(config.store, Roo.data);
24739 this.ds = this.store;
24740 this.ds.xmodule = this.xmodule || false;
24743 this.toolbarItems = [];
24744 if (config.items) {
24745 this.toolbarItems = config.items;
24748 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24753 this.bind(this.ds);
24756 if (Roo.bootstrap.version == 4) {
24757 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24759 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24764 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24766 * @cfg {Roo.data.Store} dataSource
24767 * The underlying data store providing the paged data
24770 * @cfg {String/HTMLElement/Element} container
24771 * container The id or element that will contain the toolbar
24774 * @cfg {Boolean} displayInfo
24775 * True to display the displayMsg (defaults to false)
24778 * @cfg {Number} pageSize
24779 * The number of records to display per page (defaults to 20)
24783 * @cfg {String} displayMsg
24784 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24786 displayMsg : 'Displaying {0} - {1} of {2}',
24788 * @cfg {String} emptyMsg
24789 * The message to display when no records are found (defaults to "No data to display")
24791 emptyMsg : 'No data to display',
24793 * Customizable piece of the default paging text (defaults to "Page")
24796 beforePageText : "Page",
24798 * Customizable piece of the default paging text (defaults to "of %0")
24801 afterPageText : "of {0}",
24803 * Customizable piece of the default paging text (defaults to "First Page")
24806 firstText : "First Page",
24808 * Customizable piece of the default paging text (defaults to "Previous Page")
24811 prevText : "Previous Page",
24813 * Customizable piece of the default paging text (defaults to "Next Page")
24816 nextText : "Next Page",
24818 * Customizable piece of the default paging text (defaults to "Last Page")
24821 lastText : "Last Page",
24823 * Customizable piece of the default paging text (defaults to "Refresh")
24826 refreshText : "Refresh",
24830 onRender : function(ct, position)
24832 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24833 this.navgroup.parentId = this.id;
24834 this.navgroup.onRender(this.el, null);
24835 // add the buttons to the navgroup
24837 if(this.displayInfo){
24838 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24839 this.displayEl = this.el.select('.x-paging-info', true).first();
24840 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24841 // this.displayEl = navel.el.select('span',true).first();
24847 Roo.each(_this.buttons, function(e){ // this might need to use render????
24848 Roo.factory(e).render(_this.el);
24852 Roo.each(_this.toolbarItems, function(e) {
24853 _this.navgroup.addItem(e);
24857 this.first = this.navgroup.addItem({
24858 tooltip: this.firstText,
24859 cls: "prev btn-outline-secondary",
24860 html : ' <i class="fa fa-step-backward"></i>',
24862 preventDefault: true,
24863 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24866 this.prev = this.navgroup.addItem({
24867 tooltip: this.prevText,
24868 cls: "prev btn-outline-secondary",
24869 html : ' <i class="fa fa-backward"></i>',
24871 preventDefault: true,
24872 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24874 //this.addSeparator();
24877 var field = this.navgroup.addItem( {
24879 cls : 'x-paging-position btn-outline-secondary',
24881 html : this.beforePageText +
24882 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24883 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24886 this.field = field.el.select('input', true).first();
24887 this.field.on("keydown", this.onPagingKeydown, this);
24888 this.field.on("focus", function(){this.dom.select();});
24891 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24892 //this.field.setHeight(18);
24893 //this.addSeparator();
24894 this.next = this.navgroup.addItem({
24895 tooltip: this.nextText,
24896 cls: "next btn-outline-secondary",
24897 html : ' <i class="fa fa-forward"></i>',
24899 preventDefault: true,
24900 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24902 this.last = this.navgroup.addItem({
24903 tooltip: this.lastText,
24904 html : ' <i class="fa fa-step-forward"></i>',
24905 cls: "next btn-outline-secondary",
24907 preventDefault: true,
24908 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24910 //this.addSeparator();
24911 this.loading = this.navgroup.addItem({
24912 tooltip: this.refreshText,
24913 cls: "btn-outline-secondary",
24914 html : ' <i class="fa fa-refresh"></i>',
24915 preventDefault: true,
24916 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24922 updateInfo : function(){
24923 if(this.displayEl){
24924 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24925 var msg = count == 0 ?
24929 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24931 this.displayEl.update(msg);
24936 onLoad : function(ds, r, o)
24938 this.cursor = o.params.start ? o.params.start : 0;
24940 var d = this.getPageData(),
24945 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24946 this.field.dom.value = ap;
24947 this.first.setDisabled(ap == 1);
24948 this.prev.setDisabled(ap == 1);
24949 this.next.setDisabled(ap == ps);
24950 this.last.setDisabled(ap == ps);
24951 this.loading.enable();
24956 getPageData : function(){
24957 var total = this.ds.getTotalCount();
24960 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24961 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24966 onLoadError : function(){
24967 this.loading.enable();
24971 onPagingKeydown : function(e){
24972 var k = e.getKey();
24973 var d = this.getPageData();
24975 var v = this.field.dom.value, pageNum;
24976 if(!v || isNaN(pageNum = parseInt(v, 10))){
24977 this.field.dom.value = d.activePage;
24980 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24981 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24984 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))
24986 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24987 this.field.dom.value = pageNum;
24988 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24991 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24993 var v = this.field.dom.value, pageNum;
24994 var increment = (e.shiftKey) ? 10 : 1;
24995 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24998 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24999 this.field.dom.value = d.activePage;
25002 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25004 this.field.dom.value = parseInt(v, 10) + increment;
25005 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25006 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25013 beforeLoad : function(){
25015 this.loading.disable();
25020 onClick : function(which){
25029 ds.load({params:{start: 0, limit: this.pageSize}});
25032 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25035 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25038 var total = ds.getTotalCount();
25039 var extra = total % this.pageSize;
25040 var lastStart = extra ? (total - extra) : total-this.pageSize;
25041 ds.load({params:{start: lastStart, limit: this.pageSize}});
25044 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25050 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25051 * @param {Roo.data.Store} store The data store to unbind
25053 unbind : function(ds){
25054 ds.un("beforeload", this.beforeLoad, this);
25055 ds.un("load", this.onLoad, this);
25056 ds.un("loadexception", this.onLoadError, this);
25057 ds.un("remove", this.updateInfo, this);
25058 ds.un("add", this.updateInfo, this);
25059 this.ds = undefined;
25063 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25064 * @param {Roo.data.Store} store The data store to bind
25066 bind : function(ds){
25067 ds.on("beforeload", this.beforeLoad, this);
25068 ds.on("load", this.onLoad, this);
25069 ds.on("loadexception", this.onLoadError, this);
25070 ds.on("remove", this.updateInfo, this);
25071 ds.on("add", this.updateInfo, this);
25082 * @class Roo.bootstrap.MessageBar
25083 * @extends Roo.bootstrap.Component
25084 * Bootstrap MessageBar class
25085 * @cfg {String} html contents of the MessageBar
25086 * @cfg {String} weight (info | success | warning | danger) default info
25087 * @cfg {String} beforeClass insert the bar before the given class
25088 * @cfg {Boolean} closable (true | false) default false
25089 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25092 * Create a new Element
25093 * @param {Object} config The config object
25096 Roo.bootstrap.MessageBar = function(config){
25097 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25100 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25106 beforeClass: 'bootstrap-sticky-wrap',
25108 getAutoCreate : function(){
25112 cls: 'alert alert-dismissable alert-' + this.weight,
25117 html: this.html || ''
25123 cfg.cls += ' alert-messages-fixed';
25137 onRender : function(ct, position)
25139 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25142 var cfg = Roo.apply({}, this.getAutoCreate());
25146 cfg.cls += ' ' + this.cls;
25149 cfg.style = this.style;
25151 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25153 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25156 this.el.select('>button.close').on('click', this.hide, this);
25162 if (!this.rendered) {
25168 this.fireEvent('show', this);
25174 if (!this.rendered) {
25180 this.fireEvent('hide', this);
25183 update : function()
25185 // var e = this.el.dom.firstChild;
25187 // if(this.closable){
25188 // e = e.nextSibling;
25191 // e.data = this.html || '';
25193 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25209 * @class Roo.bootstrap.Graph
25210 * @extends Roo.bootstrap.Component
25211 * Bootstrap Graph class
25215 @cfg {String} graphtype bar | vbar | pie
25216 @cfg {number} g_x coodinator | centre x (pie)
25217 @cfg {number} g_y coodinator | centre y (pie)
25218 @cfg {number} g_r radius (pie)
25219 @cfg {number} g_height height of the chart (respected by all elements in the set)
25220 @cfg {number} g_width width of the chart (respected by all elements in the set)
25221 @cfg {Object} title The title of the chart
25224 -opts (object) options for the chart
25226 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25227 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25229 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.
25230 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25232 o stretch (boolean)
25234 -opts (object) options for the pie
25237 o startAngle (number)
25238 o endAngle (number)
25242 * Create a new Input
25243 * @param {Object} config The config object
25246 Roo.bootstrap.Graph = function(config){
25247 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25253 * The img click event for the img.
25254 * @param {Roo.EventObject} e
25260 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25271 //g_colors: this.colors,
25278 getAutoCreate : function(){
25289 onRender : function(ct,position){
25292 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25294 if (typeof(Raphael) == 'undefined') {
25295 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25299 this.raphael = Raphael(this.el.dom);
25301 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25302 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25303 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25304 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25306 r.text(160, 10, "Single Series Chart").attr(txtattr);
25307 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25308 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25309 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25311 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25312 r.barchart(330, 10, 300, 220, data1);
25313 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25314 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25317 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25318 // r.barchart(30, 30, 560, 250, xdata, {
25319 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25320 // axis : "0 0 1 1",
25321 // axisxlabels : xdata
25322 // //yvalues : cols,
25325 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25327 // this.load(null,xdata,{
25328 // axis : "0 0 1 1",
25329 // axisxlabels : xdata
25334 load : function(graphtype,xdata,opts)
25336 this.raphael.clear();
25338 graphtype = this.graphtype;
25343 var r = this.raphael,
25344 fin = function () {
25345 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25347 fout = function () {
25348 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25350 pfin = function() {
25351 this.sector.stop();
25352 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25355 this.label[0].stop();
25356 this.label[0].attr({ r: 7.5 });
25357 this.label[1].attr({ "font-weight": 800 });
25360 pfout = function() {
25361 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25364 this.label[0].animate({ r: 5 }, 500, "bounce");
25365 this.label[1].attr({ "font-weight": 400 });
25371 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25374 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25377 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25378 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25380 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25387 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25392 setTitle: function(o)
25397 initEvents: function() {
25400 this.el.on('click', this.onClick, this);
25404 onClick : function(e)
25406 Roo.log('img onclick');
25407 this.fireEvent('click', this, e);
25419 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25422 * @class Roo.bootstrap.dash.NumberBox
25423 * @extends Roo.bootstrap.Component
25424 * Bootstrap NumberBox class
25425 * @cfg {String} headline Box headline
25426 * @cfg {String} content Box content
25427 * @cfg {String} icon Box icon
25428 * @cfg {String} footer Footer text
25429 * @cfg {String} fhref Footer href
25432 * Create a new NumberBox
25433 * @param {Object} config The config object
25437 Roo.bootstrap.dash.NumberBox = function(config){
25438 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25442 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25451 getAutoCreate : function(){
25455 cls : 'small-box ',
25463 cls : 'roo-headline',
25464 html : this.headline
25468 cls : 'roo-content',
25469 html : this.content
25483 cls : 'ion ' + this.icon
25492 cls : 'small-box-footer',
25493 href : this.fhref || '#',
25497 cfg.cn.push(footer);
25504 onRender : function(ct,position){
25505 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25512 setHeadline: function (value)
25514 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25517 setFooter: function (value, href)
25519 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25522 this.el.select('a.small-box-footer',true).first().attr('href', href);
25527 setContent: function (value)
25529 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25532 initEvents: function()
25546 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25549 * @class Roo.bootstrap.dash.TabBox
25550 * @extends Roo.bootstrap.Component
25551 * Bootstrap TabBox class
25552 * @cfg {String} title Title of the TabBox
25553 * @cfg {String} icon Icon of the TabBox
25554 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25555 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25558 * Create a new TabBox
25559 * @param {Object} config The config object
25563 Roo.bootstrap.dash.TabBox = function(config){
25564 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25569 * When a pane is added
25570 * @param {Roo.bootstrap.dash.TabPane} pane
25574 * @event activatepane
25575 * When a pane is activated
25576 * @param {Roo.bootstrap.dash.TabPane} pane
25578 "activatepane" : true
25586 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25591 tabScrollable : false,
25593 getChildContainer : function()
25595 return this.el.select('.tab-content', true).first();
25598 getAutoCreate : function(){
25602 cls: 'pull-left header',
25610 cls: 'fa ' + this.icon
25616 cls: 'nav nav-tabs pull-right',
25622 if(this.tabScrollable){
25629 cls: 'nav nav-tabs pull-right',
25640 cls: 'nav-tabs-custom',
25645 cls: 'tab-content no-padding',
25653 initEvents : function()
25655 //Roo.log('add add pane handler');
25656 this.on('addpane', this.onAddPane, this);
25659 * Updates the box title
25660 * @param {String} html to set the title to.
25662 setTitle : function(value)
25664 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25666 onAddPane : function(pane)
25668 this.panes.push(pane);
25669 //Roo.log('addpane');
25671 // tabs are rendere left to right..
25672 if(!this.showtabs){
25676 var ctr = this.el.select('.nav-tabs', true).first();
25679 var existing = ctr.select('.nav-tab',true);
25680 var qty = existing.getCount();;
25683 var tab = ctr.createChild({
25685 cls : 'nav-tab' + (qty ? '' : ' active'),
25693 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25696 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25698 pane.el.addClass('active');
25703 onTabClick : function(ev,un,ob,pane)
25705 //Roo.log('tab - prev default');
25706 ev.preventDefault();
25709 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25710 pane.tab.addClass('active');
25711 //Roo.log(pane.title);
25712 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25713 // technically we should have a deactivate event.. but maybe add later.
25714 // and it should not de-activate the selected tab...
25715 this.fireEvent('activatepane', pane);
25716 pane.el.addClass('active');
25717 pane.fireEvent('activate');
25722 getActivePane : function()
25725 Roo.each(this.panes, function(p) {
25726 if(p.el.hasClass('active')){
25747 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25749 * @class Roo.bootstrap.TabPane
25750 * @extends Roo.bootstrap.Component
25751 * Bootstrap TabPane class
25752 * @cfg {Boolean} active (false | true) Default false
25753 * @cfg {String} title title of panel
25757 * Create a new TabPane
25758 * @param {Object} config The config object
25761 Roo.bootstrap.dash.TabPane = function(config){
25762 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25768 * When a pane is activated
25769 * @param {Roo.bootstrap.dash.TabPane} pane
25776 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25781 // the tabBox that this is attached to.
25784 getAutoCreate : function()
25792 cfg.cls += ' active';
25797 initEvents : function()
25799 //Roo.log('trigger add pane handler');
25800 this.parent().fireEvent('addpane', this)
25804 * Updates the tab title
25805 * @param {String} html to set the title to.
25807 setTitle: function(str)
25813 this.tab.select('a', true).first().dom.innerHTML = str;
25830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25833 * @class Roo.bootstrap.menu.Menu
25834 * @extends Roo.bootstrap.Component
25835 * Bootstrap Menu class - container for Menu
25836 * @cfg {String} html Text of the menu
25837 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25838 * @cfg {String} icon Font awesome icon
25839 * @cfg {String} pos Menu align to (top | bottom) default bottom
25843 * Create a new Menu
25844 * @param {Object} config The config object
25848 Roo.bootstrap.menu.Menu = function(config){
25849 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25853 * @event beforeshow
25854 * Fires before this menu is displayed
25855 * @param {Roo.bootstrap.menu.Menu} this
25859 * @event beforehide
25860 * Fires before this menu is hidden
25861 * @param {Roo.bootstrap.menu.Menu} this
25866 * Fires after this menu is displayed
25867 * @param {Roo.bootstrap.menu.Menu} this
25872 * Fires after this menu is hidden
25873 * @param {Roo.bootstrap.menu.Menu} this
25878 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25879 * @param {Roo.bootstrap.menu.Menu} this
25880 * @param {Roo.EventObject} e
25887 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25891 weight : 'default',
25896 getChildContainer : function() {
25897 if(this.isSubMenu){
25901 return this.el.select('ul.dropdown-menu', true).first();
25904 getAutoCreate : function()
25909 cls : 'roo-menu-text',
25917 cls : 'fa ' + this.icon
25928 cls : 'dropdown-button btn btn-' + this.weight,
25933 cls : 'dropdown-toggle btn btn-' + this.weight,
25943 cls : 'dropdown-menu'
25949 if(this.pos == 'top'){
25950 cfg.cls += ' dropup';
25953 if(this.isSubMenu){
25956 cls : 'dropdown-menu'
25963 onRender : function(ct, position)
25965 this.isSubMenu = ct.hasClass('dropdown-submenu');
25967 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25970 initEvents : function()
25972 if(this.isSubMenu){
25976 this.hidden = true;
25978 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25979 this.triggerEl.on('click', this.onTriggerPress, this);
25981 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25982 this.buttonEl.on('click', this.onClick, this);
25988 if(this.isSubMenu){
25992 return this.el.select('ul.dropdown-menu', true).first();
25995 onClick : function(e)
25997 this.fireEvent("click", this, e);
26000 onTriggerPress : function(e)
26002 if (this.isVisible()) {
26009 isVisible : function(){
26010 return !this.hidden;
26015 this.fireEvent("beforeshow", this);
26017 this.hidden = false;
26018 this.el.addClass('open');
26020 Roo.get(document).on("mouseup", this.onMouseUp, this);
26022 this.fireEvent("show", this);
26029 this.fireEvent("beforehide", this);
26031 this.hidden = true;
26032 this.el.removeClass('open');
26034 Roo.get(document).un("mouseup", this.onMouseUp);
26036 this.fireEvent("hide", this);
26039 onMouseUp : function()
26053 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26056 * @class Roo.bootstrap.menu.Item
26057 * @extends Roo.bootstrap.Component
26058 * Bootstrap MenuItem class
26059 * @cfg {Boolean} submenu (true | false) default false
26060 * @cfg {String} html text of the item
26061 * @cfg {String} href the link
26062 * @cfg {Boolean} disable (true | false) default false
26063 * @cfg {Boolean} preventDefault (true | false) default true
26064 * @cfg {String} icon Font awesome icon
26065 * @cfg {String} pos Submenu align to (left | right) default right
26069 * Create a new Item
26070 * @param {Object} config The config object
26074 Roo.bootstrap.menu.Item = function(config){
26075 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26079 * Fires when the mouse is hovering over this menu
26080 * @param {Roo.bootstrap.menu.Item} this
26081 * @param {Roo.EventObject} e
26086 * Fires when the mouse exits this menu
26087 * @param {Roo.bootstrap.menu.Item} this
26088 * @param {Roo.EventObject} e
26094 * The raw click event for the entire grid.
26095 * @param {Roo.EventObject} e
26101 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26106 preventDefault: true,
26111 getAutoCreate : function()
26116 cls : 'roo-menu-item-text',
26124 cls : 'fa ' + this.icon
26133 href : this.href || '#',
26140 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26144 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26146 if(this.pos == 'left'){
26147 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26154 initEvents : function()
26156 this.el.on('mouseover', this.onMouseOver, this);
26157 this.el.on('mouseout', this.onMouseOut, this);
26159 this.el.select('a', true).first().on('click', this.onClick, this);
26163 onClick : function(e)
26165 if(this.preventDefault){
26166 e.preventDefault();
26169 this.fireEvent("click", this, e);
26172 onMouseOver : function(e)
26174 if(this.submenu && this.pos == 'left'){
26175 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26178 this.fireEvent("mouseover", this, e);
26181 onMouseOut : function(e)
26183 this.fireEvent("mouseout", this, e);
26195 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26198 * @class Roo.bootstrap.menu.Separator
26199 * @extends Roo.bootstrap.Component
26200 * Bootstrap Separator class
26203 * Create a new Separator
26204 * @param {Object} config The config object
26208 Roo.bootstrap.menu.Separator = function(config){
26209 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26212 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26214 getAutoCreate : function(){
26235 * @class Roo.bootstrap.Tooltip
26236 * Bootstrap Tooltip class
26237 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26238 * to determine which dom element triggers the tooltip.
26240 * It needs to add support for additional attributes like tooltip-position
26243 * Create a new Toolti
26244 * @param {Object} config The config object
26247 Roo.bootstrap.Tooltip = function(config){
26248 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26250 this.alignment = Roo.bootstrap.Tooltip.alignment;
26252 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26253 this.alignment = config.alignment;
26258 Roo.apply(Roo.bootstrap.Tooltip, {
26260 * @function init initialize tooltip monitoring.
26264 currentTip : false,
26265 currentRegion : false,
26271 Roo.get(document).on('mouseover', this.enter ,this);
26272 Roo.get(document).on('mouseout', this.leave, this);
26275 this.currentTip = new Roo.bootstrap.Tooltip();
26278 enter : function(ev)
26280 var dom = ev.getTarget();
26282 //Roo.log(['enter',dom]);
26283 var el = Roo.fly(dom);
26284 if (this.currentEl) {
26286 //Roo.log(this.currentEl);
26287 //Roo.log(this.currentEl.contains(dom));
26288 if (this.currentEl == el) {
26291 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26297 if (this.currentTip.el) {
26298 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26302 if(!el || el.dom == document){
26308 // you can not look for children, as if el is the body.. then everythign is the child..
26309 if (!el.attr('tooltip')) { //
26310 if (!el.select("[tooltip]").elements.length) {
26313 // is the mouse over this child...?
26314 bindEl = el.select("[tooltip]").first();
26315 var xy = ev.getXY();
26316 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26317 //Roo.log("not in region.");
26320 //Roo.log("child element over..");
26323 this.currentEl = bindEl;
26324 this.currentTip.bind(bindEl);
26325 this.currentRegion = Roo.lib.Region.getRegion(dom);
26326 this.currentTip.enter();
26329 leave : function(ev)
26331 var dom = ev.getTarget();
26332 //Roo.log(['leave',dom]);
26333 if (!this.currentEl) {
26338 if (dom != this.currentEl.dom) {
26341 var xy = ev.getXY();
26342 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26345 // only activate leave if mouse cursor is outside... bounding box..
26350 if (this.currentTip) {
26351 this.currentTip.leave();
26353 //Roo.log('clear currentEl');
26354 this.currentEl = false;
26359 'left' : ['r-l', [-2,0], 'right'],
26360 'right' : ['l-r', [2,0], 'left'],
26361 'bottom' : ['t-b', [0,2], 'top'],
26362 'top' : [ 'b-t', [0,-2], 'bottom']
26368 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26373 delay : null, // can be { show : 300 , hide: 500}
26377 hoverState : null, //???
26379 placement : 'bottom',
26383 getAutoCreate : function(){
26390 cls : 'tooltip-arrow'
26393 cls : 'tooltip-inner'
26400 bind : function(el)
26406 enter : function () {
26408 if (this.timeout != null) {
26409 clearTimeout(this.timeout);
26412 this.hoverState = 'in';
26413 //Roo.log("enter - show");
26414 if (!this.delay || !this.delay.show) {
26419 this.timeout = setTimeout(function () {
26420 if (_t.hoverState == 'in') {
26423 }, this.delay.show);
26427 clearTimeout(this.timeout);
26429 this.hoverState = 'out';
26430 if (!this.delay || !this.delay.hide) {
26436 this.timeout = setTimeout(function () {
26437 //Roo.log("leave - timeout");
26439 if (_t.hoverState == 'out') {
26441 Roo.bootstrap.Tooltip.currentEl = false;
26446 show : function (msg)
26449 this.render(document.body);
26452 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26454 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26456 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26458 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26460 var placement = typeof this.placement == 'function' ?
26461 this.placement.call(this, this.el, on_el) :
26464 var autoToken = /\s?auto?\s?/i;
26465 var autoPlace = autoToken.test(placement);
26467 placement = placement.replace(autoToken, '') || 'top';
26471 //this.el.setXY([0,0]);
26473 //this.el.dom.style.display='block';
26475 //this.el.appendTo(on_el);
26477 var p = this.getPosition();
26478 var box = this.el.getBox();
26484 var align = this.alignment[placement];
26486 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26488 if(placement == 'top' || placement == 'bottom'){
26490 placement = 'right';
26493 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26494 placement = 'left';
26497 var scroll = Roo.select('body', true).first().getScroll();
26499 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26503 align = this.alignment[placement];
26506 this.el.alignTo(this.bindEl, align[0],align[1]);
26507 //var arrow = this.el.select('.arrow',true).first();
26508 //arrow.set(align[2],
26510 this.el.addClass(placement);
26512 this.el.addClass('in fade');
26514 this.hoverState = null;
26516 if (this.el.hasClass('fade')) {
26527 //this.el.setXY([0,0]);
26528 this.el.removeClass('in');
26544 * @class Roo.bootstrap.LocationPicker
26545 * @extends Roo.bootstrap.Component
26546 * Bootstrap LocationPicker class
26547 * @cfg {Number} latitude Position when init default 0
26548 * @cfg {Number} longitude Position when init default 0
26549 * @cfg {Number} zoom default 15
26550 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26551 * @cfg {Boolean} mapTypeControl default false
26552 * @cfg {Boolean} disableDoubleClickZoom default false
26553 * @cfg {Boolean} scrollwheel default true
26554 * @cfg {Boolean} streetViewControl default false
26555 * @cfg {Number} radius default 0
26556 * @cfg {String} locationName
26557 * @cfg {Boolean} draggable default true
26558 * @cfg {Boolean} enableAutocomplete default false
26559 * @cfg {Boolean} enableReverseGeocode default true
26560 * @cfg {String} markerTitle
26563 * Create a new LocationPicker
26564 * @param {Object} config The config object
26568 Roo.bootstrap.LocationPicker = function(config){
26570 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26575 * Fires when the picker initialized.
26576 * @param {Roo.bootstrap.LocationPicker} this
26577 * @param {Google Location} location
26581 * @event positionchanged
26582 * Fires when the picker position changed.
26583 * @param {Roo.bootstrap.LocationPicker} this
26584 * @param {Google Location} location
26586 positionchanged : true,
26589 * Fires when the map resize.
26590 * @param {Roo.bootstrap.LocationPicker} this
26595 * Fires when the map show.
26596 * @param {Roo.bootstrap.LocationPicker} this
26601 * Fires when the map hide.
26602 * @param {Roo.bootstrap.LocationPicker} this
26607 * Fires when click the map.
26608 * @param {Roo.bootstrap.LocationPicker} this
26609 * @param {Map event} e
26613 * @event mapRightClick
26614 * Fires when right click the map.
26615 * @param {Roo.bootstrap.LocationPicker} this
26616 * @param {Map event} e
26618 mapRightClick : true,
26620 * @event markerClick
26621 * Fires when click the marker.
26622 * @param {Roo.bootstrap.LocationPicker} this
26623 * @param {Map event} e
26625 markerClick : true,
26627 * @event markerRightClick
26628 * Fires when right click the marker.
26629 * @param {Roo.bootstrap.LocationPicker} this
26630 * @param {Map event} e
26632 markerRightClick : true,
26634 * @event OverlayViewDraw
26635 * Fires when OverlayView Draw
26636 * @param {Roo.bootstrap.LocationPicker} this
26638 OverlayViewDraw : true,
26640 * @event OverlayViewOnAdd
26641 * Fires when OverlayView Draw
26642 * @param {Roo.bootstrap.LocationPicker} this
26644 OverlayViewOnAdd : true,
26646 * @event OverlayViewOnRemove
26647 * Fires when OverlayView Draw
26648 * @param {Roo.bootstrap.LocationPicker} this
26650 OverlayViewOnRemove : true,
26652 * @event OverlayViewShow
26653 * Fires when OverlayView Draw
26654 * @param {Roo.bootstrap.LocationPicker} this
26655 * @param {Pixel} cpx
26657 OverlayViewShow : true,
26659 * @event OverlayViewHide
26660 * Fires when OverlayView Draw
26661 * @param {Roo.bootstrap.LocationPicker} this
26663 OverlayViewHide : true,
26665 * @event loadexception
26666 * Fires when load google lib failed.
26667 * @param {Roo.bootstrap.LocationPicker} this
26669 loadexception : true
26674 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26676 gMapContext: false,
26682 mapTypeControl: false,
26683 disableDoubleClickZoom: false,
26685 streetViewControl: false,
26689 enableAutocomplete: false,
26690 enableReverseGeocode: true,
26693 getAutoCreate: function()
26698 cls: 'roo-location-picker'
26704 initEvents: function(ct, position)
26706 if(!this.el.getWidth() || this.isApplied()){
26710 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26715 initial: function()
26717 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26718 this.fireEvent('loadexception', this);
26722 if(!this.mapTypeId){
26723 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26726 this.gMapContext = this.GMapContext();
26728 this.initOverlayView();
26730 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26734 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26735 _this.setPosition(_this.gMapContext.marker.position);
26738 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26739 _this.fireEvent('mapClick', this, event);
26743 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26744 _this.fireEvent('mapRightClick', this, event);
26748 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26749 _this.fireEvent('markerClick', this, event);
26753 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26754 _this.fireEvent('markerRightClick', this, event);
26758 this.setPosition(this.gMapContext.location);
26760 this.fireEvent('initial', this, this.gMapContext.location);
26763 initOverlayView: function()
26767 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26771 _this.fireEvent('OverlayViewDraw', _this);
26776 _this.fireEvent('OverlayViewOnAdd', _this);
26779 onRemove: function()
26781 _this.fireEvent('OverlayViewOnRemove', _this);
26784 show: function(cpx)
26786 _this.fireEvent('OverlayViewShow', _this, cpx);
26791 _this.fireEvent('OverlayViewHide', _this);
26797 fromLatLngToContainerPixel: function(event)
26799 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26802 isApplied: function()
26804 return this.getGmapContext() == false ? false : true;
26807 getGmapContext: function()
26809 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26812 GMapContext: function()
26814 var position = new google.maps.LatLng(this.latitude, this.longitude);
26816 var _map = new google.maps.Map(this.el.dom, {
26819 mapTypeId: this.mapTypeId,
26820 mapTypeControl: this.mapTypeControl,
26821 disableDoubleClickZoom: this.disableDoubleClickZoom,
26822 scrollwheel: this.scrollwheel,
26823 streetViewControl: this.streetViewControl,
26824 locationName: this.locationName,
26825 draggable: this.draggable,
26826 enableAutocomplete: this.enableAutocomplete,
26827 enableReverseGeocode: this.enableReverseGeocode
26830 var _marker = new google.maps.Marker({
26831 position: position,
26833 title: this.markerTitle,
26834 draggable: this.draggable
26841 location: position,
26842 radius: this.radius,
26843 locationName: this.locationName,
26844 addressComponents: {
26845 formatted_address: null,
26846 addressLine1: null,
26847 addressLine2: null,
26849 streetNumber: null,
26853 stateOrProvince: null
26856 domContainer: this.el.dom,
26857 geodecoder: new google.maps.Geocoder()
26861 drawCircle: function(center, radius, options)
26863 if (this.gMapContext.circle != null) {
26864 this.gMapContext.circle.setMap(null);
26868 options = Roo.apply({}, options, {
26869 strokeColor: "#0000FF",
26870 strokeOpacity: .35,
26872 fillColor: "#0000FF",
26876 options.map = this.gMapContext.map;
26877 options.radius = radius;
26878 options.center = center;
26879 this.gMapContext.circle = new google.maps.Circle(options);
26880 return this.gMapContext.circle;
26886 setPosition: function(location)
26888 this.gMapContext.location = location;
26889 this.gMapContext.marker.setPosition(location);
26890 this.gMapContext.map.panTo(location);
26891 this.drawCircle(location, this.gMapContext.radius, {});
26895 if (this.gMapContext.settings.enableReverseGeocode) {
26896 this.gMapContext.geodecoder.geocode({
26897 latLng: this.gMapContext.location
26898 }, function(results, status) {
26900 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26901 _this.gMapContext.locationName = results[0].formatted_address;
26902 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26904 _this.fireEvent('positionchanged', this, location);
26911 this.fireEvent('positionchanged', this, location);
26916 google.maps.event.trigger(this.gMapContext.map, "resize");
26918 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26920 this.fireEvent('resize', this);
26923 setPositionByLatLng: function(latitude, longitude)
26925 this.setPosition(new google.maps.LatLng(latitude, longitude));
26928 getCurrentPosition: function()
26931 latitude: this.gMapContext.location.lat(),
26932 longitude: this.gMapContext.location.lng()
26936 getAddressName: function()
26938 return this.gMapContext.locationName;
26941 getAddressComponents: function()
26943 return this.gMapContext.addressComponents;
26946 address_component_from_google_geocode: function(address_components)
26950 for (var i = 0; i < address_components.length; i++) {
26951 var component = address_components[i];
26952 if (component.types.indexOf("postal_code") >= 0) {
26953 result.postalCode = component.short_name;
26954 } else if (component.types.indexOf("street_number") >= 0) {
26955 result.streetNumber = component.short_name;
26956 } else if (component.types.indexOf("route") >= 0) {
26957 result.streetName = component.short_name;
26958 } else if (component.types.indexOf("neighborhood") >= 0) {
26959 result.city = component.short_name;
26960 } else if (component.types.indexOf("locality") >= 0) {
26961 result.city = component.short_name;
26962 } else if (component.types.indexOf("sublocality") >= 0) {
26963 result.district = component.short_name;
26964 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26965 result.stateOrProvince = component.short_name;
26966 } else if (component.types.indexOf("country") >= 0) {
26967 result.country = component.short_name;
26971 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26972 result.addressLine2 = "";
26976 setZoomLevel: function(zoom)
26978 this.gMapContext.map.setZoom(zoom);
26991 this.fireEvent('show', this);
27002 this.fireEvent('hide', this);
27007 Roo.apply(Roo.bootstrap.LocationPicker, {
27009 OverlayView : function(map, options)
27011 options = options || {};
27025 * @class Roo.bootstrap.Alert
27026 * @extends Roo.bootstrap.Component
27027 * Bootstrap Alert class
27028 * @cfg {String} title The title of alert
27029 * @cfg {String} html The content of alert
27030 * @cfg {String} weight ( success | info | warning | danger )
27031 * @cfg {String} faicon font-awesomeicon
27034 * Create a new alert
27035 * @param {Object} config The config object
27039 Roo.bootstrap.Alert = function(config){
27040 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27044 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27051 getAutoCreate : function()
27060 cls : 'roo-alert-icon'
27065 cls : 'roo-alert-title',
27070 cls : 'roo-alert-text',
27077 cfg.cn[0].cls += ' fa ' + this.faicon;
27081 cfg.cls += ' alert-' + this.weight;
27087 initEvents: function()
27089 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27092 setTitle : function(str)
27094 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27097 setText : function(str)
27099 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27102 setWeight : function(weight)
27105 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27108 this.weight = weight;
27110 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27113 setIcon : function(icon)
27116 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27119 this.faicon = icon;
27121 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27142 * @class Roo.bootstrap.UploadCropbox
27143 * @extends Roo.bootstrap.Component
27144 * Bootstrap UploadCropbox class
27145 * @cfg {String} emptyText show when image has been loaded
27146 * @cfg {String} rotateNotify show when image too small to rotate
27147 * @cfg {Number} errorTimeout default 3000
27148 * @cfg {Number} minWidth default 300
27149 * @cfg {Number} minHeight default 300
27150 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27151 * @cfg {Boolean} isDocument (true|false) default false
27152 * @cfg {String} url action url
27153 * @cfg {String} paramName default 'imageUpload'
27154 * @cfg {String} method default POST
27155 * @cfg {Boolean} loadMask (true|false) default true
27156 * @cfg {Boolean} loadingText default 'Loading...'
27159 * Create a new UploadCropbox
27160 * @param {Object} config The config object
27163 Roo.bootstrap.UploadCropbox = function(config){
27164 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27168 * @event beforeselectfile
27169 * Fire before select file
27170 * @param {Roo.bootstrap.UploadCropbox} this
27172 "beforeselectfile" : true,
27175 * Fire after initEvent
27176 * @param {Roo.bootstrap.UploadCropbox} this
27181 * Fire after initEvent
27182 * @param {Roo.bootstrap.UploadCropbox} this
27183 * @param {String} data
27188 * Fire when preparing the file data
27189 * @param {Roo.bootstrap.UploadCropbox} this
27190 * @param {Object} file
27195 * Fire when get exception
27196 * @param {Roo.bootstrap.UploadCropbox} this
27197 * @param {XMLHttpRequest} xhr
27199 "exception" : true,
27201 * @event beforeloadcanvas
27202 * Fire before load the canvas
27203 * @param {Roo.bootstrap.UploadCropbox} this
27204 * @param {String} src
27206 "beforeloadcanvas" : true,
27209 * Fire when trash image
27210 * @param {Roo.bootstrap.UploadCropbox} this
27215 * Fire when download the image
27216 * @param {Roo.bootstrap.UploadCropbox} this
27220 * @event footerbuttonclick
27221 * Fire when footerbuttonclick
27222 * @param {Roo.bootstrap.UploadCropbox} this
27223 * @param {String} type
27225 "footerbuttonclick" : true,
27229 * @param {Roo.bootstrap.UploadCropbox} this
27234 * Fire when rotate the image
27235 * @param {Roo.bootstrap.UploadCropbox} this
27236 * @param {String} pos
27241 * Fire when inspect the file
27242 * @param {Roo.bootstrap.UploadCropbox} this
27243 * @param {Object} file
27248 * Fire when xhr upload the file
27249 * @param {Roo.bootstrap.UploadCropbox} this
27250 * @param {Object} data
27255 * Fire when arrange the file data
27256 * @param {Roo.bootstrap.UploadCropbox} this
27257 * @param {Object} formData
27262 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27265 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27267 emptyText : 'Click to upload image',
27268 rotateNotify : 'Image is too small to rotate',
27269 errorTimeout : 3000,
27283 cropType : 'image/jpeg',
27285 canvasLoaded : false,
27286 isDocument : false,
27288 paramName : 'imageUpload',
27290 loadingText : 'Loading...',
27293 getAutoCreate : function()
27297 cls : 'roo-upload-cropbox',
27301 cls : 'roo-upload-cropbox-selector',
27306 cls : 'roo-upload-cropbox-body',
27307 style : 'cursor:pointer',
27311 cls : 'roo-upload-cropbox-preview'
27315 cls : 'roo-upload-cropbox-thumb'
27319 cls : 'roo-upload-cropbox-empty-notify',
27320 html : this.emptyText
27324 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27325 html : this.rotateNotify
27331 cls : 'roo-upload-cropbox-footer',
27334 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27344 onRender : function(ct, position)
27346 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27348 if (this.buttons.length) {
27350 Roo.each(this.buttons, function(bb) {
27352 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27354 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27360 this.maskEl = this.el;
27364 initEvents : function()
27366 this.urlAPI = (window.createObjectURL && window) ||
27367 (window.URL && URL.revokeObjectURL && URL) ||
27368 (window.webkitURL && webkitURL);
27370 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27371 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27373 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27374 this.selectorEl.hide();
27376 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27377 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27379 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27380 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27381 this.thumbEl.hide();
27383 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27384 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27386 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27387 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27388 this.errorEl.hide();
27390 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27391 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27392 this.footerEl.hide();
27394 this.setThumbBoxSize();
27400 this.fireEvent('initial', this);
27407 window.addEventListener("resize", function() { _this.resize(); } );
27409 this.bodyEl.on('click', this.beforeSelectFile, this);
27412 this.bodyEl.on('touchstart', this.onTouchStart, this);
27413 this.bodyEl.on('touchmove', this.onTouchMove, this);
27414 this.bodyEl.on('touchend', this.onTouchEnd, this);
27418 this.bodyEl.on('mousedown', this.onMouseDown, this);
27419 this.bodyEl.on('mousemove', this.onMouseMove, this);
27420 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27421 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27422 Roo.get(document).on('mouseup', this.onMouseUp, this);
27425 this.selectorEl.on('change', this.onFileSelected, this);
27431 this.baseScale = 1;
27433 this.baseRotate = 1;
27434 this.dragable = false;
27435 this.pinching = false;
27438 this.cropData = false;
27439 this.notifyEl.dom.innerHTML = this.emptyText;
27441 this.selectorEl.dom.value = '';
27445 resize : function()
27447 if(this.fireEvent('resize', this) != false){
27448 this.setThumbBoxPosition();
27449 this.setCanvasPosition();
27453 onFooterButtonClick : function(e, el, o, type)
27456 case 'rotate-left' :
27457 this.onRotateLeft(e);
27459 case 'rotate-right' :
27460 this.onRotateRight(e);
27463 this.beforeSelectFile(e);
27478 this.fireEvent('footerbuttonclick', this, type);
27481 beforeSelectFile : function(e)
27483 e.preventDefault();
27485 if(this.fireEvent('beforeselectfile', this) != false){
27486 this.selectorEl.dom.click();
27490 onFileSelected : function(e)
27492 e.preventDefault();
27494 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27498 var file = this.selectorEl.dom.files[0];
27500 if(this.fireEvent('inspect', this, file) != false){
27501 this.prepare(file);
27506 trash : function(e)
27508 this.fireEvent('trash', this);
27511 download : function(e)
27513 this.fireEvent('download', this);
27516 loadCanvas : function(src)
27518 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27522 this.imageEl = document.createElement('img');
27526 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27528 this.imageEl.src = src;
27532 onLoadCanvas : function()
27534 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27535 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27537 this.bodyEl.un('click', this.beforeSelectFile, this);
27539 this.notifyEl.hide();
27540 this.thumbEl.show();
27541 this.footerEl.show();
27543 this.baseRotateLevel();
27545 if(this.isDocument){
27546 this.setThumbBoxSize();
27549 this.setThumbBoxPosition();
27551 this.baseScaleLevel();
27557 this.canvasLoaded = true;
27560 this.maskEl.unmask();
27565 setCanvasPosition : function()
27567 if(!this.canvasEl){
27571 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27572 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27574 this.previewEl.setLeft(pw);
27575 this.previewEl.setTop(ph);
27579 onMouseDown : function(e)
27583 this.dragable = true;
27584 this.pinching = false;
27586 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27587 this.dragable = false;
27591 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27592 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27596 onMouseMove : function(e)
27600 if(!this.canvasLoaded){
27604 if (!this.dragable){
27608 var minX = Math.ceil(this.thumbEl.getLeft(true));
27609 var minY = Math.ceil(this.thumbEl.getTop(true));
27611 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27612 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27614 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27615 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27617 x = x - this.mouseX;
27618 y = y - this.mouseY;
27620 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27621 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27623 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27624 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27626 this.previewEl.setLeft(bgX);
27627 this.previewEl.setTop(bgY);
27629 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27630 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27633 onMouseUp : function(e)
27637 this.dragable = false;
27640 onMouseWheel : function(e)
27644 this.startScale = this.scale;
27646 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27648 if(!this.zoomable()){
27649 this.scale = this.startScale;
27658 zoomable : function()
27660 var minScale = this.thumbEl.getWidth() / this.minWidth;
27662 if(this.minWidth < this.minHeight){
27663 minScale = this.thumbEl.getHeight() / this.minHeight;
27666 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27667 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27671 (this.rotate == 0 || this.rotate == 180) &&
27673 width > this.imageEl.OriginWidth ||
27674 height > this.imageEl.OriginHeight ||
27675 (width < this.minWidth && height < this.minHeight)
27683 (this.rotate == 90 || this.rotate == 270) &&
27685 width > this.imageEl.OriginWidth ||
27686 height > this.imageEl.OriginHeight ||
27687 (width < this.minHeight && height < this.minWidth)
27694 !this.isDocument &&
27695 (this.rotate == 0 || this.rotate == 180) &&
27697 width < this.minWidth ||
27698 width > this.imageEl.OriginWidth ||
27699 height < this.minHeight ||
27700 height > this.imageEl.OriginHeight
27707 !this.isDocument &&
27708 (this.rotate == 90 || this.rotate == 270) &&
27710 width < this.minHeight ||
27711 width > this.imageEl.OriginWidth ||
27712 height < this.minWidth ||
27713 height > this.imageEl.OriginHeight
27723 onRotateLeft : function(e)
27725 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27727 var minScale = this.thumbEl.getWidth() / this.minWidth;
27729 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27730 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27732 this.startScale = this.scale;
27734 while (this.getScaleLevel() < minScale){
27736 this.scale = this.scale + 1;
27738 if(!this.zoomable()){
27743 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27744 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27749 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27756 this.scale = this.startScale;
27758 this.onRotateFail();
27763 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27765 if(this.isDocument){
27766 this.setThumbBoxSize();
27767 this.setThumbBoxPosition();
27768 this.setCanvasPosition();
27773 this.fireEvent('rotate', this, 'left');
27777 onRotateRight : function(e)
27779 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27781 var minScale = this.thumbEl.getWidth() / this.minWidth;
27783 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27784 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27786 this.startScale = this.scale;
27788 while (this.getScaleLevel() < minScale){
27790 this.scale = this.scale + 1;
27792 if(!this.zoomable()){
27797 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27798 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27803 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27810 this.scale = this.startScale;
27812 this.onRotateFail();
27817 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27819 if(this.isDocument){
27820 this.setThumbBoxSize();
27821 this.setThumbBoxPosition();
27822 this.setCanvasPosition();
27827 this.fireEvent('rotate', this, 'right');
27830 onRotateFail : function()
27832 this.errorEl.show(true);
27836 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27841 this.previewEl.dom.innerHTML = '';
27843 var canvasEl = document.createElement("canvas");
27845 var contextEl = canvasEl.getContext("2d");
27847 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27848 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27849 var center = this.imageEl.OriginWidth / 2;
27851 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27852 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27853 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27854 center = this.imageEl.OriginHeight / 2;
27857 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27859 contextEl.translate(center, center);
27860 contextEl.rotate(this.rotate * Math.PI / 180);
27862 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27864 this.canvasEl = document.createElement("canvas");
27866 this.contextEl = this.canvasEl.getContext("2d");
27868 switch (this.rotate) {
27871 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27872 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27874 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27879 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27880 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27882 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27883 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);
27887 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27892 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27893 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27895 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27896 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);
27900 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);
27905 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27906 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27908 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27909 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27913 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);
27920 this.previewEl.appendChild(this.canvasEl);
27922 this.setCanvasPosition();
27927 if(!this.canvasLoaded){
27931 var imageCanvas = document.createElement("canvas");
27933 var imageContext = imageCanvas.getContext("2d");
27935 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27936 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27938 var center = imageCanvas.width / 2;
27940 imageContext.translate(center, center);
27942 imageContext.rotate(this.rotate * Math.PI / 180);
27944 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27946 var canvas = document.createElement("canvas");
27948 var context = canvas.getContext("2d");
27950 canvas.width = this.minWidth;
27951 canvas.height = this.minHeight;
27953 switch (this.rotate) {
27956 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27957 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27959 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27962 var targetWidth = this.minWidth - 2 * x;
27963 var targetHeight = this.minHeight - 2 * y;
27967 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968 scale = targetWidth / width;
27971 if(x > 0 && y == 0){
27972 scale = targetHeight / height;
27975 if(x > 0 && y > 0){
27976 scale = targetWidth / width;
27978 if(width < height){
27979 scale = targetHeight / height;
27983 context.scale(scale, scale);
27985 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27988 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27991 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27996 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27997 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27999 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28000 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28002 var targetWidth = this.minWidth - 2 * x;
28003 var targetHeight = this.minHeight - 2 * y;
28007 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28008 scale = targetWidth / width;
28011 if(x > 0 && y == 0){
28012 scale = targetHeight / height;
28015 if(x > 0 && y > 0){
28016 scale = targetWidth / width;
28018 if(width < height){
28019 scale = targetHeight / height;
28023 context.scale(scale, scale);
28025 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28026 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28028 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28029 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28031 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28033 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28038 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28039 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28041 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28042 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28044 var targetWidth = this.minWidth - 2 * x;
28045 var targetHeight = this.minHeight - 2 * y;
28049 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28050 scale = targetWidth / width;
28053 if(x > 0 && y == 0){
28054 scale = targetHeight / height;
28057 if(x > 0 && y > 0){
28058 scale = targetWidth / width;
28060 if(width < height){
28061 scale = targetHeight / height;
28065 context.scale(scale, scale);
28067 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28068 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28070 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28071 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28073 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28074 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28076 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28081 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28082 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28084 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28085 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28087 var targetWidth = this.minWidth - 2 * x;
28088 var targetHeight = this.minHeight - 2 * y;
28092 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28093 scale = targetWidth / width;
28096 if(x > 0 && y == 0){
28097 scale = targetHeight / height;
28100 if(x > 0 && y > 0){
28101 scale = targetWidth / width;
28103 if(width < height){
28104 scale = targetHeight / height;
28108 context.scale(scale, scale);
28110 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28111 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28113 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28114 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28116 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28118 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28125 this.cropData = canvas.toDataURL(this.cropType);
28127 if(this.fireEvent('crop', this, this.cropData) !== false){
28128 this.process(this.file, this.cropData);
28135 setThumbBoxSize : function()
28139 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28140 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28141 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28143 this.minWidth = width;
28144 this.minHeight = height;
28146 if(this.rotate == 90 || this.rotate == 270){
28147 this.minWidth = height;
28148 this.minHeight = width;
28153 width = Math.ceil(this.minWidth * height / this.minHeight);
28155 if(this.minWidth > this.minHeight){
28157 height = Math.ceil(this.minHeight * width / this.minWidth);
28160 this.thumbEl.setStyle({
28161 width : width + 'px',
28162 height : height + 'px'
28169 setThumbBoxPosition : function()
28171 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28172 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28174 this.thumbEl.setLeft(x);
28175 this.thumbEl.setTop(y);
28179 baseRotateLevel : function()
28181 this.baseRotate = 1;
28184 typeof(this.exif) != 'undefined' &&
28185 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28186 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28188 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28191 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28195 baseScaleLevel : function()
28199 if(this.isDocument){
28201 if(this.baseRotate == 6 || this.baseRotate == 8){
28203 height = this.thumbEl.getHeight();
28204 this.baseScale = height / this.imageEl.OriginWidth;
28206 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28207 width = this.thumbEl.getWidth();
28208 this.baseScale = width / this.imageEl.OriginHeight;
28214 height = this.thumbEl.getHeight();
28215 this.baseScale = height / this.imageEl.OriginHeight;
28217 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28218 width = this.thumbEl.getWidth();
28219 this.baseScale = width / this.imageEl.OriginWidth;
28225 if(this.baseRotate == 6 || this.baseRotate == 8){
28227 width = this.thumbEl.getHeight();
28228 this.baseScale = width / this.imageEl.OriginHeight;
28230 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28231 height = this.thumbEl.getWidth();
28232 this.baseScale = height / this.imageEl.OriginHeight;
28235 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28236 height = this.thumbEl.getWidth();
28237 this.baseScale = height / this.imageEl.OriginHeight;
28239 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28240 width = this.thumbEl.getHeight();
28241 this.baseScale = width / this.imageEl.OriginWidth;
28248 width = this.thumbEl.getWidth();
28249 this.baseScale = width / this.imageEl.OriginWidth;
28251 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28252 height = this.thumbEl.getHeight();
28253 this.baseScale = height / this.imageEl.OriginHeight;
28256 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28258 height = this.thumbEl.getHeight();
28259 this.baseScale = height / this.imageEl.OriginHeight;
28261 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28262 width = this.thumbEl.getWidth();
28263 this.baseScale = width / this.imageEl.OriginWidth;
28271 getScaleLevel : function()
28273 return this.baseScale * Math.pow(1.1, this.scale);
28276 onTouchStart : function(e)
28278 if(!this.canvasLoaded){
28279 this.beforeSelectFile(e);
28283 var touches = e.browserEvent.touches;
28289 if(touches.length == 1){
28290 this.onMouseDown(e);
28294 if(touches.length != 2){
28300 for(var i = 0, finger; finger = touches[i]; i++){
28301 coords.push(finger.pageX, finger.pageY);
28304 var x = Math.pow(coords[0] - coords[2], 2);
28305 var y = Math.pow(coords[1] - coords[3], 2);
28307 this.startDistance = Math.sqrt(x + y);
28309 this.startScale = this.scale;
28311 this.pinching = true;
28312 this.dragable = false;
28316 onTouchMove : function(e)
28318 if(!this.pinching && !this.dragable){
28322 var touches = e.browserEvent.touches;
28329 this.onMouseMove(e);
28335 for(var i = 0, finger; finger = touches[i]; i++){
28336 coords.push(finger.pageX, finger.pageY);
28339 var x = Math.pow(coords[0] - coords[2], 2);
28340 var y = Math.pow(coords[1] - coords[3], 2);
28342 this.endDistance = Math.sqrt(x + y);
28344 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28346 if(!this.zoomable()){
28347 this.scale = this.startScale;
28355 onTouchEnd : function(e)
28357 this.pinching = false;
28358 this.dragable = false;
28362 process : function(file, crop)
28365 this.maskEl.mask(this.loadingText);
28368 this.xhr = new XMLHttpRequest();
28370 file.xhr = this.xhr;
28372 this.xhr.open(this.method, this.url, true);
28375 "Accept": "application/json",
28376 "Cache-Control": "no-cache",
28377 "X-Requested-With": "XMLHttpRequest"
28380 for (var headerName in headers) {
28381 var headerValue = headers[headerName];
28383 this.xhr.setRequestHeader(headerName, headerValue);
28389 this.xhr.onload = function()
28391 _this.xhrOnLoad(_this.xhr);
28394 this.xhr.onerror = function()
28396 _this.xhrOnError(_this.xhr);
28399 var formData = new FormData();
28401 formData.append('returnHTML', 'NO');
28404 formData.append('crop', crop);
28407 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28408 formData.append(this.paramName, file, file.name);
28411 if(typeof(file.filename) != 'undefined'){
28412 formData.append('filename', file.filename);
28415 if(typeof(file.mimetype) != 'undefined'){
28416 formData.append('mimetype', file.mimetype);
28419 if(this.fireEvent('arrange', this, formData) != false){
28420 this.xhr.send(formData);
28424 xhrOnLoad : function(xhr)
28427 this.maskEl.unmask();
28430 if (xhr.readyState !== 4) {
28431 this.fireEvent('exception', this, xhr);
28435 var response = Roo.decode(xhr.responseText);
28437 if(!response.success){
28438 this.fireEvent('exception', this, xhr);
28442 var response = Roo.decode(xhr.responseText);
28444 this.fireEvent('upload', this, response);
28448 xhrOnError : function()
28451 this.maskEl.unmask();
28454 Roo.log('xhr on error');
28456 var response = Roo.decode(xhr.responseText);
28462 prepare : function(file)
28465 this.maskEl.mask(this.loadingText);
28471 if(typeof(file) === 'string'){
28472 this.loadCanvas(file);
28476 if(!file || !this.urlAPI){
28481 this.cropType = file.type;
28485 if(this.fireEvent('prepare', this, this.file) != false){
28487 var reader = new FileReader();
28489 reader.onload = function (e) {
28490 if (e.target.error) {
28491 Roo.log(e.target.error);
28495 var buffer = e.target.result,
28496 dataView = new DataView(buffer),
28498 maxOffset = dataView.byteLength - 4,
28502 if (dataView.getUint16(0) === 0xffd8) {
28503 while (offset < maxOffset) {
28504 markerBytes = dataView.getUint16(offset);
28506 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28507 markerLength = dataView.getUint16(offset + 2) + 2;
28508 if (offset + markerLength > dataView.byteLength) {
28509 Roo.log('Invalid meta data: Invalid segment size.');
28513 if(markerBytes == 0xffe1){
28514 _this.parseExifData(
28521 offset += markerLength;
28531 var url = _this.urlAPI.createObjectURL(_this.file);
28533 _this.loadCanvas(url);
28538 reader.readAsArrayBuffer(this.file);
28544 parseExifData : function(dataView, offset, length)
28546 var tiffOffset = offset + 10,
28550 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28551 // No Exif data, might be XMP data instead
28555 // Check for the ASCII code for "Exif" (0x45786966):
28556 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28557 // No Exif data, might be XMP data instead
28560 if (tiffOffset + 8 > dataView.byteLength) {
28561 Roo.log('Invalid Exif data: Invalid segment size.');
28564 // Check for the two null bytes:
28565 if (dataView.getUint16(offset + 8) !== 0x0000) {
28566 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28569 // Check the byte alignment:
28570 switch (dataView.getUint16(tiffOffset)) {
28572 littleEndian = true;
28575 littleEndian = false;
28578 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28581 // Check for the TIFF tag marker (0x002A):
28582 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28583 Roo.log('Invalid Exif data: Missing TIFF marker.');
28586 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28587 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28589 this.parseExifTags(
28592 tiffOffset + dirOffset,
28597 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28602 if (dirOffset + 6 > dataView.byteLength) {
28603 Roo.log('Invalid Exif data: Invalid directory offset.');
28606 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28607 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28608 if (dirEndOffset + 4 > dataView.byteLength) {
28609 Roo.log('Invalid Exif data: Invalid directory size.');
28612 for (i = 0; i < tagsNumber; i += 1) {
28616 dirOffset + 2 + 12 * i, // tag offset
28620 // Return the offset to the next directory:
28621 return dataView.getUint32(dirEndOffset, littleEndian);
28624 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28626 var tag = dataView.getUint16(offset, littleEndian);
28628 this.exif[tag] = this.getExifValue(
28632 dataView.getUint16(offset + 2, littleEndian), // tag type
28633 dataView.getUint32(offset + 4, littleEndian), // tag length
28638 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28640 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28649 Roo.log('Invalid Exif data: Invalid tag type.');
28653 tagSize = tagType.size * length;
28654 // Determine if the value is contained in the dataOffset bytes,
28655 // or if the value at the dataOffset is a pointer to the actual data:
28656 dataOffset = tagSize > 4 ?
28657 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28658 if (dataOffset + tagSize > dataView.byteLength) {
28659 Roo.log('Invalid Exif data: Invalid data offset.');
28662 if (length === 1) {
28663 return tagType.getValue(dataView, dataOffset, littleEndian);
28666 for (i = 0; i < length; i += 1) {
28667 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28670 if (tagType.ascii) {
28672 // Concatenate the chars:
28673 for (i = 0; i < values.length; i += 1) {
28675 // Ignore the terminating NULL byte(s):
28676 if (c === '\u0000') {
28688 Roo.apply(Roo.bootstrap.UploadCropbox, {
28690 'Orientation': 0x0112
28694 1: 0, //'top-left',
28696 3: 180, //'bottom-right',
28697 // 4: 'bottom-left',
28699 6: 90, //'right-top',
28700 // 7: 'right-bottom',
28701 8: 270 //'left-bottom'
28705 // byte, 8-bit unsigned int:
28707 getValue: function (dataView, dataOffset) {
28708 return dataView.getUint8(dataOffset);
28712 // ascii, 8-bit byte:
28714 getValue: function (dataView, dataOffset) {
28715 return String.fromCharCode(dataView.getUint8(dataOffset));
28720 // short, 16 bit int:
28722 getValue: function (dataView, dataOffset, littleEndian) {
28723 return dataView.getUint16(dataOffset, littleEndian);
28727 // long, 32 bit int:
28729 getValue: function (dataView, dataOffset, littleEndian) {
28730 return dataView.getUint32(dataOffset, littleEndian);
28734 // rational = two long values, first is numerator, second is denominator:
28736 getValue: function (dataView, dataOffset, littleEndian) {
28737 return dataView.getUint32(dataOffset, littleEndian) /
28738 dataView.getUint32(dataOffset + 4, littleEndian);
28742 // slong, 32 bit signed int:
28744 getValue: function (dataView, dataOffset, littleEndian) {
28745 return dataView.getInt32(dataOffset, littleEndian);
28749 // srational, two slongs, first is numerator, second is denominator:
28751 getValue: function (dataView, dataOffset, littleEndian) {
28752 return dataView.getInt32(dataOffset, littleEndian) /
28753 dataView.getInt32(dataOffset + 4, littleEndian);
28763 cls : 'btn-group roo-upload-cropbox-rotate-left',
28764 action : 'rotate-left',
28768 cls : 'btn btn-default',
28769 html : '<i class="fa fa-undo"></i>'
28775 cls : 'btn-group roo-upload-cropbox-picture',
28776 action : 'picture',
28780 cls : 'btn btn-default',
28781 html : '<i class="fa fa-picture-o"></i>'
28787 cls : 'btn-group roo-upload-cropbox-rotate-right',
28788 action : 'rotate-right',
28792 cls : 'btn btn-default',
28793 html : '<i class="fa fa-repeat"></i>'
28801 cls : 'btn-group roo-upload-cropbox-rotate-left',
28802 action : 'rotate-left',
28806 cls : 'btn btn-default',
28807 html : '<i class="fa fa-undo"></i>'
28813 cls : 'btn-group roo-upload-cropbox-download',
28814 action : 'download',
28818 cls : 'btn btn-default',
28819 html : '<i class="fa fa-download"></i>'
28825 cls : 'btn-group roo-upload-cropbox-crop',
28830 cls : 'btn btn-default',
28831 html : '<i class="fa fa-crop"></i>'
28837 cls : 'btn-group roo-upload-cropbox-trash',
28842 cls : 'btn btn-default',
28843 html : '<i class="fa fa-trash"></i>'
28849 cls : 'btn-group roo-upload-cropbox-rotate-right',
28850 action : 'rotate-right',
28854 cls : 'btn btn-default',
28855 html : '<i class="fa fa-repeat"></i>'
28863 cls : 'btn-group roo-upload-cropbox-rotate-left',
28864 action : 'rotate-left',
28868 cls : 'btn btn-default',
28869 html : '<i class="fa fa-undo"></i>'
28875 cls : 'btn-group roo-upload-cropbox-rotate-right',
28876 action : 'rotate-right',
28880 cls : 'btn btn-default',
28881 html : '<i class="fa fa-repeat"></i>'
28894 * @class Roo.bootstrap.DocumentManager
28895 * @extends Roo.bootstrap.Component
28896 * Bootstrap DocumentManager class
28897 * @cfg {String} paramName default 'imageUpload'
28898 * @cfg {String} toolTipName default 'filename'
28899 * @cfg {String} method default POST
28900 * @cfg {String} url action url
28901 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28902 * @cfg {Boolean} multiple multiple upload default true
28903 * @cfg {Number} thumbSize default 300
28904 * @cfg {String} fieldLabel
28905 * @cfg {Number} labelWidth default 4
28906 * @cfg {String} labelAlign (left|top) default left
28907 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28908 * @cfg {Number} labellg set the width of label (1-12)
28909 * @cfg {Number} labelmd set the width of label (1-12)
28910 * @cfg {Number} labelsm set the width of label (1-12)
28911 * @cfg {Number} labelxs set the width of label (1-12)
28914 * Create a new DocumentManager
28915 * @param {Object} config The config object
28918 Roo.bootstrap.DocumentManager = function(config){
28919 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28922 this.delegates = [];
28927 * Fire when initial the DocumentManager
28928 * @param {Roo.bootstrap.DocumentManager} this
28933 * inspect selected file
28934 * @param {Roo.bootstrap.DocumentManager} this
28935 * @param {File} file
28940 * Fire when xhr load exception
28941 * @param {Roo.bootstrap.DocumentManager} this
28942 * @param {XMLHttpRequest} xhr
28944 "exception" : true,
28946 * @event afterupload
28947 * Fire when xhr load exception
28948 * @param {Roo.bootstrap.DocumentManager} this
28949 * @param {XMLHttpRequest} xhr
28951 "afterupload" : true,
28954 * prepare the form data
28955 * @param {Roo.bootstrap.DocumentManager} this
28956 * @param {Object} formData
28961 * Fire when remove the file
28962 * @param {Roo.bootstrap.DocumentManager} this
28963 * @param {Object} file
28968 * Fire after refresh the file
28969 * @param {Roo.bootstrap.DocumentManager} this
28974 * Fire after click the image
28975 * @param {Roo.bootstrap.DocumentManager} this
28976 * @param {Object} file
28981 * Fire when upload a image and editable set to true
28982 * @param {Roo.bootstrap.DocumentManager} this
28983 * @param {Object} file
28987 * @event beforeselectfile
28988 * Fire before select file
28989 * @param {Roo.bootstrap.DocumentManager} this
28991 "beforeselectfile" : true,
28994 * Fire before process file
28995 * @param {Roo.bootstrap.DocumentManager} this
28996 * @param {Object} file
29000 * @event previewrendered
29001 * Fire when preview rendered
29002 * @param {Roo.bootstrap.DocumentManager} this
29003 * @param {Object} file
29005 "previewrendered" : true,
29008 "previewResize" : true
29013 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29022 paramName : 'imageUpload',
29023 toolTipName : 'filename',
29026 labelAlign : 'left',
29036 getAutoCreate : function()
29038 var managerWidget = {
29040 cls : 'roo-document-manager',
29044 cls : 'roo-document-manager-selector',
29049 cls : 'roo-document-manager-uploader',
29053 cls : 'roo-document-manager-upload-btn',
29054 html : '<i class="fa fa-plus"></i>'
29065 cls : 'column col-md-12',
29070 if(this.fieldLabel.length){
29075 cls : 'column col-md-12',
29076 html : this.fieldLabel
29080 cls : 'column col-md-12',
29085 if(this.labelAlign == 'left'){
29090 html : this.fieldLabel
29099 if(this.labelWidth > 12){
29100 content[0].style = "width: " + this.labelWidth + 'px';
29103 if(this.labelWidth < 13 && this.labelmd == 0){
29104 this.labelmd = this.labelWidth;
29107 if(this.labellg > 0){
29108 content[0].cls += ' col-lg-' + this.labellg;
29109 content[1].cls += ' col-lg-' + (12 - this.labellg);
29112 if(this.labelmd > 0){
29113 content[0].cls += ' col-md-' + this.labelmd;
29114 content[1].cls += ' col-md-' + (12 - this.labelmd);
29117 if(this.labelsm > 0){
29118 content[0].cls += ' col-sm-' + this.labelsm;
29119 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29122 if(this.labelxs > 0){
29123 content[0].cls += ' col-xs-' + this.labelxs;
29124 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29132 cls : 'row clearfix',
29140 initEvents : function()
29142 this.managerEl = this.el.select('.roo-document-manager', true).first();
29143 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29145 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29146 this.selectorEl.hide();
29149 this.selectorEl.attr('multiple', 'multiple');
29152 this.selectorEl.on('change', this.onFileSelected, this);
29154 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29155 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29157 this.uploader.on('click', this.onUploaderClick, this);
29159 this.renderProgressDialog();
29163 window.addEventListener("resize", function() { _this.refresh(); } );
29165 this.fireEvent('initial', this);
29168 renderProgressDialog : function()
29172 this.progressDialog = new Roo.bootstrap.Modal({
29173 cls : 'roo-document-manager-progress-dialog',
29174 allow_close : false,
29184 btnclick : function() {
29185 _this.uploadCancel();
29191 this.progressDialog.render(Roo.get(document.body));
29193 this.progress = new Roo.bootstrap.Progress({
29194 cls : 'roo-document-manager-progress',
29199 this.progress.render(this.progressDialog.getChildContainer());
29201 this.progressBar = new Roo.bootstrap.ProgressBar({
29202 cls : 'roo-document-manager-progress-bar',
29205 aria_valuemax : 12,
29209 this.progressBar.render(this.progress.getChildContainer());
29212 onUploaderClick : function(e)
29214 e.preventDefault();
29216 if(this.fireEvent('beforeselectfile', this) != false){
29217 this.selectorEl.dom.click();
29222 onFileSelected : function(e)
29224 e.preventDefault();
29226 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29230 Roo.each(this.selectorEl.dom.files, function(file){
29231 if(this.fireEvent('inspect', this, file) != false){
29232 this.files.push(file);
29242 this.selectorEl.dom.value = '';
29244 if(!this.files || !this.files.length){
29248 if(this.boxes > 0 && this.files.length > this.boxes){
29249 this.files = this.files.slice(0, this.boxes);
29252 this.uploader.show();
29254 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29255 this.uploader.hide();
29264 Roo.each(this.files, function(file){
29266 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29267 var f = this.renderPreview(file);
29272 if(file.type.indexOf('image') != -1){
29273 this.delegates.push(
29275 _this.process(file);
29276 }).createDelegate(this)
29284 _this.process(file);
29285 }).createDelegate(this)
29290 this.files = files;
29292 this.delegates = this.delegates.concat(docs);
29294 if(!this.delegates.length){
29299 this.progressBar.aria_valuemax = this.delegates.length;
29306 arrange : function()
29308 if(!this.delegates.length){
29309 this.progressDialog.hide();
29314 var delegate = this.delegates.shift();
29316 this.progressDialog.show();
29318 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29320 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29325 refresh : function()
29327 this.uploader.show();
29329 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29330 this.uploader.hide();
29333 Roo.isTouch ? this.closable(false) : this.closable(true);
29335 this.fireEvent('refresh', this);
29338 onRemove : function(e, el, o)
29340 e.preventDefault();
29342 this.fireEvent('remove', this, o);
29346 remove : function(o)
29350 Roo.each(this.files, function(file){
29351 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29360 this.files = files;
29367 Roo.each(this.files, function(file){
29372 file.target.remove();
29381 onClick : function(e, el, o)
29383 e.preventDefault();
29385 this.fireEvent('click', this, o);
29389 closable : function(closable)
29391 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29393 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29405 xhrOnLoad : function(xhr)
29407 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29411 if (xhr.readyState !== 4) {
29413 this.fireEvent('exception', this, xhr);
29417 var response = Roo.decode(xhr.responseText);
29419 if(!response.success){
29421 this.fireEvent('exception', this, xhr);
29425 var file = this.renderPreview(response.data);
29427 this.files.push(file);
29431 this.fireEvent('afterupload', this, xhr);
29435 xhrOnError : function(xhr)
29437 Roo.log('xhr on error');
29439 var response = Roo.decode(xhr.responseText);
29446 process : function(file)
29448 if(this.fireEvent('process', this, file) !== false){
29449 if(this.editable && file.type.indexOf('image') != -1){
29450 this.fireEvent('edit', this, file);
29454 this.uploadStart(file, false);
29461 uploadStart : function(file, crop)
29463 this.xhr = new XMLHttpRequest();
29465 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29470 file.xhr = this.xhr;
29472 this.managerEl.createChild({
29474 cls : 'roo-document-manager-loading',
29478 tooltip : file.name,
29479 cls : 'roo-document-manager-thumb',
29480 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29486 this.xhr.open(this.method, this.url, true);
29489 "Accept": "application/json",
29490 "Cache-Control": "no-cache",
29491 "X-Requested-With": "XMLHttpRequest"
29494 for (var headerName in headers) {
29495 var headerValue = headers[headerName];
29497 this.xhr.setRequestHeader(headerName, headerValue);
29503 this.xhr.onload = function()
29505 _this.xhrOnLoad(_this.xhr);
29508 this.xhr.onerror = function()
29510 _this.xhrOnError(_this.xhr);
29513 var formData = new FormData();
29515 formData.append('returnHTML', 'NO');
29518 formData.append('crop', crop);
29521 formData.append(this.paramName, file, file.name);
29528 if(this.fireEvent('prepare', this, formData, options) != false){
29530 if(options.manually){
29534 this.xhr.send(formData);
29538 this.uploadCancel();
29541 uploadCancel : function()
29547 this.delegates = [];
29549 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29556 renderPreview : function(file)
29558 if(typeof(file.target) != 'undefined' && file.target){
29562 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29564 var previewEl = this.managerEl.createChild({
29566 cls : 'roo-document-manager-preview',
29570 tooltip : file[this.toolTipName],
29571 cls : 'roo-document-manager-thumb',
29572 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29577 html : '<i class="fa fa-times-circle"></i>'
29582 var close = previewEl.select('button.close', true).first();
29584 close.on('click', this.onRemove, this, file);
29586 file.target = previewEl;
29588 var image = previewEl.select('img', true).first();
29592 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29594 image.on('click', this.onClick, this, file);
29596 this.fireEvent('previewrendered', this, file);
29602 onPreviewLoad : function(file, image)
29604 if(typeof(file.target) == 'undefined' || !file.target){
29608 var width = image.dom.naturalWidth || image.dom.width;
29609 var height = image.dom.naturalHeight || image.dom.height;
29611 if(!this.previewResize) {
29615 if(width > height){
29616 file.target.addClass('wide');
29620 file.target.addClass('tall');
29625 uploadFromSource : function(file, crop)
29627 this.xhr = new XMLHttpRequest();
29629 this.managerEl.createChild({
29631 cls : 'roo-document-manager-loading',
29635 tooltip : file.name,
29636 cls : 'roo-document-manager-thumb',
29637 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29643 this.xhr.open(this.method, this.url, true);
29646 "Accept": "application/json",
29647 "Cache-Control": "no-cache",
29648 "X-Requested-With": "XMLHttpRequest"
29651 for (var headerName in headers) {
29652 var headerValue = headers[headerName];
29654 this.xhr.setRequestHeader(headerName, headerValue);
29660 this.xhr.onload = function()
29662 _this.xhrOnLoad(_this.xhr);
29665 this.xhr.onerror = function()
29667 _this.xhrOnError(_this.xhr);
29670 var formData = new FormData();
29672 formData.append('returnHTML', 'NO');
29674 formData.append('crop', crop);
29676 if(typeof(file.filename) != 'undefined'){
29677 formData.append('filename', file.filename);
29680 if(typeof(file.mimetype) != 'undefined'){
29681 formData.append('mimetype', file.mimetype);
29686 if(this.fireEvent('prepare', this, formData) != false){
29687 this.xhr.send(formData);
29697 * @class Roo.bootstrap.DocumentViewer
29698 * @extends Roo.bootstrap.Component
29699 * Bootstrap DocumentViewer class
29700 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29701 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29704 * Create a new DocumentViewer
29705 * @param {Object} config The config object
29708 Roo.bootstrap.DocumentViewer = function(config){
29709 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29714 * Fire after initEvent
29715 * @param {Roo.bootstrap.DocumentViewer} this
29721 * @param {Roo.bootstrap.DocumentViewer} this
29726 * Fire after download button
29727 * @param {Roo.bootstrap.DocumentViewer} this
29732 * Fire after trash button
29733 * @param {Roo.bootstrap.DocumentViewer} this
29740 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29742 showDownload : true,
29746 getAutoCreate : function()
29750 cls : 'roo-document-viewer',
29754 cls : 'roo-document-viewer-body',
29758 cls : 'roo-document-viewer-thumb',
29762 cls : 'roo-document-viewer-image'
29770 cls : 'roo-document-viewer-footer',
29773 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29777 cls : 'btn-group roo-document-viewer-download',
29781 cls : 'btn btn-default',
29782 html : '<i class="fa fa-download"></i>'
29788 cls : 'btn-group roo-document-viewer-trash',
29792 cls : 'btn btn-default',
29793 html : '<i class="fa fa-trash"></i>'
29806 initEvents : function()
29808 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29809 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29811 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29812 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29814 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29815 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29817 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29818 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29820 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29821 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29823 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29824 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29826 this.bodyEl.on('click', this.onClick, this);
29827 this.downloadBtn.on('click', this.onDownload, this);
29828 this.trashBtn.on('click', this.onTrash, this);
29830 this.downloadBtn.hide();
29831 this.trashBtn.hide();
29833 if(this.showDownload){
29834 this.downloadBtn.show();
29837 if(this.showTrash){
29838 this.trashBtn.show();
29841 if(!this.showDownload && !this.showTrash) {
29842 this.footerEl.hide();
29847 initial : function()
29849 this.fireEvent('initial', this);
29853 onClick : function(e)
29855 e.preventDefault();
29857 this.fireEvent('click', this);
29860 onDownload : function(e)
29862 e.preventDefault();
29864 this.fireEvent('download', this);
29867 onTrash : function(e)
29869 e.preventDefault();
29871 this.fireEvent('trash', this);
29883 * @class Roo.bootstrap.NavProgressBar
29884 * @extends Roo.bootstrap.Component
29885 * Bootstrap NavProgressBar class
29888 * Create a new nav progress bar
29889 * @param {Object} config The config object
29892 Roo.bootstrap.NavProgressBar = function(config){
29893 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29895 this.bullets = this.bullets || [];
29897 // Roo.bootstrap.NavProgressBar.register(this);
29901 * Fires when the active item changes
29902 * @param {Roo.bootstrap.NavProgressBar} this
29903 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29904 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29911 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29916 getAutoCreate : function()
29918 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29922 cls : 'roo-navigation-bar-group',
29926 cls : 'roo-navigation-top-bar'
29930 cls : 'roo-navigation-bullets-bar',
29934 cls : 'roo-navigation-bar'
29941 cls : 'roo-navigation-bottom-bar'
29951 initEvents: function()
29956 onRender : function(ct, position)
29958 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29960 if(this.bullets.length){
29961 Roo.each(this.bullets, function(b){
29970 addItem : function(cfg)
29972 var item = new Roo.bootstrap.NavProgressItem(cfg);
29974 item.parentId = this.id;
29975 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29978 var top = new Roo.bootstrap.Element({
29980 cls : 'roo-navigation-bar-text'
29983 var bottom = new Roo.bootstrap.Element({
29985 cls : 'roo-navigation-bar-text'
29988 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29989 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29991 var topText = new Roo.bootstrap.Element({
29993 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29996 var bottomText = new Roo.bootstrap.Element({
29998 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30001 topText.onRender(top.el, null);
30002 bottomText.onRender(bottom.el, null);
30005 item.bottomEl = bottom;
30008 this.barItems.push(item);
30013 getActive : function()
30015 var active = false;
30017 Roo.each(this.barItems, function(v){
30019 if (!v.isActive()) {
30031 setActiveItem : function(item)
30035 Roo.each(this.barItems, function(v){
30036 if (v.rid == item.rid) {
30040 if (v.isActive()) {
30041 v.setActive(false);
30046 item.setActive(true);
30048 this.fireEvent('changed', this, item, prev);
30051 getBarItem: function(rid)
30055 Roo.each(this.barItems, function(e) {
30056 if (e.rid != rid) {
30067 indexOfItem : function(item)
30071 Roo.each(this.barItems, function(v, i){
30073 if (v.rid != item.rid) {
30084 setActiveNext : function()
30086 var i = this.indexOfItem(this.getActive());
30088 if (i > this.barItems.length) {
30092 this.setActiveItem(this.barItems[i+1]);
30095 setActivePrev : function()
30097 var i = this.indexOfItem(this.getActive());
30103 this.setActiveItem(this.barItems[i-1]);
30106 format : function()
30108 if(!this.barItems.length){
30112 var width = 100 / this.barItems.length;
30114 Roo.each(this.barItems, function(i){
30115 i.el.setStyle('width', width + '%');
30116 i.topEl.el.setStyle('width', width + '%');
30117 i.bottomEl.el.setStyle('width', width + '%');
30126 * Nav Progress Item
30131 * @class Roo.bootstrap.NavProgressItem
30132 * @extends Roo.bootstrap.Component
30133 * Bootstrap NavProgressItem class
30134 * @cfg {String} rid the reference id
30135 * @cfg {Boolean} active (true|false) Is item active default false
30136 * @cfg {Boolean} disabled (true|false) Is item active default false
30137 * @cfg {String} html
30138 * @cfg {String} position (top|bottom) text position default bottom
30139 * @cfg {String} icon show icon instead of number
30142 * Create a new NavProgressItem
30143 * @param {Object} config The config object
30145 Roo.bootstrap.NavProgressItem = function(config){
30146 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30151 * The raw click event for the entire grid.
30152 * @param {Roo.bootstrap.NavProgressItem} this
30153 * @param {Roo.EventObject} e
30160 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30166 position : 'bottom',
30169 getAutoCreate : function()
30171 var iconCls = 'roo-navigation-bar-item-icon';
30173 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30177 cls: 'roo-navigation-bar-item',
30187 cfg.cls += ' active';
30190 cfg.cls += ' disabled';
30196 disable : function()
30198 this.setDisabled(true);
30201 enable : function()
30203 this.setDisabled(false);
30206 initEvents: function()
30208 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30210 this.iconEl.on('click', this.onClick, this);
30213 onClick : function(e)
30215 e.preventDefault();
30221 if(this.fireEvent('click', this, e) === false){
30225 this.parent().setActiveItem(this);
30228 isActive: function ()
30230 return this.active;
30233 setActive : function(state)
30235 if(this.active == state){
30239 this.active = state;
30242 this.el.addClass('active');
30246 this.el.removeClass('active');
30251 setDisabled : function(state)
30253 if(this.disabled == state){
30257 this.disabled = state;
30260 this.el.addClass('disabled');
30264 this.el.removeClass('disabled');
30267 tooltipEl : function()
30269 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30282 * @class Roo.bootstrap.FieldLabel
30283 * @extends Roo.bootstrap.Component
30284 * Bootstrap FieldLabel class
30285 * @cfg {String} html contents of the element
30286 * @cfg {String} tag tag of the element default label
30287 * @cfg {String} cls class of the element
30288 * @cfg {String} target label target
30289 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30290 * @cfg {String} invalidClass default "text-warning"
30291 * @cfg {String} validClass default "text-success"
30292 * @cfg {String} iconTooltip default "This field is required"
30293 * @cfg {String} indicatorpos (left|right) default left
30296 * Create a new FieldLabel
30297 * @param {Object} config The config object
30300 Roo.bootstrap.FieldLabel = function(config){
30301 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30306 * Fires after the field has been marked as invalid.
30307 * @param {Roo.form.FieldLabel} this
30308 * @param {String} msg The validation message
30313 * Fires after the field has been validated with no errors.
30314 * @param {Roo.form.FieldLabel} this
30320 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30327 invalidClass : 'has-warning',
30328 validClass : 'has-success',
30329 iconTooltip : 'This field is required',
30330 indicatorpos : 'left',
30332 getAutoCreate : function(){
30335 if (!this.allowBlank) {
30341 cls : 'roo-bootstrap-field-label ' + this.cls,
30346 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30347 tooltip : this.iconTooltip
30356 if(this.indicatorpos == 'right'){
30359 cls : 'roo-bootstrap-field-label ' + this.cls,
30368 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30369 tooltip : this.iconTooltip
30378 initEvents: function()
30380 Roo.bootstrap.Element.superclass.initEvents.call(this);
30382 this.indicator = this.indicatorEl();
30384 if(this.indicator){
30385 this.indicator.removeClass('visible');
30386 this.indicator.addClass('invisible');
30389 Roo.bootstrap.FieldLabel.register(this);
30392 indicatorEl : function()
30394 var indicator = this.el.select('i.roo-required-indicator',true).first();
30405 * Mark this field as valid
30407 markValid : function()
30409 if(this.indicator){
30410 this.indicator.removeClass('visible');
30411 this.indicator.addClass('invisible');
30414 this.el.removeClass(this.invalidClass);
30416 this.el.addClass(this.validClass);
30418 this.fireEvent('valid', this);
30422 * Mark this field as invalid
30423 * @param {String} msg The validation message
30425 markInvalid : function(msg)
30427 if(this.indicator){
30428 this.indicator.removeClass('invisible');
30429 this.indicator.addClass('visible');
30432 this.el.removeClass(this.validClass);
30434 this.el.addClass(this.invalidClass);
30436 this.fireEvent('invalid', this, msg);
30442 Roo.apply(Roo.bootstrap.FieldLabel, {
30447 * register a FieldLabel Group
30448 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30450 register : function(label)
30452 if(this.groups.hasOwnProperty(label.target)){
30456 this.groups[label.target] = label;
30460 * fetch a FieldLabel Group based on the target
30461 * @param {string} target
30462 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30464 get: function(target) {
30465 if (typeof(this.groups[target]) == 'undefined') {
30469 return this.groups[target] ;
30478 * page DateSplitField.
30484 * @class Roo.bootstrap.DateSplitField
30485 * @extends Roo.bootstrap.Component
30486 * Bootstrap DateSplitField class
30487 * @cfg {string} fieldLabel - the label associated
30488 * @cfg {Number} labelWidth set the width of label (0-12)
30489 * @cfg {String} labelAlign (top|left)
30490 * @cfg {Boolean} dayAllowBlank (true|false) default false
30491 * @cfg {Boolean} monthAllowBlank (true|false) default false
30492 * @cfg {Boolean} yearAllowBlank (true|false) default false
30493 * @cfg {string} dayPlaceholder
30494 * @cfg {string} monthPlaceholder
30495 * @cfg {string} yearPlaceholder
30496 * @cfg {string} dayFormat default 'd'
30497 * @cfg {string} monthFormat default 'm'
30498 * @cfg {string} yearFormat default 'Y'
30499 * @cfg {Number} labellg set the width of label (1-12)
30500 * @cfg {Number} labelmd set the width of label (1-12)
30501 * @cfg {Number} labelsm set the width of label (1-12)
30502 * @cfg {Number} labelxs set the width of label (1-12)
30506 * Create a new DateSplitField
30507 * @param {Object} config The config object
30510 Roo.bootstrap.DateSplitField = function(config){
30511 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30517 * getting the data of years
30518 * @param {Roo.bootstrap.DateSplitField} this
30519 * @param {Object} years
30524 * getting the data of days
30525 * @param {Roo.bootstrap.DateSplitField} this
30526 * @param {Object} days
30531 * Fires after the field has been marked as invalid.
30532 * @param {Roo.form.Field} this
30533 * @param {String} msg The validation message
30538 * Fires after the field has been validated with no errors.
30539 * @param {Roo.form.Field} this
30545 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30548 labelAlign : 'top',
30550 dayAllowBlank : false,
30551 monthAllowBlank : false,
30552 yearAllowBlank : false,
30553 dayPlaceholder : '',
30554 monthPlaceholder : '',
30555 yearPlaceholder : '',
30559 isFormField : true,
30565 getAutoCreate : function()
30569 cls : 'row roo-date-split-field-group',
30574 cls : 'form-hidden-field roo-date-split-field-group-value',
30580 var labelCls = 'col-md-12';
30581 var contentCls = 'col-md-4';
30583 if(this.fieldLabel){
30587 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30591 html : this.fieldLabel
30596 if(this.labelAlign == 'left'){
30598 if(this.labelWidth > 12){
30599 label.style = "width: " + this.labelWidth + 'px';
30602 if(this.labelWidth < 13 && this.labelmd == 0){
30603 this.labelmd = this.labelWidth;
30606 if(this.labellg > 0){
30607 labelCls = ' col-lg-' + this.labellg;
30608 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30611 if(this.labelmd > 0){
30612 labelCls = ' col-md-' + this.labelmd;
30613 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30616 if(this.labelsm > 0){
30617 labelCls = ' col-sm-' + this.labelsm;
30618 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30621 if(this.labelxs > 0){
30622 labelCls = ' col-xs-' + this.labelxs;
30623 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30627 label.cls += ' ' + labelCls;
30629 cfg.cn.push(label);
30632 Roo.each(['day', 'month', 'year'], function(t){
30635 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30642 inputEl: function ()
30644 return this.el.select('.roo-date-split-field-group-value', true).first();
30647 onRender : function(ct, position)
30651 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30653 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30655 this.dayField = new Roo.bootstrap.ComboBox({
30656 allowBlank : this.dayAllowBlank,
30657 alwaysQuery : true,
30658 displayField : 'value',
30661 forceSelection : true,
30663 placeholder : this.dayPlaceholder,
30664 selectOnFocus : true,
30665 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30666 triggerAction : 'all',
30668 valueField : 'value',
30669 store : new Roo.data.SimpleStore({
30670 data : (function() {
30672 _this.fireEvent('days', _this, days);
30675 fields : [ 'value' ]
30678 select : function (_self, record, index)
30680 _this.setValue(_this.getValue());
30685 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30687 this.monthField = new Roo.bootstrap.MonthField({
30688 after : '<i class=\"fa fa-calendar\"></i>',
30689 allowBlank : this.monthAllowBlank,
30690 placeholder : this.monthPlaceholder,
30693 render : function (_self)
30695 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30696 e.preventDefault();
30700 select : function (_self, oldvalue, newvalue)
30702 _this.setValue(_this.getValue());
30707 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30709 this.yearField = new Roo.bootstrap.ComboBox({
30710 allowBlank : this.yearAllowBlank,
30711 alwaysQuery : true,
30712 displayField : 'value',
30715 forceSelection : true,
30717 placeholder : this.yearPlaceholder,
30718 selectOnFocus : true,
30719 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30720 triggerAction : 'all',
30722 valueField : 'value',
30723 store : new Roo.data.SimpleStore({
30724 data : (function() {
30726 _this.fireEvent('years', _this, years);
30729 fields : [ 'value' ]
30732 select : function (_self, record, index)
30734 _this.setValue(_this.getValue());
30739 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30742 setValue : function(v, format)
30744 this.inputEl.dom.value = v;
30746 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30748 var d = Date.parseDate(v, f);
30755 this.setDay(d.format(this.dayFormat));
30756 this.setMonth(d.format(this.monthFormat));
30757 this.setYear(d.format(this.yearFormat));
30764 setDay : function(v)
30766 this.dayField.setValue(v);
30767 this.inputEl.dom.value = this.getValue();
30772 setMonth : function(v)
30774 this.monthField.setValue(v, true);
30775 this.inputEl.dom.value = this.getValue();
30780 setYear : function(v)
30782 this.yearField.setValue(v);
30783 this.inputEl.dom.value = this.getValue();
30788 getDay : function()
30790 return this.dayField.getValue();
30793 getMonth : function()
30795 return this.monthField.getValue();
30798 getYear : function()
30800 return this.yearField.getValue();
30803 getValue : function()
30805 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30807 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30817 this.inputEl.dom.value = '';
30822 validate : function()
30824 var d = this.dayField.validate();
30825 var m = this.monthField.validate();
30826 var y = this.yearField.validate();
30831 (!this.dayAllowBlank && !d) ||
30832 (!this.monthAllowBlank && !m) ||
30833 (!this.yearAllowBlank && !y)
30838 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30847 this.markInvalid();
30852 markValid : function()
30855 var label = this.el.select('label', true).first();
30856 var icon = this.el.select('i.fa-star', true).first();
30862 this.fireEvent('valid', this);
30866 * Mark this field as invalid
30867 * @param {String} msg The validation message
30869 markInvalid : function(msg)
30872 var label = this.el.select('label', true).first();
30873 var icon = this.el.select('i.fa-star', true).first();
30875 if(label && !icon){
30876 this.el.select('.roo-date-split-field-label', true).createChild({
30878 cls : 'text-danger fa fa-lg fa-star',
30879 tooltip : 'This field is required',
30880 style : 'margin-right:5px;'
30884 this.fireEvent('invalid', this, msg);
30887 clearInvalid : function()
30889 var label = this.el.select('label', true).first();
30890 var icon = this.el.select('i.fa-star', true).first();
30896 this.fireEvent('valid', this);
30899 getName: function()
30909 * http://masonry.desandro.com
30911 * The idea is to render all the bricks based on vertical width...
30913 * The original code extends 'outlayer' - we might need to use that....
30919 * @class Roo.bootstrap.LayoutMasonry
30920 * @extends Roo.bootstrap.Component
30921 * Bootstrap Layout Masonry class
30924 * Create a new Element
30925 * @param {Object} config The config object
30928 Roo.bootstrap.LayoutMasonry = function(config){
30930 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30934 Roo.bootstrap.LayoutMasonry.register(this);
30940 * Fire after layout the items
30941 * @param {Roo.bootstrap.LayoutMasonry} this
30942 * @param {Roo.EventObject} e
30949 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30952 * @cfg {Boolean} isLayoutInstant = no animation?
30954 isLayoutInstant : false, // needed?
30957 * @cfg {Number} boxWidth width of the columns
30962 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30967 * @cfg {Number} padWidth padding below box..
30972 * @cfg {Number} gutter gutter width..
30977 * @cfg {Number} maxCols maximum number of columns
30983 * @cfg {Boolean} isAutoInitial defalut true
30985 isAutoInitial : true,
30990 * @cfg {Boolean} isHorizontal defalut false
30992 isHorizontal : false,
30994 currentSize : null,
31000 bricks: null, //CompositeElement
31004 _isLayoutInited : false,
31006 // isAlternative : false, // only use for vertical layout...
31009 * @cfg {Number} alternativePadWidth padding below box..
31011 alternativePadWidth : 50,
31013 selectedBrick : [],
31015 getAutoCreate : function(){
31017 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31021 cls: 'blog-masonary-wrapper ' + this.cls,
31023 cls : 'mas-boxes masonary'
31030 getChildContainer: function( )
31032 if (this.boxesEl) {
31033 return this.boxesEl;
31036 this.boxesEl = this.el.select('.mas-boxes').first();
31038 return this.boxesEl;
31042 initEvents : function()
31046 if(this.isAutoInitial){
31047 Roo.log('hook children rendered');
31048 this.on('childrenrendered', function() {
31049 Roo.log('children rendered');
31055 initial : function()
31057 this.selectedBrick = [];
31059 this.currentSize = this.el.getBox(true);
31061 Roo.EventManager.onWindowResize(this.resize, this);
31063 if(!this.isAutoInitial){
31071 //this.layout.defer(500,this);
31075 resize : function()
31077 var cs = this.el.getBox(true);
31080 this.currentSize.width == cs.width &&
31081 this.currentSize.x == cs.x &&
31082 this.currentSize.height == cs.height &&
31083 this.currentSize.y == cs.y
31085 Roo.log("no change in with or X or Y");
31089 this.currentSize = cs;
31095 layout : function()
31097 this._resetLayout();
31099 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31101 this.layoutItems( isInstant );
31103 this._isLayoutInited = true;
31105 this.fireEvent('layout', this);
31109 _resetLayout : function()
31111 if(this.isHorizontal){
31112 this.horizontalMeasureColumns();
31116 this.verticalMeasureColumns();
31120 verticalMeasureColumns : function()
31122 this.getContainerWidth();
31124 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31125 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31129 var boxWidth = this.boxWidth + this.padWidth;
31131 if(this.containerWidth < this.boxWidth){
31132 boxWidth = this.containerWidth
31135 var containerWidth = this.containerWidth;
31137 var cols = Math.floor(containerWidth / boxWidth);
31139 this.cols = Math.max( cols, 1 );
31141 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31143 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31145 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31147 this.colWidth = boxWidth + avail - this.padWidth;
31149 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31150 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31153 horizontalMeasureColumns : function()
31155 this.getContainerWidth();
31157 var boxWidth = this.boxWidth;
31159 if(this.containerWidth < boxWidth){
31160 boxWidth = this.containerWidth;
31163 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31165 this.el.setHeight(boxWidth);
31169 getContainerWidth : function()
31171 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31174 layoutItems : function( isInstant )
31176 Roo.log(this.bricks);
31178 var items = Roo.apply([], this.bricks);
31180 if(this.isHorizontal){
31181 this._horizontalLayoutItems( items , isInstant );
31185 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31186 // this._verticalAlternativeLayoutItems( items , isInstant );
31190 this._verticalLayoutItems( items , isInstant );
31194 _verticalLayoutItems : function ( items , isInstant)
31196 if ( !items || !items.length ) {
31201 ['xs', 'xs', 'xs', 'tall'],
31202 ['xs', 'xs', 'tall'],
31203 ['xs', 'xs', 'sm'],
31204 ['xs', 'xs', 'xs'],
31210 ['sm', 'xs', 'xs'],
31214 ['tall', 'xs', 'xs', 'xs'],
31215 ['tall', 'xs', 'xs'],
31227 Roo.each(items, function(item, k){
31229 switch (item.size) {
31230 // these layouts take up a full box,
31241 boxes.push([item]);
31264 var filterPattern = function(box, length)
31272 var pattern = box.slice(0, length);
31276 Roo.each(pattern, function(i){
31277 format.push(i.size);
31280 Roo.each(standard, function(s){
31282 if(String(s) != String(format)){
31291 if(!match && length == 1){
31296 filterPattern(box, length - 1);
31300 queue.push(pattern);
31302 box = box.slice(length, box.length);
31304 filterPattern(box, 4);
31310 Roo.each(boxes, function(box, k){
31316 if(box.length == 1){
31321 filterPattern(box, 4);
31325 this._processVerticalLayoutQueue( queue, isInstant );
31329 // _verticalAlternativeLayoutItems : function( items , isInstant )
31331 // if ( !items || !items.length ) {
31335 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31339 _horizontalLayoutItems : function ( items , isInstant)
31341 if ( !items || !items.length || items.length < 3) {
31347 var eItems = items.slice(0, 3);
31349 items = items.slice(3, items.length);
31352 ['xs', 'xs', 'xs', 'wide'],
31353 ['xs', 'xs', 'wide'],
31354 ['xs', 'xs', 'sm'],
31355 ['xs', 'xs', 'xs'],
31361 ['sm', 'xs', 'xs'],
31365 ['wide', 'xs', 'xs', 'xs'],
31366 ['wide', 'xs', 'xs'],
31379 Roo.each(items, function(item, k){
31381 switch (item.size) {
31392 boxes.push([item]);
31416 var filterPattern = function(box, length)
31424 var pattern = box.slice(0, length);
31428 Roo.each(pattern, function(i){
31429 format.push(i.size);
31432 Roo.each(standard, function(s){
31434 if(String(s) != String(format)){
31443 if(!match && length == 1){
31448 filterPattern(box, length - 1);
31452 queue.push(pattern);
31454 box = box.slice(length, box.length);
31456 filterPattern(box, 4);
31462 Roo.each(boxes, function(box, k){
31468 if(box.length == 1){
31473 filterPattern(box, 4);
31480 var pos = this.el.getBox(true);
31484 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31486 var hit_end = false;
31488 Roo.each(queue, function(box){
31492 Roo.each(box, function(b){
31494 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31504 Roo.each(box, function(b){
31506 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31509 mx = Math.max(mx, b.x);
31513 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31517 Roo.each(box, function(b){
31519 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31533 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31536 /** Sets position of item in DOM
31537 * @param {Element} item
31538 * @param {Number} x - horizontal position
31539 * @param {Number} y - vertical position
31540 * @param {Boolean} isInstant - disables transitions
31542 _processVerticalLayoutQueue : function( queue, isInstant )
31544 var pos = this.el.getBox(true);
31549 for (var i = 0; i < this.cols; i++){
31553 Roo.each(queue, function(box, k){
31555 var col = k % this.cols;
31557 Roo.each(box, function(b,kk){
31559 b.el.position('absolute');
31561 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31562 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31564 if(b.size == 'md-left' || b.size == 'md-right'){
31565 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31566 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31569 b.el.setWidth(width);
31570 b.el.setHeight(height);
31572 b.el.select('iframe',true).setSize(width,height);
31576 for (var i = 0; i < this.cols; i++){
31578 if(maxY[i] < maxY[col]){
31583 col = Math.min(col, i);
31587 x = pos.x + col * (this.colWidth + this.padWidth);
31591 var positions = [];
31593 switch (box.length){
31595 positions = this.getVerticalOneBoxColPositions(x, y, box);
31598 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31601 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31604 positions = this.getVerticalFourBoxColPositions(x, y, box);
31610 Roo.each(box, function(b,kk){
31612 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31614 var sz = b.el.getSize();
31616 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31624 for (var i = 0; i < this.cols; i++){
31625 mY = Math.max(mY, maxY[i]);
31628 this.el.setHeight(mY - pos.y);
31632 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31634 // var pos = this.el.getBox(true);
31637 // var maxX = pos.right;
31639 // var maxHeight = 0;
31641 // Roo.each(items, function(item, k){
31645 // item.el.position('absolute');
31647 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31649 // item.el.setWidth(width);
31651 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31653 // item.el.setHeight(height);
31656 // item.el.setXY([x, y], isInstant ? false : true);
31658 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31661 // y = y + height + this.alternativePadWidth;
31663 // maxHeight = maxHeight + height + this.alternativePadWidth;
31667 // this.el.setHeight(maxHeight);
31671 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31673 var pos = this.el.getBox(true);
31678 var maxX = pos.right;
31680 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31682 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31684 Roo.each(queue, function(box, k){
31686 Roo.each(box, function(b, kk){
31688 b.el.position('absolute');
31690 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31691 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31693 if(b.size == 'md-left' || b.size == 'md-right'){
31694 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31695 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31698 b.el.setWidth(width);
31699 b.el.setHeight(height);
31707 var positions = [];
31709 switch (box.length){
31711 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31714 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31717 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31720 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31726 Roo.each(box, function(b,kk){
31728 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31730 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31738 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31740 Roo.each(eItems, function(b,k){
31742 b.size = (k == 0) ? 'sm' : 'xs';
31743 b.x = (k == 0) ? 2 : 1;
31744 b.y = (k == 0) ? 2 : 1;
31746 b.el.position('absolute');
31748 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31750 b.el.setWidth(width);
31752 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31754 b.el.setHeight(height);
31758 var positions = [];
31761 x : maxX - this.unitWidth * 2 - this.gutter,
31766 x : maxX - this.unitWidth,
31767 y : minY + (this.unitWidth + this.gutter) * 2
31771 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31775 Roo.each(eItems, function(b,k){
31777 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31783 getVerticalOneBoxColPositions : function(x, y, box)
31787 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31789 if(box[0].size == 'md-left'){
31793 if(box[0].size == 'md-right'){
31798 x : x + (this.unitWidth + this.gutter) * rand,
31805 getVerticalTwoBoxColPositions : function(x, y, box)
31809 if(box[0].size == 'xs'){
31813 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31817 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31831 x : x + (this.unitWidth + this.gutter) * 2,
31832 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31839 getVerticalThreeBoxColPositions : function(x, y, box)
31843 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31851 x : x + (this.unitWidth + this.gutter) * 1,
31856 x : x + (this.unitWidth + this.gutter) * 2,
31864 if(box[0].size == 'xs' && box[1].size == 'xs'){
31873 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31877 x : x + (this.unitWidth + this.gutter) * 1,
31891 x : x + (this.unitWidth + this.gutter) * 2,
31896 x : x + (this.unitWidth + this.gutter) * 2,
31897 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31904 getVerticalFourBoxColPositions : function(x, y, box)
31908 if(box[0].size == 'xs'){
31917 y : y + (this.unitHeight + this.gutter) * 1
31922 y : y + (this.unitHeight + this.gutter) * 2
31926 x : x + (this.unitWidth + this.gutter) * 1,
31940 x : x + (this.unitWidth + this.gutter) * 2,
31945 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31946 y : y + (this.unitHeight + this.gutter) * 1
31950 x : x + (this.unitWidth + this.gutter) * 2,
31951 y : y + (this.unitWidth + this.gutter) * 2
31958 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31962 if(box[0].size == 'md-left'){
31964 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31971 if(box[0].size == 'md-right'){
31973 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31974 y : minY + (this.unitWidth + this.gutter) * 1
31980 var rand = Math.floor(Math.random() * (4 - box[0].y));
31983 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31984 y : minY + (this.unitWidth + this.gutter) * rand
31991 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31995 if(box[0].size == 'xs'){
31998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32003 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32012 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32018 y : minY + (this.unitWidth + this.gutter) * 2
32025 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32029 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32032 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32037 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32038 y : minY + (this.unitWidth + this.gutter) * 1
32042 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32043 y : minY + (this.unitWidth + this.gutter) * 2
32050 if(box[0].size == 'xs' && box[1].size == 'xs'){
32053 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32058 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32063 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32064 y : minY + (this.unitWidth + this.gutter) * 1
32072 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32077 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32078 y : minY + (this.unitWidth + this.gutter) * 2
32082 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32083 y : minY + (this.unitWidth + this.gutter) * 2
32090 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32094 if(box[0].size == 'xs'){
32097 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32102 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32107 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),
32112 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32113 y : minY + (this.unitWidth + this.gutter) * 1
32121 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32126 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32127 y : minY + (this.unitWidth + this.gutter) * 2
32131 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32132 y : minY + (this.unitWidth + this.gutter) * 2
32136 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),
32137 y : minY + (this.unitWidth + this.gutter) * 2
32145 * remove a Masonry Brick
32146 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32148 removeBrick : function(brick_id)
32154 for (var i = 0; i<this.bricks.length; i++) {
32155 if (this.bricks[i].id == brick_id) {
32156 this.bricks.splice(i,1);
32157 this.el.dom.removeChild(Roo.get(brick_id).dom);
32164 * adds a Masonry Brick
32165 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32167 addBrick : function(cfg)
32169 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32170 //this.register(cn);
32171 cn.parentId = this.id;
32172 cn.render(this.el);
32177 * register a Masonry Brick
32178 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32181 register : function(brick)
32183 this.bricks.push(brick);
32184 brick.masonryId = this.id;
32188 * clear all the Masonry Brick
32190 clearAll : function()
32193 //this.getChildContainer().dom.innerHTML = "";
32194 this.el.dom.innerHTML = '';
32197 getSelected : function()
32199 if (!this.selectedBrick) {
32203 return this.selectedBrick;
32207 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32211 * register a Masonry Layout
32212 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32215 register : function(layout)
32217 this.groups[layout.id] = layout;
32220 * fetch a Masonry Layout based on the masonry layout ID
32221 * @param {string} the masonry layout to add
32222 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32225 get: function(layout_id) {
32226 if (typeof(this.groups[layout_id]) == 'undefined') {
32229 return this.groups[layout_id] ;
32241 * http://masonry.desandro.com
32243 * The idea is to render all the bricks based on vertical width...
32245 * The original code extends 'outlayer' - we might need to use that....
32251 * @class Roo.bootstrap.LayoutMasonryAuto
32252 * @extends Roo.bootstrap.Component
32253 * Bootstrap Layout Masonry class
32256 * Create a new Element
32257 * @param {Object} config The config object
32260 Roo.bootstrap.LayoutMasonryAuto = function(config){
32261 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32264 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32267 * @cfg {Boolean} isFitWidth - resize the width..
32269 isFitWidth : false, // options..
32271 * @cfg {Boolean} isOriginLeft = left align?
32273 isOriginLeft : true,
32275 * @cfg {Boolean} isOriginTop = top align?
32277 isOriginTop : false,
32279 * @cfg {Boolean} isLayoutInstant = no animation?
32281 isLayoutInstant : false, // needed?
32283 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32285 isResizingContainer : true,
32287 * @cfg {Number} columnWidth width of the columns
32293 * @cfg {Number} maxCols maximum number of columns
32298 * @cfg {Number} padHeight padding below box..
32304 * @cfg {Boolean} isAutoInitial defalut true
32307 isAutoInitial : true,
32313 initialColumnWidth : 0,
32314 currentSize : null,
32316 colYs : null, // array.
32323 bricks: null, //CompositeElement
32324 cols : 0, // array?
32325 // element : null, // wrapped now this.el
32326 _isLayoutInited : null,
32329 getAutoCreate : function(){
32333 cls: 'blog-masonary-wrapper ' + this.cls,
32335 cls : 'mas-boxes masonary'
32342 getChildContainer: function( )
32344 if (this.boxesEl) {
32345 return this.boxesEl;
32348 this.boxesEl = this.el.select('.mas-boxes').first();
32350 return this.boxesEl;
32354 initEvents : function()
32358 if(this.isAutoInitial){
32359 Roo.log('hook children rendered');
32360 this.on('childrenrendered', function() {
32361 Roo.log('children rendered');
32368 initial : function()
32370 this.reloadItems();
32372 this.currentSize = this.el.getBox(true);
32374 /// was window resize... - let's see if this works..
32375 Roo.EventManager.onWindowResize(this.resize, this);
32377 if(!this.isAutoInitial){
32382 this.layout.defer(500,this);
32385 reloadItems: function()
32387 this.bricks = this.el.select('.masonry-brick', true);
32389 this.bricks.each(function(b) {
32390 //Roo.log(b.getSize());
32391 if (!b.attr('originalwidth')) {
32392 b.attr('originalwidth', b.getSize().width);
32397 Roo.log(this.bricks.elements.length);
32400 resize : function()
32403 var cs = this.el.getBox(true);
32405 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32406 Roo.log("no change in with or X");
32409 this.currentSize = cs;
32413 layout : function()
32416 this._resetLayout();
32417 //this._manageStamps();
32419 // don't animate first layout
32420 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32421 this.layoutItems( isInstant );
32423 // flag for initalized
32424 this._isLayoutInited = true;
32427 layoutItems : function( isInstant )
32429 //var items = this._getItemsForLayout( this.items );
32430 // original code supports filtering layout items.. we just ignore it..
32432 this._layoutItems( this.bricks , isInstant );
32434 this._postLayout();
32436 _layoutItems : function ( items , isInstant)
32438 //this.fireEvent( 'layout', this, items );
32441 if ( !items || !items.elements.length ) {
32442 // no items, emit event with empty array
32447 items.each(function(item) {
32448 Roo.log("layout item");
32450 // get x/y object from method
32451 var position = this._getItemLayoutPosition( item );
32453 position.item = item;
32454 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32455 queue.push( position );
32458 this._processLayoutQueue( queue );
32460 /** Sets position of item in DOM
32461 * @param {Element} item
32462 * @param {Number} x - horizontal position
32463 * @param {Number} y - vertical position
32464 * @param {Boolean} isInstant - disables transitions
32466 _processLayoutQueue : function( queue )
32468 for ( var i=0, len = queue.length; i < len; i++ ) {
32469 var obj = queue[i];
32470 obj.item.position('absolute');
32471 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32477 * Any logic you want to do after each layout,
32478 * i.e. size the container
32480 _postLayout : function()
32482 this.resizeContainer();
32485 resizeContainer : function()
32487 if ( !this.isResizingContainer ) {
32490 var size = this._getContainerSize();
32492 this.el.setSize(size.width,size.height);
32493 this.boxesEl.setSize(size.width,size.height);
32499 _resetLayout : function()
32501 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32502 this.colWidth = this.el.getWidth();
32503 //this.gutter = this.el.getWidth();
32505 this.measureColumns();
32511 this.colYs.push( 0 );
32517 measureColumns : function()
32519 this.getContainerWidth();
32520 // if columnWidth is 0, default to outerWidth of first item
32521 if ( !this.columnWidth ) {
32522 var firstItem = this.bricks.first();
32523 Roo.log(firstItem);
32524 this.columnWidth = this.containerWidth;
32525 if (firstItem && firstItem.attr('originalwidth') ) {
32526 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32528 // columnWidth fall back to item of first element
32529 Roo.log("set column width?");
32530 this.initialColumnWidth = this.columnWidth ;
32532 // if first elem has no width, default to size of container
32537 if (this.initialColumnWidth) {
32538 this.columnWidth = this.initialColumnWidth;
32543 // column width is fixed at the top - however if container width get's smaller we should
32546 // this bit calcs how man columns..
32548 var columnWidth = this.columnWidth += this.gutter;
32550 // calculate columns
32551 var containerWidth = this.containerWidth + this.gutter;
32553 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32554 // fix rounding errors, typically with gutters
32555 var excess = columnWidth - containerWidth % columnWidth;
32558 // if overshoot is less than a pixel, round up, otherwise floor it
32559 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32560 cols = Math[ mathMethod ]( cols );
32561 this.cols = Math.max( cols, 1 );
32562 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32564 // padding positioning..
32565 var totalColWidth = this.cols * this.columnWidth;
32566 var padavail = this.containerWidth - totalColWidth;
32567 // so for 2 columns - we need 3 'pads'
32569 var padNeeded = (1+this.cols) * this.padWidth;
32571 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32573 this.columnWidth += padExtra
32574 //this.padWidth = Math.floor(padavail / ( this.cols));
32576 // adjust colum width so that padding is fixed??
32578 // we have 3 columns ... total = width * 3
32579 // we have X left over... that should be used by
32581 //if (this.expandC) {
32589 getContainerWidth : function()
32591 /* // container is parent if fit width
32592 var container = this.isFitWidth ? this.element.parentNode : this.element;
32593 // check that this.size and size are there
32594 // IE8 triggers resize on body size change, so they might not be
32596 var size = getSize( container ); //FIXME
32597 this.containerWidth = size && size.innerWidth; //FIXME
32600 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32604 _getItemLayoutPosition : function( item ) // what is item?
32606 // we resize the item to our columnWidth..
32608 item.setWidth(this.columnWidth);
32609 item.autoBoxAdjust = false;
32611 var sz = item.getSize();
32613 // how many columns does this brick span
32614 var remainder = this.containerWidth % this.columnWidth;
32616 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32617 // round if off by 1 pixel, otherwise use ceil
32618 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32619 colSpan = Math.min( colSpan, this.cols );
32621 // normally this should be '1' as we dont' currently allow multi width columns..
32623 var colGroup = this._getColGroup( colSpan );
32624 // get the minimum Y value from the columns
32625 var minimumY = Math.min.apply( Math, colGroup );
32626 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32628 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32630 // position the brick
32632 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32633 y: this.currentSize.y + minimumY + this.padHeight
32637 // apply setHeight to necessary columns
32638 var setHeight = minimumY + sz.height + this.padHeight;
32639 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32641 var setSpan = this.cols + 1 - colGroup.length;
32642 for ( var i = 0; i < setSpan; i++ ) {
32643 this.colYs[ shortColIndex + i ] = setHeight ;
32650 * @param {Number} colSpan - number of columns the element spans
32651 * @returns {Array} colGroup
32653 _getColGroup : function( colSpan )
32655 if ( colSpan < 2 ) {
32656 // if brick spans only one column, use all the column Ys
32661 // how many different places could this brick fit horizontally
32662 var groupCount = this.cols + 1 - colSpan;
32663 // for each group potential horizontal position
32664 for ( var i = 0; i < groupCount; i++ ) {
32665 // make an array of colY values for that one group
32666 var groupColYs = this.colYs.slice( i, i + colSpan );
32667 // and get the max value of the array
32668 colGroup[i] = Math.max.apply( Math, groupColYs );
32673 _manageStamp : function( stamp )
32675 var stampSize = stamp.getSize();
32676 var offset = stamp.getBox();
32677 // get the columns that this stamp affects
32678 var firstX = this.isOriginLeft ? offset.x : offset.right;
32679 var lastX = firstX + stampSize.width;
32680 var firstCol = Math.floor( firstX / this.columnWidth );
32681 firstCol = Math.max( 0, firstCol );
32683 var lastCol = Math.floor( lastX / this.columnWidth );
32684 // lastCol should not go over if multiple of columnWidth #425
32685 lastCol -= lastX % this.columnWidth ? 0 : 1;
32686 lastCol = Math.min( this.cols - 1, lastCol );
32688 // set colYs to bottom of the stamp
32689 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32692 for ( var i = firstCol; i <= lastCol; i++ ) {
32693 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32698 _getContainerSize : function()
32700 this.maxY = Math.max.apply( Math, this.colYs );
32705 if ( this.isFitWidth ) {
32706 size.width = this._getContainerFitWidth();
32712 _getContainerFitWidth : function()
32714 var unusedCols = 0;
32715 // count unused columns
32718 if ( this.colYs[i] !== 0 ) {
32723 // fit container to columns that have been used
32724 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32727 needsResizeLayout : function()
32729 var previousWidth = this.containerWidth;
32730 this.getContainerWidth();
32731 return previousWidth !== this.containerWidth;
32746 * @class Roo.bootstrap.MasonryBrick
32747 * @extends Roo.bootstrap.Component
32748 * Bootstrap MasonryBrick class
32751 * Create a new MasonryBrick
32752 * @param {Object} config The config object
32755 Roo.bootstrap.MasonryBrick = function(config){
32757 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32759 Roo.bootstrap.MasonryBrick.register(this);
32765 * When a MasonryBrick is clcik
32766 * @param {Roo.bootstrap.MasonryBrick} this
32767 * @param {Roo.EventObject} e
32773 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32776 * @cfg {String} title
32780 * @cfg {String} html
32784 * @cfg {String} bgimage
32788 * @cfg {String} videourl
32792 * @cfg {String} cls
32796 * @cfg {String} href
32800 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32805 * @cfg {String} placetitle (center|bottom)
32810 * @cfg {Boolean} isFitContainer defalut true
32812 isFitContainer : true,
32815 * @cfg {Boolean} preventDefault defalut false
32817 preventDefault : false,
32820 * @cfg {Boolean} inverse defalut false
32822 maskInverse : false,
32824 getAutoCreate : function()
32826 if(!this.isFitContainer){
32827 return this.getSplitAutoCreate();
32830 var cls = 'masonry-brick masonry-brick-full';
32832 if(this.href.length){
32833 cls += ' masonry-brick-link';
32836 if(this.bgimage.length){
32837 cls += ' masonry-brick-image';
32840 if(this.maskInverse){
32841 cls += ' mask-inverse';
32844 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32845 cls += ' enable-mask';
32849 cls += ' masonry-' + this.size + '-brick';
32852 if(this.placetitle.length){
32854 switch (this.placetitle) {
32856 cls += ' masonry-center-title';
32859 cls += ' masonry-bottom-title';
32866 if(!this.html.length && !this.bgimage.length){
32867 cls += ' masonry-center-title';
32870 if(!this.html.length && this.bgimage.length){
32871 cls += ' masonry-bottom-title';
32876 cls += ' ' + this.cls;
32880 tag: (this.href.length) ? 'a' : 'div',
32885 cls: 'masonry-brick-mask'
32889 cls: 'masonry-brick-paragraph',
32895 if(this.href.length){
32896 cfg.href = this.href;
32899 var cn = cfg.cn[1].cn;
32901 if(this.title.length){
32904 cls: 'masonry-brick-title',
32909 if(this.html.length){
32912 cls: 'masonry-brick-text',
32917 if (!this.title.length && !this.html.length) {
32918 cfg.cn[1].cls += ' hide';
32921 if(this.bgimage.length){
32924 cls: 'masonry-brick-image-view',
32929 if(this.videourl.length){
32930 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32931 // youtube support only?
32934 cls: 'masonry-brick-image-view',
32937 allowfullscreen : true
32945 getSplitAutoCreate : function()
32947 var cls = 'masonry-brick masonry-brick-split';
32949 if(this.href.length){
32950 cls += ' masonry-brick-link';
32953 if(this.bgimage.length){
32954 cls += ' masonry-brick-image';
32958 cls += ' masonry-' + this.size + '-brick';
32961 switch (this.placetitle) {
32963 cls += ' masonry-center-title';
32966 cls += ' masonry-bottom-title';
32969 if(!this.bgimage.length){
32970 cls += ' masonry-center-title';
32973 if(this.bgimage.length){
32974 cls += ' masonry-bottom-title';
32980 cls += ' ' + this.cls;
32984 tag: (this.href.length) ? 'a' : 'div',
32989 cls: 'masonry-brick-split-head',
32993 cls: 'masonry-brick-paragraph',
33000 cls: 'masonry-brick-split-body',
33006 if(this.href.length){
33007 cfg.href = this.href;
33010 if(this.title.length){
33011 cfg.cn[0].cn[0].cn.push({
33013 cls: 'masonry-brick-title',
33018 if(this.html.length){
33019 cfg.cn[1].cn.push({
33021 cls: 'masonry-brick-text',
33026 if(this.bgimage.length){
33027 cfg.cn[0].cn.push({
33029 cls: 'masonry-brick-image-view',
33034 if(this.videourl.length){
33035 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33036 // youtube support only?
33037 cfg.cn[0].cn.cn.push({
33039 cls: 'masonry-brick-image-view',
33042 allowfullscreen : true
33049 initEvents: function()
33051 switch (this.size) {
33084 this.el.on('touchstart', this.onTouchStart, this);
33085 this.el.on('touchmove', this.onTouchMove, this);
33086 this.el.on('touchend', this.onTouchEnd, this);
33087 this.el.on('contextmenu', this.onContextMenu, this);
33089 this.el.on('mouseenter' ,this.enter, this);
33090 this.el.on('mouseleave', this.leave, this);
33091 this.el.on('click', this.onClick, this);
33094 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33095 this.parent().bricks.push(this);
33100 onClick: function(e, el)
33102 var time = this.endTimer - this.startTimer;
33103 // Roo.log(e.preventDefault());
33106 e.preventDefault();
33111 if(!this.preventDefault){
33115 e.preventDefault();
33117 if (this.activeClass != '') {
33118 this.selectBrick();
33121 this.fireEvent('click', this, e);
33124 enter: function(e, el)
33126 e.preventDefault();
33128 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33132 if(this.bgimage.length && this.html.length){
33133 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33137 leave: function(e, el)
33139 e.preventDefault();
33141 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33145 if(this.bgimage.length && this.html.length){
33146 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33150 onTouchStart: function(e, el)
33152 // e.preventDefault();
33154 this.touchmoved = false;
33156 if(!this.isFitContainer){
33160 if(!this.bgimage.length || !this.html.length){
33164 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33166 this.timer = new Date().getTime();
33170 onTouchMove: function(e, el)
33172 this.touchmoved = true;
33175 onContextMenu : function(e,el)
33177 e.preventDefault();
33178 e.stopPropagation();
33182 onTouchEnd: function(e, el)
33184 // e.preventDefault();
33186 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33193 if(!this.bgimage.length || !this.html.length){
33195 if(this.href.length){
33196 window.location.href = this.href;
33202 if(!this.isFitContainer){
33206 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33208 window.location.href = this.href;
33211 //selection on single brick only
33212 selectBrick : function() {
33214 if (!this.parentId) {
33218 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33219 var index = m.selectedBrick.indexOf(this.id);
33222 m.selectedBrick.splice(index,1);
33223 this.el.removeClass(this.activeClass);
33227 for(var i = 0; i < m.selectedBrick.length; i++) {
33228 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33229 b.el.removeClass(b.activeClass);
33232 m.selectedBrick = [];
33234 m.selectedBrick.push(this.id);
33235 this.el.addClass(this.activeClass);
33239 isSelected : function(){
33240 return this.el.hasClass(this.activeClass);
33245 Roo.apply(Roo.bootstrap.MasonryBrick, {
33248 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33250 * register a Masonry Brick
33251 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33254 register : function(brick)
33256 //this.groups[brick.id] = brick;
33257 this.groups.add(brick.id, brick);
33260 * fetch a masonry brick based on the masonry brick ID
33261 * @param {string} the masonry brick to add
33262 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33265 get: function(brick_id)
33267 // if (typeof(this.groups[brick_id]) == 'undefined') {
33270 // return this.groups[brick_id] ;
33272 if(this.groups.key(brick_id)) {
33273 return this.groups.key(brick_id);
33291 * @class Roo.bootstrap.Brick
33292 * @extends Roo.bootstrap.Component
33293 * Bootstrap Brick class
33296 * Create a new Brick
33297 * @param {Object} config The config object
33300 Roo.bootstrap.Brick = function(config){
33301 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33307 * When a Brick is click
33308 * @param {Roo.bootstrap.Brick} this
33309 * @param {Roo.EventObject} e
33315 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33318 * @cfg {String} title
33322 * @cfg {String} html
33326 * @cfg {String} bgimage
33330 * @cfg {String} cls
33334 * @cfg {String} href
33338 * @cfg {String} video
33342 * @cfg {Boolean} square
33346 getAutoCreate : function()
33348 var cls = 'roo-brick';
33350 if(this.href.length){
33351 cls += ' roo-brick-link';
33354 if(this.bgimage.length){
33355 cls += ' roo-brick-image';
33358 if(!this.html.length && !this.bgimage.length){
33359 cls += ' roo-brick-center-title';
33362 if(!this.html.length && this.bgimage.length){
33363 cls += ' roo-brick-bottom-title';
33367 cls += ' ' + this.cls;
33371 tag: (this.href.length) ? 'a' : 'div',
33376 cls: 'roo-brick-paragraph',
33382 if(this.href.length){
33383 cfg.href = this.href;
33386 var cn = cfg.cn[0].cn;
33388 if(this.title.length){
33391 cls: 'roo-brick-title',
33396 if(this.html.length){
33399 cls: 'roo-brick-text',
33406 if(this.bgimage.length){
33409 cls: 'roo-brick-image-view',
33417 initEvents: function()
33419 if(this.title.length || this.html.length){
33420 this.el.on('mouseenter' ,this.enter, this);
33421 this.el.on('mouseleave', this.leave, this);
33424 Roo.EventManager.onWindowResize(this.resize, this);
33426 if(this.bgimage.length){
33427 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33428 this.imageEl.on('load', this.onImageLoad, this);
33435 onImageLoad : function()
33440 resize : function()
33442 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33444 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33446 if(this.bgimage.length){
33447 var image = this.el.select('.roo-brick-image-view', true).first();
33449 image.setWidth(paragraph.getWidth());
33452 image.setHeight(paragraph.getWidth());
33455 this.el.setHeight(image.getHeight());
33456 paragraph.setHeight(image.getHeight());
33462 enter: function(e, el)
33464 e.preventDefault();
33466 if(this.bgimage.length){
33467 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33468 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33472 leave: function(e, el)
33474 e.preventDefault();
33476 if(this.bgimage.length){
33477 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33478 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33493 * @class Roo.bootstrap.NumberField
33494 * @extends Roo.bootstrap.Input
33495 * Bootstrap NumberField class
33501 * Create a new NumberField
33502 * @param {Object} config The config object
33505 Roo.bootstrap.NumberField = function(config){
33506 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33509 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33512 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33514 allowDecimals : true,
33516 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33518 decimalSeparator : ".",
33520 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33522 decimalPrecision : 2,
33524 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33526 allowNegative : true,
33529 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33533 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33535 minValue : Number.NEGATIVE_INFINITY,
33537 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33539 maxValue : Number.MAX_VALUE,
33541 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33543 minText : "The minimum value for this field is {0}",
33545 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33547 maxText : "The maximum value for this field is {0}",
33549 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33550 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33552 nanText : "{0} is not a valid number",
33554 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33556 thousandsDelimiter : false,
33558 * @cfg {String} valueAlign alignment of value
33560 valueAlign : "left",
33562 getAutoCreate : function()
33564 var hiddenInput = {
33568 cls: 'hidden-number-input'
33572 hiddenInput.name = this.name;
33577 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33579 this.name = hiddenInput.name;
33581 if(cfg.cn.length > 0) {
33582 cfg.cn.push(hiddenInput);
33589 initEvents : function()
33591 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33593 var allowed = "0123456789";
33595 if(this.allowDecimals){
33596 allowed += this.decimalSeparator;
33599 if(this.allowNegative){
33603 if(this.thousandsDelimiter) {
33607 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33609 var keyPress = function(e){
33611 var k = e.getKey();
33613 var c = e.getCharCode();
33616 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33617 allowed.indexOf(String.fromCharCode(c)) === -1
33623 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33627 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33632 this.el.on("keypress", keyPress, this);
33635 validateValue : function(value)
33638 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33642 var num = this.parseValue(value);
33645 this.markInvalid(String.format(this.nanText, value));
33649 if(num < this.minValue){
33650 this.markInvalid(String.format(this.minText, this.minValue));
33654 if(num > this.maxValue){
33655 this.markInvalid(String.format(this.maxText, this.maxValue));
33662 getValue : function()
33664 var v = this.hiddenEl().getValue();
33666 return this.fixPrecision(this.parseValue(v));
33669 parseValue : function(value)
33671 if(this.thousandsDelimiter) {
33673 r = new RegExp(",", "g");
33674 value = value.replace(r, "");
33677 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33678 return isNaN(value) ? '' : value;
33681 fixPrecision : function(value)
33683 if(this.thousandsDelimiter) {
33685 r = new RegExp(",", "g");
33686 value = value.replace(r, "");
33689 var nan = isNaN(value);
33691 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33692 return nan ? '' : value;
33694 return parseFloat(value).toFixed(this.decimalPrecision);
33697 setValue : function(v)
33699 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33705 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33707 this.inputEl().dom.value = (v == '') ? '' :
33708 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33710 if(!this.allowZero && v === '0') {
33711 this.hiddenEl().dom.value = '';
33712 this.inputEl().dom.value = '';
33719 decimalPrecisionFcn : function(v)
33721 return Math.floor(v);
33724 beforeBlur : function()
33726 var v = this.parseValue(this.getRawValue());
33728 if(v || v === 0 || v === ''){
33733 hiddenEl : function()
33735 return this.el.select('input.hidden-number-input',true).first();
33747 * @class Roo.bootstrap.DocumentSlider
33748 * @extends Roo.bootstrap.Component
33749 * Bootstrap DocumentSlider class
33752 * Create a new DocumentViewer
33753 * @param {Object} config The config object
33756 Roo.bootstrap.DocumentSlider = function(config){
33757 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33764 * Fire after initEvent
33765 * @param {Roo.bootstrap.DocumentSlider} this
33770 * Fire after update
33771 * @param {Roo.bootstrap.DocumentSlider} this
33777 * @param {Roo.bootstrap.DocumentSlider} this
33783 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33789 getAutoCreate : function()
33793 cls : 'roo-document-slider',
33797 cls : 'roo-document-slider-header',
33801 cls : 'roo-document-slider-header-title'
33807 cls : 'roo-document-slider-body',
33811 cls : 'roo-document-slider-prev',
33815 cls : 'fa fa-chevron-left'
33821 cls : 'roo-document-slider-thumb',
33825 cls : 'roo-document-slider-image'
33831 cls : 'roo-document-slider-next',
33835 cls : 'fa fa-chevron-right'
33847 initEvents : function()
33849 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33850 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33852 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33853 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33855 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33856 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33858 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33859 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33861 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33862 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33864 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33865 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33867 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33868 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33870 this.thumbEl.on('click', this.onClick, this);
33872 this.prevIndicator.on('click', this.prev, this);
33874 this.nextIndicator.on('click', this.next, this);
33878 initial : function()
33880 if(this.files.length){
33881 this.indicator = 1;
33885 this.fireEvent('initial', this);
33888 update : function()
33890 this.imageEl.attr('src', this.files[this.indicator - 1]);
33892 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33894 this.prevIndicator.show();
33896 if(this.indicator == 1){
33897 this.prevIndicator.hide();
33900 this.nextIndicator.show();
33902 if(this.indicator == this.files.length){
33903 this.nextIndicator.hide();
33906 this.thumbEl.scrollTo('top');
33908 this.fireEvent('update', this);
33911 onClick : function(e)
33913 e.preventDefault();
33915 this.fireEvent('click', this);
33920 e.preventDefault();
33922 this.indicator = Math.max(1, this.indicator - 1);
33929 e.preventDefault();
33931 this.indicator = Math.min(this.files.length, this.indicator + 1);
33945 * @class Roo.bootstrap.RadioSet
33946 * @extends Roo.bootstrap.Input
33947 * Bootstrap RadioSet class
33948 * @cfg {String} indicatorpos (left|right) default left
33949 * @cfg {Boolean} inline (true|false) inline the element (default true)
33950 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33952 * Create a new RadioSet
33953 * @param {Object} config The config object
33956 Roo.bootstrap.RadioSet = function(config){
33958 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33962 Roo.bootstrap.RadioSet.register(this);
33967 * Fires when the element is checked or unchecked.
33968 * @param {Roo.bootstrap.RadioSet} this This radio
33969 * @param {Roo.bootstrap.Radio} item The checked item
33974 * Fires when the element is click.
33975 * @param {Roo.bootstrap.RadioSet} this This radio set
33976 * @param {Roo.bootstrap.Radio} item The checked item
33977 * @param {Roo.EventObject} e The event object
33984 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33992 indicatorpos : 'left',
33994 getAutoCreate : function()
33998 cls : 'roo-radio-set-label',
34002 html : this.fieldLabel
34007 if(this.indicatorpos == 'left'){
34010 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34011 tooltip : 'This field is required'
34016 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34017 tooltip : 'This field is required'
34023 cls : 'roo-radio-set-items'
34026 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34028 if (align === 'left' && this.fieldLabel.length) {
34031 cls : "roo-radio-set-right",
34037 if(this.labelWidth > 12){
34038 label.style = "width: " + this.labelWidth + 'px';
34041 if(this.labelWidth < 13 && this.labelmd == 0){
34042 this.labelmd = this.labelWidth;
34045 if(this.labellg > 0){
34046 label.cls += ' col-lg-' + this.labellg;
34047 items.cls += ' col-lg-' + (12 - this.labellg);
34050 if(this.labelmd > 0){
34051 label.cls += ' col-md-' + this.labelmd;
34052 items.cls += ' col-md-' + (12 - this.labelmd);
34055 if(this.labelsm > 0){
34056 label.cls += ' col-sm-' + this.labelsm;
34057 items.cls += ' col-sm-' + (12 - this.labelsm);
34060 if(this.labelxs > 0){
34061 label.cls += ' col-xs-' + this.labelxs;
34062 items.cls += ' col-xs-' + (12 - this.labelxs);
34068 cls : 'roo-radio-set',
34072 cls : 'roo-radio-set-input',
34075 value : this.value ? this.value : ''
34082 if(this.weight.length){
34083 cfg.cls += ' roo-radio-' + this.weight;
34087 cfg.cls += ' roo-radio-set-inline';
34091 ['xs','sm','md','lg'].map(function(size){
34092 if (settings[size]) {
34093 cfg.cls += ' col-' + size + '-' + settings[size];
34101 initEvents : function()
34103 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34104 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34106 if(!this.fieldLabel.length){
34107 this.labelEl.hide();
34110 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34111 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34113 this.indicator = this.indicatorEl();
34115 if(this.indicator){
34116 this.indicator.addClass('invisible');
34119 this.originalValue = this.getValue();
34123 inputEl: function ()
34125 return this.el.select('.roo-radio-set-input', true).first();
34128 getChildContainer : function()
34130 return this.itemsEl;
34133 register : function(item)
34135 this.radioes.push(item);
34139 validate : function()
34141 if(this.getVisibilityEl().hasClass('hidden')){
34147 Roo.each(this.radioes, function(i){
34156 if(this.allowBlank) {
34160 if(this.disabled || valid){
34165 this.markInvalid();
34170 markValid : function()
34172 if(this.labelEl.isVisible(true)){
34173 this.indicatorEl().removeClass('visible');
34174 this.indicatorEl().addClass('invisible');
34177 this.el.removeClass([this.invalidClass, this.validClass]);
34178 this.el.addClass(this.validClass);
34180 this.fireEvent('valid', this);
34183 markInvalid : function(msg)
34185 if(this.allowBlank || this.disabled){
34189 if(this.labelEl.isVisible(true)){
34190 this.indicatorEl().removeClass('invisible');
34191 this.indicatorEl().addClass('visible');
34194 this.el.removeClass([this.invalidClass, this.validClass]);
34195 this.el.addClass(this.invalidClass);
34197 this.fireEvent('invalid', this, msg);
34201 setValue : function(v, suppressEvent)
34203 if(this.value === v){
34210 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34213 Roo.each(this.radioes, function(i){
34215 i.el.removeClass('checked');
34218 Roo.each(this.radioes, function(i){
34220 if(i.value === v || i.value.toString() === v.toString()){
34222 i.el.addClass('checked');
34224 if(suppressEvent !== true){
34225 this.fireEvent('check', this, i);
34236 clearInvalid : function(){
34238 if(!this.el || this.preventMark){
34242 this.el.removeClass([this.invalidClass]);
34244 this.fireEvent('valid', this);
34249 Roo.apply(Roo.bootstrap.RadioSet, {
34253 register : function(set)
34255 this.groups[set.name] = set;
34258 get: function(name)
34260 if (typeof(this.groups[name]) == 'undefined') {
34264 return this.groups[name] ;
34270 * Ext JS Library 1.1.1
34271 * Copyright(c) 2006-2007, Ext JS, LLC.
34273 * Originally Released Under LGPL - original licence link has changed is not relivant.
34276 * <script type="text/javascript">
34281 * @class Roo.bootstrap.SplitBar
34282 * @extends Roo.util.Observable
34283 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34287 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34288 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34289 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34290 split.minSize = 100;
34291 split.maxSize = 600;
34292 split.animate = true;
34293 split.on('moved', splitterMoved);
34296 * Create a new SplitBar
34297 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34298 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34299 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34300 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34301 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34302 position of the SplitBar).
34304 Roo.bootstrap.SplitBar = function(cfg){
34309 // dragElement : elm
34310 // resizingElement: el,
34312 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34313 // placement : Roo.bootstrap.SplitBar.LEFT ,
34314 // existingProxy ???
34317 this.el = Roo.get(cfg.dragElement, true);
34318 this.el.dom.unselectable = "on";
34320 this.resizingEl = Roo.get(cfg.resizingElement, true);
34324 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34325 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34328 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34331 * The minimum size of the resizing element. (Defaults to 0)
34337 * The maximum size of the resizing element. (Defaults to 2000)
34340 this.maxSize = 2000;
34343 * Whether to animate the transition to the new size
34346 this.animate = false;
34349 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34352 this.useShim = false;
34357 if(!cfg.existingProxy){
34359 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34361 this.proxy = Roo.get(cfg.existingProxy).dom;
34364 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34367 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34370 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34373 this.dragSpecs = {};
34376 * @private The adapter to use to positon and resize elements
34378 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34379 this.adapter.init(this);
34381 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34383 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34384 this.el.addClass("roo-splitbar-h");
34387 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34388 this.el.addClass("roo-splitbar-v");
34394 * Fires when the splitter is moved (alias for {@link #event-moved})
34395 * @param {Roo.bootstrap.SplitBar} this
34396 * @param {Number} newSize the new width or height
34401 * Fires when the splitter is moved
34402 * @param {Roo.bootstrap.SplitBar} this
34403 * @param {Number} newSize the new width or height
34407 * @event beforeresize
34408 * Fires before the splitter is dragged
34409 * @param {Roo.bootstrap.SplitBar} this
34411 "beforeresize" : true,
34413 "beforeapply" : true
34416 Roo.util.Observable.call(this);
34419 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34420 onStartProxyDrag : function(x, y){
34421 this.fireEvent("beforeresize", this);
34423 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34425 o.enableDisplayMode("block");
34426 // all splitbars share the same overlay
34427 Roo.bootstrap.SplitBar.prototype.overlay = o;
34429 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34430 this.overlay.show();
34431 Roo.get(this.proxy).setDisplayed("block");
34432 var size = this.adapter.getElementSize(this);
34433 this.activeMinSize = this.getMinimumSize();;
34434 this.activeMaxSize = this.getMaximumSize();;
34435 var c1 = size - this.activeMinSize;
34436 var c2 = Math.max(this.activeMaxSize - size, 0);
34437 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34438 this.dd.resetConstraints();
34439 this.dd.setXConstraint(
34440 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34441 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34443 this.dd.setYConstraint(0, 0);
34445 this.dd.resetConstraints();
34446 this.dd.setXConstraint(0, 0);
34447 this.dd.setYConstraint(
34448 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34449 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34452 this.dragSpecs.startSize = size;
34453 this.dragSpecs.startPoint = [x, y];
34454 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34458 * @private Called after the drag operation by the DDProxy
34460 onEndProxyDrag : function(e){
34461 Roo.get(this.proxy).setDisplayed(false);
34462 var endPoint = Roo.lib.Event.getXY(e);
34464 this.overlay.hide();
34467 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34468 newSize = this.dragSpecs.startSize +
34469 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34470 endPoint[0] - this.dragSpecs.startPoint[0] :
34471 this.dragSpecs.startPoint[0] - endPoint[0]
34474 newSize = this.dragSpecs.startSize +
34475 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34476 endPoint[1] - this.dragSpecs.startPoint[1] :
34477 this.dragSpecs.startPoint[1] - endPoint[1]
34480 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34481 if(newSize != this.dragSpecs.startSize){
34482 if(this.fireEvent('beforeapply', this, newSize) !== false){
34483 this.adapter.setElementSize(this, newSize);
34484 this.fireEvent("moved", this, newSize);
34485 this.fireEvent("resize", this, newSize);
34491 * Get the adapter this SplitBar uses
34492 * @return The adapter object
34494 getAdapter : function(){
34495 return this.adapter;
34499 * Set the adapter this SplitBar uses
34500 * @param {Object} adapter A SplitBar adapter object
34502 setAdapter : function(adapter){
34503 this.adapter = adapter;
34504 this.adapter.init(this);
34508 * Gets the minimum size for the resizing element
34509 * @return {Number} The minimum size
34511 getMinimumSize : function(){
34512 return this.minSize;
34516 * Sets the minimum size for the resizing element
34517 * @param {Number} minSize The minimum size
34519 setMinimumSize : function(minSize){
34520 this.minSize = minSize;
34524 * Gets the maximum size for the resizing element
34525 * @return {Number} The maximum size
34527 getMaximumSize : function(){
34528 return this.maxSize;
34532 * Sets the maximum size for the resizing element
34533 * @param {Number} maxSize The maximum size
34535 setMaximumSize : function(maxSize){
34536 this.maxSize = maxSize;
34540 * Sets the initialize size for the resizing element
34541 * @param {Number} size The initial size
34543 setCurrentSize : function(size){
34544 var oldAnimate = this.animate;
34545 this.animate = false;
34546 this.adapter.setElementSize(this, size);
34547 this.animate = oldAnimate;
34551 * Destroy this splitbar.
34552 * @param {Boolean} removeEl True to remove the element
34554 destroy : function(removeEl){
34556 this.shim.remove();
34559 this.proxy.parentNode.removeChild(this.proxy);
34567 * @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.
34569 Roo.bootstrap.SplitBar.createProxy = function(dir){
34570 var proxy = new Roo.Element(document.createElement("div"));
34571 proxy.unselectable();
34572 var cls = 'roo-splitbar-proxy';
34573 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34574 document.body.appendChild(proxy.dom);
34579 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34580 * Default Adapter. It assumes the splitter and resizing element are not positioned
34581 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34583 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34586 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34587 // do nothing for now
34588 init : function(s){
34592 * Called before drag operations to get the current size of the resizing element.
34593 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34595 getElementSize : function(s){
34596 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34597 return s.resizingEl.getWidth();
34599 return s.resizingEl.getHeight();
34604 * Called after drag operations to set the size of the resizing element.
34605 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34606 * @param {Number} newSize The new size to set
34607 * @param {Function} onComplete A function to be invoked when resizing is complete
34609 setElementSize : function(s, newSize, onComplete){
34610 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34612 s.resizingEl.setWidth(newSize);
34614 onComplete(s, newSize);
34617 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34622 s.resizingEl.setHeight(newSize);
34624 onComplete(s, newSize);
34627 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34634 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34635 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34636 * Adapter that moves the splitter element to align with the resized sizing element.
34637 * Used with an absolute positioned SplitBar.
34638 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34639 * document.body, make sure you assign an id to the body element.
34641 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34642 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34643 this.container = Roo.get(container);
34646 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34647 init : function(s){
34648 this.basic.init(s);
34651 getElementSize : function(s){
34652 return this.basic.getElementSize(s);
34655 setElementSize : function(s, newSize, onComplete){
34656 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34659 moveSplitter : function(s){
34660 var yes = Roo.bootstrap.SplitBar;
34661 switch(s.placement){
34663 s.el.setX(s.resizingEl.getRight());
34666 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34669 s.el.setY(s.resizingEl.getBottom());
34672 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34679 * Orientation constant - Create a vertical SplitBar
34683 Roo.bootstrap.SplitBar.VERTICAL = 1;
34686 * Orientation constant - Create a horizontal SplitBar
34690 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34693 * Placement constant - The resizing element is to the left of the splitter element
34697 Roo.bootstrap.SplitBar.LEFT = 1;
34700 * Placement constant - The resizing element is to the right of the splitter element
34704 Roo.bootstrap.SplitBar.RIGHT = 2;
34707 * Placement constant - The resizing element is positioned above the splitter element
34711 Roo.bootstrap.SplitBar.TOP = 3;
34714 * Placement constant - The resizing element is positioned under splitter element
34718 Roo.bootstrap.SplitBar.BOTTOM = 4;
34719 Roo.namespace("Roo.bootstrap.layout");/*
34721 * Ext JS Library 1.1.1
34722 * Copyright(c) 2006-2007, Ext JS, LLC.
34724 * Originally Released Under LGPL - original licence link has changed is not relivant.
34727 * <script type="text/javascript">
34731 * @class Roo.bootstrap.layout.Manager
34732 * @extends Roo.bootstrap.Component
34733 * Base class for layout managers.
34735 Roo.bootstrap.layout.Manager = function(config)
34737 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34743 /** false to disable window resize monitoring @type Boolean */
34744 this.monitorWindowResize = true;
34749 * Fires when a layout is performed.
34750 * @param {Roo.LayoutManager} this
34754 * @event regionresized
34755 * Fires when the user resizes a region.
34756 * @param {Roo.LayoutRegion} region The resized region
34757 * @param {Number} newSize The new size (width for east/west, height for north/south)
34759 "regionresized" : true,
34761 * @event regioncollapsed
34762 * Fires when a region is collapsed.
34763 * @param {Roo.LayoutRegion} region The collapsed region
34765 "regioncollapsed" : true,
34767 * @event regionexpanded
34768 * Fires when a region is expanded.
34769 * @param {Roo.LayoutRegion} region The expanded region
34771 "regionexpanded" : true
34773 this.updating = false;
34776 this.el = Roo.get(config.el);
34782 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34787 monitorWindowResize : true,
34793 onRender : function(ct, position)
34796 this.el = Roo.get(ct);
34799 //this.fireEvent('render',this);
34803 initEvents: function()
34807 // ie scrollbar fix
34808 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34809 document.body.scroll = "no";
34810 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34811 this.el.position('relative');
34813 this.id = this.el.id;
34814 this.el.addClass("roo-layout-container");
34815 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34816 if(this.el.dom != document.body ) {
34817 this.el.on('resize', this.layout,this);
34818 this.el.on('show', this.layout,this);
34824 * Returns true if this layout is currently being updated
34825 * @return {Boolean}
34827 isUpdating : function(){
34828 return this.updating;
34832 * Suspend the LayoutManager from doing auto-layouts while
34833 * making multiple add or remove calls
34835 beginUpdate : function(){
34836 this.updating = true;
34840 * Restore auto-layouts and optionally disable the manager from performing a layout
34841 * @param {Boolean} noLayout true to disable a layout update
34843 endUpdate : function(noLayout){
34844 this.updating = false;
34850 layout: function(){
34854 onRegionResized : function(region, newSize){
34855 this.fireEvent("regionresized", region, newSize);
34859 onRegionCollapsed : function(region){
34860 this.fireEvent("regioncollapsed", region);
34863 onRegionExpanded : function(region){
34864 this.fireEvent("regionexpanded", region);
34868 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34869 * performs box-model adjustments.
34870 * @return {Object} The size as an object {width: (the width), height: (the height)}
34872 getViewSize : function()
34875 if(this.el.dom != document.body){
34876 size = this.el.getSize();
34878 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34880 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34881 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34886 * Returns the Element this layout is bound to.
34887 * @return {Roo.Element}
34889 getEl : function(){
34894 * Returns the specified region.
34895 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34896 * @return {Roo.LayoutRegion}
34898 getRegion : function(target){
34899 return this.regions[target.toLowerCase()];
34902 onWindowResize : function(){
34903 if(this.monitorWindowResize){
34910 * Ext JS Library 1.1.1
34911 * Copyright(c) 2006-2007, Ext JS, LLC.
34913 * Originally Released Under LGPL - original licence link has changed is not relivant.
34916 * <script type="text/javascript">
34919 * @class Roo.bootstrap.layout.Border
34920 * @extends Roo.bootstrap.layout.Manager
34921 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34922 * please see: examples/bootstrap/nested.html<br><br>
34924 <b>The container the layout is rendered into can be either the body element or any other element.
34925 If it is not the body element, the container needs to either be an absolute positioned element,
34926 or you will need to add "position:relative" to the css of the container. You will also need to specify
34927 the container size if it is not the body element.</b>
34930 * Create a new Border
34931 * @param {Object} config Configuration options
34933 Roo.bootstrap.layout.Border = function(config){
34934 config = config || {};
34935 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34939 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34940 if(config[region]){
34941 config[region].region = region;
34942 this.addRegion(config[region]);
34948 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34950 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34952 * Creates and adds a new region if it doesn't already exist.
34953 * @param {String} target The target region key (north, south, east, west or center).
34954 * @param {Object} config The regions config object
34955 * @return {BorderLayoutRegion} The new region
34957 addRegion : function(config)
34959 if(!this.regions[config.region]){
34960 var r = this.factory(config);
34961 this.bindRegion(r);
34963 return this.regions[config.region];
34967 bindRegion : function(r){
34968 this.regions[r.config.region] = r;
34970 r.on("visibilitychange", this.layout, this);
34971 r.on("paneladded", this.layout, this);
34972 r.on("panelremoved", this.layout, this);
34973 r.on("invalidated", this.layout, this);
34974 r.on("resized", this.onRegionResized, this);
34975 r.on("collapsed", this.onRegionCollapsed, this);
34976 r.on("expanded", this.onRegionExpanded, this);
34980 * Performs a layout update.
34982 layout : function()
34984 if(this.updating) {
34988 // render all the rebions if they have not been done alreayd?
34989 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34990 if(this.regions[region] && !this.regions[region].bodyEl){
34991 this.regions[region].onRender(this.el)
34995 var size = this.getViewSize();
34996 var w = size.width;
34997 var h = size.height;
35002 //var x = 0, y = 0;
35004 var rs = this.regions;
35005 var north = rs["north"];
35006 var south = rs["south"];
35007 var west = rs["west"];
35008 var east = rs["east"];
35009 var center = rs["center"];
35010 //if(this.hideOnLayout){ // not supported anymore
35011 //c.el.setStyle("display", "none");
35013 if(north && north.isVisible()){
35014 var b = north.getBox();
35015 var m = north.getMargins();
35016 b.width = w - (m.left+m.right);
35019 centerY = b.height + b.y + m.bottom;
35020 centerH -= centerY;
35021 north.updateBox(this.safeBox(b));
35023 if(south && south.isVisible()){
35024 var b = south.getBox();
35025 var m = south.getMargins();
35026 b.width = w - (m.left+m.right);
35028 var totalHeight = (b.height + m.top + m.bottom);
35029 b.y = h - totalHeight + m.top;
35030 centerH -= totalHeight;
35031 south.updateBox(this.safeBox(b));
35033 if(west && west.isVisible()){
35034 var b = west.getBox();
35035 var m = west.getMargins();
35036 b.height = centerH - (m.top+m.bottom);
35038 b.y = centerY + m.top;
35039 var totalWidth = (b.width + m.left + m.right);
35040 centerX += totalWidth;
35041 centerW -= totalWidth;
35042 west.updateBox(this.safeBox(b));
35044 if(east && east.isVisible()){
35045 var b = east.getBox();
35046 var m = east.getMargins();
35047 b.height = centerH - (m.top+m.bottom);
35048 var totalWidth = (b.width + m.left + m.right);
35049 b.x = w - totalWidth + m.left;
35050 b.y = centerY + m.top;
35051 centerW -= totalWidth;
35052 east.updateBox(this.safeBox(b));
35055 var m = center.getMargins();
35057 x: centerX + m.left,
35058 y: centerY + m.top,
35059 width: centerW - (m.left+m.right),
35060 height: centerH - (m.top+m.bottom)
35062 //if(this.hideOnLayout){
35063 //center.el.setStyle("display", "block");
35065 center.updateBox(this.safeBox(centerBox));
35068 this.fireEvent("layout", this);
35072 safeBox : function(box){
35073 box.width = Math.max(0, box.width);
35074 box.height = Math.max(0, box.height);
35079 * Adds a ContentPanel (or subclass) to this layout.
35080 * @param {String} target The target region key (north, south, east, west or center).
35081 * @param {Roo.ContentPanel} panel The panel to add
35082 * @return {Roo.ContentPanel} The added panel
35084 add : function(target, panel){
35086 target = target.toLowerCase();
35087 return this.regions[target].add(panel);
35091 * Remove a ContentPanel (or subclass) to this layout.
35092 * @param {String} target The target region key (north, south, east, west or center).
35093 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35094 * @return {Roo.ContentPanel} The removed panel
35096 remove : function(target, panel){
35097 target = target.toLowerCase();
35098 return this.regions[target].remove(panel);
35102 * Searches all regions for a panel with the specified id
35103 * @param {String} panelId
35104 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35106 findPanel : function(panelId){
35107 var rs = this.regions;
35108 for(var target in rs){
35109 if(typeof rs[target] != "function"){
35110 var p = rs[target].getPanel(panelId);
35120 * Searches all regions for a panel with the specified id and activates (shows) it.
35121 * @param {String/ContentPanel} panelId The panels id or the panel itself
35122 * @return {Roo.ContentPanel} The shown panel or null
35124 showPanel : function(panelId) {
35125 var rs = this.regions;
35126 for(var target in rs){
35127 var r = rs[target];
35128 if(typeof r != "function"){
35129 if(r.hasPanel(panelId)){
35130 return r.showPanel(panelId);
35138 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35139 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35142 restoreState : function(provider){
35144 provider = Roo.state.Manager;
35146 var sm = new Roo.LayoutStateManager();
35147 sm.init(this, provider);
35153 * Adds a xtype elements to the layout.
35157 xtype : 'ContentPanel',
35164 xtype : 'NestedLayoutPanel',
35170 items : [ ... list of content panels or nested layout panels.. ]
35174 * @param {Object} cfg Xtype definition of item to add.
35176 addxtype : function(cfg)
35178 // basically accepts a pannel...
35179 // can accept a layout region..!?!?
35180 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35183 // theory? children can only be panels??
35185 //if (!cfg.xtype.match(/Panel$/)) {
35190 if (typeof(cfg.region) == 'undefined') {
35191 Roo.log("Failed to add Panel, region was not set");
35195 var region = cfg.region;
35201 xitems = cfg.items;
35208 case 'Content': // ContentPanel (el, cfg)
35209 case 'Scroll': // ContentPanel (el, cfg)
35211 cfg.autoCreate = true;
35212 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35214 // var el = this.el.createChild();
35215 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35218 this.add(region, ret);
35222 case 'TreePanel': // our new panel!
35223 cfg.el = this.el.createChild();
35224 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35225 this.add(region, ret);
35230 // create a new Layout (which is a Border Layout...
35232 var clayout = cfg.layout;
35233 clayout.el = this.el.createChild();
35234 clayout.items = clayout.items || [];
35238 // replace this exitems with the clayout ones..
35239 xitems = clayout.items;
35241 // force background off if it's in center...
35242 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35243 cfg.background = false;
35245 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35248 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35249 //console.log('adding nested layout panel ' + cfg.toSource());
35250 this.add(region, ret);
35251 nb = {}; /// find first...
35256 // needs grid and region
35258 //var el = this.getRegion(region).el.createChild();
35260 *var el = this.el.createChild();
35261 // create the grid first...
35262 cfg.grid.container = el;
35263 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35266 if (region == 'center' && this.active ) {
35267 cfg.background = false;
35270 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35272 this.add(region, ret);
35274 if (cfg.background) {
35275 // render grid on panel activation (if panel background)
35276 ret.on('activate', function(gp) {
35277 if (!gp.grid.rendered) {
35278 // gp.grid.render(el);
35282 // cfg.grid.render(el);
35288 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35289 // it was the old xcomponent building that caused this before.
35290 // espeically if border is the top element in the tree.
35300 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35302 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35303 this.add(region, ret);
35307 throw "Can not add '" + cfg.xtype + "' to Border";
35313 this.beginUpdate();
35317 Roo.each(xitems, function(i) {
35318 region = nb && i.region ? i.region : false;
35320 var add = ret.addxtype(i);
35323 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35324 if (!i.background) {
35325 abn[region] = nb[region] ;
35332 // make the last non-background panel active..
35333 //if (nb) { Roo.log(abn); }
35336 for(var r in abn) {
35337 region = this.getRegion(r);
35339 // tried using nb[r], but it does not work..
35341 region.showPanel(abn[r]);
35352 factory : function(cfg)
35355 var validRegions = Roo.bootstrap.layout.Border.regions;
35357 var target = cfg.region;
35360 var r = Roo.bootstrap.layout;
35364 return new r.North(cfg);
35366 return new r.South(cfg);
35368 return new r.East(cfg);
35370 return new r.West(cfg);
35372 return new r.Center(cfg);
35374 throw 'Layout region "'+target+'" not supported.';
35381 * Ext JS Library 1.1.1
35382 * Copyright(c) 2006-2007, Ext JS, LLC.
35384 * Originally Released Under LGPL - original licence link has changed is not relivant.
35387 * <script type="text/javascript">
35391 * @class Roo.bootstrap.layout.Basic
35392 * @extends Roo.util.Observable
35393 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35394 * and does not have a titlebar, tabs or any other features. All it does is size and position
35395 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35396 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35397 * @cfg {string} region the region that it inhabits..
35398 * @cfg {bool} skipConfig skip config?
35402 Roo.bootstrap.layout.Basic = function(config){
35404 this.mgr = config.mgr;
35406 this.position = config.region;
35408 var skipConfig = config.skipConfig;
35412 * @scope Roo.BasicLayoutRegion
35416 * @event beforeremove
35417 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35418 * @param {Roo.LayoutRegion} this
35419 * @param {Roo.ContentPanel} panel The panel
35420 * @param {Object} e The cancel event object
35422 "beforeremove" : true,
35424 * @event invalidated
35425 * Fires when the layout for this region is changed.
35426 * @param {Roo.LayoutRegion} this
35428 "invalidated" : true,
35430 * @event visibilitychange
35431 * Fires when this region is shown or hidden
35432 * @param {Roo.LayoutRegion} this
35433 * @param {Boolean} visibility true or false
35435 "visibilitychange" : true,
35437 * @event paneladded
35438 * Fires when a panel is added.
35439 * @param {Roo.LayoutRegion} this
35440 * @param {Roo.ContentPanel} panel The panel
35442 "paneladded" : true,
35444 * @event panelremoved
35445 * Fires when a panel is removed.
35446 * @param {Roo.LayoutRegion} this
35447 * @param {Roo.ContentPanel} panel The panel
35449 "panelremoved" : true,
35451 * @event beforecollapse
35452 * Fires when this region before collapse.
35453 * @param {Roo.LayoutRegion} this
35455 "beforecollapse" : true,
35458 * Fires when this region is collapsed.
35459 * @param {Roo.LayoutRegion} this
35461 "collapsed" : true,
35464 * Fires when this region is expanded.
35465 * @param {Roo.LayoutRegion} this
35470 * Fires when this region is slid into view.
35471 * @param {Roo.LayoutRegion} this
35473 "slideshow" : true,
35476 * Fires when this region slides out of view.
35477 * @param {Roo.LayoutRegion} this
35479 "slidehide" : true,
35481 * @event panelactivated
35482 * Fires when a panel is activated.
35483 * @param {Roo.LayoutRegion} this
35484 * @param {Roo.ContentPanel} panel The activated panel
35486 "panelactivated" : true,
35489 * Fires when the user resizes this region.
35490 * @param {Roo.LayoutRegion} this
35491 * @param {Number} newSize The new size (width for east/west, height for north/south)
35495 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35496 this.panels = new Roo.util.MixedCollection();
35497 this.panels.getKey = this.getPanelId.createDelegate(this);
35499 this.activePanel = null;
35500 // ensure listeners are added...
35502 if (config.listeners || config.events) {
35503 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35504 listeners : config.listeners || {},
35505 events : config.events || {}
35509 if(skipConfig !== true){
35510 this.applyConfig(config);
35514 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35516 getPanelId : function(p){
35520 applyConfig : function(config){
35521 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35522 this.config = config;
35527 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35528 * the width, for horizontal (north, south) the height.
35529 * @param {Number} newSize The new width or height
35531 resizeTo : function(newSize){
35532 var el = this.el ? this.el :
35533 (this.activePanel ? this.activePanel.getEl() : null);
35535 switch(this.position){
35538 el.setWidth(newSize);
35539 this.fireEvent("resized", this, newSize);
35543 el.setHeight(newSize);
35544 this.fireEvent("resized", this, newSize);
35550 getBox : function(){
35551 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35554 getMargins : function(){
35555 return this.margins;
35558 updateBox : function(box){
35560 var el = this.activePanel.getEl();
35561 el.dom.style.left = box.x + "px";
35562 el.dom.style.top = box.y + "px";
35563 this.activePanel.setSize(box.width, box.height);
35567 * Returns the container element for this region.
35568 * @return {Roo.Element}
35570 getEl : function(){
35571 return this.activePanel;
35575 * Returns true if this region is currently visible.
35576 * @return {Boolean}
35578 isVisible : function(){
35579 return this.activePanel ? true : false;
35582 setActivePanel : function(panel){
35583 panel = this.getPanel(panel);
35584 if(this.activePanel && this.activePanel != panel){
35585 this.activePanel.setActiveState(false);
35586 this.activePanel.getEl().setLeftTop(-10000,-10000);
35588 this.activePanel = panel;
35589 panel.setActiveState(true);
35591 panel.setSize(this.box.width, this.box.height);
35593 this.fireEvent("panelactivated", this, panel);
35594 this.fireEvent("invalidated");
35598 * Show the specified panel.
35599 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35600 * @return {Roo.ContentPanel} The shown panel or null
35602 showPanel : function(panel){
35603 panel = this.getPanel(panel);
35605 this.setActivePanel(panel);
35611 * Get the active panel for this region.
35612 * @return {Roo.ContentPanel} The active panel or null
35614 getActivePanel : function(){
35615 return this.activePanel;
35619 * Add the passed ContentPanel(s)
35620 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35621 * @return {Roo.ContentPanel} The panel added (if only one was added)
35623 add : function(panel){
35624 if(arguments.length > 1){
35625 for(var i = 0, len = arguments.length; i < len; i++) {
35626 this.add(arguments[i]);
35630 if(this.hasPanel(panel)){
35631 this.showPanel(panel);
35634 var el = panel.getEl();
35635 if(el.dom.parentNode != this.mgr.el.dom){
35636 this.mgr.el.dom.appendChild(el.dom);
35638 if(panel.setRegion){
35639 panel.setRegion(this);
35641 this.panels.add(panel);
35642 el.setStyle("position", "absolute");
35643 if(!panel.background){
35644 this.setActivePanel(panel);
35645 if(this.config.initialSize && this.panels.getCount()==1){
35646 this.resizeTo(this.config.initialSize);
35649 this.fireEvent("paneladded", this, panel);
35654 * Returns true if the panel is in this region.
35655 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35656 * @return {Boolean}
35658 hasPanel : function(panel){
35659 if(typeof panel == "object"){ // must be panel obj
35660 panel = panel.getId();
35662 return this.getPanel(panel) ? true : false;
35666 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35667 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35668 * @param {Boolean} preservePanel Overrides the config preservePanel option
35669 * @return {Roo.ContentPanel} The panel that was removed
35671 remove : function(panel, preservePanel){
35672 panel = this.getPanel(panel);
35677 this.fireEvent("beforeremove", this, panel, e);
35678 if(e.cancel === true){
35681 var panelId = panel.getId();
35682 this.panels.removeKey(panelId);
35687 * Returns the panel specified or null if it's not in this region.
35688 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35689 * @return {Roo.ContentPanel}
35691 getPanel : function(id){
35692 if(typeof id == "object"){ // must be panel obj
35695 return this.panels.get(id);
35699 * Returns this regions position (north/south/east/west/center).
35702 getPosition: function(){
35703 return this.position;
35707 * Ext JS Library 1.1.1
35708 * Copyright(c) 2006-2007, Ext JS, LLC.
35710 * Originally Released Under LGPL - original licence link has changed is not relivant.
35713 * <script type="text/javascript">
35717 * @class Roo.bootstrap.layout.Region
35718 * @extends Roo.bootstrap.layout.Basic
35719 * This class represents a region in a layout manager.
35721 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35722 * @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})
35723 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35724 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35725 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35726 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35727 * @cfg {String} title The title for the region (overrides panel titles)
35728 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35729 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35730 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35731 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35732 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35733 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35734 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35735 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35736 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35737 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35739 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35740 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35741 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35742 * @cfg {Number} width For East/West panels
35743 * @cfg {Number} height For North/South panels
35744 * @cfg {Boolean} split To show the splitter
35745 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35747 * @cfg {string} cls Extra CSS classes to add to region
35749 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35750 * @cfg {string} region the region that it inhabits..
35753 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35754 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35756 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35757 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35758 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35760 Roo.bootstrap.layout.Region = function(config)
35762 this.applyConfig(config);
35764 var mgr = config.mgr;
35765 var pos = config.region;
35766 config.skipConfig = true;
35767 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35770 this.onRender(mgr.el);
35773 this.visible = true;
35774 this.collapsed = false;
35775 this.unrendered_panels = [];
35778 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35780 position: '', // set by wrapper (eg. north/south etc..)
35781 unrendered_panels : null, // unrendered panels.
35782 createBody : function(){
35783 /** This region's body element
35784 * @type Roo.Element */
35785 this.bodyEl = this.el.createChild({
35787 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35791 onRender: function(ctr, pos)
35793 var dh = Roo.DomHelper;
35794 /** This region's container element
35795 * @type Roo.Element */
35796 this.el = dh.append(ctr.dom, {
35798 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35800 /** This region's title element
35801 * @type Roo.Element */
35803 this.titleEl = dh.append(this.el.dom,
35806 unselectable: "on",
35807 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35809 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35810 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35813 this.titleEl.enableDisplayMode();
35814 /** This region's title text element
35815 * @type HTMLElement */
35816 this.titleTextEl = this.titleEl.dom.firstChild;
35817 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35819 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35820 this.closeBtn.enableDisplayMode();
35821 this.closeBtn.on("click", this.closeClicked, this);
35822 this.closeBtn.hide();
35824 this.createBody(this.config);
35825 if(this.config.hideWhenEmpty){
35827 this.on("paneladded", this.validateVisibility, this);
35828 this.on("panelremoved", this.validateVisibility, this);
35830 if(this.autoScroll){
35831 this.bodyEl.setStyle("overflow", "auto");
35833 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35835 //if(c.titlebar !== false){
35836 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35837 this.titleEl.hide();
35839 this.titleEl.show();
35840 if(this.config.title){
35841 this.titleTextEl.innerHTML = this.config.title;
35845 if(this.config.collapsed){
35846 this.collapse(true);
35848 if(this.config.hidden){
35852 if (this.unrendered_panels && this.unrendered_panels.length) {
35853 for (var i =0;i< this.unrendered_panels.length; i++) {
35854 this.add(this.unrendered_panels[i]);
35856 this.unrendered_panels = null;
35862 applyConfig : function(c)
35865 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35866 var dh = Roo.DomHelper;
35867 if(c.titlebar !== false){
35868 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35869 this.collapseBtn.on("click", this.collapse, this);
35870 this.collapseBtn.enableDisplayMode();
35872 if(c.showPin === true || this.showPin){
35873 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35874 this.stickBtn.enableDisplayMode();
35875 this.stickBtn.on("click", this.expand, this);
35876 this.stickBtn.hide();
35881 /** This region's collapsed element
35882 * @type Roo.Element */
35885 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35886 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35889 if(c.floatable !== false){
35890 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35891 this.collapsedEl.on("click", this.collapseClick, this);
35894 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35895 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35896 id: "message", unselectable: "on", style:{"float":"left"}});
35897 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35899 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35900 this.expandBtn.on("click", this.expand, this);
35904 if(this.collapseBtn){
35905 this.collapseBtn.setVisible(c.collapsible == true);
35908 this.cmargins = c.cmargins || this.cmargins ||
35909 (this.position == "west" || this.position == "east" ?
35910 {top: 0, left: 2, right:2, bottom: 0} :
35911 {top: 2, left: 0, right:0, bottom: 2});
35913 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35916 this.bottomTabs = c.tabPosition != "top";
35918 this.autoScroll = c.autoScroll || false;
35923 this.duration = c.duration || .30;
35924 this.slideDuration = c.slideDuration || .45;
35929 * Returns true if this region is currently visible.
35930 * @return {Boolean}
35932 isVisible : function(){
35933 return this.visible;
35937 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35938 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35940 //setCollapsedTitle : function(title){
35941 // title = title || " ";
35942 // if(this.collapsedTitleTextEl){
35943 // this.collapsedTitleTextEl.innerHTML = title;
35947 getBox : function(){
35949 // if(!this.collapsed){
35950 b = this.el.getBox(false, true);
35952 // b = this.collapsedEl.getBox(false, true);
35957 getMargins : function(){
35958 return this.margins;
35959 //return this.collapsed ? this.cmargins : this.margins;
35962 highlight : function(){
35963 this.el.addClass("x-layout-panel-dragover");
35966 unhighlight : function(){
35967 this.el.removeClass("x-layout-panel-dragover");
35970 updateBox : function(box)
35972 if (!this.bodyEl) {
35973 return; // not rendered yet..
35977 if(!this.collapsed){
35978 this.el.dom.style.left = box.x + "px";
35979 this.el.dom.style.top = box.y + "px";
35980 this.updateBody(box.width, box.height);
35982 this.collapsedEl.dom.style.left = box.x + "px";
35983 this.collapsedEl.dom.style.top = box.y + "px";
35984 this.collapsedEl.setSize(box.width, box.height);
35987 this.tabs.autoSizeTabs();
35991 updateBody : function(w, h)
35994 this.el.setWidth(w);
35995 w -= this.el.getBorderWidth("rl");
35996 if(this.config.adjustments){
35997 w += this.config.adjustments[0];
36000 if(h !== null && h > 0){
36001 this.el.setHeight(h);
36002 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36003 h -= this.el.getBorderWidth("tb");
36004 if(this.config.adjustments){
36005 h += this.config.adjustments[1];
36007 this.bodyEl.setHeight(h);
36009 h = this.tabs.syncHeight(h);
36012 if(this.panelSize){
36013 w = w !== null ? w : this.panelSize.width;
36014 h = h !== null ? h : this.panelSize.height;
36016 if(this.activePanel){
36017 var el = this.activePanel.getEl();
36018 w = w !== null ? w : el.getWidth();
36019 h = h !== null ? h : el.getHeight();
36020 this.panelSize = {width: w, height: h};
36021 this.activePanel.setSize(w, h);
36023 if(Roo.isIE && this.tabs){
36024 this.tabs.el.repaint();
36029 * Returns the container element for this region.
36030 * @return {Roo.Element}
36032 getEl : function(){
36037 * Hides this region.
36040 //if(!this.collapsed){
36041 this.el.dom.style.left = "-2000px";
36044 // this.collapsedEl.dom.style.left = "-2000px";
36045 // this.collapsedEl.hide();
36047 this.visible = false;
36048 this.fireEvent("visibilitychange", this, false);
36052 * Shows this region if it was previously hidden.
36055 //if(!this.collapsed){
36058 // this.collapsedEl.show();
36060 this.visible = true;
36061 this.fireEvent("visibilitychange", this, true);
36064 closeClicked : function(){
36065 if(this.activePanel){
36066 this.remove(this.activePanel);
36070 collapseClick : function(e){
36072 e.stopPropagation();
36075 e.stopPropagation();
36081 * Collapses this region.
36082 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36085 collapse : function(skipAnim, skipCheck = false){
36086 if(this.collapsed) {
36090 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36092 this.collapsed = true;
36094 this.split.el.hide();
36096 if(this.config.animate && skipAnim !== true){
36097 this.fireEvent("invalidated", this);
36098 this.animateCollapse();
36100 this.el.setLocation(-20000,-20000);
36102 this.collapsedEl.show();
36103 this.fireEvent("collapsed", this);
36104 this.fireEvent("invalidated", this);
36110 animateCollapse : function(){
36115 * Expands this region if it was previously collapsed.
36116 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36117 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36120 expand : function(e, skipAnim){
36122 e.stopPropagation();
36124 if(!this.collapsed || this.el.hasActiveFx()) {
36128 this.afterSlideIn();
36131 this.collapsed = false;
36132 if(this.config.animate && skipAnim !== true){
36133 this.animateExpand();
36137 this.split.el.show();
36139 this.collapsedEl.setLocation(-2000,-2000);
36140 this.collapsedEl.hide();
36141 this.fireEvent("invalidated", this);
36142 this.fireEvent("expanded", this);
36146 animateExpand : function(){
36150 initTabs : function()
36152 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36154 var ts = new Roo.bootstrap.panel.Tabs({
36155 el: this.bodyEl.dom,
36156 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36157 disableTooltips: this.config.disableTabTips,
36158 toolbar : this.config.toolbar
36161 if(this.config.hideTabs){
36162 ts.stripWrap.setDisplayed(false);
36165 ts.resizeTabs = this.config.resizeTabs === true;
36166 ts.minTabWidth = this.config.minTabWidth || 40;
36167 ts.maxTabWidth = this.config.maxTabWidth || 250;
36168 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36169 ts.monitorResize = false;
36170 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36171 ts.bodyEl.addClass('roo-layout-tabs-body');
36172 this.panels.each(this.initPanelAsTab, this);
36175 initPanelAsTab : function(panel){
36176 var ti = this.tabs.addTab(
36180 this.config.closeOnTab && panel.isClosable(),
36183 if(panel.tabTip !== undefined){
36184 ti.setTooltip(panel.tabTip);
36186 ti.on("activate", function(){
36187 this.setActivePanel(panel);
36190 if(this.config.closeOnTab){
36191 ti.on("beforeclose", function(t, e){
36193 this.remove(panel);
36197 panel.tabItem = ti;
36202 updatePanelTitle : function(panel, title)
36204 if(this.activePanel == panel){
36205 this.updateTitle(title);
36208 var ti = this.tabs.getTab(panel.getEl().id);
36210 if(panel.tabTip !== undefined){
36211 ti.setTooltip(panel.tabTip);
36216 updateTitle : function(title){
36217 if(this.titleTextEl && !this.config.title){
36218 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36222 setActivePanel : function(panel)
36224 panel = this.getPanel(panel);
36225 if(this.activePanel && this.activePanel != panel){
36226 if(this.activePanel.setActiveState(false) === false){
36230 this.activePanel = panel;
36231 panel.setActiveState(true);
36232 if(this.panelSize){
36233 panel.setSize(this.panelSize.width, this.panelSize.height);
36236 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36238 this.updateTitle(panel.getTitle());
36240 this.fireEvent("invalidated", this);
36242 this.fireEvent("panelactivated", this, panel);
36246 * Shows the specified panel.
36247 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36248 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36250 showPanel : function(panel)
36252 panel = this.getPanel(panel);
36255 var tab = this.tabs.getTab(panel.getEl().id);
36256 if(tab.isHidden()){
36257 this.tabs.unhideTab(tab.id);
36261 this.setActivePanel(panel);
36268 * Get the active panel for this region.
36269 * @return {Roo.ContentPanel} The active panel or null
36271 getActivePanel : function(){
36272 return this.activePanel;
36275 validateVisibility : function(){
36276 if(this.panels.getCount() < 1){
36277 this.updateTitle(" ");
36278 this.closeBtn.hide();
36281 if(!this.isVisible()){
36288 * Adds the passed ContentPanel(s) to this region.
36289 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36290 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36292 add : function(panel)
36294 if(arguments.length > 1){
36295 for(var i = 0, len = arguments.length; i < len; i++) {
36296 this.add(arguments[i]);
36301 // if we have not been rendered yet, then we can not really do much of this..
36302 if (!this.bodyEl) {
36303 this.unrendered_panels.push(panel);
36310 if(this.hasPanel(panel)){
36311 this.showPanel(panel);
36314 panel.setRegion(this);
36315 this.panels.add(panel);
36316 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36317 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36318 // and hide them... ???
36319 this.bodyEl.dom.appendChild(panel.getEl().dom);
36320 if(panel.background !== true){
36321 this.setActivePanel(panel);
36323 this.fireEvent("paneladded", this, panel);
36330 this.initPanelAsTab(panel);
36334 if(panel.background !== true){
36335 this.tabs.activate(panel.getEl().id);
36337 this.fireEvent("paneladded", this, panel);
36342 * Hides the tab for the specified panel.
36343 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36345 hidePanel : function(panel){
36346 if(this.tabs && (panel = this.getPanel(panel))){
36347 this.tabs.hideTab(panel.getEl().id);
36352 * Unhides the tab for a previously hidden panel.
36353 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36355 unhidePanel : function(panel){
36356 if(this.tabs && (panel = this.getPanel(panel))){
36357 this.tabs.unhideTab(panel.getEl().id);
36361 clearPanels : function(){
36362 while(this.panels.getCount() > 0){
36363 this.remove(this.panels.first());
36368 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36369 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36370 * @param {Boolean} preservePanel Overrides the config preservePanel option
36371 * @return {Roo.ContentPanel} The panel that was removed
36373 remove : function(panel, preservePanel)
36375 panel = this.getPanel(panel);
36380 this.fireEvent("beforeremove", this, panel, e);
36381 if(e.cancel === true){
36384 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36385 var panelId = panel.getId();
36386 this.panels.removeKey(panelId);
36388 document.body.appendChild(panel.getEl().dom);
36391 this.tabs.removeTab(panel.getEl().id);
36392 }else if (!preservePanel){
36393 this.bodyEl.dom.removeChild(panel.getEl().dom);
36395 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36396 var p = this.panels.first();
36397 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36398 tempEl.appendChild(p.getEl().dom);
36399 this.bodyEl.update("");
36400 this.bodyEl.dom.appendChild(p.getEl().dom);
36402 this.updateTitle(p.getTitle());
36404 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36405 this.setActivePanel(p);
36407 panel.setRegion(null);
36408 if(this.activePanel == panel){
36409 this.activePanel = null;
36411 if(this.config.autoDestroy !== false && preservePanel !== true){
36412 try{panel.destroy();}catch(e){}
36414 this.fireEvent("panelremoved", this, panel);
36419 * Returns the TabPanel component used by this region
36420 * @return {Roo.TabPanel}
36422 getTabs : function(){
36426 createTool : function(parentEl, className){
36427 var btn = Roo.DomHelper.append(parentEl, {
36429 cls: "x-layout-tools-button",
36432 cls: "roo-layout-tools-button-inner " + className,
36436 btn.addClassOnOver("roo-layout-tools-button-over");
36441 * Ext JS Library 1.1.1
36442 * Copyright(c) 2006-2007, Ext JS, LLC.
36444 * Originally Released Under LGPL - original licence link has changed is not relivant.
36447 * <script type="text/javascript">
36453 * @class Roo.SplitLayoutRegion
36454 * @extends Roo.LayoutRegion
36455 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36457 Roo.bootstrap.layout.Split = function(config){
36458 this.cursor = config.cursor;
36459 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36462 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36464 splitTip : "Drag to resize.",
36465 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36466 useSplitTips : false,
36468 applyConfig : function(config){
36469 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36472 onRender : function(ctr,pos) {
36474 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36475 if(!this.config.split){
36480 var splitEl = Roo.DomHelper.append(ctr.dom, {
36482 id: this.el.id + "-split",
36483 cls: "roo-layout-split roo-layout-split-"+this.position,
36486 /** The SplitBar for this region
36487 * @type Roo.SplitBar */
36488 // does not exist yet...
36489 Roo.log([this.position, this.orientation]);
36491 this.split = new Roo.bootstrap.SplitBar({
36492 dragElement : splitEl,
36493 resizingElement: this.el,
36494 orientation : this.orientation
36497 this.split.on("moved", this.onSplitMove, this);
36498 this.split.useShim = this.config.useShim === true;
36499 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36500 if(this.useSplitTips){
36501 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36503 //if(config.collapsible){
36504 // this.split.el.on("dblclick", this.collapse, this);
36507 if(typeof this.config.minSize != "undefined"){
36508 this.split.minSize = this.config.minSize;
36510 if(typeof this.config.maxSize != "undefined"){
36511 this.split.maxSize = this.config.maxSize;
36513 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36514 this.hideSplitter();
36519 getHMaxSize : function(){
36520 var cmax = this.config.maxSize || 10000;
36521 var center = this.mgr.getRegion("center");
36522 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36525 getVMaxSize : function(){
36526 var cmax = this.config.maxSize || 10000;
36527 var center = this.mgr.getRegion("center");
36528 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36531 onSplitMove : function(split, newSize){
36532 this.fireEvent("resized", this, newSize);
36536 * Returns the {@link Roo.SplitBar} for this region.
36537 * @return {Roo.SplitBar}
36539 getSplitBar : function(){
36544 this.hideSplitter();
36545 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36548 hideSplitter : function(){
36550 this.split.el.setLocation(-2000,-2000);
36551 this.split.el.hide();
36557 this.split.el.show();
36559 Roo.bootstrap.layout.Split.superclass.show.call(this);
36562 beforeSlide: function(){
36563 if(Roo.isGecko){// firefox overflow auto bug workaround
36564 this.bodyEl.clip();
36566 this.tabs.bodyEl.clip();
36568 if(this.activePanel){
36569 this.activePanel.getEl().clip();
36571 if(this.activePanel.beforeSlide){
36572 this.activePanel.beforeSlide();
36578 afterSlide : function(){
36579 if(Roo.isGecko){// firefox overflow auto bug workaround
36580 this.bodyEl.unclip();
36582 this.tabs.bodyEl.unclip();
36584 if(this.activePanel){
36585 this.activePanel.getEl().unclip();
36586 if(this.activePanel.afterSlide){
36587 this.activePanel.afterSlide();
36593 initAutoHide : function(){
36594 if(this.autoHide !== false){
36595 if(!this.autoHideHd){
36596 var st = new Roo.util.DelayedTask(this.slideIn, this);
36597 this.autoHideHd = {
36598 "mouseout": function(e){
36599 if(!e.within(this.el, true)){
36603 "mouseover" : function(e){
36609 this.el.on(this.autoHideHd);
36613 clearAutoHide : function(){
36614 if(this.autoHide !== false){
36615 this.el.un("mouseout", this.autoHideHd.mouseout);
36616 this.el.un("mouseover", this.autoHideHd.mouseover);
36620 clearMonitor : function(){
36621 Roo.get(document).un("click", this.slideInIf, this);
36624 // these names are backwards but not changed for compat
36625 slideOut : function(){
36626 if(this.isSlid || this.el.hasActiveFx()){
36629 this.isSlid = true;
36630 if(this.collapseBtn){
36631 this.collapseBtn.hide();
36633 this.closeBtnState = this.closeBtn.getStyle('display');
36634 this.closeBtn.hide();
36636 this.stickBtn.show();
36639 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36640 this.beforeSlide();
36641 this.el.setStyle("z-index", 10001);
36642 this.el.slideIn(this.getSlideAnchor(), {
36643 callback: function(){
36645 this.initAutoHide();
36646 Roo.get(document).on("click", this.slideInIf, this);
36647 this.fireEvent("slideshow", this);
36654 afterSlideIn : function(){
36655 this.clearAutoHide();
36656 this.isSlid = false;
36657 this.clearMonitor();
36658 this.el.setStyle("z-index", "");
36659 if(this.collapseBtn){
36660 this.collapseBtn.show();
36662 this.closeBtn.setStyle('display', this.closeBtnState);
36664 this.stickBtn.hide();
36666 this.fireEvent("slidehide", this);
36669 slideIn : function(cb){
36670 if(!this.isSlid || this.el.hasActiveFx()){
36674 this.isSlid = false;
36675 this.beforeSlide();
36676 this.el.slideOut(this.getSlideAnchor(), {
36677 callback: function(){
36678 this.el.setLeftTop(-10000, -10000);
36680 this.afterSlideIn();
36688 slideInIf : function(e){
36689 if(!e.within(this.el)){
36694 animateCollapse : function(){
36695 this.beforeSlide();
36696 this.el.setStyle("z-index", 20000);
36697 var anchor = this.getSlideAnchor();
36698 this.el.slideOut(anchor, {
36699 callback : function(){
36700 this.el.setStyle("z-index", "");
36701 this.collapsedEl.slideIn(anchor, {duration:.3});
36703 this.el.setLocation(-10000,-10000);
36705 this.fireEvent("collapsed", this);
36712 animateExpand : function(){
36713 this.beforeSlide();
36714 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36715 this.el.setStyle("z-index", 20000);
36716 this.collapsedEl.hide({
36719 this.el.slideIn(this.getSlideAnchor(), {
36720 callback : function(){
36721 this.el.setStyle("z-index", "");
36724 this.split.el.show();
36726 this.fireEvent("invalidated", this);
36727 this.fireEvent("expanded", this);
36755 getAnchor : function(){
36756 return this.anchors[this.position];
36759 getCollapseAnchor : function(){
36760 return this.canchors[this.position];
36763 getSlideAnchor : function(){
36764 return this.sanchors[this.position];
36767 getAlignAdj : function(){
36768 var cm = this.cmargins;
36769 switch(this.position){
36785 getExpandAdj : function(){
36786 var c = this.collapsedEl, cm = this.cmargins;
36787 switch(this.position){
36789 return [-(cm.right+c.getWidth()+cm.left), 0];
36792 return [cm.right+c.getWidth()+cm.left, 0];
36795 return [0, -(cm.top+cm.bottom+c.getHeight())];
36798 return [0, cm.top+cm.bottom+c.getHeight()];
36804 * Ext JS Library 1.1.1
36805 * Copyright(c) 2006-2007, Ext JS, LLC.
36807 * Originally Released Under LGPL - original licence link has changed is not relivant.
36810 * <script type="text/javascript">
36813 * These classes are private internal classes
36815 Roo.bootstrap.layout.Center = function(config){
36816 config.region = "center";
36817 Roo.bootstrap.layout.Region.call(this, config);
36818 this.visible = true;
36819 this.minWidth = config.minWidth || 20;
36820 this.minHeight = config.minHeight || 20;
36823 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36825 // center panel can't be hidden
36829 // center panel can't be hidden
36832 getMinWidth: function(){
36833 return this.minWidth;
36836 getMinHeight: function(){
36837 return this.minHeight;
36850 Roo.bootstrap.layout.North = function(config)
36852 config.region = 'north';
36853 config.cursor = 'n-resize';
36855 Roo.bootstrap.layout.Split.call(this, config);
36859 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36860 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36861 this.split.el.addClass("roo-layout-split-v");
36863 var size = config.initialSize || config.height;
36864 if(typeof size != "undefined"){
36865 this.el.setHeight(size);
36868 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36870 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36874 getBox : function(){
36875 if(this.collapsed){
36876 return this.collapsedEl.getBox();
36878 var box = this.el.getBox();
36880 box.height += this.split.el.getHeight();
36885 updateBox : function(box){
36886 if(this.split && !this.collapsed){
36887 box.height -= this.split.el.getHeight();
36888 this.split.el.setLeft(box.x);
36889 this.split.el.setTop(box.y+box.height);
36890 this.split.el.setWidth(box.width);
36892 if(this.collapsed){
36893 this.updateBody(box.width, null);
36895 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36903 Roo.bootstrap.layout.South = function(config){
36904 config.region = 'south';
36905 config.cursor = 's-resize';
36906 Roo.bootstrap.layout.Split.call(this, config);
36908 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36909 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36910 this.split.el.addClass("roo-layout-split-v");
36912 var size = config.initialSize || config.height;
36913 if(typeof size != "undefined"){
36914 this.el.setHeight(size);
36918 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36919 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36920 getBox : function(){
36921 if(this.collapsed){
36922 return this.collapsedEl.getBox();
36924 var box = this.el.getBox();
36926 var sh = this.split.el.getHeight();
36933 updateBox : function(box){
36934 if(this.split && !this.collapsed){
36935 var sh = this.split.el.getHeight();
36938 this.split.el.setLeft(box.x);
36939 this.split.el.setTop(box.y-sh);
36940 this.split.el.setWidth(box.width);
36942 if(this.collapsed){
36943 this.updateBody(box.width, null);
36945 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36949 Roo.bootstrap.layout.East = function(config){
36950 config.region = "east";
36951 config.cursor = "e-resize";
36952 Roo.bootstrap.layout.Split.call(this, config);
36954 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36955 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36956 this.split.el.addClass("roo-layout-split-h");
36958 var size = config.initialSize || config.width;
36959 if(typeof size != "undefined"){
36960 this.el.setWidth(size);
36963 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36964 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36965 getBox : function(){
36966 if(this.collapsed){
36967 return this.collapsedEl.getBox();
36969 var box = this.el.getBox();
36971 var sw = this.split.el.getWidth();
36978 updateBox : function(box){
36979 if(this.split && !this.collapsed){
36980 var sw = this.split.el.getWidth();
36982 this.split.el.setLeft(box.x);
36983 this.split.el.setTop(box.y);
36984 this.split.el.setHeight(box.height);
36987 if(this.collapsed){
36988 this.updateBody(null, box.height);
36990 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36994 Roo.bootstrap.layout.West = function(config){
36995 config.region = "west";
36996 config.cursor = "w-resize";
36998 Roo.bootstrap.layout.Split.call(this, config);
37000 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37001 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37002 this.split.el.addClass("roo-layout-split-h");
37006 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37007 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37009 onRender: function(ctr, pos)
37011 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37012 var size = this.config.initialSize || this.config.width;
37013 if(typeof size != "undefined"){
37014 this.el.setWidth(size);
37018 getBox : function(){
37019 if(this.collapsed){
37020 return this.collapsedEl.getBox();
37022 var box = this.el.getBox();
37024 box.width += this.split.el.getWidth();
37029 updateBox : function(box){
37030 if(this.split && !this.collapsed){
37031 var sw = this.split.el.getWidth();
37033 this.split.el.setLeft(box.x+box.width);
37034 this.split.el.setTop(box.y);
37035 this.split.el.setHeight(box.height);
37037 if(this.collapsed){
37038 this.updateBody(null, box.height);
37040 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37043 Roo.namespace("Roo.bootstrap.panel");/*
37045 * Ext JS Library 1.1.1
37046 * Copyright(c) 2006-2007, Ext JS, LLC.
37048 * Originally Released Under LGPL - original licence link has changed is not relivant.
37051 * <script type="text/javascript">
37054 * @class Roo.ContentPanel
37055 * @extends Roo.util.Observable
37056 * A basic ContentPanel element.
37057 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37058 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37059 * @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
37060 * @cfg {Boolean} closable True if the panel can be closed/removed
37061 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37062 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37063 * @cfg {Toolbar} toolbar A toolbar for this panel
37064 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37065 * @cfg {String} title The title for this panel
37066 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37067 * @cfg {String} url Calls {@link #setUrl} with this value
37068 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37069 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37070 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37071 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37072 * @cfg {Boolean} badges render the badges
37075 * Create a new ContentPanel.
37076 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37077 * @param {String/Object} config A string to set only the title or a config object
37078 * @param {String} content (optional) Set the HTML content for this panel
37079 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37081 Roo.bootstrap.panel.Content = function( config){
37083 this.tpl = config.tpl || false;
37085 var el = config.el;
37086 var content = config.content;
37088 if(config.autoCreate){ // xtype is available if this is called from factory
37091 this.el = Roo.get(el);
37092 if(!this.el && config && config.autoCreate){
37093 if(typeof config.autoCreate == "object"){
37094 if(!config.autoCreate.id){
37095 config.autoCreate.id = config.id||el;
37097 this.el = Roo.DomHelper.append(document.body,
37098 config.autoCreate, true);
37100 var elcfg = { tag: "div",
37101 cls: "roo-layout-inactive-content",
37105 elcfg.html = config.html;
37109 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37112 this.closable = false;
37113 this.loaded = false;
37114 this.active = false;
37117 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37119 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37121 this.wrapEl = this.el; //this.el.wrap();
37123 if (config.toolbar.items) {
37124 ti = config.toolbar.items ;
37125 delete config.toolbar.items ;
37129 this.toolbar.render(this.wrapEl, 'before');
37130 for(var i =0;i < ti.length;i++) {
37131 // Roo.log(['add child', items[i]]);
37132 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37134 this.toolbar.items = nitems;
37135 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37136 delete config.toolbar;
37140 // xtype created footer. - not sure if will work as we normally have to render first..
37141 if (this.footer && !this.footer.el && this.footer.xtype) {
37142 if (!this.wrapEl) {
37143 this.wrapEl = this.el.wrap();
37146 this.footer.container = this.wrapEl.createChild();
37148 this.footer = Roo.factory(this.footer, Roo);
37153 if(typeof config == "string"){
37154 this.title = config;
37156 Roo.apply(this, config);
37160 this.resizeEl = Roo.get(this.resizeEl, true);
37162 this.resizeEl = this.el;
37164 // handle view.xtype
37172 * Fires when this panel is activated.
37173 * @param {Roo.ContentPanel} this
37177 * @event deactivate
37178 * Fires when this panel is activated.
37179 * @param {Roo.ContentPanel} this
37181 "deactivate" : true,
37185 * Fires when this panel is resized if fitToFrame is true.
37186 * @param {Roo.ContentPanel} this
37187 * @param {Number} width The width after any component adjustments
37188 * @param {Number} height The height after any component adjustments
37194 * Fires when this tab is created
37195 * @param {Roo.ContentPanel} this
37206 if(this.autoScroll){
37207 this.resizeEl.setStyle("overflow", "auto");
37209 // fix randome scrolling
37210 //this.el.on('scroll', function() {
37211 // Roo.log('fix random scolling');
37212 // this.scrollTo('top',0);
37215 content = content || this.content;
37217 this.setContent(content);
37219 if(config && config.url){
37220 this.setUrl(this.url, this.params, this.loadOnce);
37225 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37227 if (this.view && typeof(this.view.xtype) != 'undefined') {
37228 this.view.el = this.el.appendChild(document.createElement("div"));
37229 this.view = Roo.factory(this.view);
37230 this.view.render && this.view.render(false, '');
37234 this.fireEvent('render', this);
37237 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37241 setRegion : function(region){
37242 this.region = region;
37243 this.setActiveClass(region && !this.background);
37247 setActiveClass: function(state)
37250 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37251 this.el.setStyle('position','relative');
37253 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37254 this.el.setStyle('position', 'absolute');
37259 * Returns the toolbar for this Panel if one was configured.
37260 * @return {Roo.Toolbar}
37262 getToolbar : function(){
37263 return this.toolbar;
37266 setActiveState : function(active)
37268 this.active = active;
37269 this.setActiveClass(active);
37271 if(this.fireEvent("deactivate", this) === false){
37276 this.fireEvent("activate", this);
37280 * Updates this panel's element
37281 * @param {String} content The new content
37282 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37284 setContent : function(content, loadScripts){
37285 this.el.update(content, loadScripts);
37288 ignoreResize : function(w, h){
37289 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37292 this.lastSize = {width: w, height: h};
37297 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37298 * @return {Roo.UpdateManager} The UpdateManager
37300 getUpdateManager : function(){
37301 return this.el.getUpdateManager();
37304 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37305 * @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:
37308 url: "your-url.php",
37309 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37310 callback: yourFunction,
37311 scope: yourObject, //(optional scope)
37314 text: "Loading...",
37319 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37320 * 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.
37321 * @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}
37322 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37323 * @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.
37324 * @return {Roo.ContentPanel} this
37327 var um = this.el.getUpdateManager();
37328 um.update.apply(um, arguments);
37334 * 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.
37335 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37336 * @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)
37337 * @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)
37338 * @return {Roo.UpdateManager} The UpdateManager
37340 setUrl : function(url, params, loadOnce){
37341 if(this.refreshDelegate){
37342 this.removeListener("activate", this.refreshDelegate);
37344 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37345 this.on("activate", this.refreshDelegate);
37346 return this.el.getUpdateManager();
37349 _handleRefresh : function(url, params, loadOnce){
37350 if(!loadOnce || !this.loaded){
37351 var updater = this.el.getUpdateManager();
37352 updater.update(url, params, this._setLoaded.createDelegate(this));
37356 _setLoaded : function(){
37357 this.loaded = true;
37361 * Returns this panel's id
37364 getId : function(){
37369 * Returns this panel's element - used by regiosn to add.
37370 * @return {Roo.Element}
37372 getEl : function(){
37373 return this.wrapEl || this.el;
37378 adjustForComponents : function(width, height)
37380 //Roo.log('adjustForComponents ');
37381 if(this.resizeEl != this.el){
37382 width -= this.el.getFrameWidth('lr');
37383 height -= this.el.getFrameWidth('tb');
37386 var te = this.toolbar.getEl();
37387 te.setWidth(width);
37388 height -= te.getHeight();
37391 var te = this.footer.getEl();
37392 te.setWidth(width);
37393 height -= te.getHeight();
37397 if(this.adjustments){
37398 width += this.adjustments[0];
37399 height += this.adjustments[1];
37401 return {"width": width, "height": height};
37404 setSize : function(width, height){
37405 if(this.fitToFrame && !this.ignoreResize(width, height)){
37406 if(this.fitContainer && this.resizeEl != this.el){
37407 this.el.setSize(width, height);
37409 var size = this.adjustForComponents(width, height);
37410 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37411 this.fireEvent('resize', this, size.width, size.height);
37416 * Returns this panel's title
37419 getTitle : function(){
37421 if (typeof(this.title) != 'object') {
37426 for (var k in this.title) {
37427 if (!this.title.hasOwnProperty(k)) {
37431 if (k.indexOf('-') >= 0) {
37432 var s = k.split('-');
37433 for (var i = 0; i<s.length; i++) {
37434 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37437 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37444 * Set this panel's title
37445 * @param {String} title
37447 setTitle : function(title){
37448 this.title = title;
37450 this.region.updatePanelTitle(this, title);
37455 * Returns true is this panel was configured to be closable
37456 * @return {Boolean}
37458 isClosable : function(){
37459 return this.closable;
37462 beforeSlide : function(){
37464 this.resizeEl.clip();
37467 afterSlide : function(){
37469 this.resizeEl.unclip();
37473 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37474 * Will fail silently if the {@link #setUrl} method has not been called.
37475 * This does not activate the panel, just updates its content.
37477 refresh : function(){
37478 if(this.refreshDelegate){
37479 this.loaded = false;
37480 this.refreshDelegate();
37485 * Destroys this panel
37487 destroy : function(){
37488 this.el.removeAllListeners();
37489 var tempEl = document.createElement("span");
37490 tempEl.appendChild(this.el.dom);
37491 tempEl.innerHTML = "";
37497 * form - if the content panel contains a form - this is a reference to it.
37498 * @type {Roo.form.Form}
37502 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37503 * This contains a reference to it.
37509 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37519 * @param {Object} cfg Xtype definition of item to add.
37523 getChildContainer: function () {
37524 return this.getEl();
37529 var ret = new Roo.factory(cfg);
37534 if (cfg.xtype.match(/^Form$/)) {
37537 //if (this.footer) {
37538 // el = this.footer.container.insertSibling(false, 'before');
37540 el = this.el.createChild();
37543 this.form = new Roo.form.Form(cfg);
37546 if ( this.form.allItems.length) {
37547 this.form.render(el.dom);
37551 // should only have one of theses..
37552 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37553 // views.. should not be just added - used named prop 'view''
37555 cfg.el = this.el.appendChild(document.createElement("div"));
37558 var ret = new Roo.factory(cfg);
37560 ret.render && ret.render(false, ''); // render blank..
37570 * @class Roo.bootstrap.panel.Grid
37571 * @extends Roo.bootstrap.panel.Content
37573 * Create a new GridPanel.
37574 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37575 * @param {Object} config A the config object
37581 Roo.bootstrap.panel.Grid = function(config)
37585 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37586 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37588 config.el = this.wrapper;
37589 //this.el = this.wrapper;
37591 if (config.container) {
37592 // ctor'ed from a Border/panel.grid
37595 this.wrapper.setStyle("overflow", "hidden");
37596 this.wrapper.addClass('roo-grid-container');
37601 if(config.toolbar){
37602 var tool_el = this.wrapper.createChild();
37603 this.toolbar = Roo.factory(config.toolbar);
37605 if (config.toolbar.items) {
37606 ti = config.toolbar.items ;
37607 delete config.toolbar.items ;
37611 this.toolbar.render(tool_el);
37612 for(var i =0;i < ti.length;i++) {
37613 // Roo.log(['add child', items[i]]);
37614 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37616 this.toolbar.items = nitems;
37618 delete config.toolbar;
37621 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37622 config.grid.scrollBody = true;;
37623 config.grid.monitorWindowResize = false; // turn off autosizing
37624 config.grid.autoHeight = false;
37625 config.grid.autoWidth = false;
37627 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37629 if (config.background) {
37630 // render grid on panel activation (if panel background)
37631 this.on('activate', function(gp) {
37632 if (!gp.grid.rendered) {
37633 gp.grid.render(this.wrapper);
37634 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37639 this.grid.render(this.wrapper);
37640 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37643 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37644 // ??? needed ??? config.el = this.wrapper;
37649 // xtype created footer. - not sure if will work as we normally have to render first..
37650 if (this.footer && !this.footer.el && this.footer.xtype) {
37652 var ctr = this.grid.getView().getFooterPanel(true);
37653 this.footer.dataSource = this.grid.dataSource;
37654 this.footer = Roo.factory(this.footer, Roo);
37655 this.footer.render(ctr);
37665 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37666 getId : function(){
37667 return this.grid.id;
37671 * Returns the grid for this panel
37672 * @return {Roo.bootstrap.Table}
37674 getGrid : function(){
37678 setSize : function(width, height){
37679 if(!this.ignoreResize(width, height)){
37680 var grid = this.grid;
37681 var size = this.adjustForComponents(width, height);
37682 var gridel = grid.getGridEl();
37683 gridel.setSize(size.width, size.height);
37685 var thd = grid.getGridEl().select('thead',true).first();
37686 var tbd = grid.getGridEl().select('tbody', true).first();
37688 tbd.setSize(width, height - thd.getHeight());
37697 beforeSlide : function(){
37698 this.grid.getView().scroller.clip();
37701 afterSlide : function(){
37702 this.grid.getView().scroller.unclip();
37705 destroy : function(){
37706 this.grid.destroy();
37708 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37713 * @class Roo.bootstrap.panel.Nest
37714 * @extends Roo.bootstrap.panel.Content
37716 * Create a new Panel, that can contain a layout.Border.
37719 * @param {Roo.BorderLayout} layout The layout for this panel
37720 * @param {String/Object} config A string to set only the title or a config object
37722 Roo.bootstrap.panel.Nest = function(config)
37724 // construct with only one argument..
37725 /* FIXME - implement nicer consturctors
37726 if (layout.layout) {
37728 layout = config.layout;
37729 delete config.layout;
37731 if (layout.xtype && !layout.getEl) {
37732 // then layout needs constructing..
37733 layout = Roo.factory(layout, Roo);
37737 config.el = config.layout.getEl();
37739 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37741 config.layout.monitorWindowResize = false; // turn off autosizing
37742 this.layout = config.layout;
37743 this.layout.getEl().addClass("roo-layout-nested-layout");
37750 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37752 setSize : function(width, height){
37753 if(!this.ignoreResize(width, height)){
37754 var size = this.adjustForComponents(width, height);
37755 var el = this.layout.getEl();
37756 if (size.height < 1) {
37757 el.setWidth(size.width);
37759 el.setSize(size.width, size.height);
37761 var touch = el.dom.offsetWidth;
37762 this.layout.layout();
37763 // ie requires a double layout on the first pass
37764 if(Roo.isIE && !this.initialized){
37765 this.initialized = true;
37766 this.layout.layout();
37771 // activate all subpanels if not currently active..
37773 setActiveState : function(active){
37774 this.active = active;
37775 this.setActiveClass(active);
37778 this.fireEvent("deactivate", this);
37782 this.fireEvent("activate", this);
37783 // not sure if this should happen before or after..
37784 if (!this.layout) {
37785 return; // should not happen..
37788 for (var r in this.layout.regions) {
37789 reg = this.layout.getRegion(r);
37790 if (reg.getActivePanel()) {
37791 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37792 reg.setActivePanel(reg.getActivePanel());
37795 if (!reg.panels.length) {
37798 reg.showPanel(reg.getPanel(0));
37807 * Returns the nested BorderLayout for this panel
37808 * @return {Roo.BorderLayout}
37810 getLayout : function(){
37811 return this.layout;
37815 * Adds a xtype elements to the layout of the nested panel
37819 xtype : 'ContentPanel',
37826 xtype : 'NestedLayoutPanel',
37832 items : [ ... list of content panels or nested layout panels.. ]
37836 * @param {Object} cfg Xtype definition of item to add.
37838 addxtype : function(cfg) {
37839 return this.layout.addxtype(cfg);
37844 * Ext JS Library 1.1.1
37845 * Copyright(c) 2006-2007, Ext JS, LLC.
37847 * Originally Released Under LGPL - original licence link has changed is not relivant.
37850 * <script type="text/javascript">
37853 * @class Roo.TabPanel
37854 * @extends Roo.util.Observable
37855 * A lightweight tab container.
37859 // basic tabs 1, built from existing content
37860 var tabs = new Roo.TabPanel("tabs1");
37861 tabs.addTab("script", "View Script");
37862 tabs.addTab("markup", "View Markup");
37863 tabs.activate("script");
37865 // more advanced tabs, built from javascript
37866 var jtabs = new Roo.TabPanel("jtabs");
37867 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37869 // set up the UpdateManager
37870 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37871 var updater = tab2.getUpdateManager();
37872 updater.setDefaultUrl("ajax1.htm");
37873 tab2.on('activate', updater.refresh, updater, true);
37875 // Use setUrl for Ajax loading
37876 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37877 tab3.setUrl("ajax2.htm", null, true);
37880 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37883 jtabs.activate("jtabs-1");
37886 * Create a new TabPanel.
37887 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37888 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37890 Roo.bootstrap.panel.Tabs = function(config){
37892 * The container element for this TabPanel.
37893 * @type Roo.Element
37895 this.el = Roo.get(config.el);
37898 if(typeof config == "boolean"){
37899 this.tabPosition = config ? "bottom" : "top";
37901 Roo.apply(this, config);
37905 if(this.tabPosition == "bottom"){
37906 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37907 this.el.addClass("roo-tabs-bottom");
37909 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37910 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37911 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37913 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37915 if(this.tabPosition != "bottom"){
37916 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37917 * @type Roo.Element
37919 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37920 this.el.addClass("roo-tabs-top");
37924 this.bodyEl.setStyle("position", "relative");
37926 this.active = null;
37927 this.activateDelegate = this.activate.createDelegate(this);
37932 * Fires when the active tab changes
37933 * @param {Roo.TabPanel} this
37934 * @param {Roo.TabPanelItem} activePanel The new active tab
37938 * @event beforetabchange
37939 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37940 * @param {Roo.TabPanel} this
37941 * @param {Object} e Set cancel to true on this object to cancel the tab change
37942 * @param {Roo.TabPanelItem} tab The tab being changed to
37944 "beforetabchange" : true
37947 Roo.EventManager.onWindowResize(this.onResize, this);
37948 this.cpad = this.el.getPadding("lr");
37949 this.hiddenCount = 0;
37952 // toolbar on the tabbar support...
37953 if (this.toolbar) {
37954 alert("no toolbar support yet");
37955 this.toolbar = false;
37957 var tcfg = this.toolbar;
37958 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37959 this.toolbar = new Roo.Toolbar(tcfg);
37960 if (Roo.isSafari) {
37961 var tbl = tcfg.container.child('table', true);
37962 tbl.setAttribute('width', '100%');
37970 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37973 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37975 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37977 tabPosition : "top",
37979 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37981 currentTabWidth : 0,
37983 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37987 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37991 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37993 preferredTabWidth : 175,
37995 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37997 resizeTabs : false,
37999 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38001 monitorResize : true,
38003 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38008 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38009 * @param {String} id The id of the div to use <b>or create</b>
38010 * @param {String} text The text for the tab
38011 * @param {String} content (optional) Content to put in the TabPanelItem body
38012 * @param {Boolean} closable (optional) True to create a close icon on the tab
38013 * @return {Roo.TabPanelItem} The created TabPanelItem
38015 addTab : function(id, text, content, closable, tpl)
38017 var item = new Roo.bootstrap.panel.TabItem({
38021 closable : closable,
38024 this.addTabItem(item);
38026 item.setContent(content);
38032 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38033 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38034 * @return {Roo.TabPanelItem}
38036 getTab : function(id){
38037 return this.items[id];
38041 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38042 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38044 hideTab : function(id){
38045 var t = this.items[id];
38048 this.hiddenCount++;
38049 this.autoSizeTabs();
38054 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38055 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38057 unhideTab : function(id){
38058 var t = this.items[id];
38060 t.setHidden(false);
38061 this.hiddenCount--;
38062 this.autoSizeTabs();
38067 * Adds an existing {@link Roo.TabPanelItem}.
38068 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38070 addTabItem : function(item){
38071 this.items[item.id] = item;
38072 this.items.push(item);
38073 // if(this.resizeTabs){
38074 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38075 // this.autoSizeTabs();
38077 // item.autoSize();
38082 * Removes a {@link Roo.TabPanelItem}.
38083 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38085 removeTab : function(id){
38086 var items = this.items;
38087 var tab = items[id];
38088 if(!tab) { return; }
38089 var index = items.indexOf(tab);
38090 if(this.active == tab && items.length > 1){
38091 var newTab = this.getNextAvailable(index);
38096 this.stripEl.dom.removeChild(tab.pnode.dom);
38097 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38098 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38100 items.splice(index, 1);
38101 delete this.items[tab.id];
38102 tab.fireEvent("close", tab);
38103 tab.purgeListeners();
38104 this.autoSizeTabs();
38107 getNextAvailable : function(start){
38108 var items = this.items;
38110 // look for a next tab that will slide over to
38111 // replace the one being removed
38112 while(index < items.length){
38113 var item = items[++index];
38114 if(item && !item.isHidden()){
38118 // if one isn't found select the previous tab (on the left)
38121 var item = items[--index];
38122 if(item && !item.isHidden()){
38130 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38131 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38133 disableTab : function(id){
38134 var tab = this.items[id];
38135 if(tab && this.active != tab){
38141 * Enables a {@link Roo.TabPanelItem} that is disabled.
38142 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38144 enableTab : function(id){
38145 var tab = this.items[id];
38150 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38151 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38152 * @return {Roo.TabPanelItem} The TabPanelItem.
38154 activate : function(id){
38155 var tab = this.items[id];
38159 if(tab == this.active || tab.disabled){
38163 this.fireEvent("beforetabchange", this, e, tab);
38164 if(e.cancel !== true && !tab.disabled){
38166 this.active.hide();
38168 this.active = this.items[id];
38169 this.active.show();
38170 this.fireEvent("tabchange", this, this.active);
38176 * Gets the active {@link Roo.TabPanelItem}.
38177 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38179 getActiveTab : function(){
38180 return this.active;
38184 * Updates the tab body element to fit the height of the container element
38185 * for overflow scrolling
38186 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38188 syncHeight : function(targetHeight){
38189 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38190 var bm = this.bodyEl.getMargins();
38191 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38192 this.bodyEl.setHeight(newHeight);
38196 onResize : function(){
38197 if(this.monitorResize){
38198 this.autoSizeTabs();
38203 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38205 beginUpdate : function(){
38206 this.updating = true;
38210 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38212 endUpdate : function(){
38213 this.updating = false;
38214 this.autoSizeTabs();
38218 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38220 autoSizeTabs : function(){
38221 var count = this.items.length;
38222 var vcount = count - this.hiddenCount;
38223 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38226 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38227 var availWidth = Math.floor(w / vcount);
38228 var b = this.stripBody;
38229 if(b.getWidth() > w){
38230 var tabs = this.items;
38231 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38232 if(availWidth < this.minTabWidth){
38233 /*if(!this.sleft){ // incomplete scrolling code
38234 this.createScrollButtons();
38237 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38240 if(this.currentTabWidth < this.preferredTabWidth){
38241 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38247 * Returns the number of tabs in this TabPanel.
38250 getCount : function(){
38251 return this.items.length;
38255 * Resizes all the tabs to the passed width
38256 * @param {Number} The new width
38258 setTabWidth : function(width){
38259 this.currentTabWidth = width;
38260 for(var i = 0, len = this.items.length; i < len; i++) {
38261 if(!this.items[i].isHidden()) {
38262 this.items[i].setWidth(width);
38268 * Destroys this TabPanel
38269 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38271 destroy : function(removeEl){
38272 Roo.EventManager.removeResizeListener(this.onResize, this);
38273 for(var i = 0, len = this.items.length; i < len; i++){
38274 this.items[i].purgeListeners();
38276 if(removeEl === true){
38277 this.el.update("");
38282 createStrip : function(container)
38284 var strip = document.createElement("nav");
38285 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38286 container.appendChild(strip);
38290 createStripList : function(strip)
38292 // div wrapper for retard IE
38293 // returns the "tr" element.
38294 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38295 //'<div class="x-tabs-strip-wrap">'+
38296 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38297 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38298 return strip.firstChild; //.firstChild.firstChild.firstChild;
38300 createBody : function(container)
38302 var body = document.createElement("div");
38303 Roo.id(body, "tab-body");
38304 //Roo.fly(body).addClass("x-tabs-body");
38305 Roo.fly(body).addClass("tab-content");
38306 container.appendChild(body);
38309 createItemBody :function(bodyEl, id){
38310 var body = Roo.getDom(id);
38312 body = document.createElement("div");
38315 //Roo.fly(body).addClass("x-tabs-item-body");
38316 Roo.fly(body).addClass("tab-pane");
38317 bodyEl.insertBefore(body, bodyEl.firstChild);
38321 createStripElements : function(stripEl, text, closable, tpl)
38323 var td = document.createElement("li"); // was td..
38326 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38329 stripEl.appendChild(td);
38331 td.className = "x-tabs-closable";
38332 if(!this.closeTpl){
38333 this.closeTpl = new Roo.Template(
38334 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38335 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38336 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38339 var el = this.closeTpl.overwrite(td, {"text": text});
38340 var close = el.getElementsByTagName("div")[0];
38341 var inner = el.getElementsByTagName("em")[0];
38342 return {"el": el, "close": close, "inner": inner};
38345 // not sure what this is..
38346 // if(!this.tabTpl){
38347 //this.tabTpl = new Roo.Template(
38348 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38349 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38351 // this.tabTpl = new Roo.Template(
38352 // '<a href="#">' +
38353 // '<span unselectable="on"' +
38354 // (this.disableTooltips ? '' : ' title="{text}"') +
38355 // ' >{text}</span></a>'
38361 var template = tpl || this.tabTpl || false;
38365 template = new Roo.Template(
38367 '<span unselectable="on"' +
38368 (this.disableTooltips ? '' : ' title="{text}"') +
38369 ' >{text}</span></a>'
38373 switch (typeof(template)) {
38377 template = new Roo.Template(template);
38383 var el = template.overwrite(td, {"text": text});
38385 var inner = el.getElementsByTagName("span")[0];
38387 return {"el": el, "inner": inner};
38395 * @class Roo.TabPanelItem
38396 * @extends Roo.util.Observable
38397 * Represents an individual item (tab plus body) in a TabPanel.
38398 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38399 * @param {String} id The id of this TabPanelItem
38400 * @param {String} text The text for the tab of this TabPanelItem
38401 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38403 Roo.bootstrap.panel.TabItem = function(config){
38405 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38406 * @type Roo.TabPanel
38408 this.tabPanel = config.panel;
38410 * The id for this TabPanelItem
38413 this.id = config.id;
38415 this.disabled = false;
38417 this.text = config.text;
38419 this.loaded = false;
38420 this.closable = config.closable;
38423 * The body element for this TabPanelItem.
38424 * @type Roo.Element
38426 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38427 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38428 this.bodyEl.setStyle("display", "block");
38429 this.bodyEl.setStyle("zoom", "1");
38430 //this.hideAction();
38432 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38434 this.el = Roo.get(els.el);
38435 this.inner = Roo.get(els.inner, true);
38436 this.textEl = Roo.get(this.el.dom.firstChild, true);
38437 this.pnode = Roo.get(els.el.parentNode, true);
38438 // this.el.on("mousedown", this.onTabMouseDown, this);
38439 this.el.on("click", this.onTabClick, this);
38441 if(config.closable){
38442 var c = Roo.get(els.close, true);
38443 c.dom.title = this.closeText;
38444 c.addClassOnOver("close-over");
38445 c.on("click", this.closeClick, this);
38451 * Fires when this tab becomes the active tab.
38452 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38453 * @param {Roo.TabPanelItem} this
38457 * @event beforeclose
38458 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38459 * @param {Roo.TabPanelItem} this
38460 * @param {Object} e Set cancel to true on this object to cancel the close.
38462 "beforeclose": true,
38465 * Fires when this tab is closed.
38466 * @param {Roo.TabPanelItem} this
38470 * @event deactivate
38471 * Fires when this tab is no longer the active tab.
38472 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38473 * @param {Roo.TabPanelItem} this
38475 "deactivate" : true
38477 this.hidden = false;
38479 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38482 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38484 purgeListeners : function(){
38485 Roo.util.Observable.prototype.purgeListeners.call(this);
38486 this.el.removeAllListeners();
38489 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38492 this.pnode.addClass("active");
38495 this.tabPanel.stripWrap.repaint();
38497 this.fireEvent("activate", this.tabPanel, this);
38501 * Returns true if this tab is the active tab.
38502 * @return {Boolean}
38504 isActive : function(){
38505 return this.tabPanel.getActiveTab() == this;
38509 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38512 this.pnode.removeClass("active");
38514 this.fireEvent("deactivate", this.tabPanel, this);
38517 hideAction : function(){
38518 this.bodyEl.hide();
38519 this.bodyEl.setStyle("position", "absolute");
38520 this.bodyEl.setLeft("-20000px");
38521 this.bodyEl.setTop("-20000px");
38524 showAction : function(){
38525 this.bodyEl.setStyle("position", "relative");
38526 this.bodyEl.setTop("");
38527 this.bodyEl.setLeft("");
38528 this.bodyEl.show();
38532 * Set the tooltip for the tab.
38533 * @param {String} tooltip The tab's tooltip
38535 setTooltip : function(text){
38536 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38537 this.textEl.dom.qtip = text;
38538 this.textEl.dom.removeAttribute('title');
38540 this.textEl.dom.title = text;
38544 onTabClick : function(e){
38545 e.preventDefault();
38546 this.tabPanel.activate(this.id);
38549 onTabMouseDown : function(e){
38550 e.preventDefault();
38551 this.tabPanel.activate(this.id);
38554 getWidth : function(){
38555 return this.inner.getWidth();
38558 setWidth : function(width){
38559 var iwidth = width - this.pnode.getPadding("lr");
38560 this.inner.setWidth(iwidth);
38561 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38562 this.pnode.setWidth(width);
38566 * Show or hide the tab
38567 * @param {Boolean} hidden True to hide or false to show.
38569 setHidden : function(hidden){
38570 this.hidden = hidden;
38571 this.pnode.setStyle("display", hidden ? "none" : "");
38575 * Returns true if this tab is "hidden"
38576 * @return {Boolean}
38578 isHidden : function(){
38579 return this.hidden;
38583 * Returns the text for this tab
38586 getText : function(){
38590 autoSize : function(){
38591 //this.el.beginMeasure();
38592 this.textEl.setWidth(1);
38594 * #2804 [new] Tabs in Roojs
38595 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38597 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38598 //this.el.endMeasure();
38602 * Sets the text for the tab (Note: this also sets the tooltip text)
38603 * @param {String} text The tab's text and tooltip
38605 setText : function(text){
38607 this.textEl.update(text);
38608 this.setTooltip(text);
38609 //if(!this.tabPanel.resizeTabs){
38610 // this.autoSize();
38614 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38616 activate : function(){
38617 this.tabPanel.activate(this.id);
38621 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38623 disable : function(){
38624 if(this.tabPanel.active != this){
38625 this.disabled = true;
38626 this.pnode.addClass("disabled");
38631 * Enables this TabPanelItem if it was previously disabled.
38633 enable : function(){
38634 this.disabled = false;
38635 this.pnode.removeClass("disabled");
38639 * Sets the content for this TabPanelItem.
38640 * @param {String} content The content
38641 * @param {Boolean} loadScripts true to look for and load scripts
38643 setContent : function(content, loadScripts){
38644 this.bodyEl.update(content, loadScripts);
38648 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38649 * @return {Roo.UpdateManager} The UpdateManager
38651 getUpdateManager : function(){
38652 return this.bodyEl.getUpdateManager();
38656 * Set a URL to be used to load the content for this TabPanelItem.
38657 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38658 * @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)
38659 * @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)
38660 * @return {Roo.UpdateManager} The UpdateManager
38662 setUrl : function(url, params, loadOnce){
38663 if(this.refreshDelegate){
38664 this.un('activate', this.refreshDelegate);
38666 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38667 this.on("activate", this.refreshDelegate);
38668 return this.bodyEl.getUpdateManager();
38672 _handleRefresh : function(url, params, loadOnce){
38673 if(!loadOnce || !this.loaded){
38674 var updater = this.bodyEl.getUpdateManager();
38675 updater.update(url, params, this._setLoaded.createDelegate(this));
38680 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38681 * Will fail silently if the setUrl method has not been called.
38682 * This does not activate the panel, just updates its content.
38684 refresh : function(){
38685 if(this.refreshDelegate){
38686 this.loaded = false;
38687 this.refreshDelegate();
38692 _setLoaded : function(){
38693 this.loaded = true;
38697 closeClick : function(e){
38700 this.fireEvent("beforeclose", this, o);
38701 if(o.cancel !== true){
38702 this.tabPanel.removeTab(this.id);
38706 * The text displayed in the tooltip for the close icon.
38709 closeText : "Close this tab"
38712 * This script refer to:
38713 * Title: International Telephone Input
38714 * Author: Jack O'Connor
38715 * Code version: v12.1.12
38716 * Availability: https://github.com/jackocnr/intl-tel-input.git
38719 Roo.bootstrap.PhoneInputData = function() {
38722 "Afghanistan (افغانستان)",
38727 "Albania (Shqipëri)",
38732 "Algeria (الجزائر)",
38757 "Antigua and Barbuda",
38767 "Armenia (Հայաստան)",
38783 "Austria (Österreich)",
38788 "Azerbaijan (Azərbaycan)",
38798 "Bahrain (البحرين)",
38803 "Bangladesh (বাংলাদেশ)",
38813 "Belarus (Беларусь)",
38818 "Belgium (België)",
38848 "Bosnia and Herzegovina (Босна и Херцеговина)",
38863 "British Indian Ocean Territory",
38868 "British Virgin Islands",
38878 "Bulgaria (България)",
38888 "Burundi (Uburundi)",
38893 "Cambodia (កម្ពុជា)",
38898 "Cameroon (Cameroun)",
38907 ["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"]
38910 "Cape Verde (Kabu Verdi)",
38915 "Caribbean Netherlands",
38926 "Central African Republic (République centrafricaine)",
38946 "Christmas Island",
38952 "Cocos (Keeling) Islands",
38963 "Comoros (جزر القمر)",
38968 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38973 "Congo (Republic) (Congo-Brazzaville)",
38993 "Croatia (Hrvatska)",
39014 "Czech Republic (Česká republika)",
39019 "Denmark (Danmark)",
39034 "Dominican Republic (República Dominicana)",
39038 ["809", "829", "849"]
39056 "Equatorial Guinea (Guinea Ecuatorial)",
39076 "Falkland Islands (Islas Malvinas)",
39081 "Faroe Islands (Føroyar)",
39102 "French Guiana (Guyane française)",
39107 "French Polynesia (Polynésie française)",
39122 "Georgia (საქართველო)",
39127 "Germany (Deutschland)",
39147 "Greenland (Kalaallit Nunaat)",
39184 "Guinea-Bissau (Guiné Bissau)",
39209 "Hungary (Magyarország)",
39214 "Iceland (Ísland)",
39234 "Iraq (العراق)",
39250 "Israel (ישראל)",
39277 "Jordan (الأردن)",
39282 "Kazakhstan (Казахстан)",
39303 "Kuwait (الكويت)",
39308 "Kyrgyzstan (Кыргызстан)",
39318 "Latvia (Latvija)",
39323 "Lebanon (لبنان)",
39338 "Libya (ليبيا)",
39348 "Lithuania (Lietuva)",
39363 "Macedonia (FYROM) (Македонија)",
39368 "Madagascar (Madagasikara)",
39398 "Marshall Islands",
39408 "Mauritania (موريتانيا)",
39413 "Mauritius (Moris)",
39434 "Moldova (Republica Moldova)",
39444 "Mongolia (Монгол)",
39449 "Montenegro (Crna Gora)",
39459 "Morocco (المغرب)",
39465 "Mozambique (Moçambique)",
39470 "Myanmar (Burma) (မြန်မာ)",
39475 "Namibia (Namibië)",
39490 "Netherlands (Nederland)",
39495 "New Caledonia (Nouvelle-Calédonie)",
39530 "North Korea (조선 민주주의 인민 공화국)",
39535 "Northern Mariana Islands",
39551 "Pakistan (پاکستان)",
39561 "Palestine (فلسطين)",
39571 "Papua New Guinea",
39613 "Réunion (La Réunion)",
39619 "Romania (România)",
39635 "Saint Barthélemy",
39646 "Saint Kitts and Nevis",
39656 "Saint Martin (Saint-Martin (partie française))",
39662 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39667 "Saint Vincent and the Grenadines",
39682 "São Tomé and Príncipe (São Tomé e Príncipe)",
39687 "Saudi Arabia (المملكة العربية السعودية)",
39692 "Senegal (Sénégal)",
39722 "Slovakia (Slovensko)",
39727 "Slovenia (Slovenija)",
39737 "Somalia (Soomaaliya)",
39747 "South Korea (대한민국)",
39752 "South Sudan (جنوب السودان)",
39762 "Sri Lanka (ශ්රී ලංකාව)",
39767 "Sudan (السودان)",
39777 "Svalbard and Jan Mayen",
39788 "Sweden (Sverige)",
39793 "Switzerland (Schweiz)",
39798 "Syria (سوريا)",
39843 "Trinidad and Tobago",
39848 "Tunisia (تونس)",
39853 "Turkey (Türkiye)",
39863 "Turks and Caicos Islands",
39873 "U.S. Virgin Islands",
39883 "Ukraine (Україна)",
39888 "United Arab Emirates (الإمارات العربية المتحدة)",
39910 "Uzbekistan (Oʻzbekiston)",
39920 "Vatican City (Città del Vaticano)",
39931 "Vietnam (Việt Nam)",
39936 "Wallis and Futuna (Wallis-et-Futuna)",
39941 "Western Sahara (الصحراء الغربية)",
39947 "Yemen (اليمن)",
39971 * This script refer to:
39972 * Title: International Telephone Input
39973 * Author: Jack O'Connor
39974 * Code version: v12.1.12
39975 * Availability: https://github.com/jackocnr/intl-tel-input.git
39979 * @class Roo.bootstrap.PhoneInput
39980 * @extends Roo.bootstrap.TriggerField
39981 * An input with International dial-code selection
39983 * @cfg {String} defaultDialCode default '+852'
39984 * @cfg {Array} preferedCountries default []
39987 * Create a new PhoneInput.
39988 * @param {Object} config Configuration options
39991 Roo.bootstrap.PhoneInput = function(config) {
39992 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39995 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39997 listWidth: undefined,
39999 selectedClass: 'active',
40001 invalidClass : "has-warning",
40003 validClass: 'has-success',
40005 allowed: '0123456789',
40010 * @cfg {String} defaultDialCode The default dial code when initializing the input
40012 defaultDialCode: '+852',
40015 * @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
40017 preferedCountries: false,
40019 getAutoCreate : function()
40021 var data = Roo.bootstrap.PhoneInputData();
40022 var align = this.labelAlign || this.parentLabelAlign();
40025 this.allCountries = [];
40026 this.dialCodeMapping = [];
40028 for (var i = 0; i < data.length; i++) {
40030 this.allCountries[i] = {
40034 priority: c[3] || 0,
40035 areaCodes: c[4] || null
40037 this.dialCodeMapping[c[2]] = {
40040 priority: c[3] || 0,
40041 areaCodes: c[4] || null
40053 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40054 maxlength: this.max_length,
40055 cls : 'form-control tel-input',
40056 autocomplete: 'new-password'
40059 var hiddenInput = {
40062 cls: 'hidden-tel-input'
40066 hiddenInput.name = this.name;
40069 if (this.disabled) {
40070 input.disabled = true;
40073 var flag_container = {
40090 cls: this.hasFeedback ? 'has-feedback' : '',
40096 cls: 'dial-code-holder',
40103 cls: 'roo-select2-container input-group',
40110 if (this.fieldLabel.length) {
40113 tooltip: 'This field is required'
40119 cls: 'control-label',
40125 html: this.fieldLabel
40128 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40134 if(this.indicatorpos == 'right') {
40135 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40142 if(align == 'left') {
40150 if(this.labelWidth > 12){
40151 label.style = "width: " + this.labelWidth + 'px';
40153 if(this.labelWidth < 13 && this.labelmd == 0){
40154 this.labelmd = this.labelWidth;
40156 if(this.labellg > 0){
40157 label.cls += ' col-lg-' + this.labellg;
40158 input.cls += ' col-lg-' + (12 - this.labellg);
40160 if(this.labelmd > 0){
40161 label.cls += ' col-md-' + this.labelmd;
40162 container.cls += ' col-md-' + (12 - this.labelmd);
40164 if(this.labelsm > 0){
40165 label.cls += ' col-sm-' + this.labelsm;
40166 container.cls += ' col-sm-' + (12 - this.labelsm);
40168 if(this.labelxs > 0){
40169 label.cls += ' col-xs-' + this.labelxs;
40170 container.cls += ' col-xs-' + (12 - this.labelxs);
40180 var settings = this;
40182 ['xs','sm','md','lg'].map(function(size){
40183 if (settings[size]) {
40184 cfg.cls += ' col-' + size + '-' + settings[size];
40188 this.store = new Roo.data.Store({
40189 proxy : new Roo.data.MemoryProxy({}),
40190 reader : new Roo.data.JsonReader({
40201 'name' : 'dialCode',
40205 'name' : 'priority',
40209 'name' : 'areaCodes',
40216 if(!this.preferedCountries) {
40217 this.preferedCountries = [
40224 var p = this.preferedCountries.reverse();
40227 for (var i = 0; i < p.length; i++) {
40228 for (var j = 0; j < this.allCountries.length; j++) {
40229 if(this.allCountries[j].iso2 == p[i]) {
40230 var t = this.allCountries[j];
40231 this.allCountries.splice(j,1);
40232 this.allCountries.unshift(t);
40238 this.store.proxy.data = {
40240 data: this.allCountries
40246 initEvents : function()
40249 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40251 this.indicator = this.indicatorEl();
40252 this.flag = this.flagEl();
40253 this.dialCodeHolder = this.dialCodeHolderEl();
40255 this.trigger = this.el.select('div.flag-box',true).first();
40256 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40261 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40262 _this.list.setWidth(lw);
40265 this.list.on('mouseover', this.onViewOver, this);
40266 this.list.on('mousemove', this.onViewMove, this);
40267 this.inputEl().on("keyup", this.onKeyUp, this);
40268 this.inputEl().on("keypress", this.onKeyPress, this);
40270 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40272 this.view = new Roo.View(this.list, this.tpl, {
40273 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40276 this.view.on('click', this.onViewClick, this);
40277 this.setValue(this.defaultDialCode);
40280 onTriggerClick : function(e)
40282 Roo.log('trigger click');
40287 if(this.isExpanded()){
40289 this.hasFocus = false;
40291 this.store.load({});
40292 this.hasFocus = true;
40297 isExpanded : function()
40299 return this.list.isVisible();
40302 collapse : function()
40304 if(!this.isExpanded()){
40308 Roo.get(document).un('mousedown', this.collapseIf, this);
40309 Roo.get(document).un('mousewheel', this.collapseIf, this);
40310 this.fireEvent('collapse', this);
40314 expand : function()
40318 if(this.isExpanded() || !this.hasFocus){
40322 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40323 this.list.setWidth(lw);
40326 this.restrictHeight();
40328 Roo.get(document).on('mousedown', this.collapseIf, this);
40329 Roo.get(document).on('mousewheel', this.collapseIf, this);
40331 this.fireEvent('expand', this);
40334 restrictHeight : function()
40336 this.list.alignTo(this.inputEl(), this.listAlign);
40337 this.list.alignTo(this.inputEl(), this.listAlign);
40340 onViewOver : function(e, t)
40342 if(this.inKeyMode){
40345 var item = this.view.findItemFromChild(t);
40348 var index = this.view.indexOf(item);
40349 this.select(index, false);
40354 onViewClick : function(view, doFocus, el, e)
40356 var index = this.view.getSelectedIndexes()[0];
40358 var r = this.store.getAt(index);
40361 this.onSelect(r, index);
40363 if(doFocus !== false && !this.blockFocus){
40364 this.inputEl().focus();
40368 onViewMove : function(e, t)
40370 this.inKeyMode = false;
40373 select : function(index, scrollIntoView)
40375 this.selectedIndex = index;
40376 this.view.select(index);
40377 if(scrollIntoView !== false){
40378 var el = this.view.getNode(index);
40380 this.list.scrollChildIntoView(el, false);
40385 createList : function()
40387 this.list = Roo.get(document.body).createChild({
40389 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40390 style: 'display:none'
40393 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40396 collapseIf : function(e)
40398 var in_combo = e.within(this.el);
40399 var in_list = e.within(this.list);
40400 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40402 if (in_combo || in_list || is_list) {
40408 onSelect : function(record, index)
40410 if(this.fireEvent('beforeselect', this, record, index) !== false){
40412 this.setFlagClass(record.data.iso2);
40413 this.setDialCode(record.data.dialCode);
40414 this.hasFocus = false;
40416 this.fireEvent('select', this, record, index);
40420 flagEl : function()
40422 var flag = this.el.select('div.flag',true).first();
40429 dialCodeHolderEl : function()
40431 var d = this.el.select('input.dial-code-holder',true).first();
40438 setDialCode : function(v)
40440 this.dialCodeHolder.dom.value = '+'+v;
40443 setFlagClass : function(n)
40445 this.flag.dom.className = 'flag '+n;
40448 getValue : function()
40450 var v = this.inputEl().getValue();
40451 if(this.dialCodeHolder) {
40452 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40457 setValue : function(v)
40459 var d = this.getDialCode(v);
40461 //invalid dial code
40462 if(v.length == 0 || !d || d.length == 0) {
40464 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40465 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40471 this.setFlagClass(this.dialCodeMapping[d].iso2);
40472 this.setDialCode(d);
40473 this.inputEl().dom.value = v.replace('+'+d,'');
40474 this.hiddenEl().dom.value = this.getValue();
40479 getDialCode : function(v)
40483 if (v.length == 0) {
40484 return this.dialCodeHolder.dom.value;
40488 if (v.charAt(0) != "+") {
40491 var numericChars = "";
40492 for (var i = 1; i < v.length; i++) {
40493 var c = v.charAt(i);
40496 if (this.dialCodeMapping[numericChars]) {
40497 dialCode = v.substr(1, i);
40499 if (numericChars.length == 4) {
40509 this.setValue(this.defaultDialCode);
40513 hiddenEl : function()
40515 return this.el.select('input.hidden-tel-input',true).first();
40518 // after setting val
40519 onKeyUp : function(e){
40520 this.setValue(this.getValue());
40523 onKeyPress : function(e){
40524 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40531 * @class Roo.bootstrap.MoneyField
40532 * @extends Roo.bootstrap.ComboBox
40533 * Bootstrap MoneyField class
40536 * Create a new MoneyField.
40537 * @param {Object} config Configuration options
40540 Roo.bootstrap.MoneyField = function(config) {
40542 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40546 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40549 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40551 allowDecimals : true,
40553 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40555 decimalSeparator : ".",
40557 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40559 decimalPrecision : 0,
40561 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40563 allowNegative : true,
40565 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40569 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40571 minValue : Number.NEGATIVE_INFINITY,
40573 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40575 maxValue : Number.MAX_VALUE,
40577 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40579 minText : "The minimum value for this field is {0}",
40581 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40583 maxText : "The maximum value for this field is {0}",
40585 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40586 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40588 nanText : "{0} is not a valid number",
40590 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40594 * @cfg {String} defaults currency of the MoneyField
40595 * value should be in lkey
40597 defaultCurrency : false,
40599 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40601 thousandsDelimiter : false,
40603 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40614 getAutoCreate : function()
40616 var align = this.labelAlign || this.parentLabelAlign();
40628 cls : 'form-control roo-money-amount-input',
40629 autocomplete: 'new-password'
40632 var hiddenInput = {
40636 cls: 'hidden-number-input'
40639 if(this.max_length) {
40640 input.maxlength = this.max_length;
40644 hiddenInput.name = this.name;
40647 if (this.disabled) {
40648 input.disabled = true;
40651 var clg = 12 - this.inputlg;
40652 var cmd = 12 - this.inputmd;
40653 var csm = 12 - this.inputsm;
40654 var cxs = 12 - this.inputxs;
40658 cls : 'row roo-money-field',
40662 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40666 cls: 'roo-select2-container input-group',
40670 cls : 'form-control roo-money-currency-input',
40671 autocomplete: 'new-password',
40673 name : this.currencyName
40677 cls : 'input-group-addon',
40691 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40695 cls: this.hasFeedback ? 'has-feedback' : '',
40706 if (this.fieldLabel.length) {
40709 tooltip: 'This field is required'
40715 cls: 'control-label',
40721 html: this.fieldLabel
40724 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40730 if(this.indicatorpos == 'right') {
40731 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40738 if(align == 'left') {
40746 if(this.labelWidth > 12){
40747 label.style = "width: " + this.labelWidth + 'px';
40749 if(this.labelWidth < 13 && this.labelmd == 0){
40750 this.labelmd = this.labelWidth;
40752 if(this.labellg > 0){
40753 label.cls += ' col-lg-' + this.labellg;
40754 input.cls += ' col-lg-' + (12 - this.labellg);
40756 if(this.labelmd > 0){
40757 label.cls += ' col-md-' + this.labelmd;
40758 container.cls += ' col-md-' + (12 - this.labelmd);
40760 if(this.labelsm > 0){
40761 label.cls += ' col-sm-' + this.labelsm;
40762 container.cls += ' col-sm-' + (12 - this.labelsm);
40764 if(this.labelxs > 0){
40765 label.cls += ' col-xs-' + this.labelxs;
40766 container.cls += ' col-xs-' + (12 - this.labelxs);
40777 var settings = this;
40779 ['xs','sm','md','lg'].map(function(size){
40780 if (settings[size]) {
40781 cfg.cls += ' col-' + size + '-' + settings[size];
40788 initEvents : function()
40790 this.indicator = this.indicatorEl();
40792 this.initCurrencyEvent();
40794 this.initNumberEvent();
40797 initCurrencyEvent : function()
40800 throw "can not find store for combo";
40803 this.store = Roo.factory(this.store, Roo.data);
40804 this.store.parent = this;
40808 this.triggerEl = this.el.select('.input-group-addon', true).first();
40810 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40815 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40816 _this.list.setWidth(lw);
40819 this.list.on('mouseover', this.onViewOver, this);
40820 this.list.on('mousemove', this.onViewMove, this);
40821 this.list.on('scroll', this.onViewScroll, this);
40824 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40827 this.view = new Roo.View(this.list, this.tpl, {
40828 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40831 this.view.on('click', this.onViewClick, this);
40833 this.store.on('beforeload', this.onBeforeLoad, this);
40834 this.store.on('load', this.onLoad, this);
40835 this.store.on('loadexception', this.onLoadException, this);
40837 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40838 "up" : function(e){
40839 this.inKeyMode = true;
40843 "down" : function(e){
40844 if(!this.isExpanded()){
40845 this.onTriggerClick();
40847 this.inKeyMode = true;
40852 "enter" : function(e){
40855 if(this.fireEvent("specialkey", this, e)){
40856 this.onViewClick(false);
40862 "esc" : function(e){
40866 "tab" : function(e){
40869 if(this.fireEvent("specialkey", this, e)){
40870 this.onViewClick(false);
40878 doRelay : function(foo, bar, hname){
40879 if(hname == 'down' || this.scope.isExpanded()){
40880 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40888 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40892 initNumberEvent : function(e)
40894 this.inputEl().on("keydown" , this.fireKey, this);
40895 this.inputEl().on("focus", this.onFocus, this);
40896 this.inputEl().on("blur", this.onBlur, this);
40898 this.inputEl().relayEvent('keyup', this);
40900 if(this.indicator){
40901 this.indicator.addClass('invisible');
40904 this.originalValue = this.getValue();
40906 if(this.validationEvent == 'keyup'){
40907 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40908 this.inputEl().on('keyup', this.filterValidation, this);
40910 else if(this.validationEvent !== false){
40911 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40914 if(this.selectOnFocus){
40915 this.on("focus", this.preFocus, this);
40918 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40919 this.inputEl().on("keypress", this.filterKeys, this);
40921 this.inputEl().relayEvent('keypress', this);
40924 var allowed = "0123456789";
40926 if(this.allowDecimals){
40927 allowed += this.decimalSeparator;
40930 if(this.allowNegative){
40934 if(this.thousandsDelimiter) {
40938 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40940 var keyPress = function(e){
40942 var k = e.getKey();
40944 var c = e.getCharCode();
40947 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40948 allowed.indexOf(String.fromCharCode(c)) === -1
40954 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40958 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40963 this.inputEl().on("keypress", keyPress, this);
40967 onTriggerClick : function(e)
40974 this.loadNext = false;
40976 if(this.isExpanded()){
40981 this.hasFocus = true;
40983 if(this.triggerAction == 'all') {
40984 this.doQuery(this.allQuery, true);
40988 this.doQuery(this.getRawValue());
40991 getCurrency : function()
40993 var v = this.currencyEl().getValue();
40998 restrictHeight : function()
41000 this.list.alignTo(this.currencyEl(), this.listAlign);
41001 this.list.alignTo(this.currencyEl(), this.listAlign);
41004 onViewClick : function(view, doFocus, el, e)
41006 var index = this.view.getSelectedIndexes()[0];
41008 var r = this.store.getAt(index);
41011 this.onSelect(r, index);
41015 onSelect : function(record, index){
41017 if(this.fireEvent('beforeselect', this, record, index) !== false){
41019 this.setFromCurrencyData(index > -1 ? record.data : false);
41023 this.fireEvent('select', this, record, index);
41027 setFromCurrencyData : function(o)
41031 this.lastCurrency = o;
41033 if (this.currencyField) {
41034 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41036 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41039 this.lastSelectionText = currency;
41041 //setting default currency
41042 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41043 this.setCurrency(this.defaultCurrency);
41047 this.setCurrency(currency);
41050 setFromData : function(o)
41054 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41056 this.setFromCurrencyData(c);
41061 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41063 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41066 this.setValue(value);
41070 setCurrency : function(v)
41072 this.currencyValue = v;
41075 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41080 setValue : function(v)
41082 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41088 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41090 this.inputEl().dom.value = (v == '') ? '' :
41091 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41093 if(!this.allowZero && v === '0') {
41094 this.hiddenEl().dom.value = '';
41095 this.inputEl().dom.value = '';
41102 getRawValue : function()
41104 var v = this.inputEl().getValue();
41109 getValue : function()
41111 return this.fixPrecision(this.parseValue(this.getRawValue()));
41114 parseValue : function(value)
41116 if(this.thousandsDelimiter) {
41118 r = new RegExp(",", "g");
41119 value = value.replace(r, "");
41122 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41123 return isNaN(value) ? '' : value;
41127 fixPrecision : function(value)
41129 if(this.thousandsDelimiter) {
41131 r = new RegExp(",", "g");
41132 value = value.replace(r, "");
41135 var nan = isNaN(value);
41137 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41138 return nan ? '' : value;
41140 return parseFloat(value).toFixed(this.decimalPrecision);
41143 decimalPrecisionFcn : function(v)
41145 return Math.floor(v);
41148 validateValue : function(value)
41150 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41154 var num = this.parseValue(value);
41157 this.markInvalid(String.format(this.nanText, value));
41161 if(num < this.minValue){
41162 this.markInvalid(String.format(this.minText, this.minValue));
41166 if(num > this.maxValue){
41167 this.markInvalid(String.format(this.maxText, this.maxValue));
41174 validate : function()
41176 if(this.disabled || this.allowBlank){
41181 var currency = this.getCurrency();
41183 if(this.validateValue(this.getRawValue()) && currency.length){
41188 this.markInvalid();
41192 getName: function()
41197 beforeBlur : function()
41203 var v = this.parseValue(this.getRawValue());
41210 onBlur : function()
41214 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41215 //this.el.removeClass(this.focusClass);
41218 this.hasFocus = false;
41220 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41224 var v = this.getValue();
41226 if(String(v) !== String(this.startValue)){
41227 this.fireEvent('change', this, v, this.startValue);
41230 this.fireEvent("blur", this);
41233 inputEl : function()
41235 return this.el.select('.roo-money-amount-input', true).first();
41238 currencyEl : function()
41240 return this.el.select('.roo-money-currency-input', true).first();
41243 hiddenEl : function()
41245 return this.el.select('input.hidden-number-input',true).first();