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('collapsing');
3870 var h = ce.getHeight();
3871 ce.setHeight(0); // resize it ...
3875 // now flag it as moving..
3878 ce.removeClass('collapsing');
3879 ce.addClass('show');
3880 ce.removeClass('collapse');
3882 ce.dom.style.height = '';
3887 ce.addClass('collapsing');
3888 ce.removeClass('show');
3890 ce.removeClass('collapsing');
3891 ce.addClass('collapse');
3905 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3907 var size = this.el.getSize();
3908 this.maskEl.setSize(size.width, size.height);
3909 this.maskEl.enableDisplayMode("block");
3918 getChildContainer : function()
3920 if (this.el.select('.collapse').getCount()) {
3921 return this.el.select('.collapse',true).first();
3954 * @class Roo.bootstrap.NavSimplebar
3955 * @extends Roo.bootstrap.Navbar
3956 * Bootstrap Sidebar class
3958 * @cfg {Boolean} inverse is inverted color
3960 * @cfg {String} type (nav | pills | tabs)
3961 * @cfg {Boolean} arrangement stacked | justified
3962 * @cfg {String} align (left | right) alignment
3964 * @cfg {Boolean} main (true|false) main nav bar? default false
3965 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3967 * @cfg {String} tag (header|footer|nav|div) default is nav
3969 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3973 * Create a new Sidebar
3974 * @param {Object} config The config object
3978 Roo.bootstrap.NavSimplebar = function(config){
3979 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3982 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3998 getAutoCreate : function(){
4002 tag : this.tag || 'div',
4003 cls : 'navbar navbar-expand-lg'
4005 if (['light','white'].indexOf(this.weight) > -1) {
4006 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4008 cfg.cls += ' bg-' + this.weight;
4020 this.type = this.type || 'nav';
4021 if (['tabs','pills'].indexOf(this.type)!==-1) {
4022 cfg.cn[0].cls += ' nav-' + this.type
4026 if (this.type!=='nav') {
4027 Roo.log('nav type must be nav/tabs/pills')
4029 cfg.cn[0].cls += ' navbar-nav'
4035 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4036 cfg.cn[0].cls += ' nav-' + this.arrangement;
4040 if (this.align === 'right') {
4041 cfg.cn[0].cls += ' navbar-right';
4045 cfg.cls += ' navbar-inverse';
4069 * navbar-expand-md fixed-top
4073 * @class Roo.bootstrap.NavHeaderbar
4074 * @extends Roo.bootstrap.NavSimplebar
4075 * Bootstrap Sidebar class
4077 * @cfg {String} brand what is brand
4078 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4079 * @cfg {String} brand_href href of the brand
4080 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4081 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4082 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4083 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4086 * Create a new Sidebar
4087 * @param {Object} config The config object
4091 Roo.bootstrap.NavHeaderbar = function(config){
4092 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4096 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4103 desktopCenter : false,
4106 getAutoCreate : function(){
4109 tag: this.nav || 'nav',
4110 cls: 'navbar navbar-expand-md',
4116 if (this.desktopCenter) {
4117 cn.push({cls : 'container', cn : []});
4125 cls: 'navbar-toggle navbar-toggler',
4126 'data-toggle': 'collapse',
4131 html: 'Toggle navigation'
4135 cls: 'icon-bar navbar-toggler-icon'
4148 cn.push( Roo.bootstrap.version == 4 ? btn : {
4150 cls: 'navbar-header',
4159 cls: 'collapse navbar-collapse',
4163 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4165 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4166 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4168 // tag can override this..
4170 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4173 if (this.brand !== '') {
4174 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4175 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4177 href: this.brand_href ? this.brand_href : '#',
4178 cls: 'navbar-brand',
4186 cfg.cls += ' main-nav';
4194 getHeaderChildContainer : function()
4196 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4197 return this.el.select('.navbar-header',true).first();
4200 return this.getChildContainer();
4204 initEvents : function()
4206 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4208 if (this.autohide) {
4213 Roo.get(document).on('scroll',function(e) {
4214 var ns = Roo.get(document).getScroll().top;
4215 var os = prevScroll;
4219 ft.removeClass('slideDown');
4220 ft.addClass('slideUp');
4223 ft.removeClass('slideUp');
4224 ft.addClass('slideDown');
4245 * @class Roo.bootstrap.NavSidebar
4246 * @extends Roo.bootstrap.Navbar
4247 * Bootstrap Sidebar class
4250 * Create a new Sidebar
4251 * @param {Object} config The config object
4255 Roo.bootstrap.NavSidebar = function(config){
4256 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4259 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4261 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4263 getAutoCreate : function(){
4268 cls: 'sidebar sidebar-nav'
4290 * @class Roo.bootstrap.NavGroup
4291 * @extends Roo.bootstrap.Component
4292 * Bootstrap NavGroup class
4293 * @cfg {String} align (left|right)
4294 * @cfg {Boolean} inverse
4295 * @cfg {String} type (nav|pills|tab) default nav
4296 * @cfg {String} navId - reference Id for navbar.
4300 * Create a new nav group
4301 * @param {Object} config The config object
4304 Roo.bootstrap.NavGroup = function(config){
4305 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4308 Roo.bootstrap.NavGroup.register(this);
4312 * Fires when the active item changes
4313 * @param {Roo.bootstrap.NavGroup} this
4314 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4315 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4322 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4333 getAutoCreate : function()
4335 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4342 if (['tabs','pills'].indexOf(this.type)!==-1) {
4343 cfg.cls += ' nav-' + this.type
4345 if (this.type!=='nav') {
4346 Roo.log('nav type must be nav/tabs/pills')
4348 cfg.cls += ' navbar-nav'
4351 if (this.parent() && this.parent().sidebar) {
4354 cls: 'dashboard-menu sidebar-menu'
4360 if (this.form === true) {
4366 if (this.align === 'right') {
4367 cfg.cls += ' navbar-right ml-md-auto';
4369 cfg.cls += ' navbar-left';
4373 if (this.align === 'right') {
4374 cfg.cls += ' navbar-right ml-md-auto';
4376 cfg.cls += ' mr-auto';
4380 cfg.cls += ' navbar-inverse';
4388 * sets the active Navigation item
4389 * @param {Roo.bootstrap.NavItem} the new current navitem
4391 setActiveItem : function(item)
4394 Roo.each(this.navItems, function(v){
4399 v.setActive(false, true);
4406 item.setActive(true, true);
4407 this.fireEvent('changed', this, item, prev);
4412 * gets the active Navigation item
4413 * @return {Roo.bootstrap.NavItem} the current navitem
4415 getActive : function()
4419 Roo.each(this.navItems, function(v){
4430 indexOfNav : function()
4434 Roo.each(this.navItems, function(v,i){
4445 * adds a Navigation item
4446 * @param {Roo.bootstrap.NavItem} the navitem to add
4448 addItem : function(cfg)
4450 var cn = new Roo.bootstrap.NavItem(cfg);
4452 cn.parentId = this.id;
4453 cn.onRender(this.el, null);
4457 * register a Navigation item
4458 * @param {Roo.bootstrap.NavItem} the navitem to add
4460 register : function(item)
4462 this.navItems.push( item);
4463 item.navId = this.navId;
4468 * clear all the Navigation item
4471 clearAll : function()
4474 this.el.dom.innerHTML = '';
4477 getNavItem: function(tabId)
4480 Roo.each(this.navItems, function(e) {
4481 if (e.tabId == tabId) {
4491 setActiveNext : function()
4493 var i = this.indexOfNav(this.getActive());
4494 if (i > this.navItems.length) {
4497 this.setActiveItem(this.navItems[i+1]);
4499 setActivePrev : function()
4501 var i = this.indexOfNav(this.getActive());
4505 this.setActiveItem(this.navItems[i-1]);
4507 clearWasActive : function(except) {
4508 Roo.each(this.navItems, function(e) {
4509 if (e.tabId != except.tabId && e.was_active) {
4510 e.was_active = false;
4517 getWasActive : function ()
4520 Roo.each(this.navItems, function(e) {
4535 Roo.apply(Roo.bootstrap.NavGroup, {
4539 * register a Navigation Group
4540 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4542 register : function(navgrp)
4544 this.groups[navgrp.navId] = navgrp;
4548 * fetch a Navigation Group based on the navigation ID
4549 * @param {string} the navgroup to add
4550 * @returns {Roo.bootstrap.NavGroup} the navgroup
4552 get: function(navId) {
4553 if (typeof(this.groups[navId]) == 'undefined') {
4555 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4557 return this.groups[navId] ;
4572 * @class Roo.bootstrap.NavItem
4573 * @extends Roo.bootstrap.Component
4574 * Bootstrap Navbar.NavItem class
4575 * @cfg {String} href link to
4576 * @cfg {String} html content of button
4577 * @cfg {String} badge text inside badge
4578 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4579 * @cfg {String} glyphicon DEPRICATED - use fa
4580 * @cfg {String} icon DEPRICATED - use fa
4581 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4582 * @cfg {Boolean} active Is item active
4583 * @cfg {Boolean} disabled Is item disabled
4585 * @cfg {Boolean} preventDefault (true | false) default false
4586 * @cfg {String} tabId the tab that this item activates.
4587 * @cfg {String} tagtype (a|span) render as a href or span?
4588 * @cfg {Boolean} animateRef (true|false) link to element default false
4591 * Create a new Navbar Item
4592 * @param {Object} config The config object
4594 Roo.bootstrap.NavItem = function(config){
4595 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4600 * The raw click event for the entire grid.
4601 * @param {Roo.EventObject} e
4606 * Fires when the active item active state changes
4607 * @param {Roo.bootstrap.NavItem} this
4608 * @param {boolean} state the new state
4614 * Fires when scroll to element
4615 * @param {Roo.bootstrap.NavItem} this
4616 * @param {Object} options
4617 * @param {Roo.EventObject} e
4625 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4634 preventDefault : false,
4641 getAutoCreate : function(){
4650 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4652 if (this.disabled) {
4653 cfg.cls += ' disabled';
4656 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4660 href : this.href || "#",
4661 html: this.html || ''
4664 if (this.tagtype == 'a') {
4665 cfg.cn[0].cls = 'nav-link';
4668 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4671 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4673 if(this.glyphicon) {
4674 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4679 cfg.cn[0].html += " <span class='caret'></span>";
4683 if (this.badge !== '') {
4685 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4693 initEvents: function()
4695 if (typeof (this.menu) != 'undefined') {
4696 this.menu.parentType = this.xtype;
4697 this.menu.triggerEl = this.el;
4698 this.menu = this.addxtype(Roo.apply({}, this.menu));
4701 this.el.select('a',true).on('click', this.onClick, this);
4703 if(this.tagtype == 'span'){
4704 this.el.select('span',true).on('click', this.onClick, this);
4707 // at this point parent should be available..
4708 this.parent().register(this);
4711 onClick : function(e)
4713 if (e.getTarget('.dropdown-menu-item')) {
4714 // did you click on a menu itemm.... - then don't trigger onclick..
4719 this.preventDefault ||
4722 Roo.log("NavItem - prevent Default?");
4726 if (this.disabled) {
4730 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4731 if (tg && tg.transition) {
4732 Roo.log("waiting for the transitionend");
4738 //Roo.log("fire event clicked");
4739 if(this.fireEvent('click', this, e) === false){
4743 if(this.tagtype == 'span'){
4747 //Roo.log(this.href);
4748 var ael = this.el.select('a',true).first();
4751 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4752 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4753 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4754 return; // ignore... - it's a 'hash' to another page.
4756 Roo.log("NavItem - prevent Default?");
4758 this.scrollToElement(e);
4762 var p = this.parent();
4764 if (['tabs','pills'].indexOf(p.type)!==-1) {
4765 if (typeof(p.setActiveItem) !== 'undefined') {
4766 p.setActiveItem(this);
4770 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4771 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4772 // remove the collapsed menu expand...
4773 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4777 isActive: function () {
4780 setActive : function(state, fire, is_was_active)
4782 if (this.active && !state && this.navId) {
4783 this.was_active = true;
4784 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4786 nv.clearWasActive(this);
4790 this.active = state;
4793 this.el.removeClass('active');
4794 } else if (!this.el.hasClass('active')) {
4795 this.el.addClass('active');
4798 this.fireEvent('changed', this, state);
4801 // show a panel if it's registered and related..
4803 if (!this.navId || !this.tabId || !state || is_was_active) {
4807 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4811 var pan = tg.getPanelByName(this.tabId);
4815 // if we can not flip to new panel - go back to old nav highlight..
4816 if (false == tg.showPanel(pan)) {
4817 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4819 var onav = nv.getWasActive();
4821 onav.setActive(true, false, true);
4830 // this should not be here...
4831 setDisabled : function(state)
4833 this.disabled = state;
4835 this.el.removeClass('disabled');
4836 } else if (!this.el.hasClass('disabled')) {
4837 this.el.addClass('disabled');
4843 * Fetch the element to display the tooltip on.
4844 * @return {Roo.Element} defaults to this.el
4846 tooltipEl : function()
4848 return this.el.select('' + this.tagtype + '', true).first();
4851 scrollToElement : function(e)
4853 var c = document.body;
4856 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4858 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4859 c = document.documentElement;
4862 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4868 var o = target.calcOffsetsTo(c);
4875 this.fireEvent('scrollto', this, options, e);
4877 Roo.get(c).scrollTo('top', options.value, true);
4890 * <span> icon </span>
4891 * <span> text </span>
4892 * <span>badge </span>
4896 * @class Roo.bootstrap.NavSidebarItem
4897 * @extends Roo.bootstrap.NavItem
4898 * Bootstrap Navbar.NavSidebarItem class
4899 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4900 * {Boolean} open is the menu open
4901 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4902 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4903 * {String} buttonSize (sm|md|lg)the extra classes for the button
4904 * {Boolean} showArrow show arrow next to the text (default true)
4906 * Create a new Navbar Button
4907 * @param {Object} config The config object
4909 Roo.bootstrap.NavSidebarItem = function(config){
4910 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4915 * The raw click event for the entire grid.
4916 * @param {Roo.EventObject} e
4921 * Fires when the active item active state changes
4922 * @param {Roo.bootstrap.NavSidebarItem} this
4923 * @param {boolean} state the new state
4931 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4933 badgeWeight : 'default',
4939 buttonWeight : 'default',
4945 getAutoCreate : function(){
4950 href : this.href || '#',
4956 if(this.buttonView){
4959 href : this.href || '#',
4960 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4973 cfg.cls += ' active';
4976 if (this.disabled) {
4977 cfg.cls += ' disabled';
4980 cfg.cls += ' open x-open';
4983 if (this.glyphicon || this.icon) {
4984 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4985 a.cn.push({ tag : 'i', cls : c }) ;
4988 if(!this.buttonView){
4991 html : this.html || ''
4998 if (this.badge !== '') {
4999 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5005 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5008 a.cls += ' dropdown-toggle treeview' ;
5014 initEvents : function()
5016 if (typeof (this.menu) != 'undefined') {
5017 this.menu.parentType = this.xtype;
5018 this.menu.triggerEl = this.el;
5019 this.menu = this.addxtype(Roo.apply({}, this.menu));
5022 this.el.on('click', this.onClick, this);
5024 if(this.badge !== ''){
5025 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5030 onClick : function(e)
5037 if(this.preventDefault){
5041 this.fireEvent('click', this);
5044 disable : function()
5046 this.setDisabled(true);
5051 this.setDisabled(false);
5054 setDisabled : function(state)
5056 if(this.disabled == state){
5060 this.disabled = state;
5063 this.el.addClass('disabled');
5067 this.el.removeClass('disabled');
5072 setActive : function(state)
5074 if(this.active == state){
5078 this.active = state;
5081 this.el.addClass('active');
5085 this.el.removeClass('active');
5090 isActive: function ()
5095 setBadge : function(str)
5101 this.badgeEl.dom.innerHTML = str;
5118 * @class Roo.bootstrap.Row
5119 * @extends Roo.bootstrap.Component
5120 * Bootstrap Row class (contains columns...)
5124 * @param {Object} config The config object
5127 Roo.bootstrap.Row = function(config){
5128 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5131 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5133 getAutoCreate : function(){
5152 * @class Roo.bootstrap.Element
5153 * @extends Roo.bootstrap.Component
5154 * Bootstrap Element class
5155 * @cfg {String} html contents of the element
5156 * @cfg {String} tag tag of the element
5157 * @cfg {String} cls class of the element
5158 * @cfg {Boolean} preventDefault (true|false) default false
5159 * @cfg {Boolean} clickable (true|false) default false
5162 * Create a new Element
5163 * @param {Object} config The config object
5166 Roo.bootstrap.Element = function(config){
5167 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5173 * When a element is chick
5174 * @param {Roo.bootstrap.Element} this
5175 * @param {Roo.EventObject} e
5181 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5186 preventDefault: false,
5189 getAutoCreate : function(){
5193 // cls: this.cls, double assign in parent class Component.js :: onRender
5200 initEvents: function()
5202 Roo.bootstrap.Element.superclass.initEvents.call(this);
5205 this.el.on('click', this.onClick, this);
5210 onClick : function(e)
5212 if(this.preventDefault){
5216 this.fireEvent('click', this, e);
5219 getValue : function()
5221 return this.el.dom.innerHTML;
5224 setValue : function(value)
5226 this.el.dom.innerHTML = value;
5241 * @class Roo.bootstrap.Pagination
5242 * @extends Roo.bootstrap.Component
5243 * Bootstrap Pagination class
5244 * @cfg {String} size xs | sm | md | lg
5245 * @cfg {Boolean} inverse false | true
5248 * Create a new Pagination
5249 * @param {Object} config The config object
5252 Roo.bootstrap.Pagination = function(config){
5253 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5256 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5262 getAutoCreate : function(){
5268 cfg.cls += ' inverse';
5274 cfg.cls += " " + this.cls;
5292 * @class Roo.bootstrap.PaginationItem
5293 * @extends Roo.bootstrap.Component
5294 * Bootstrap PaginationItem class
5295 * @cfg {String} html text
5296 * @cfg {String} href the link
5297 * @cfg {Boolean} preventDefault (true | false) default true
5298 * @cfg {Boolean} active (true | false) default false
5299 * @cfg {Boolean} disabled default false
5303 * Create a new PaginationItem
5304 * @param {Object} config The config object
5308 Roo.bootstrap.PaginationItem = function(config){
5309 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5314 * The raw click event for the entire grid.
5315 * @param {Roo.EventObject} e
5321 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5325 preventDefault: true,
5330 getAutoCreate : function(){
5336 href : this.href ? this.href : '#',
5337 html : this.html ? this.html : ''
5347 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5351 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5357 initEvents: function() {
5359 this.el.on('click', this.onClick, this);
5362 onClick : function(e)
5364 Roo.log('PaginationItem on click ');
5365 if(this.preventDefault){
5373 this.fireEvent('click', this, e);
5389 * @class Roo.bootstrap.Slider
5390 * @extends Roo.bootstrap.Component
5391 * Bootstrap Slider class
5394 * Create a new Slider
5395 * @param {Object} config The config object
5398 Roo.bootstrap.Slider = function(config){
5399 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5402 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5404 getAutoCreate : function(){
5408 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5412 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5424 * Ext JS Library 1.1.1
5425 * Copyright(c) 2006-2007, Ext JS, LLC.
5427 * Originally Released Under LGPL - original licence link has changed is not relivant.
5430 * <script type="text/javascript">
5435 * @class Roo.grid.ColumnModel
5436 * @extends Roo.util.Observable
5437 * This is the default implementation of a ColumnModel used by the Grid. It defines
5438 * the columns in the grid.
5441 var colModel = new Roo.grid.ColumnModel([
5442 {header: "Ticker", width: 60, sortable: true, locked: true},
5443 {header: "Company Name", width: 150, sortable: true},
5444 {header: "Market Cap.", width: 100, sortable: true},
5445 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5446 {header: "Employees", width: 100, sortable: true, resizable: false}
5451 * The config options listed for this class are options which may appear in each
5452 * individual column definition.
5453 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5455 * @param {Object} config An Array of column config objects. See this class's
5456 * config objects for details.
5458 Roo.grid.ColumnModel = function(config){
5460 * The config passed into the constructor
5462 this.config = config;
5465 // if no id, create one
5466 // if the column does not have a dataIndex mapping,
5467 // map it to the order it is in the config
5468 for(var i = 0, len = config.length; i < len; i++){
5470 if(typeof c.dataIndex == "undefined"){
5473 if(typeof c.renderer == "string"){
5474 c.renderer = Roo.util.Format[c.renderer];
5476 if(typeof c.id == "undefined"){
5479 if(c.editor && c.editor.xtype){
5480 c.editor = Roo.factory(c.editor, Roo.grid);
5482 if(c.editor && c.editor.isFormField){
5483 c.editor = new Roo.grid.GridEditor(c.editor);
5485 this.lookup[c.id] = c;
5489 * The width of columns which have no width specified (defaults to 100)
5492 this.defaultWidth = 100;
5495 * Default sortable of columns which have no sortable specified (defaults to false)
5498 this.defaultSortable = false;
5502 * @event widthchange
5503 * Fires when the width of a column changes.
5504 * @param {ColumnModel} this
5505 * @param {Number} columnIndex The column index
5506 * @param {Number} newWidth The new width
5508 "widthchange": true,
5510 * @event headerchange
5511 * Fires when the text of a header changes.
5512 * @param {ColumnModel} this
5513 * @param {Number} columnIndex The column index
5514 * @param {Number} newText The new header text
5516 "headerchange": true,
5518 * @event hiddenchange
5519 * Fires when a column is hidden or "unhidden".
5520 * @param {ColumnModel} this
5521 * @param {Number} columnIndex The column index
5522 * @param {Boolean} hidden true if hidden, false otherwise
5524 "hiddenchange": true,
5526 * @event columnmoved
5527 * Fires when a column is moved.
5528 * @param {ColumnModel} this
5529 * @param {Number} oldIndex
5530 * @param {Number} newIndex
5532 "columnmoved" : true,
5534 * @event columlockchange
5535 * Fires when a column's locked state is changed
5536 * @param {ColumnModel} this
5537 * @param {Number} colIndex
5538 * @param {Boolean} locked true if locked
5540 "columnlockchange" : true
5542 Roo.grid.ColumnModel.superclass.constructor.call(this);
5544 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5546 * @cfg {String} header The header text to display in the Grid view.
5549 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5550 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5551 * specified, the column's index is used as an index into the Record's data Array.
5554 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5555 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5558 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5559 * Defaults to the value of the {@link #defaultSortable} property.
5560 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5563 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5566 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5569 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5572 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5575 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5576 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5577 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5578 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5581 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5584 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5587 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5590 * @cfg {String} cursor (Optional)
5593 * @cfg {String} tooltip (Optional)
5596 * @cfg {Number} xs (Optional)
5599 * @cfg {Number} sm (Optional)
5602 * @cfg {Number} md (Optional)
5605 * @cfg {Number} lg (Optional)
5608 * Returns the id of the column at the specified index.
5609 * @param {Number} index The column index
5610 * @return {String} the id
5612 getColumnId : function(index){
5613 return this.config[index].id;
5617 * Returns the column for a specified id.
5618 * @param {String} id The column id
5619 * @return {Object} the column
5621 getColumnById : function(id){
5622 return this.lookup[id];
5627 * Returns the column for a specified dataIndex.
5628 * @param {String} dataIndex The column dataIndex
5629 * @return {Object|Boolean} the column or false if not found
5631 getColumnByDataIndex: function(dataIndex){
5632 var index = this.findColumnIndex(dataIndex);
5633 return index > -1 ? this.config[index] : false;
5637 * Returns the index for a specified column id.
5638 * @param {String} id The column id
5639 * @return {Number} the index, or -1 if not found
5641 getIndexById : function(id){
5642 for(var i = 0, len = this.config.length; i < len; i++){
5643 if(this.config[i].id == id){
5651 * Returns the index for a specified column dataIndex.
5652 * @param {String} dataIndex The column dataIndex
5653 * @return {Number} the index, or -1 if not found
5656 findColumnIndex : function(dataIndex){
5657 for(var i = 0, len = this.config.length; i < len; i++){
5658 if(this.config[i].dataIndex == dataIndex){
5666 moveColumn : function(oldIndex, newIndex){
5667 var c = this.config[oldIndex];
5668 this.config.splice(oldIndex, 1);
5669 this.config.splice(newIndex, 0, c);
5670 this.dataMap = null;
5671 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5674 isLocked : function(colIndex){
5675 return this.config[colIndex].locked === true;
5678 setLocked : function(colIndex, value, suppressEvent){
5679 if(this.isLocked(colIndex) == value){
5682 this.config[colIndex].locked = value;
5684 this.fireEvent("columnlockchange", this, colIndex, value);
5688 getTotalLockedWidth : function(){
5690 for(var i = 0; i < this.config.length; i++){
5691 if(this.isLocked(i) && !this.isHidden(i)){
5692 this.totalWidth += this.getColumnWidth(i);
5698 getLockedCount : function(){
5699 for(var i = 0, len = this.config.length; i < len; i++){
5700 if(!this.isLocked(i)){
5705 return this.config.length;
5709 * Returns the number of columns.
5712 getColumnCount : function(visibleOnly){
5713 if(visibleOnly === true){
5715 for(var i = 0, len = this.config.length; i < len; i++){
5716 if(!this.isHidden(i)){
5722 return this.config.length;
5726 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5727 * @param {Function} fn
5728 * @param {Object} scope (optional)
5729 * @return {Array} result
5731 getColumnsBy : function(fn, scope){
5733 for(var i = 0, len = this.config.length; i < len; i++){
5734 var c = this.config[i];
5735 if(fn.call(scope||this, c, i) === true){
5743 * Returns true if the specified column is sortable.
5744 * @param {Number} col The column index
5747 isSortable : function(col){
5748 if(typeof this.config[col].sortable == "undefined"){
5749 return this.defaultSortable;
5751 return this.config[col].sortable;
5755 * Returns the rendering (formatting) function defined for the column.
5756 * @param {Number} col The column index.
5757 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5759 getRenderer : function(col){
5760 if(!this.config[col].renderer){
5761 return Roo.grid.ColumnModel.defaultRenderer;
5763 return this.config[col].renderer;
5767 * Sets the rendering (formatting) function for a column.
5768 * @param {Number} col The column index
5769 * @param {Function} fn The function to use to process the cell's raw data
5770 * to return HTML markup for the grid view. The render function is called with
5771 * the following parameters:<ul>
5772 * <li>Data value.</li>
5773 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5774 * <li>css A CSS style string to apply to the table cell.</li>
5775 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5776 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5777 * <li>Row index</li>
5778 * <li>Column index</li>
5779 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5781 setRenderer : function(col, fn){
5782 this.config[col].renderer = fn;
5786 * Returns the width for the specified column.
5787 * @param {Number} col The column index
5790 getColumnWidth : function(col){
5791 return this.config[col].width * 1 || this.defaultWidth;
5795 * Sets the width for a column.
5796 * @param {Number} col The column index
5797 * @param {Number} width The new width
5799 setColumnWidth : function(col, width, suppressEvent){
5800 this.config[col].width = width;
5801 this.totalWidth = null;
5803 this.fireEvent("widthchange", this, col, width);
5808 * Returns the total width of all columns.
5809 * @param {Boolean} includeHidden True to include hidden column widths
5812 getTotalWidth : function(includeHidden){
5813 if(!this.totalWidth){
5814 this.totalWidth = 0;
5815 for(var i = 0, len = this.config.length; i < len; i++){
5816 if(includeHidden || !this.isHidden(i)){
5817 this.totalWidth += this.getColumnWidth(i);
5821 return this.totalWidth;
5825 * Returns the header for the specified column.
5826 * @param {Number} col The column index
5829 getColumnHeader : function(col){
5830 return this.config[col].header;
5834 * Sets the header for a column.
5835 * @param {Number} col The column index
5836 * @param {String} header The new header
5838 setColumnHeader : function(col, header){
5839 this.config[col].header = header;
5840 this.fireEvent("headerchange", this, col, header);
5844 * Returns the tooltip for the specified column.
5845 * @param {Number} col The column index
5848 getColumnTooltip : function(col){
5849 return this.config[col].tooltip;
5852 * Sets the tooltip for a column.
5853 * @param {Number} col The column index
5854 * @param {String} tooltip The new tooltip
5856 setColumnTooltip : function(col, tooltip){
5857 this.config[col].tooltip = tooltip;
5861 * Returns the dataIndex for the specified column.
5862 * @param {Number} col The column index
5865 getDataIndex : function(col){
5866 return this.config[col].dataIndex;
5870 * Sets the dataIndex for a column.
5871 * @param {Number} col The column index
5872 * @param {Number} dataIndex The new dataIndex
5874 setDataIndex : function(col, dataIndex){
5875 this.config[col].dataIndex = dataIndex;
5881 * Returns true if the cell is editable.
5882 * @param {Number} colIndex The column index
5883 * @param {Number} rowIndex The row index - this is nto actually used..?
5886 isCellEditable : function(colIndex, rowIndex){
5887 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5891 * Returns the editor defined for the cell/column.
5892 * return false or null to disable editing.
5893 * @param {Number} colIndex The column index
5894 * @param {Number} rowIndex The row index
5897 getCellEditor : function(colIndex, rowIndex){
5898 return this.config[colIndex].editor;
5902 * Sets if a column is editable.
5903 * @param {Number} col The column index
5904 * @param {Boolean} editable True if the column is editable
5906 setEditable : function(col, editable){
5907 this.config[col].editable = editable;
5912 * Returns true if the column is hidden.
5913 * @param {Number} colIndex The column index
5916 isHidden : function(colIndex){
5917 return this.config[colIndex].hidden;
5922 * Returns true if the column width cannot be changed
5924 isFixed : function(colIndex){
5925 return this.config[colIndex].fixed;
5929 * Returns true if the column can be resized
5932 isResizable : function(colIndex){
5933 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5936 * Sets if a column is hidden.
5937 * @param {Number} colIndex The column index
5938 * @param {Boolean} hidden True if the column is hidden
5940 setHidden : function(colIndex, hidden){
5941 this.config[colIndex].hidden = hidden;
5942 this.totalWidth = null;
5943 this.fireEvent("hiddenchange", this, colIndex, hidden);
5947 * Sets the editor for a column.
5948 * @param {Number} col The column index
5949 * @param {Object} editor The editor object
5951 setEditor : function(col, editor){
5952 this.config[col].editor = editor;
5956 Roo.grid.ColumnModel.defaultRenderer = function(value)
5958 if(typeof value == "object") {
5961 if(typeof value == "string" && value.length < 1){
5965 return String.format("{0}", value);
5968 // Alias for backwards compatibility
5969 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5972 * Ext JS Library 1.1.1
5973 * Copyright(c) 2006-2007, Ext JS, LLC.
5975 * Originally Released Under LGPL - original licence link has changed is not relivant.
5978 * <script type="text/javascript">
5982 * @class Roo.LoadMask
5983 * A simple utility class for generically masking elements while loading data. If the element being masked has
5984 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5985 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5986 * element's UpdateManager load indicator and will be destroyed after the initial load.
5988 * Create a new LoadMask
5989 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5990 * @param {Object} config The config object
5992 Roo.LoadMask = function(el, config){
5993 this.el = Roo.get(el);
5994 Roo.apply(this, config);
5996 this.store.on('beforeload', this.onBeforeLoad, this);
5997 this.store.on('load', this.onLoad, this);
5998 this.store.on('loadexception', this.onLoadException, this);
5999 this.removeMask = false;
6001 var um = this.el.getUpdateManager();
6002 um.showLoadIndicator = false; // disable the default indicator
6003 um.on('beforeupdate', this.onBeforeLoad, this);
6004 um.on('update', this.onLoad, this);
6005 um.on('failure', this.onLoad, this);
6006 this.removeMask = true;
6010 Roo.LoadMask.prototype = {
6012 * @cfg {Boolean} removeMask
6013 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6014 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6018 * The text to display in a centered loading message box (defaults to 'Loading...')
6022 * @cfg {String} msgCls
6023 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6025 msgCls : 'x-mask-loading',
6028 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6034 * Disables the mask to prevent it from being displayed
6036 disable : function(){
6037 this.disabled = true;
6041 * Enables the mask so that it can be displayed
6043 enable : function(){
6044 this.disabled = false;
6047 onLoadException : function()
6051 if (typeof(arguments[3]) != 'undefined') {
6052 Roo.MessageBox.alert("Error loading",arguments[3]);
6056 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6057 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6064 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6069 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6073 onBeforeLoad : function(){
6075 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6080 destroy : function(){
6082 this.store.un('beforeload', this.onBeforeLoad, this);
6083 this.store.un('load', this.onLoad, this);
6084 this.store.un('loadexception', this.onLoadException, this);
6086 var um = this.el.getUpdateManager();
6087 um.un('beforeupdate', this.onBeforeLoad, this);
6088 um.un('update', this.onLoad, this);
6089 um.un('failure', this.onLoad, this);
6100 * @class Roo.bootstrap.Table
6101 * @extends Roo.bootstrap.Component
6102 * Bootstrap Table class
6103 * @cfg {String} cls table class
6104 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6105 * @cfg {String} bgcolor Specifies the background color for a table
6106 * @cfg {Number} border Specifies whether the table cells should have borders or not
6107 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6108 * @cfg {Number} cellspacing Specifies the space between cells
6109 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6110 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6111 * @cfg {String} sortable Specifies that the table should be sortable
6112 * @cfg {String} summary Specifies a summary of the content of a table
6113 * @cfg {Number} width Specifies the width of a table
6114 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6116 * @cfg {boolean} striped Should the rows be alternative striped
6117 * @cfg {boolean} bordered Add borders to the table
6118 * @cfg {boolean} hover Add hover highlighting
6119 * @cfg {boolean} condensed Format condensed
6120 * @cfg {boolean} responsive Format condensed
6121 * @cfg {Boolean} loadMask (true|false) default false
6122 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6123 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6124 * @cfg {Boolean} rowSelection (true|false) default false
6125 * @cfg {Boolean} cellSelection (true|false) default false
6126 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6127 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6128 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6129 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6133 * Create a new Table
6134 * @param {Object} config The config object
6137 Roo.bootstrap.Table = function(config){
6138 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6143 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6144 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6145 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6146 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6148 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6150 this.sm.grid = this;
6151 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6152 this.sm = this.selModel;
6153 this.sm.xmodule = this.xmodule || false;
6156 if (this.cm && typeof(this.cm.config) == 'undefined') {
6157 this.colModel = new Roo.grid.ColumnModel(this.cm);
6158 this.cm = this.colModel;
6159 this.cm.xmodule = this.xmodule || false;
6162 this.store= Roo.factory(this.store, Roo.data);
6163 this.ds = this.store;
6164 this.ds.xmodule = this.xmodule || false;
6167 if (this.footer && this.store) {
6168 this.footer.dataSource = this.ds;
6169 this.footer = Roo.factory(this.footer);
6176 * Fires when a cell is clicked
6177 * @param {Roo.bootstrap.Table} this
6178 * @param {Roo.Element} el
6179 * @param {Number} rowIndex
6180 * @param {Number} columnIndex
6181 * @param {Roo.EventObject} e
6185 * @event celldblclick
6186 * Fires when a cell is double clicked
6187 * @param {Roo.bootstrap.Table} this
6188 * @param {Roo.Element} el
6189 * @param {Number} rowIndex
6190 * @param {Number} columnIndex
6191 * @param {Roo.EventObject} e
6193 "celldblclick" : true,
6196 * Fires when a row is clicked
6197 * @param {Roo.bootstrap.Table} this
6198 * @param {Roo.Element} el
6199 * @param {Number} rowIndex
6200 * @param {Roo.EventObject} e
6204 * @event rowdblclick
6205 * Fires when a row is double clicked
6206 * @param {Roo.bootstrap.Table} this
6207 * @param {Roo.Element} el
6208 * @param {Number} rowIndex
6209 * @param {Roo.EventObject} e
6211 "rowdblclick" : true,
6214 * Fires when a mouseover occur
6215 * @param {Roo.bootstrap.Table} this
6216 * @param {Roo.Element} el
6217 * @param {Number} rowIndex
6218 * @param {Number} columnIndex
6219 * @param {Roo.EventObject} e
6224 * Fires when a mouseout occur
6225 * @param {Roo.bootstrap.Table} this
6226 * @param {Roo.Element} el
6227 * @param {Number} rowIndex
6228 * @param {Number} columnIndex
6229 * @param {Roo.EventObject} e
6234 * Fires when a row is rendered, so you can change add a style to it.
6235 * @param {Roo.bootstrap.Table} this
6236 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6240 * @event rowsrendered
6241 * Fires when all the rows have been rendered
6242 * @param {Roo.bootstrap.Table} this
6244 'rowsrendered' : true,
6246 * @event contextmenu
6247 * The raw contextmenu event for the entire grid.
6248 * @param {Roo.EventObject} e
6250 "contextmenu" : true,
6252 * @event rowcontextmenu
6253 * Fires when a row is right clicked
6254 * @param {Roo.bootstrap.Table} this
6255 * @param {Number} rowIndex
6256 * @param {Roo.EventObject} e
6258 "rowcontextmenu" : true,
6260 * @event cellcontextmenu
6261 * Fires when a cell is right clicked
6262 * @param {Roo.bootstrap.Table} this
6263 * @param {Number} rowIndex
6264 * @param {Number} cellIndex
6265 * @param {Roo.EventObject} e
6267 "cellcontextmenu" : true,
6269 * @event headercontextmenu
6270 * Fires when a header is right clicked
6271 * @param {Roo.bootstrap.Table} this
6272 * @param {Number} columnIndex
6273 * @param {Roo.EventObject} e
6275 "headercontextmenu" : true
6279 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6305 rowSelection : false,
6306 cellSelection : false,
6309 // Roo.Element - the tbody
6311 // Roo.Element - thead element
6314 container: false, // used by gridpanel...
6320 auto_hide_footer : false,
6322 getAutoCreate : function()
6324 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6331 if (this.scrollBody) {
6332 cfg.cls += ' table-body-fixed';
6335 cfg.cls += ' table-striped';
6339 cfg.cls += ' table-hover';
6341 if (this.bordered) {
6342 cfg.cls += ' table-bordered';
6344 if (this.condensed) {
6345 cfg.cls += ' table-condensed';
6347 if (this.responsive) {
6348 cfg.cls += ' table-responsive';
6352 cfg.cls+= ' ' +this.cls;
6355 // this lot should be simplifed...
6368 ].forEach(function(k) {
6376 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6379 if(this.store || this.cm){
6380 if(this.headerShow){
6381 cfg.cn.push(this.renderHeader());
6384 cfg.cn.push(this.renderBody());
6386 if(this.footerShow){
6387 cfg.cn.push(this.renderFooter());
6389 // where does this come from?
6390 //cfg.cls+= ' TableGrid';
6393 return { cn : [ cfg ] };
6396 initEvents : function()
6398 if(!this.store || !this.cm){
6401 if (this.selModel) {
6402 this.selModel.initEvents();
6406 //Roo.log('initEvents with ds!!!!');
6408 this.mainBody = this.el.select('tbody', true).first();
6409 this.mainHead = this.el.select('thead', true).first();
6410 this.mainFoot = this.el.select('tfoot', true).first();
6416 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417 e.on('click', _this.sort, _this);
6420 this.mainBody.on("click", this.onClick, this);
6421 this.mainBody.on("dblclick", this.onDblClick, this);
6423 // why is this done????? = it breaks dialogs??
6424 //this.parent().el.setStyle('position', 'relative');
6428 this.footer.parentId = this.id;
6429 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6432 this.el.select('tfoot tr td').first().addClass('hide');
6437 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6440 this.store.on('load', this.onLoad, this);
6441 this.store.on('beforeload', this.onBeforeLoad, this);
6442 this.store.on('update', this.onUpdate, this);
6443 this.store.on('add', this.onAdd, this);
6444 this.store.on("clear", this.clear, this);
6446 this.el.on("contextmenu", this.onContextMenu, this);
6448 this.mainBody.on('scroll', this.onBodyScroll, this);
6450 this.cm.on("headerchange", this.onHeaderChange, this);
6452 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6456 onContextMenu : function(e, t)
6458 this.processEvent("contextmenu", e);
6461 processEvent : function(name, e)
6463 if (name != 'touchstart' ) {
6464 this.fireEvent(name, e);
6467 var t = e.getTarget();
6469 var cell = Roo.get(t);
6475 if(cell.findParent('tfoot', false, true)){
6479 if(cell.findParent('thead', false, true)){
6481 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6482 cell = Roo.get(t).findParent('th', false, true);
6484 Roo.log("failed to find th in thead?");
6485 Roo.log(e.getTarget());
6490 var cellIndex = cell.dom.cellIndex;
6492 var ename = name == 'touchstart' ? 'click' : name;
6493 this.fireEvent("header" + ename, this, cellIndex, e);
6498 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6499 cell = Roo.get(t).findParent('td', false, true);
6501 Roo.log("failed to find th in tbody?");
6502 Roo.log(e.getTarget());
6507 var row = cell.findParent('tr', false, true);
6508 var cellIndex = cell.dom.cellIndex;
6509 var rowIndex = row.dom.rowIndex - 1;
6513 this.fireEvent("row" + name, this, rowIndex, e);
6517 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6523 onMouseover : function(e, el)
6525 var cell = Roo.get(el);
6531 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6532 cell = cell.findParent('td', false, true);
6535 var row = cell.findParent('tr', false, true);
6536 var cellIndex = cell.dom.cellIndex;
6537 var rowIndex = row.dom.rowIndex - 1; // start from 0
6539 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6543 onMouseout : function(e, el)
6545 var cell = Roo.get(el);
6551 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6552 cell = cell.findParent('td', false, true);
6555 var row = cell.findParent('tr', false, true);
6556 var cellIndex = cell.dom.cellIndex;
6557 var rowIndex = row.dom.rowIndex - 1; // start from 0
6559 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6563 onClick : function(e, el)
6565 var cell = Roo.get(el);
6567 if(!cell || (!this.cellSelection && !this.rowSelection)){
6571 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6572 cell = cell.findParent('td', false, true);
6575 if(!cell || typeof(cell) == 'undefined'){
6579 var row = cell.findParent('tr', false, true);
6581 if(!row || typeof(row) == 'undefined'){
6585 var cellIndex = cell.dom.cellIndex;
6586 var rowIndex = this.getRowIndex(row);
6588 // why??? - should these not be based on SelectionModel?
6589 if(this.cellSelection){
6590 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6593 if(this.rowSelection){
6594 this.fireEvent('rowclick', this, row, rowIndex, e);
6600 onDblClick : function(e,el)
6602 var cell = Roo.get(el);
6604 if(!cell || (!this.cellSelection && !this.rowSelection)){
6608 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6609 cell = cell.findParent('td', false, true);
6612 if(!cell || typeof(cell) == 'undefined'){
6616 var row = cell.findParent('tr', false, true);
6618 if(!row || typeof(row) == 'undefined'){
6622 var cellIndex = cell.dom.cellIndex;
6623 var rowIndex = this.getRowIndex(row);
6625 if(this.cellSelection){
6626 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6629 if(this.rowSelection){
6630 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6634 sort : function(e,el)
6636 var col = Roo.get(el);
6638 if(!col.hasClass('sortable')){
6642 var sort = col.attr('sort');
6645 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6649 this.store.sortInfo = {field : sort, direction : dir};
6652 Roo.log("calling footer first");
6653 this.footer.onClick('first');
6656 this.store.load({ params : { start : 0 } });
6660 renderHeader : function()
6668 this.totalWidth = 0;
6670 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6672 var config = cm.config[i];
6676 cls : 'x-hcol-' + i,
6678 html: cm.getColumnHeader(i)
6683 if(typeof(config.sortable) != 'undefined' && config.sortable){
6685 c.html = '<i class="glyphicon"></i>' + c.html;
6688 if(typeof(config.lgHeader) != 'undefined'){
6689 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6692 if(typeof(config.mdHeader) != 'undefined'){
6693 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6696 if(typeof(config.smHeader) != 'undefined'){
6697 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6700 if(typeof(config.xsHeader) != 'undefined'){
6701 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6708 if(typeof(config.tooltip) != 'undefined'){
6709 c.tooltip = config.tooltip;
6712 if(typeof(config.colspan) != 'undefined'){
6713 c.colspan = config.colspan;
6716 if(typeof(config.hidden) != 'undefined' && config.hidden){
6717 c.style += ' display:none;';
6720 if(typeof(config.dataIndex) != 'undefined'){
6721 c.sort = config.dataIndex;
6726 if(typeof(config.align) != 'undefined' && config.align.length){
6727 c.style += ' text-align:' + config.align + ';';
6730 if(typeof(config.width) != 'undefined'){
6731 c.style += ' width:' + config.width + 'px;';
6732 this.totalWidth += config.width;
6734 this.totalWidth += 100; // assume minimum of 100 per column?
6737 if(typeof(config.cls) != 'undefined'){
6738 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6741 ['xs','sm','md','lg'].map(function(size){
6743 if(typeof(config[size]) == 'undefined'){
6747 if (!config[size]) { // 0 = hidden
6748 c.cls += ' hidden-' + size;
6752 c.cls += ' col-' + size + '-' + config[size];
6762 renderBody : function()
6772 colspan : this.cm.getColumnCount()
6782 renderFooter : function()
6792 colspan : this.cm.getColumnCount()
6806 // Roo.log('ds onload');
6811 var ds = this.store;
6813 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6814 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6815 if (_this.store.sortInfo) {
6817 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6818 e.select('i', true).addClass(['glyphicon-arrow-up']);
6821 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6822 e.select('i', true).addClass(['glyphicon-arrow-down']);
6827 var tbody = this.mainBody;
6829 if(ds.getCount() > 0){
6830 ds.data.each(function(d,rowIndex){
6831 var row = this.renderRow(cm, ds, rowIndex);
6833 tbody.createChild(row);
6837 if(row.cellObjects.length){
6838 Roo.each(row.cellObjects, function(r){
6839 _this.renderCellObject(r);
6846 var tfoot = this.el.select('tfoot', true).first();
6848 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6850 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6852 var total = this.ds.getTotalCount();
6854 if(this.footer.pageSize < total){
6855 this.mainFoot.show();
6859 Roo.each(this.el.select('tbody td', true).elements, function(e){
6860 e.on('mouseover', _this.onMouseover, _this);
6863 Roo.each(this.el.select('tbody td', true).elements, function(e){
6864 e.on('mouseout', _this.onMouseout, _this);
6866 this.fireEvent('rowsrendered', this);
6872 onUpdate : function(ds,record)
6874 this.refreshRow(record);
6878 onRemove : function(ds, record, index, isUpdate){
6879 if(isUpdate !== true){
6880 this.fireEvent("beforerowremoved", this, index, record);
6882 var bt = this.mainBody.dom;
6884 var rows = this.el.select('tbody > tr', true).elements;
6886 if(typeof(rows[index]) != 'undefined'){
6887 bt.removeChild(rows[index].dom);
6890 // if(bt.rows[index]){
6891 // bt.removeChild(bt.rows[index]);
6894 if(isUpdate !== true){
6895 //this.stripeRows(index);
6896 //this.syncRowHeights(index, index);
6898 this.fireEvent("rowremoved", this, index, record);
6902 onAdd : function(ds, records, rowIndex)
6904 //Roo.log('on Add called');
6905 // - note this does not handle multiple adding very well..
6906 var bt = this.mainBody.dom;
6907 for (var i =0 ; i < records.length;i++) {
6908 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6909 //Roo.log(records[i]);
6910 //Roo.log(this.store.getAt(rowIndex+i));
6911 this.insertRow(this.store, rowIndex + i, false);
6918 refreshRow : function(record){
6919 var ds = this.store, index;
6920 if(typeof record == 'number'){
6922 record = ds.getAt(index);
6924 index = ds.indexOf(record);
6926 this.insertRow(ds, index, true);
6928 this.onRemove(ds, record, index+1, true);
6930 //this.syncRowHeights(index, index);
6932 this.fireEvent("rowupdated", this, index, record);
6935 insertRow : function(dm, rowIndex, isUpdate){
6938 this.fireEvent("beforerowsinserted", this, rowIndex);
6940 //var s = this.getScrollState();
6941 var row = this.renderRow(this.cm, this.store, rowIndex);
6942 // insert before rowIndex..
6943 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6947 if(row.cellObjects.length){
6948 Roo.each(row.cellObjects, function(r){
6949 _this.renderCellObject(r);
6954 this.fireEvent("rowsinserted", this, rowIndex);
6955 //this.syncRowHeights(firstRow, lastRow);
6956 //this.stripeRows(firstRow);
6963 getRowDom : function(rowIndex)
6965 var rows = this.el.select('tbody > tr', true).elements;
6967 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6970 // returns the object tree for a tr..
6973 renderRow : function(cm, ds, rowIndex)
6975 var d = ds.getAt(rowIndex);
6979 cls : 'x-row-' + rowIndex,
6983 var cellObjects = [];
6985 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6986 var config = cm.config[i];
6988 var renderer = cm.getRenderer(i);
6992 if(typeof(renderer) !== 'undefined'){
6993 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6995 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6996 // and are rendered into the cells after the row is rendered - using the id for the element.
6998 if(typeof(value) === 'object'){
7008 rowIndex : rowIndex,
7013 this.fireEvent('rowclass', this, rowcfg);
7017 cls : rowcfg.rowClass + ' x-col-' + i,
7019 html: (typeof(value) === 'object') ? '' : value
7026 if(typeof(config.colspan) != 'undefined'){
7027 td.colspan = config.colspan;
7030 if(typeof(config.hidden) != 'undefined' && config.hidden){
7031 td.style += ' display:none;';
7034 if(typeof(config.align) != 'undefined' && config.align.length){
7035 td.style += ' text-align:' + config.align + ';';
7037 if(typeof(config.valign) != 'undefined' && config.valign.length){
7038 td.style += ' vertical-align:' + config.valign + ';';
7041 if(typeof(config.width) != 'undefined'){
7042 td.style += ' width:' + config.width + 'px;';
7045 if(typeof(config.cursor) != 'undefined'){
7046 td.style += ' cursor:' + config.cursor + ';';
7049 if(typeof(config.cls) != 'undefined'){
7050 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7053 ['xs','sm','md','lg'].map(function(size){
7055 if(typeof(config[size]) == 'undefined'){
7059 if (!config[size]) { // 0 = hidden
7060 td.cls += ' hidden-' + size;
7064 td.cls += ' col-' + size + '-' + config[size];
7072 row.cellObjects = cellObjects;
7080 onBeforeLoad : function()
7089 this.el.select('tbody', true).first().dom.innerHTML = '';
7092 * Show or hide a row.
7093 * @param {Number} rowIndex to show or hide
7094 * @param {Boolean} state hide
7096 setRowVisibility : function(rowIndex, state)
7098 var bt = this.mainBody.dom;
7100 var rows = this.el.select('tbody > tr', true).elements;
7102 if(typeof(rows[rowIndex]) == 'undefined'){
7105 rows[rowIndex].dom.style.display = state ? '' : 'none';
7109 getSelectionModel : function(){
7111 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7113 return this.selModel;
7116 * Render the Roo.bootstrap object from renderder
7118 renderCellObject : function(r)
7122 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7124 var t = r.cfg.render(r.container);
7127 Roo.each(r.cfg.cn, function(c){
7129 container: t.getChildContainer(),
7132 _this.renderCellObject(child);
7137 getRowIndex : function(row)
7141 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7152 * Returns the grid's underlying element = used by panel.Grid
7153 * @return {Element} The element
7155 getGridEl : function(){
7159 * Forces a resize - used by panel.Grid
7160 * @return {Element} The element
7162 autoSize : function()
7164 //var ctr = Roo.get(this.container.dom.parentElement);
7165 var ctr = Roo.get(this.el.dom);
7167 var thd = this.getGridEl().select('thead',true).first();
7168 var tbd = this.getGridEl().select('tbody', true).first();
7169 var tfd = this.getGridEl().select('tfoot', true).first();
7171 var cw = ctr.getWidth();
7175 tbd.setSize(ctr.getWidth(),
7176 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7178 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7181 cw = Math.max(cw, this.totalWidth);
7182 this.getGridEl().select('tr',true).setWidth(cw);
7183 // resize 'expandable coloumn?
7185 return; // we doe not have a view in this design..
7188 onBodyScroll: function()
7190 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7192 this.mainHead.setStyle({
7193 'position' : 'relative',
7194 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7200 var scrollHeight = this.mainBody.dom.scrollHeight;
7202 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7204 var height = this.mainBody.getHeight();
7206 if(scrollHeight - height == scrollTop) {
7208 var total = this.ds.getTotalCount();
7210 if(this.footer.cursor + this.footer.pageSize < total){
7212 this.footer.ds.load({
7214 start : this.footer.cursor + this.footer.pageSize,
7215 limit : this.footer.pageSize
7225 onHeaderChange : function()
7227 var header = this.renderHeader();
7228 var table = this.el.select('table', true).first();
7230 this.mainHead.remove();
7231 this.mainHead = table.createChild(header, this.mainBody, false);
7234 onHiddenChange : function(colModel, colIndex, hidden)
7236 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7237 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7239 this.CSS.updateRule(thSelector, "display", "");
7240 this.CSS.updateRule(tdSelector, "display", "");
7243 this.CSS.updateRule(thSelector, "display", "none");
7244 this.CSS.updateRule(tdSelector, "display", "none");
7247 this.onHeaderChange();
7251 setColumnWidth: function(col_index, width)
7253 // width = "md-2 xs-2..."
7254 if(!this.colModel.config[col_index]) {
7258 var w = width.split(" ");
7260 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7262 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7265 for(var j = 0; j < w.length; j++) {
7271 var size_cls = w[j].split("-");
7273 if(!Number.isInteger(size_cls[1] * 1)) {
7277 if(!this.colModel.config[col_index][size_cls[0]]) {
7281 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7285 h_row[0].classList.replace(
7286 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7287 "col-"+size_cls[0]+"-"+size_cls[1]
7290 for(var i = 0; i < rows.length; i++) {
7292 var size_cls = w[j].split("-");
7294 if(!Number.isInteger(size_cls[1] * 1)) {
7298 if(!this.colModel.config[col_index][size_cls[0]]) {
7302 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7306 rows[i].classList.replace(
7307 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7308 "col-"+size_cls[0]+"-"+size_cls[1]
7312 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7327 * @class Roo.bootstrap.TableCell
7328 * @extends Roo.bootstrap.Component
7329 * Bootstrap TableCell class
7330 * @cfg {String} html cell contain text
7331 * @cfg {String} cls cell class
7332 * @cfg {String} tag cell tag (td|th) default td
7333 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7334 * @cfg {String} align Aligns the content in a cell
7335 * @cfg {String} axis Categorizes cells
7336 * @cfg {String} bgcolor Specifies the background color of a cell
7337 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7338 * @cfg {Number} colspan Specifies the number of columns a cell should span
7339 * @cfg {String} headers Specifies one or more header cells a cell is related to
7340 * @cfg {Number} height Sets the height of a cell
7341 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7342 * @cfg {Number} rowspan Sets the number of rows a cell should span
7343 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7344 * @cfg {String} valign Vertical aligns the content in a cell
7345 * @cfg {Number} width Specifies the width of a cell
7348 * Create a new TableCell
7349 * @param {Object} config The config object
7352 Roo.bootstrap.TableCell = function(config){
7353 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7356 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7376 getAutoCreate : function(){
7377 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7397 cfg.align=this.align
7403 cfg.bgcolor=this.bgcolor
7406 cfg.charoff=this.charoff
7409 cfg.colspan=this.colspan
7412 cfg.headers=this.headers
7415 cfg.height=this.height
7418 cfg.nowrap=this.nowrap
7421 cfg.rowspan=this.rowspan
7424 cfg.scope=this.scope
7427 cfg.valign=this.valign
7430 cfg.width=this.width
7449 * @class Roo.bootstrap.TableRow
7450 * @extends Roo.bootstrap.Component
7451 * Bootstrap TableRow class
7452 * @cfg {String} cls row class
7453 * @cfg {String} align Aligns the content in a table row
7454 * @cfg {String} bgcolor Specifies a background color for a table row
7455 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7456 * @cfg {String} valign Vertical aligns the content in a table row
7459 * Create a new TableRow
7460 * @param {Object} config The config object
7463 Roo.bootstrap.TableRow = function(config){
7464 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7467 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7475 getAutoCreate : function(){
7476 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7486 cfg.align = this.align;
7489 cfg.bgcolor = this.bgcolor;
7492 cfg.charoff = this.charoff;
7495 cfg.valign = this.valign;
7513 * @class Roo.bootstrap.TableBody
7514 * @extends Roo.bootstrap.Component
7515 * Bootstrap TableBody class
7516 * @cfg {String} cls element class
7517 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7518 * @cfg {String} align Aligns the content inside the element
7519 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7520 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7523 * Create a new TableBody
7524 * @param {Object} config The config object
7527 Roo.bootstrap.TableBody = function(config){
7528 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7531 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7539 getAutoCreate : function(){
7540 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7554 cfg.align = this.align;
7557 cfg.charoff = this.charoff;
7560 cfg.valign = this.valign;
7567 // initEvents : function()
7574 // this.store = Roo.factory(this.store, Roo.data);
7575 // this.store.on('load', this.onLoad, this);
7577 // this.store.load();
7581 // onLoad: function ()
7583 // this.fireEvent('load', this);
7593 * Ext JS Library 1.1.1
7594 * Copyright(c) 2006-2007, Ext JS, LLC.
7596 * Originally Released Under LGPL - original licence link has changed is not relivant.
7599 * <script type="text/javascript">
7602 // as we use this in bootstrap.
7603 Roo.namespace('Roo.form');
7605 * @class Roo.form.Action
7606 * Internal Class used to handle form actions
7608 * @param {Roo.form.BasicForm} el The form element or its id
7609 * @param {Object} config Configuration options
7614 // define the action interface
7615 Roo.form.Action = function(form, options){
7617 this.options = options || {};
7620 * Client Validation Failed
7623 Roo.form.Action.CLIENT_INVALID = 'client';
7625 * Server Validation Failed
7628 Roo.form.Action.SERVER_INVALID = 'server';
7630 * Connect to Server Failed
7633 Roo.form.Action.CONNECT_FAILURE = 'connect';
7635 * Reading Data from Server Failed
7638 Roo.form.Action.LOAD_FAILURE = 'load';
7640 Roo.form.Action.prototype = {
7642 failureType : undefined,
7643 response : undefined,
7647 run : function(options){
7652 success : function(response){
7657 handleResponse : function(response){
7661 // default connection failure
7662 failure : function(response){
7664 this.response = response;
7665 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7666 this.form.afterAction(this, false);
7669 processResponse : function(response){
7670 this.response = response;
7671 if(!response.responseText){
7674 this.result = this.handleResponse(response);
7678 // utility functions used internally
7679 getUrl : function(appendParams){
7680 var url = this.options.url || this.form.url || this.form.el.dom.action;
7682 var p = this.getParams();
7684 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7690 getMethod : function(){
7691 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7694 getParams : function(){
7695 var bp = this.form.baseParams;
7696 var p = this.options.params;
7698 if(typeof p == "object"){
7699 p = Roo.urlEncode(Roo.applyIf(p, bp));
7700 }else if(typeof p == 'string' && bp){
7701 p += '&' + Roo.urlEncode(bp);
7704 p = Roo.urlEncode(bp);
7709 createCallback : function(){
7711 success: this.success,
7712 failure: this.failure,
7714 timeout: (this.form.timeout*1000),
7715 upload: this.form.fileUpload ? this.success : undefined
7720 Roo.form.Action.Submit = function(form, options){
7721 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7724 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7727 haveProgress : false,
7728 uploadComplete : false,
7730 // uploadProgress indicator.
7731 uploadProgress : function()
7733 if (!this.form.progressUrl) {
7737 if (!this.haveProgress) {
7738 Roo.MessageBox.progress("Uploading", "Uploading");
7740 if (this.uploadComplete) {
7741 Roo.MessageBox.hide();
7745 this.haveProgress = true;
7747 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7749 var c = new Roo.data.Connection();
7751 url : this.form.progressUrl,
7756 success : function(req){
7757 //console.log(data);
7761 rdata = Roo.decode(req.responseText)
7763 Roo.log("Invalid data from server..");
7767 if (!rdata || !rdata.success) {
7769 Roo.MessageBox.alert(Roo.encode(rdata));
7772 var data = rdata.data;
7774 if (this.uploadComplete) {
7775 Roo.MessageBox.hide();
7780 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7781 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7784 this.uploadProgress.defer(2000,this);
7787 failure: function(data) {
7788 Roo.log('progress url failed ');
7799 // run get Values on the form, so it syncs any secondary forms.
7800 this.form.getValues();
7802 var o = this.options;
7803 var method = this.getMethod();
7804 var isPost = method == 'POST';
7805 if(o.clientValidation === false || this.form.isValid()){
7807 if (this.form.progressUrl) {
7808 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7809 (new Date() * 1) + '' + Math.random());
7814 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7815 form:this.form.el.dom,
7816 url:this.getUrl(!isPost),
7818 params:isPost ? this.getParams() : null,
7819 isUpload: this.form.fileUpload
7822 this.uploadProgress();
7824 }else if (o.clientValidation !== false){ // client validation failed
7825 this.failureType = Roo.form.Action.CLIENT_INVALID;
7826 this.form.afterAction(this, false);
7830 success : function(response)
7832 this.uploadComplete= true;
7833 if (this.haveProgress) {
7834 Roo.MessageBox.hide();
7838 var result = this.processResponse(response);
7839 if(result === true || result.success){
7840 this.form.afterAction(this, true);
7844 this.form.markInvalid(result.errors);
7845 this.failureType = Roo.form.Action.SERVER_INVALID;
7847 this.form.afterAction(this, false);
7849 failure : function(response)
7851 this.uploadComplete= true;
7852 if (this.haveProgress) {
7853 Roo.MessageBox.hide();
7856 this.response = response;
7857 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7858 this.form.afterAction(this, false);
7861 handleResponse : function(response){
7862 if(this.form.errorReader){
7863 var rs = this.form.errorReader.read(response);
7866 for(var i = 0, len = rs.records.length; i < len; i++) {
7867 var r = rs.records[i];
7871 if(errors.length < 1){
7875 success : rs.success,
7881 ret = Roo.decode(response.responseText);
7885 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7895 Roo.form.Action.Load = function(form, options){
7896 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7897 this.reader = this.form.reader;
7900 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7905 Roo.Ajax.request(Roo.apply(
7906 this.createCallback(), {
7907 method:this.getMethod(),
7908 url:this.getUrl(false),
7909 params:this.getParams()
7913 success : function(response){
7915 var result = this.processResponse(response);
7916 if(result === true || !result.success || !result.data){
7917 this.failureType = Roo.form.Action.LOAD_FAILURE;
7918 this.form.afterAction(this, false);
7921 this.form.clearInvalid();
7922 this.form.setValues(result.data);
7923 this.form.afterAction(this, true);
7926 handleResponse : function(response){
7927 if(this.form.reader){
7928 var rs = this.form.reader.read(response);
7929 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7931 success : rs.success,
7935 return Roo.decode(response.responseText);
7939 Roo.form.Action.ACTION_TYPES = {
7940 'load' : Roo.form.Action.Load,
7941 'submit' : Roo.form.Action.Submit
7950 * @class Roo.bootstrap.Form
7951 * @extends Roo.bootstrap.Component
7952 * Bootstrap Form class
7953 * @cfg {String} method GET | POST (default POST)
7954 * @cfg {String} labelAlign top | left (default top)
7955 * @cfg {String} align left | right - for navbars
7956 * @cfg {Boolean} loadMask load mask when submit (default true)
7961 * @param {Object} config The config object
7965 Roo.bootstrap.Form = function(config){
7967 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7969 Roo.bootstrap.Form.popover.apply();
7973 * @event clientvalidation
7974 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7975 * @param {Form} this
7976 * @param {Boolean} valid true if the form has passed client-side validation
7978 clientvalidation: true,
7980 * @event beforeaction
7981 * Fires before any action is performed. Return false to cancel the action.
7982 * @param {Form} this
7983 * @param {Action} action The action to be performed
7987 * @event actionfailed
7988 * Fires when an action fails.
7989 * @param {Form} this
7990 * @param {Action} action The action that failed
7992 actionfailed : true,
7994 * @event actioncomplete
7995 * Fires when an action is completed.
7996 * @param {Form} this
7997 * @param {Action} action The action that completed
7999 actioncomplete : true
8003 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8006 * @cfg {String} method
8007 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8012 * The URL to use for form actions if one isn't supplied in the action options.
8015 * @cfg {Boolean} fileUpload
8016 * Set to true if this form is a file upload.
8020 * @cfg {Object} baseParams
8021 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8025 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8029 * @cfg {Sting} align (left|right) for navbar forms
8034 activeAction : null,
8037 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8038 * element by passing it or its id or mask the form itself by passing in true.
8041 waitMsgTarget : false,
8046 * @cfg {Boolean} errorMask (true|false) default false
8051 * @cfg {Number} maskOffset Default 100
8056 * @cfg {Boolean} maskBody
8060 getAutoCreate : function(){
8064 method : this.method || 'POST',
8065 id : this.id || Roo.id(),
8068 if (this.parent().xtype.match(/^Nav/)) {
8069 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8073 if (this.labelAlign == 'left' ) {
8074 cfg.cls += ' form-horizontal';
8080 initEvents : function()
8082 this.el.on('submit', this.onSubmit, this);
8083 // this was added as random key presses on the form where triggering form submit.
8084 this.el.on('keypress', function(e) {
8085 if (e.getCharCode() != 13) {
8088 // we might need to allow it for textareas.. and some other items.
8089 // check e.getTarget().
8091 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8095 Roo.log("keypress blocked");
8103 onSubmit : function(e){
8108 * Returns true if client-side validation on the form is successful.
8111 isValid : function(){
8112 var items = this.getItems();
8116 items.each(function(f){
8122 Roo.log('invalid field: ' + f.name);
8126 if(!target && f.el.isVisible(true)){
8132 if(this.errorMask && !valid){
8133 Roo.bootstrap.Form.popover.mask(this, target);
8140 * Returns true if any fields in this form have changed since their original load.
8143 isDirty : function(){
8145 var items = this.getItems();
8146 items.each(function(f){
8156 * Performs a predefined action (submit or load) or custom actions you define on this form.
8157 * @param {String} actionName The name of the action type
8158 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8159 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8160 * accept other config options):
8162 Property Type Description
8163 ---------------- --------------- ----------------------------------------------------------------------------------
8164 url String The url for the action (defaults to the form's url)
8165 method String The form method to use (defaults to the form's method, or POST if not defined)
8166 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8167 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8168 validate the form on the client (defaults to false)
8170 * @return {BasicForm} this
8172 doAction : function(action, options){
8173 if(typeof action == 'string'){
8174 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8176 if(this.fireEvent('beforeaction', this, action) !== false){
8177 this.beforeAction(action);
8178 action.run.defer(100, action);
8184 beforeAction : function(action){
8185 var o = action.options;
8190 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8192 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8195 // not really supported yet.. ??
8197 //if(this.waitMsgTarget === true){
8198 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8199 //}else if(this.waitMsgTarget){
8200 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8201 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8203 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8209 afterAction : function(action, success){
8210 this.activeAction = null;
8211 var o = action.options;
8216 Roo.get(document.body).unmask();
8222 //if(this.waitMsgTarget === true){
8223 // this.el.unmask();
8224 //}else if(this.waitMsgTarget){
8225 // this.waitMsgTarget.unmask();
8227 // Roo.MessageBox.updateProgress(1);
8228 // Roo.MessageBox.hide();
8235 Roo.callback(o.success, o.scope, [this, action]);
8236 this.fireEvent('actioncomplete', this, action);
8240 // failure condition..
8241 // we have a scenario where updates need confirming.
8242 // eg. if a locking scenario exists..
8243 // we look for { errors : { needs_confirm : true }} in the response.
8245 (typeof(action.result) != 'undefined') &&
8246 (typeof(action.result.errors) != 'undefined') &&
8247 (typeof(action.result.errors.needs_confirm) != 'undefined')
8250 Roo.log("not supported yet");
8253 Roo.MessageBox.confirm(
8254 "Change requires confirmation",
8255 action.result.errorMsg,
8260 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8270 Roo.callback(o.failure, o.scope, [this, action]);
8271 // show an error message if no failed handler is set..
8272 if (!this.hasListener('actionfailed')) {
8273 Roo.log("need to add dialog support");
8275 Roo.MessageBox.alert("Error",
8276 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8277 action.result.errorMsg :
8278 "Saving Failed, please check your entries or try again"
8283 this.fireEvent('actionfailed', this, action);
8288 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8289 * @param {String} id The value to search for
8292 findField : function(id){
8293 var items = this.getItems();
8294 var field = items.get(id);
8296 items.each(function(f){
8297 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8304 return field || null;
8307 * Mark fields in this form invalid in bulk.
8308 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8309 * @return {BasicForm} this
8311 markInvalid : function(errors){
8312 if(errors instanceof Array){
8313 for(var i = 0, len = errors.length; i < len; i++){
8314 var fieldError = errors[i];
8315 var f = this.findField(fieldError.id);
8317 f.markInvalid(fieldError.msg);
8323 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8324 field.markInvalid(errors[id]);
8328 //Roo.each(this.childForms || [], function (f) {
8329 // f.markInvalid(errors);
8336 * Set values for fields in this form in bulk.
8337 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8338 * @return {BasicForm} this
8340 setValues : function(values){
8341 if(values instanceof Array){ // array of objects
8342 for(var i = 0, len = values.length; i < len; i++){
8344 var f = this.findField(v.id);
8346 f.setValue(v.value);
8347 if(this.trackResetOnLoad){
8348 f.originalValue = f.getValue();
8352 }else{ // object hash
8355 if(typeof values[id] != 'function' && (field = this.findField(id))){
8357 if (field.setFromData &&
8359 field.displayField &&
8360 // combos' with local stores can
8361 // be queried via setValue()
8362 // to set their value..
8363 (field.store && !field.store.isLocal)
8367 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8368 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8369 field.setFromData(sd);
8371 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8373 field.setFromData(values);
8376 field.setValue(values[id]);
8380 if(this.trackResetOnLoad){
8381 field.originalValue = field.getValue();
8387 //Roo.each(this.childForms || [], function (f) {
8388 // f.setValues(values);
8395 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8396 * they are returned as an array.
8397 * @param {Boolean} asString
8400 getValues : function(asString){
8401 //if (this.childForms) {
8402 // copy values from the child forms
8403 // Roo.each(this.childForms, function (f) {
8404 // this.setValues(f.getValues());
8410 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8411 if(asString === true){
8414 return Roo.urlDecode(fs);
8418 * Returns the fields in this form as an object with key/value pairs.
8419 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8422 getFieldValues : function(with_hidden)
8424 var items = this.getItems();
8426 items.each(function(f){
8432 var v = f.getValue();
8434 if (f.inputType =='radio') {
8435 if (typeof(ret[f.getName()]) == 'undefined') {
8436 ret[f.getName()] = ''; // empty..
8439 if (!f.el.dom.checked) {
8447 if(f.xtype == 'MoneyField'){
8448 ret[f.currencyName] = f.getCurrency();
8451 // not sure if this supported any more..
8452 if ((typeof(v) == 'object') && f.getRawValue) {
8453 v = f.getRawValue() ; // dates..
8455 // combo boxes where name != hiddenName...
8456 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8457 ret[f.name] = f.getRawValue();
8459 ret[f.getName()] = v;
8466 * Clears all invalid messages in this form.
8467 * @return {BasicForm} this
8469 clearInvalid : function(){
8470 var items = this.getItems();
8472 items.each(function(f){
8481 * @return {BasicForm} this
8484 var items = this.getItems();
8485 items.each(function(f){
8489 Roo.each(this.childForms || [], function (f) {
8497 getItems : function()
8499 var r=new Roo.util.MixedCollection(false, function(o){
8500 return o.id || (o.id = Roo.id());
8502 var iter = function(el) {
8509 Roo.each(el.items,function(e) {
8518 hideFields : function(items)
8520 Roo.each(items, function(i){
8522 var f = this.findField(i);
8533 showFields : function(items)
8535 Roo.each(items, function(i){
8537 var f = this.findField(i);
8550 Roo.apply(Roo.bootstrap.Form, {
8577 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8578 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8579 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8580 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8583 this.maskEl.top.enableDisplayMode("block");
8584 this.maskEl.left.enableDisplayMode("block");
8585 this.maskEl.bottom.enableDisplayMode("block");
8586 this.maskEl.right.enableDisplayMode("block");
8588 this.toolTip = new Roo.bootstrap.Tooltip({
8589 cls : 'roo-form-error-popover',
8591 'left' : ['r-l', [-2,0], 'right'],
8592 'right' : ['l-r', [2,0], 'left'],
8593 'bottom' : ['tl-bl', [0,2], 'top'],
8594 'top' : [ 'bl-tl', [0,-2], 'bottom']
8598 this.toolTip.render(Roo.get(document.body));
8600 this.toolTip.el.enableDisplayMode("block");
8602 Roo.get(document.body).on('click', function(){
8606 Roo.get(document.body).on('touchstart', function(){
8610 this.isApplied = true
8613 mask : function(form, target)
8617 this.target = target;
8619 if(!this.form.errorMask || !target.el){
8623 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8625 Roo.log(scrollable);
8627 var ot = this.target.el.calcOffsetsTo(scrollable);
8629 var scrollTo = ot[1] - this.form.maskOffset;
8631 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8633 scrollable.scrollTo('top', scrollTo);
8635 var box = this.target.el.getBox();
8637 var zIndex = Roo.bootstrap.Modal.zIndex++;
8640 this.maskEl.top.setStyle('position', 'absolute');
8641 this.maskEl.top.setStyle('z-index', zIndex);
8642 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8643 this.maskEl.top.setLeft(0);
8644 this.maskEl.top.setTop(0);
8645 this.maskEl.top.show();
8647 this.maskEl.left.setStyle('position', 'absolute');
8648 this.maskEl.left.setStyle('z-index', zIndex);
8649 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8650 this.maskEl.left.setLeft(0);
8651 this.maskEl.left.setTop(box.y - this.padding);
8652 this.maskEl.left.show();
8654 this.maskEl.bottom.setStyle('position', 'absolute');
8655 this.maskEl.bottom.setStyle('z-index', zIndex);
8656 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8657 this.maskEl.bottom.setLeft(0);
8658 this.maskEl.bottom.setTop(box.bottom + this.padding);
8659 this.maskEl.bottom.show();
8661 this.maskEl.right.setStyle('position', 'absolute');
8662 this.maskEl.right.setStyle('z-index', zIndex);
8663 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8664 this.maskEl.right.setLeft(box.right + this.padding);
8665 this.maskEl.right.setTop(box.y - this.padding);
8666 this.maskEl.right.show();
8668 this.toolTip.bindEl = this.target.el;
8670 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8672 var tip = this.target.blankText;
8674 if(this.target.getValue() !== '' ) {
8676 if (this.target.invalidText.length) {
8677 tip = this.target.invalidText;
8678 } else if (this.target.regexText.length){
8679 tip = this.target.regexText;
8683 this.toolTip.show(tip);
8685 this.intervalID = window.setInterval(function() {
8686 Roo.bootstrap.Form.popover.unmask();
8689 window.onwheel = function(){ return false;};
8691 (function(){ this.isMasked = true; }).defer(500, this);
8697 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8701 this.maskEl.top.setStyle('position', 'absolute');
8702 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8703 this.maskEl.top.hide();
8705 this.maskEl.left.setStyle('position', 'absolute');
8706 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8707 this.maskEl.left.hide();
8709 this.maskEl.bottom.setStyle('position', 'absolute');
8710 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8711 this.maskEl.bottom.hide();
8713 this.maskEl.right.setStyle('position', 'absolute');
8714 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8715 this.maskEl.right.hide();
8717 this.toolTip.hide();
8719 this.toolTip.el.hide();
8721 window.onwheel = function(){ return true;};
8723 if(this.intervalID){
8724 window.clearInterval(this.intervalID);
8725 this.intervalID = false;
8728 this.isMasked = false;
8738 * Ext JS Library 1.1.1
8739 * Copyright(c) 2006-2007, Ext JS, LLC.
8741 * Originally Released Under LGPL - original licence link has changed is not relivant.
8744 * <script type="text/javascript">
8747 * @class Roo.form.VTypes
8748 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8751 Roo.form.VTypes = function(){
8752 // closure these in so they are only created once.
8753 var alpha = /^[a-zA-Z_]+$/;
8754 var alphanum = /^[a-zA-Z0-9_]+$/;
8755 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8756 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8758 // All these messages and functions are configurable
8761 * The function used to validate email addresses
8762 * @param {String} value The email address
8764 'email' : function(v){
8765 return email.test(v);
8768 * The error text to display when the email validation function returns false
8771 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8773 * The keystroke filter mask to be applied on email input
8776 'emailMask' : /[a-z0-9_\.\-@]/i,
8779 * The function used to validate URLs
8780 * @param {String} value The URL
8782 'url' : function(v){
8786 * The error text to display when the url validation function returns false
8789 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8792 * The function used to validate alpha values
8793 * @param {String} value The value
8795 'alpha' : function(v){
8796 return alpha.test(v);
8799 * The error text to display when the alpha validation function returns false
8802 'alphaText' : 'This field should only contain letters and _',
8804 * The keystroke filter mask to be applied on alpha input
8807 'alphaMask' : /[a-z_]/i,
8810 * The function used to validate alphanumeric values
8811 * @param {String} value The value
8813 'alphanum' : function(v){
8814 return alphanum.test(v);
8817 * The error text to display when the alphanumeric validation function returns false
8820 'alphanumText' : 'This field should only contain letters, numbers and _',
8822 * The keystroke filter mask to be applied on alphanumeric input
8825 'alphanumMask' : /[a-z0-9_]/i
8835 * @class Roo.bootstrap.Input
8836 * @extends Roo.bootstrap.Component
8837 * Bootstrap Input class
8838 * @cfg {Boolean} disabled is it disabled
8839 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8840 * @cfg {String} name name of the input
8841 * @cfg {string} fieldLabel - the label associated
8842 * @cfg {string} placeholder - placeholder to put in text.
8843 * @cfg {string} before - input group add on before
8844 * @cfg {string} after - input group add on after
8845 * @cfg {string} size - (lg|sm) or leave empty..
8846 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8847 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8848 * @cfg {Number} md colspan out of 12 for computer-sized screens
8849 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8850 * @cfg {string} value default value of the input
8851 * @cfg {Number} labelWidth set the width of label
8852 * @cfg {Number} labellg set the width of label (1-12)
8853 * @cfg {Number} labelmd set the width of label (1-12)
8854 * @cfg {Number} labelsm set the width of label (1-12)
8855 * @cfg {Number} labelxs set the width of label (1-12)
8856 * @cfg {String} labelAlign (top|left)
8857 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8858 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8859 * @cfg {String} indicatorpos (left|right) default left
8860 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8861 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8863 * @cfg {String} align (left|center|right) Default left
8864 * @cfg {Boolean} forceFeedback (true|false) Default false
8867 * Create a new Input
8868 * @param {Object} config The config object
8871 Roo.bootstrap.Input = function(config){
8873 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8878 * Fires when this field receives input focus.
8879 * @param {Roo.form.Field} this
8884 * Fires when this field loses input focus.
8885 * @param {Roo.form.Field} this
8890 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8891 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8892 * @param {Roo.form.Field} this
8893 * @param {Roo.EventObject} e The event object
8898 * Fires just before the field blurs if the field value has changed.
8899 * @param {Roo.form.Field} this
8900 * @param {Mixed} newValue The new value
8901 * @param {Mixed} oldValue The original value
8906 * Fires after the field has been marked as invalid.
8907 * @param {Roo.form.Field} this
8908 * @param {String} msg The validation message
8913 * Fires after the field has been validated with no errors.
8914 * @param {Roo.form.Field} this
8919 * Fires after the key up
8920 * @param {Roo.form.Field} this
8921 * @param {Roo.EventObject} e The event Object
8927 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8929 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8930 automatic validation (defaults to "keyup").
8932 validationEvent : "keyup",
8934 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8936 validateOnBlur : true,
8938 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8940 validationDelay : 250,
8942 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8944 focusClass : "x-form-focus", // not needed???
8948 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8950 invalidClass : "has-warning",
8953 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8955 validClass : "has-success",
8958 * @cfg {Boolean} hasFeedback (true|false) default true
8963 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8965 invalidFeedbackClass : "glyphicon-warning-sign",
8968 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8970 validFeedbackClass : "glyphicon-ok",
8973 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8975 selectOnFocus : false,
8978 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8982 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8987 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8989 disableKeyFilter : false,
8992 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8996 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9000 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9002 blankText : "Please complete this mandatory field",
9005 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9009 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9011 maxLength : Number.MAX_VALUE,
9013 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9015 minLengthText : "The minimum length for this field is {0}",
9017 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9019 maxLengthText : "The maximum length for this field is {0}",
9023 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9024 * If available, this function will be called only after the basic validators all return true, and will be passed the
9025 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9029 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9030 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9031 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9035 * @cfg {String} regexText -- Depricated - use Invalid Text
9040 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9046 autocomplete: false,
9065 formatedValue : false,
9066 forceFeedback : false,
9068 indicatorpos : 'left',
9078 parentLabelAlign : function()
9081 while (parent.parent()) {
9082 parent = parent.parent();
9083 if (typeof(parent.labelAlign) !='undefined') {
9084 return parent.labelAlign;
9091 getAutoCreate : function()
9093 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9099 if(this.inputType != 'hidden'){
9100 cfg.cls = 'form-group' //input-group
9106 type : this.inputType,
9108 cls : 'form-control',
9109 placeholder : this.placeholder || '',
9110 autocomplete : this.autocomplete || 'new-password'
9113 if(this.capture.length){
9114 input.capture = this.capture;
9117 if(this.accept.length){
9118 input.accept = this.accept + "/*";
9122 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9125 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9126 input.maxLength = this.maxLength;
9129 if (this.disabled) {
9130 input.disabled=true;
9133 if (this.readOnly) {
9134 input.readonly=true;
9138 input.name = this.name;
9142 input.cls += ' input-' + this.size;
9146 ['xs','sm','md','lg'].map(function(size){
9147 if (settings[size]) {
9148 cfg.cls += ' col-' + size + '-' + settings[size];
9152 var inputblock = input;
9156 cls: 'glyphicon form-control-feedback'
9159 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9162 cls : 'has-feedback',
9170 if (this.before || this.after) {
9173 cls : 'input-group',
9177 if (this.before && typeof(this.before) == 'string') {
9179 inputblock.cn.push({
9181 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9185 if (this.before && typeof(this.before) == 'object') {
9186 this.before = Roo.factory(this.before);
9188 inputblock.cn.push({
9190 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9191 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9195 inputblock.cn.push(input);
9197 if (this.after && typeof(this.after) == 'string') {
9198 inputblock.cn.push({
9200 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9204 if (this.after && typeof(this.after) == 'object') {
9205 this.after = Roo.factory(this.after);
9207 inputblock.cn.push({
9209 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9210 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9214 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9215 inputblock.cls += ' has-feedback';
9216 inputblock.cn.push(feedback);
9221 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9222 tooltip : 'This field is required'
9224 if (Roo.bootstrap.version == 4) {
9227 style : 'display-none'
9230 if (align ==='left' && this.fieldLabel.length) {
9232 cfg.cls += ' roo-form-group-label-left row';
9239 cls : 'control-label col-form-label',
9240 html : this.fieldLabel
9251 var labelCfg = cfg.cn[1];
9252 var contentCfg = cfg.cn[2];
9254 if(this.indicatorpos == 'right'){
9259 cls : 'control-label col-form-label',
9263 html : this.fieldLabel
9277 labelCfg = cfg.cn[0];
9278 contentCfg = cfg.cn[1];
9282 if(this.labelWidth > 12){
9283 labelCfg.style = "width: " + this.labelWidth + 'px';
9286 if(this.labelWidth < 13 && this.labelmd == 0){
9287 this.labelmd = this.labelWidth;
9290 if(this.labellg > 0){
9291 labelCfg.cls += ' col-lg-' + this.labellg;
9292 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9295 if(this.labelmd > 0){
9296 labelCfg.cls += ' col-md-' + this.labelmd;
9297 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9300 if(this.labelsm > 0){
9301 labelCfg.cls += ' col-sm-' + this.labelsm;
9302 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9305 if(this.labelxs > 0){
9306 labelCfg.cls += ' col-xs-' + this.labelxs;
9307 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9311 } else if ( this.fieldLabel.length) {
9316 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9317 tooltip : 'This field is required'
9321 //cls : 'input-group-addon',
9322 html : this.fieldLabel
9330 if(this.indicatorpos == 'right'){
9335 //cls : 'input-group-addon',
9336 html : this.fieldLabel
9341 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9342 tooltip : 'This field is required'
9362 if (this.parentType === 'Navbar' && this.parent().bar) {
9363 cfg.cls += ' navbar-form';
9366 if (this.parentType === 'NavGroup') {
9367 cfg.cls += ' navbar-form';
9375 * return the real input element.
9377 inputEl: function ()
9379 return this.el.select('input.form-control',true).first();
9382 tooltipEl : function()
9384 return this.inputEl();
9387 indicatorEl : function()
9389 if (Roo.bootstrap.version == 4) {
9390 return false; // not enabled in v4 yet.
9393 var indicator = this.el.select('i.roo-required-indicator',true).first();
9403 setDisabled : function(v)
9405 var i = this.inputEl().dom;
9407 i.removeAttribute('disabled');
9411 i.setAttribute('disabled','true');
9413 initEvents : function()
9416 this.inputEl().on("keydown" , this.fireKey, this);
9417 this.inputEl().on("focus", this.onFocus, this);
9418 this.inputEl().on("blur", this.onBlur, this);
9420 this.inputEl().relayEvent('keyup', this);
9422 this.indicator = this.indicatorEl();
9425 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9428 // reference to original value for reset
9429 this.originalValue = this.getValue();
9430 //Roo.form.TextField.superclass.initEvents.call(this);
9431 if(this.validationEvent == 'keyup'){
9432 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9433 this.inputEl().on('keyup', this.filterValidation, this);
9435 else if(this.validationEvent !== false){
9436 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9439 if(this.selectOnFocus){
9440 this.on("focus", this.preFocus, this);
9443 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9444 this.inputEl().on("keypress", this.filterKeys, this);
9446 this.inputEl().relayEvent('keypress', this);
9449 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9450 this.el.on("click", this.autoSize, this);
9453 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9454 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9457 if (typeof(this.before) == 'object') {
9458 this.before.render(this.el.select('.roo-input-before',true).first());
9460 if (typeof(this.after) == 'object') {
9461 this.after.render(this.el.select('.roo-input-after',true).first());
9464 this.inputEl().on('change', this.onChange, this);
9467 filterValidation : function(e){
9468 if(!e.isNavKeyPress()){
9469 this.validationTask.delay(this.validationDelay);
9473 * Validates the field value
9474 * @return {Boolean} True if the value is valid, else false
9476 validate : function(){
9477 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9478 if(this.disabled || this.validateValue(this.getRawValue())){
9489 * Validates a value according to the field's validation rules and marks the field as invalid
9490 * if the validation fails
9491 * @param {Mixed} value The value to validate
9492 * @return {Boolean} True if the value is valid, else false
9494 validateValue : function(value)
9496 if(this.getVisibilityEl().hasClass('hidden')){
9500 if(value.length < 1) { // if it's blank
9501 if(this.allowBlank){
9507 if(value.length < this.minLength){
9510 if(value.length > this.maxLength){
9514 var vt = Roo.form.VTypes;
9515 if(!vt[this.vtype](value, this)){
9519 if(typeof this.validator == "function"){
9520 var msg = this.validator(value);
9524 if (typeof(msg) == 'string') {
9525 this.invalidText = msg;
9529 if(this.regex && !this.regex.test(value)){
9537 fireKey : function(e){
9538 //Roo.log('field ' + e.getKey());
9539 if(e.isNavKeyPress()){
9540 this.fireEvent("specialkey", this, e);
9543 focus : function (selectText){
9545 this.inputEl().focus();
9546 if(selectText === true){
9547 this.inputEl().dom.select();
9553 onFocus : function(){
9554 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9555 // this.el.addClass(this.focusClass);
9558 this.hasFocus = true;
9559 this.startValue = this.getValue();
9560 this.fireEvent("focus", this);
9564 beforeBlur : Roo.emptyFn,
9568 onBlur : function(){
9570 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9571 //this.el.removeClass(this.focusClass);
9573 this.hasFocus = false;
9574 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9577 var v = this.getValue();
9578 if(String(v) !== String(this.startValue)){
9579 this.fireEvent('change', this, v, this.startValue);
9581 this.fireEvent("blur", this);
9584 onChange : function(e)
9586 var v = this.getValue();
9587 if(String(v) !== String(this.startValue)){
9588 this.fireEvent('change', this, v, this.startValue);
9594 * Resets the current field value to the originally loaded value and clears any validation messages
9597 this.setValue(this.originalValue);
9601 * Returns the name of the field
9602 * @return {Mixed} name The name field
9604 getName: function(){
9608 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9609 * @return {Mixed} value The field value
9611 getValue : function(){
9613 var v = this.inputEl().getValue();
9618 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9619 * @return {Mixed} value The field value
9621 getRawValue : function(){
9622 var v = this.inputEl().getValue();
9628 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9629 * @param {Mixed} value The value to set
9631 setRawValue : function(v){
9632 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9635 selectText : function(start, end){
9636 var v = this.getRawValue();
9638 start = start === undefined ? 0 : start;
9639 end = end === undefined ? v.length : end;
9640 var d = this.inputEl().dom;
9641 if(d.setSelectionRange){
9642 d.setSelectionRange(start, end);
9643 }else if(d.createTextRange){
9644 var range = d.createTextRange();
9645 range.moveStart("character", start);
9646 range.moveEnd("character", v.length-end);
9653 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9654 * @param {Mixed} value The value to set
9656 setValue : function(v){
9659 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9665 processValue : function(value){
9666 if(this.stripCharsRe){
9667 var newValue = value.replace(this.stripCharsRe, '');
9668 if(newValue !== value){
9669 this.setRawValue(newValue);
9676 preFocus : function(){
9678 if(this.selectOnFocus){
9679 this.inputEl().dom.select();
9682 filterKeys : function(e){
9684 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9687 var c = e.getCharCode(), cc = String.fromCharCode(c);
9688 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9691 if(!this.maskRe.test(cc)){
9696 * Clear any invalid styles/messages for this field
9698 clearInvalid : function(){
9700 if(!this.el || this.preventMark){ // not rendered
9705 this.el.removeClass(this.invalidClass);
9707 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709 var feedback = this.el.select('.form-control-feedback', true).first();
9712 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9718 this.indicator.removeClass('visible');
9719 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9722 this.fireEvent('valid', this);
9726 * Mark this field as valid
9728 markValid : function()
9730 if(!this.el || this.preventMark){ // not rendered...
9734 this.el.removeClass([this.invalidClass, this.validClass]);
9736 var feedback = this.el.select('.form-control-feedback', true).first();
9739 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9743 this.indicator.removeClass('visible');
9744 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9751 if(this.allowBlank && !this.getRawValue().length){
9755 this.el.addClass(this.validClass);
9757 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9759 var feedback = this.el.select('.form-control-feedback', true).first();
9762 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9763 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9768 this.fireEvent('valid', this);
9772 * Mark this field as invalid
9773 * @param {String} msg The validation message
9775 markInvalid : function(msg)
9777 if(!this.el || this.preventMark){ // not rendered
9781 this.el.removeClass([this.invalidClass, this.validClass]);
9783 var feedback = this.el.select('.form-control-feedback', true).first();
9786 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9793 if(this.allowBlank && !this.getRawValue().length){
9798 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9799 this.indicator.addClass('visible');
9802 this.el.addClass(this.invalidClass);
9804 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9806 var feedback = this.el.select('.form-control-feedback', true).first();
9809 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9811 if(this.getValue().length || this.forceFeedback){
9812 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9819 this.fireEvent('invalid', this, msg);
9822 SafariOnKeyDown : function(event)
9824 // this is a workaround for a password hang bug on chrome/ webkit.
9825 if (this.inputEl().dom.type != 'password') {
9829 var isSelectAll = false;
9831 if(this.inputEl().dom.selectionEnd > 0){
9832 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9834 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9835 event.preventDefault();
9840 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9842 event.preventDefault();
9843 // this is very hacky as keydown always get's upper case.
9845 var cc = String.fromCharCode(event.getCharCode());
9846 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9850 adjustWidth : function(tag, w){
9851 tag = tag.toLowerCase();
9852 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9853 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9857 if(tag == 'textarea'){
9860 }else if(Roo.isOpera){
9864 if(tag == 'textarea'){
9872 setFieldLabel : function(v)
9878 if(this.indicatorEl()){
9879 var ar = this.el.select('label > span',true);
9881 if (ar.elements.length) {
9882 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9883 this.fieldLabel = v;
9887 var br = this.el.select('label',true);
9889 if(br.elements.length) {
9890 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9891 this.fieldLabel = v;
9895 Roo.log('Cannot Found any of label > span || label in input');
9899 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9900 this.fieldLabel = v;
9915 * @class Roo.bootstrap.TextArea
9916 * @extends Roo.bootstrap.Input
9917 * Bootstrap TextArea class
9918 * @cfg {Number} cols Specifies the visible width of a text area
9919 * @cfg {Number} rows Specifies the visible number of lines in a text area
9920 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9921 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9922 * @cfg {string} html text
9925 * Create a new TextArea
9926 * @param {Object} config The config object
9929 Roo.bootstrap.TextArea = function(config){
9930 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9934 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9944 getAutoCreate : function(){
9946 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9952 if(this.inputType != 'hidden'){
9953 cfg.cls = 'form-group' //input-group
9961 value : this.value || '',
9962 html: this.html || '',
9963 cls : 'form-control',
9964 placeholder : this.placeholder || ''
9968 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9969 input.maxLength = this.maxLength;
9973 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9977 input.cols = this.cols;
9980 if (this.readOnly) {
9981 input.readonly = true;
9985 input.name = this.name;
9989 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9993 ['xs','sm','md','lg'].map(function(size){
9994 if (settings[size]) {
9995 cfg.cls += ' col-' + size + '-' + settings[size];
9999 var inputblock = input;
10001 if(this.hasFeedback && !this.allowBlank){
10005 cls: 'glyphicon form-control-feedback'
10009 cls : 'has-feedback',
10018 if (this.before || this.after) {
10021 cls : 'input-group',
10025 inputblock.cn.push({
10027 cls : 'input-group-addon',
10032 inputblock.cn.push(input);
10034 if(this.hasFeedback && !this.allowBlank){
10035 inputblock.cls += ' has-feedback';
10036 inputblock.cn.push(feedback);
10040 inputblock.cn.push({
10042 cls : 'input-group-addon',
10049 if (align ==='left' && this.fieldLabel.length) {
10054 cls : 'control-label',
10055 html : this.fieldLabel
10066 if(this.labelWidth > 12){
10067 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10070 if(this.labelWidth < 13 && this.labelmd == 0){
10071 this.labelmd = this.labelWidth;
10074 if(this.labellg > 0){
10075 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10076 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10079 if(this.labelmd > 0){
10080 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10081 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10084 if(this.labelsm > 0){
10085 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10086 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10089 if(this.labelxs > 0){
10090 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10091 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10094 } else if ( this.fieldLabel.length) {
10099 //cls : 'input-group-addon',
10100 html : this.fieldLabel
10118 if (this.disabled) {
10119 input.disabled=true;
10126 * return the real textarea element.
10128 inputEl: function ()
10130 return this.el.select('textarea.form-control',true).first();
10134 * Clear any invalid styles/messages for this field
10136 clearInvalid : function()
10139 if(!this.el || this.preventMark){ // not rendered
10143 var label = this.el.select('label', true).first();
10144 var icon = this.el.select('i.fa-star', true).first();
10150 this.el.removeClass(this.invalidClass);
10152 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10154 var feedback = this.el.select('.form-control-feedback', true).first();
10157 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10162 this.fireEvent('valid', this);
10166 * Mark this field as valid
10168 markValid : function()
10170 if(!this.el || this.preventMark){ // not rendered
10174 this.el.removeClass([this.invalidClass, this.validClass]);
10176 var feedback = this.el.select('.form-control-feedback', true).first();
10179 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10182 if(this.disabled || this.allowBlank){
10186 var label = this.el.select('label', true).first();
10187 var icon = this.el.select('i.fa-star', true).first();
10193 this.el.addClass(this.validClass);
10195 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10197 var feedback = this.el.select('.form-control-feedback', true).first();
10200 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10201 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10206 this.fireEvent('valid', this);
10210 * Mark this field as invalid
10211 * @param {String} msg The validation message
10213 markInvalid : function(msg)
10215 if(!this.el || this.preventMark){ // not rendered
10219 this.el.removeClass([this.invalidClass, this.validClass]);
10221 var feedback = this.el.select('.form-control-feedback', true).first();
10224 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10227 if(this.disabled || this.allowBlank){
10231 var label = this.el.select('label', true).first();
10232 var icon = this.el.select('i.fa-star', true).first();
10234 if(!this.getValue().length && label && !icon){
10235 this.el.createChild({
10237 cls : 'text-danger fa fa-lg fa-star',
10238 tooltip : 'This field is required',
10239 style : 'margin-right:5px;'
10243 this.el.addClass(this.invalidClass);
10245 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10247 var feedback = this.el.select('.form-control-feedback', true).first();
10250 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10252 if(this.getValue().length || this.forceFeedback){
10253 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10260 this.fireEvent('invalid', this, msg);
10268 * trigger field - base class for combo..
10273 * @class Roo.bootstrap.TriggerField
10274 * @extends Roo.bootstrap.Input
10275 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10276 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10277 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10278 * for which you can provide a custom implementation. For example:
10280 var trigger = new Roo.bootstrap.TriggerField();
10281 trigger.onTriggerClick = myTriggerFn;
10282 trigger.applyTo('my-field');
10285 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10286 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10287 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10288 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10289 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10292 * Create a new TriggerField.
10293 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10294 * to the base TextField)
10296 Roo.bootstrap.TriggerField = function(config){
10297 this.mimicing = false;
10298 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10301 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10303 * @cfg {String} triggerClass A CSS class to apply to the trigger
10306 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10311 * @cfg {Boolean} removable (true|false) special filter default false
10315 /** @cfg {Boolean} grow @hide */
10316 /** @cfg {Number} growMin @hide */
10317 /** @cfg {Number} growMax @hide */
10323 autoSize: Roo.emptyFn,
10327 deferHeight : true,
10330 actionMode : 'wrap',
10335 getAutoCreate : function(){
10337 var align = this.labelAlign || this.parentLabelAlign();
10342 cls: 'form-group' //input-group
10349 type : this.inputType,
10350 cls : 'form-control',
10351 autocomplete: 'new-password',
10352 placeholder : this.placeholder || ''
10356 input.name = this.name;
10359 input.cls += ' input-' + this.size;
10362 if (this.disabled) {
10363 input.disabled=true;
10366 var inputblock = input;
10368 if(this.hasFeedback && !this.allowBlank){
10372 cls: 'glyphicon form-control-feedback'
10375 if(this.removable && !this.editable && !this.tickable){
10377 cls : 'has-feedback',
10383 cls : 'roo-combo-removable-btn close'
10390 cls : 'has-feedback',
10399 if(this.removable && !this.editable && !this.tickable){
10401 cls : 'roo-removable',
10407 cls : 'roo-combo-removable-btn close'
10414 if (this.before || this.after) {
10417 cls : 'input-group',
10421 inputblock.cn.push({
10423 cls : 'input-group-addon input-group-prepend input-group-text',
10428 inputblock.cn.push(input);
10430 if(this.hasFeedback && !this.allowBlank){
10431 inputblock.cls += ' has-feedback';
10432 inputblock.cn.push(feedback);
10436 inputblock.cn.push({
10438 cls : 'input-group-addon input-group-append input-group-text',
10447 var ibwrap = inputblock;
10452 cls: 'roo-select2-choices',
10456 cls: 'roo-select2-search-field',
10468 cls: 'roo-select2-container input-group',
10473 cls: 'form-hidden-field'
10479 if(!this.multiple && this.showToggleBtn){
10485 if (this.caret != false) {
10488 cls: 'fa fa-' + this.caret
10495 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10500 cls: 'combobox-clear',
10514 combobox.cls += ' roo-select2-container-multi';
10518 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10519 tooltip : 'This field is required'
10521 if (Roo.bootstrap.version == 4) {
10524 style : 'display:none'
10529 if (align ==='left' && this.fieldLabel.length) {
10531 cfg.cls += ' roo-form-group-label-left row';
10538 cls : 'control-label',
10539 html : this.fieldLabel
10551 var labelCfg = cfg.cn[1];
10552 var contentCfg = cfg.cn[2];
10554 if(this.indicatorpos == 'right'){
10559 cls : 'control-label',
10563 html : this.fieldLabel
10577 labelCfg = cfg.cn[0];
10578 contentCfg = cfg.cn[1];
10581 if(this.labelWidth > 12){
10582 labelCfg.style = "width: " + this.labelWidth + 'px';
10585 if(this.labelWidth < 13 && this.labelmd == 0){
10586 this.labelmd = this.labelWidth;
10589 if(this.labellg > 0){
10590 labelCfg.cls += ' col-lg-' + this.labellg;
10591 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10594 if(this.labelmd > 0){
10595 labelCfg.cls += ' col-md-' + this.labelmd;
10596 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10599 if(this.labelsm > 0){
10600 labelCfg.cls += ' col-sm-' + this.labelsm;
10601 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10604 if(this.labelxs > 0){
10605 labelCfg.cls += ' col-xs-' + this.labelxs;
10606 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10609 } else if ( this.fieldLabel.length) {
10610 // Roo.log(" label");
10615 //cls : 'input-group-addon',
10616 html : this.fieldLabel
10624 if(this.indicatorpos == 'right'){
10632 html : this.fieldLabel
10646 // Roo.log(" no label && no align");
10653 ['xs','sm','md','lg'].map(function(size){
10654 if (settings[size]) {
10655 cfg.cls += ' col-' + size + '-' + settings[size];
10666 onResize : function(w, h){
10667 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10668 // if(typeof w == 'number'){
10669 // var x = w - this.trigger.getWidth();
10670 // this.inputEl().setWidth(this.adjustWidth('input', x));
10671 // this.trigger.setStyle('left', x+'px');
10676 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10679 getResizeEl : function(){
10680 return this.inputEl();
10684 getPositionEl : function(){
10685 return this.inputEl();
10689 alignErrorIcon : function(){
10690 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10694 initEvents : function(){
10698 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10699 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10700 if(!this.multiple && this.showToggleBtn){
10701 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10702 if(this.hideTrigger){
10703 this.trigger.setDisplayed(false);
10705 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10709 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10712 if(this.removable && !this.editable && !this.tickable){
10713 var close = this.closeTriggerEl();
10716 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10717 close.on('click', this.removeBtnClick, this, close);
10721 //this.trigger.addClassOnOver('x-form-trigger-over');
10722 //this.trigger.addClassOnClick('x-form-trigger-click');
10725 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10729 closeTriggerEl : function()
10731 var close = this.el.select('.roo-combo-removable-btn', true).first();
10732 return close ? close : false;
10735 removeBtnClick : function(e, h, el)
10737 e.preventDefault();
10739 if(this.fireEvent("remove", this) !== false){
10741 this.fireEvent("afterremove", this)
10745 createList : function()
10747 this.list = Roo.get(document.body).createChild({
10748 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10749 cls: 'typeahead typeahead-long dropdown-menu',
10750 style: 'display:none'
10753 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10758 initTrigger : function(){
10763 onDestroy : function(){
10765 this.trigger.removeAllListeners();
10766 // this.trigger.remove();
10769 // this.wrap.remove();
10771 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10775 onFocus : function(){
10776 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10778 if(!this.mimicing){
10779 this.wrap.addClass('x-trigger-wrap-focus');
10780 this.mimicing = true;
10781 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10782 if(this.monitorTab){
10783 this.el.on("keydown", this.checkTab, this);
10790 checkTab : function(e){
10791 if(e.getKey() == e.TAB){
10792 this.triggerBlur();
10797 onBlur : function(){
10802 mimicBlur : function(e, t){
10804 if(!this.wrap.contains(t) && this.validateBlur()){
10805 this.triggerBlur();
10811 triggerBlur : function(){
10812 this.mimicing = false;
10813 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10814 if(this.monitorTab){
10815 this.el.un("keydown", this.checkTab, this);
10817 //this.wrap.removeClass('x-trigger-wrap-focus');
10818 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10822 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10823 validateBlur : function(e, t){
10828 onDisable : function(){
10829 this.inputEl().dom.disabled = true;
10830 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10832 // this.wrap.addClass('x-item-disabled');
10837 onEnable : function(){
10838 this.inputEl().dom.disabled = false;
10839 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10841 // this.el.removeClass('x-item-disabled');
10846 onShow : function(){
10847 var ae = this.getActionEl();
10850 ae.dom.style.display = '';
10851 ae.dom.style.visibility = 'visible';
10857 onHide : function(){
10858 var ae = this.getActionEl();
10859 ae.dom.style.display = 'none';
10863 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10864 * by an implementing function.
10866 * @param {EventObject} e
10868 onTriggerClick : Roo.emptyFn
10872 * Ext JS Library 1.1.1
10873 * Copyright(c) 2006-2007, Ext JS, LLC.
10875 * Originally Released Under LGPL - original licence link has changed is not relivant.
10878 * <script type="text/javascript">
10883 * @class Roo.data.SortTypes
10885 * Defines the default sorting (casting?) comparison functions used when sorting data.
10887 Roo.data.SortTypes = {
10889 * Default sort that does nothing
10890 * @param {Mixed} s The value being converted
10891 * @return {Mixed} The comparison value
10893 none : function(s){
10898 * The regular expression used to strip tags
10902 stripTagsRE : /<\/?[^>]+>/gi,
10905 * Strips all HTML tags to sort on text only
10906 * @param {Mixed} s The value being converted
10907 * @return {String} The comparison value
10909 asText : function(s){
10910 return String(s).replace(this.stripTagsRE, "");
10914 * Strips all HTML tags to sort on text only - Case insensitive
10915 * @param {Mixed} s The value being converted
10916 * @return {String} The comparison value
10918 asUCText : function(s){
10919 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10923 * Case insensitive string
10924 * @param {Mixed} s The value being converted
10925 * @return {String} The comparison value
10927 asUCString : function(s) {
10928 return String(s).toUpperCase();
10933 * @param {Mixed} s The value being converted
10934 * @return {Number} The comparison value
10936 asDate : function(s) {
10940 if(s instanceof Date){
10941 return s.getTime();
10943 return Date.parse(String(s));
10948 * @param {Mixed} s The value being converted
10949 * @return {Float} The comparison value
10951 asFloat : function(s) {
10952 var val = parseFloat(String(s).replace(/,/g, ""));
10961 * @param {Mixed} s The value being converted
10962 * @return {Number} The comparison value
10964 asInt : function(s) {
10965 var val = parseInt(String(s).replace(/,/g, ""));
10973 * Ext JS Library 1.1.1
10974 * Copyright(c) 2006-2007, Ext JS, LLC.
10976 * Originally Released Under LGPL - original licence link has changed is not relivant.
10979 * <script type="text/javascript">
10983 * @class Roo.data.Record
10984 * Instances of this class encapsulate both record <em>definition</em> information, and record
10985 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10986 * to access Records cached in an {@link Roo.data.Store} object.<br>
10988 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10989 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10992 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10994 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10995 * {@link #create}. The parameters are the same.
10996 * @param {Array} data An associative Array of data values keyed by the field name.
10997 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10998 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10999 * not specified an integer id is generated.
11001 Roo.data.Record = function(data, id){
11002 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11007 * Generate a constructor for a specific record layout.
11008 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11009 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11010 * Each field definition object may contain the following properties: <ul>
11011 * <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,
11012 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11013 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11014 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11015 * is being used, then this is a string containing the javascript expression to reference the data relative to
11016 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11017 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11018 * this may be omitted.</p></li>
11019 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11020 * <ul><li>auto (Default, implies no conversion)</li>
11025 * <li>date</li></ul></p></li>
11026 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11027 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11028 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11029 * by the Reader into an object that will be stored in the Record. It is passed the
11030 * following parameters:<ul>
11031 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11033 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11035 * <br>usage:<br><pre><code>
11036 var TopicRecord = Roo.data.Record.create(
11037 {name: 'title', mapping: 'topic_title'},
11038 {name: 'author', mapping: 'username'},
11039 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11040 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11041 {name: 'lastPoster', mapping: 'user2'},
11042 {name: 'excerpt', mapping: 'post_text'}
11045 var myNewRecord = new TopicRecord({
11046 title: 'Do my job please',
11049 lastPost: new Date(),
11050 lastPoster: 'Animal',
11051 excerpt: 'No way dude!'
11053 myStore.add(myNewRecord);
11058 Roo.data.Record.create = function(o){
11059 var f = function(){
11060 f.superclass.constructor.apply(this, arguments);
11062 Roo.extend(f, Roo.data.Record);
11063 var p = f.prototype;
11064 p.fields = new Roo.util.MixedCollection(false, function(field){
11067 for(var i = 0, len = o.length; i < len; i++){
11068 p.fields.add(new Roo.data.Field(o[i]));
11070 f.getField = function(name){
11071 return p.fields.get(name);
11076 Roo.data.Record.AUTO_ID = 1000;
11077 Roo.data.Record.EDIT = 'edit';
11078 Roo.data.Record.REJECT = 'reject';
11079 Roo.data.Record.COMMIT = 'commit';
11081 Roo.data.Record.prototype = {
11083 * Readonly flag - true if this record has been modified.
11092 join : function(store){
11093 this.store = store;
11097 * Set the named field to the specified value.
11098 * @param {String} name The name of the field to set.
11099 * @param {Object} value The value to set the field to.
11101 set : function(name, value){
11102 if(this.data[name] == value){
11106 if(!this.modified){
11107 this.modified = {};
11109 if(typeof this.modified[name] == 'undefined'){
11110 this.modified[name] = this.data[name];
11112 this.data[name] = value;
11113 if(!this.editing && this.store){
11114 this.store.afterEdit(this);
11119 * Get the value of the named field.
11120 * @param {String} name The name of the field to get the value of.
11121 * @return {Object} The value of the field.
11123 get : function(name){
11124 return this.data[name];
11128 beginEdit : function(){
11129 this.editing = true;
11130 this.modified = {};
11134 cancelEdit : function(){
11135 this.editing = false;
11136 delete this.modified;
11140 endEdit : function(){
11141 this.editing = false;
11142 if(this.dirty && this.store){
11143 this.store.afterEdit(this);
11148 * Usually called by the {@link Roo.data.Store} which owns the Record.
11149 * Rejects all changes made to the Record since either creation, or the last commit operation.
11150 * Modified fields are reverted to their original values.
11152 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11153 * of reject operations.
11155 reject : function(){
11156 var m = this.modified;
11158 if(typeof m[n] != "function"){
11159 this.data[n] = m[n];
11162 this.dirty = false;
11163 delete this.modified;
11164 this.editing = false;
11166 this.store.afterReject(this);
11171 * Usually called by the {@link Roo.data.Store} which owns the Record.
11172 * Commits all changes made to the Record since either creation, or the last commit operation.
11174 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11175 * of commit operations.
11177 commit : function(){
11178 this.dirty = false;
11179 delete this.modified;
11180 this.editing = false;
11182 this.store.afterCommit(this);
11187 hasError : function(){
11188 return this.error != null;
11192 clearError : function(){
11197 * Creates a copy of this record.
11198 * @param {String} id (optional) A new record id if you don't want to use this record's id
11201 copy : function(newId) {
11202 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11206 * Ext JS Library 1.1.1
11207 * Copyright(c) 2006-2007, Ext JS, LLC.
11209 * Originally Released Under LGPL - original licence link has changed is not relivant.
11212 * <script type="text/javascript">
11218 * @class Roo.data.Store
11219 * @extends Roo.util.Observable
11220 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11221 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11223 * 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
11224 * has no knowledge of the format of the data returned by the Proxy.<br>
11226 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11227 * instances from the data object. These records are cached and made available through accessor functions.
11229 * Creates a new Store.
11230 * @param {Object} config A config object containing the objects needed for the Store to access data,
11231 * and read the data into Records.
11233 Roo.data.Store = function(config){
11234 this.data = new Roo.util.MixedCollection(false);
11235 this.data.getKey = function(o){
11238 this.baseParams = {};
11240 this.paramNames = {
11245 "multisort" : "_multisort"
11248 if(config && config.data){
11249 this.inlineData = config.data;
11250 delete config.data;
11253 Roo.apply(this, config);
11255 if(this.reader){ // reader passed
11256 this.reader = Roo.factory(this.reader, Roo.data);
11257 this.reader.xmodule = this.xmodule || false;
11258 if(!this.recordType){
11259 this.recordType = this.reader.recordType;
11261 if(this.reader.onMetaChange){
11262 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11266 if(this.recordType){
11267 this.fields = this.recordType.prototype.fields;
11269 this.modified = [];
11273 * @event datachanged
11274 * Fires when the data cache has changed, and a widget which is using this Store
11275 * as a Record cache should refresh its view.
11276 * @param {Store} this
11278 datachanged : true,
11280 * @event metachange
11281 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11282 * @param {Store} this
11283 * @param {Object} meta The JSON metadata
11288 * Fires when Records have been added to the Store
11289 * @param {Store} this
11290 * @param {Roo.data.Record[]} records The array of Records added
11291 * @param {Number} index The index at which the record(s) were added
11296 * Fires when a Record has been removed from the Store
11297 * @param {Store} this
11298 * @param {Roo.data.Record} record The Record that was removed
11299 * @param {Number} index The index at which the record was removed
11304 * Fires when a Record has been updated
11305 * @param {Store} this
11306 * @param {Roo.data.Record} record The Record that was updated
11307 * @param {String} operation The update operation being performed. Value may be one of:
11309 Roo.data.Record.EDIT
11310 Roo.data.Record.REJECT
11311 Roo.data.Record.COMMIT
11317 * Fires when the data cache has been cleared.
11318 * @param {Store} this
11322 * @event beforeload
11323 * Fires before a request is made for a new data object. If the beforeload handler returns false
11324 * the load action will be canceled.
11325 * @param {Store} this
11326 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11330 * @event beforeloadadd
11331 * Fires after a new set of Records has been loaded.
11332 * @param {Store} this
11333 * @param {Roo.data.Record[]} records The Records that were loaded
11334 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11336 beforeloadadd : true,
11339 * Fires after a new set of Records has been loaded, before they are added to the store.
11340 * @param {Store} this
11341 * @param {Roo.data.Record[]} records The Records that were loaded
11342 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11343 * @params {Object} return from reader
11347 * @event loadexception
11348 * Fires if an exception occurs in the Proxy during loading.
11349 * Called with the signature of the Proxy's "loadexception" event.
11350 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11353 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11354 * @param {Object} load options
11355 * @param {Object} jsonData from your request (normally this contains the Exception)
11357 loadexception : true
11361 this.proxy = Roo.factory(this.proxy, Roo.data);
11362 this.proxy.xmodule = this.xmodule || false;
11363 this.relayEvents(this.proxy, ["loadexception"]);
11365 this.sortToggle = {};
11366 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11368 Roo.data.Store.superclass.constructor.call(this);
11370 if(this.inlineData){
11371 this.loadData(this.inlineData);
11372 delete this.inlineData;
11376 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11378 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11379 * without a remote query - used by combo/forms at present.
11383 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11386 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11389 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11390 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11393 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11394 * on any HTTP request
11397 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11400 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11404 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11405 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11407 remoteSort : false,
11410 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11411 * loaded or when a record is removed. (defaults to false).
11413 pruneModifiedRecords : false,
11416 lastOptions : null,
11419 * Add Records to the Store and fires the add event.
11420 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11422 add : function(records){
11423 records = [].concat(records);
11424 for(var i = 0, len = records.length; i < len; i++){
11425 records[i].join(this);
11427 var index = this.data.length;
11428 this.data.addAll(records);
11429 this.fireEvent("add", this, records, index);
11433 * Remove a Record from the Store and fires the remove event.
11434 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11436 remove : function(record){
11437 var index = this.data.indexOf(record);
11438 this.data.removeAt(index);
11440 if(this.pruneModifiedRecords){
11441 this.modified.remove(record);
11443 this.fireEvent("remove", this, record, index);
11447 * Remove all Records from the Store and fires the clear event.
11449 removeAll : function(){
11451 if(this.pruneModifiedRecords){
11452 this.modified = [];
11454 this.fireEvent("clear", this);
11458 * Inserts Records to the Store at the given index and fires the add event.
11459 * @param {Number} index The start index at which to insert the passed Records.
11460 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11462 insert : function(index, records){
11463 records = [].concat(records);
11464 for(var i = 0, len = records.length; i < len; i++){
11465 this.data.insert(index, records[i]);
11466 records[i].join(this);
11468 this.fireEvent("add", this, records, index);
11472 * Get the index within the cache of the passed Record.
11473 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11474 * @return {Number} The index of the passed Record. Returns -1 if not found.
11476 indexOf : function(record){
11477 return this.data.indexOf(record);
11481 * Get the index within the cache of the Record with the passed id.
11482 * @param {String} id The id of the Record to find.
11483 * @return {Number} The index of the Record. Returns -1 if not found.
11485 indexOfId : function(id){
11486 return this.data.indexOfKey(id);
11490 * Get the Record with the specified id.
11491 * @param {String} id The id of the Record to find.
11492 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11494 getById : function(id){
11495 return this.data.key(id);
11499 * Get the Record at the specified index.
11500 * @param {Number} index The index of the Record to find.
11501 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11503 getAt : function(index){
11504 return this.data.itemAt(index);
11508 * Returns a range of Records between specified indices.
11509 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11510 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11511 * @return {Roo.data.Record[]} An array of Records
11513 getRange : function(start, end){
11514 return this.data.getRange(start, end);
11518 storeOptions : function(o){
11519 o = Roo.apply({}, o);
11522 this.lastOptions = o;
11526 * Loads the Record cache from the configured Proxy using the configured Reader.
11528 * If using remote paging, then the first load call must specify the <em>start</em>
11529 * and <em>limit</em> properties in the options.params property to establish the initial
11530 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11532 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11533 * and this call will return before the new data has been loaded. Perform any post-processing
11534 * in a callback function, or in a "load" event handler.</strong>
11536 * @param {Object} options An object containing properties which control loading options:<ul>
11537 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11538 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11539 * passed the following arguments:<ul>
11540 * <li>r : Roo.data.Record[]</li>
11541 * <li>options: Options object from the load call</li>
11542 * <li>success: Boolean success indicator</li></ul></li>
11543 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11544 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11547 load : function(options){
11548 options = options || {};
11549 if(this.fireEvent("beforeload", this, options) !== false){
11550 this.storeOptions(options);
11551 var p = Roo.apply(options.params || {}, this.baseParams);
11552 // if meta was not loaded from remote source.. try requesting it.
11553 if (!this.reader.metaFromRemote) {
11554 p._requestMeta = 1;
11556 if(this.sortInfo && this.remoteSort){
11557 var pn = this.paramNames;
11558 p[pn["sort"]] = this.sortInfo.field;
11559 p[pn["dir"]] = this.sortInfo.direction;
11561 if (this.multiSort) {
11562 var pn = this.paramNames;
11563 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11566 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11571 * Reloads the Record cache from the configured Proxy using the configured Reader and
11572 * the options from the last load operation performed.
11573 * @param {Object} options (optional) An object containing properties which may override the options
11574 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11575 * the most recently used options are reused).
11577 reload : function(options){
11578 this.load(Roo.applyIf(options||{}, this.lastOptions));
11582 // Called as a callback by the Reader during a load operation.
11583 loadRecords : function(o, options, success){
11584 if(!o || success === false){
11585 if(success !== false){
11586 this.fireEvent("load", this, [], options, o);
11588 if(options.callback){
11589 options.callback.call(options.scope || this, [], options, false);
11593 // if data returned failure - throw an exception.
11594 if (o.success === false) {
11595 // show a message if no listener is registered.
11596 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11597 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11599 // loadmask wil be hooked into this..
11600 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11603 var r = o.records, t = o.totalRecords || r.length;
11605 this.fireEvent("beforeloadadd", this, r, options, o);
11607 if(!options || options.add !== true){
11608 if(this.pruneModifiedRecords){
11609 this.modified = [];
11611 for(var i = 0, len = r.length; i < len; i++){
11615 this.data = this.snapshot;
11616 delete this.snapshot;
11619 this.data.addAll(r);
11620 this.totalLength = t;
11622 this.fireEvent("datachanged", this);
11624 this.totalLength = Math.max(t, this.data.length+r.length);
11628 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11630 var e = new Roo.data.Record({});
11632 e.set(this.parent.displayField, this.parent.emptyTitle);
11633 e.set(this.parent.valueField, '');
11638 this.fireEvent("load", this, r, options, o);
11639 if(options.callback){
11640 options.callback.call(options.scope || this, r, options, true);
11646 * Loads data from a passed data block. A Reader which understands the format of the data
11647 * must have been configured in the constructor.
11648 * @param {Object} data The data block from which to read the Records. The format of the data expected
11649 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11650 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11652 loadData : function(o, append){
11653 var r = this.reader.readRecords(o);
11654 this.loadRecords(r, {add: append}, true);
11658 * Gets the number of cached records.
11660 * <em>If using paging, this may not be the total size of the dataset. If the data object
11661 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11662 * the data set size</em>
11664 getCount : function(){
11665 return this.data.length || 0;
11669 * Gets the total number of records in the dataset as returned by the server.
11671 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11672 * the dataset size</em>
11674 getTotalCount : function(){
11675 return this.totalLength || 0;
11679 * Returns the sort state of the Store as an object with two properties:
11681 field {String} The name of the field by which the Records are sorted
11682 direction {String} The sort order, "ASC" or "DESC"
11685 getSortState : function(){
11686 return this.sortInfo;
11690 applySort : function(){
11691 if(this.sortInfo && !this.remoteSort){
11692 var s = this.sortInfo, f = s.field;
11693 var st = this.fields.get(f).sortType;
11694 var fn = function(r1, r2){
11695 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11696 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11698 this.data.sort(s.direction, fn);
11699 if(this.snapshot && this.snapshot != this.data){
11700 this.snapshot.sort(s.direction, fn);
11706 * Sets the default sort column and order to be used by the next load operation.
11707 * @param {String} fieldName The name of the field to sort by.
11708 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11710 setDefaultSort : function(field, dir){
11711 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11715 * Sort the Records.
11716 * If remote sorting is used, the sort is performed on the server, and the cache is
11717 * reloaded. If local sorting is used, the cache is sorted internally.
11718 * @param {String} fieldName The name of the field to sort by.
11719 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11721 sort : function(fieldName, dir){
11722 var f = this.fields.get(fieldName);
11724 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11726 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11727 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11732 this.sortToggle[f.name] = dir;
11733 this.sortInfo = {field: f.name, direction: dir};
11734 if(!this.remoteSort){
11736 this.fireEvent("datachanged", this);
11738 this.load(this.lastOptions);
11743 * Calls the specified function for each of the Records in the cache.
11744 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11745 * Returning <em>false</em> aborts and exits the iteration.
11746 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11748 each : function(fn, scope){
11749 this.data.each(fn, scope);
11753 * Gets all records modified since the last commit. Modified records are persisted across load operations
11754 * (e.g., during paging).
11755 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11757 getModifiedRecords : function(){
11758 return this.modified;
11762 createFilterFn : function(property, value, anyMatch){
11763 if(!value.exec){ // not a regex
11764 value = String(value);
11765 if(value.length == 0){
11768 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11770 return function(r){
11771 return value.test(r.data[property]);
11776 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11777 * @param {String} property A field on your records
11778 * @param {Number} start The record index to start at (defaults to 0)
11779 * @param {Number} end The last record index to include (defaults to length - 1)
11780 * @return {Number} The sum
11782 sum : function(property, start, end){
11783 var rs = this.data.items, v = 0;
11784 start = start || 0;
11785 end = (end || end === 0) ? end : rs.length-1;
11787 for(var i = start; i <= end; i++){
11788 v += (rs[i].data[property] || 0);
11794 * Filter the records by a specified property.
11795 * @param {String} field A field on your records
11796 * @param {String/RegExp} value Either a string that the field
11797 * should start with or a RegExp to test against the field
11798 * @param {Boolean} anyMatch True to match any part not just the beginning
11800 filter : function(property, value, anyMatch){
11801 var fn = this.createFilterFn(property, value, anyMatch);
11802 return fn ? this.filterBy(fn) : this.clearFilter();
11806 * Filter by a function. The specified function will be called with each
11807 * record in this data source. If the function returns true the record is included,
11808 * otherwise it is filtered.
11809 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11810 * @param {Object} scope (optional) The scope of the function (defaults to this)
11812 filterBy : function(fn, scope){
11813 this.snapshot = this.snapshot || this.data;
11814 this.data = this.queryBy(fn, scope||this);
11815 this.fireEvent("datachanged", this);
11819 * Query the records by a specified property.
11820 * @param {String} field A field on your records
11821 * @param {String/RegExp} value Either a string that the field
11822 * should start with or a RegExp to test against the field
11823 * @param {Boolean} anyMatch True to match any part not just the beginning
11824 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11826 query : function(property, value, anyMatch){
11827 var fn = this.createFilterFn(property, value, anyMatch);
11828 return fn ? this.queryBy(fn) : this.data.clone();
11832 * Query by a function. The specified function will be called with each
11833 * record in this data source. If the function returns true the record is included
11835 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11836 * @param {Object} scope (optional) The scope of the function (defaults to this)
11837 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11839 queryBy : function(fn, scope){
11840 var data = this.snapshot || this.data;
11841 return data.filterBy(fn, scope||this);
11845 * Collects unique values for a particular dataIndex from this store.
11846 * @param {String} dataIndex The property to collect
11847 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11848 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11849 * @return {Array} An array of the unique values
11851 collect : function(dataIndex, allowNull, bypassFilter){
11852 var d = (bypassFilter === true && this.snapshot) ?
11853 this.snapshot.items : this.data.items;
11854 var v, sv, r = [], l = {};
11855 for(var i = 0, len = d.length; i < len; i++){
11856 v = d[i].data[dataIndex];
11858 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11867 * Revert to a view of the Record cache with no filtering applied.
11868 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11870 clearFilter : function(suppressEvent){
11871 if(this.snapshot && this.snapshot != this.data){
11872 this.data = this.snapshot;
11873 delete this.snapshot;
11874 if(suppressEvent !== true){
11875 this.fireEvent("datachanged", this);
11881 afterEdit : function(record){
11882 if(this.modified.indexOf(record) == -1){
11883 this.modified.push(record);
11885 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11889 afterReject : function(record){
11890 this.modified.remove(record);
11891 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11895 afterCommit : function(record){
11896 this.modified.remove(record);
11897 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11901 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11902 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11904 commitChanges : function(){
11905 var m = this.modified.slice(0);
11906 this.modified = [];
11907 for(var i = 0, len = m.length; i < len; i++){
11913 * Cancel outstanding changes on all changed records.
11915 rejectChanges : function(){
11916 var m = this.modified.slice(0);
11917 this.modified = [];
11918 for(var i = 0, len = m.length; i < len; i++){
11923 onMetaChange : function(meta, rtype, o){
11924 this.recordType = rtype;
11925 this.fields = rtype.prototype.fields;
11926 delete this.snapshot;
11927 this.sortInfo = meta.sortInfo || this.sortInfo;
11928 this.modified = [];
11929 this.fireEvent('metachange', this, this.reader.meta);
11932 moveIndex : function(data, type)
11934 var index = this.indexOf(data);
11936 var newIndex = index + type;
11940 this.insert(newIndex, data);
11945 * Ext JS Library 1.1.1
11946 * Copyright(c) 2006-2007, Ext JS, LLC.
11948 * Originally Released Under LGPL - original licence link has changed is not relivant.
11951 * <script type="text/javascript">
11955 * @class Roo.data.SimpleStore
11956 * @extends Roo.data.Store
11957 * Small helper class to make creating Stores from Array data easier.
11958 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11959 * @cfg {Array} fields An array of field definition objects, or field name strings.
11960 * @cfg {Array} data The multi-dimensional array of data
11962 * @param {Object} config
11964 Roo.data.SimpleStore = function(config){
11965 Roo.data.SimpleStore.superclass.constructor.call(this, {
11967 reader: new Roo.data.ArrayReader({
11970 Roo.data.Record.create(config.fields)
11972 proxy : new Roo.data.MemoryProxy(config.data)
11976 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11978 * Ext JS Library 1.1.1
11979 * Copyright(c) 2006-2007, Ext JS, LLC.
11981 * Originally Released Under LGPL - original licence link has changed is not relivant.
11984 * <script type="text/javascript">
11989 * @extends Roo.data.Store
11990 * @class Roo.data.JsonStore
11991 * Small helper class to make creating Stores for JSON data easier. <br/>
11993 var store = new Roo.data.JsonStore({
11994 url: 'get-images.php',
11996 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11999 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12000 * JsonReader and HttpProxy (unless inline data is provided).</b>
12001 * @cfg {Array} fields An array of field definition objects, or field name strings.
12003 * @param {Object} config
12005 Roo.data.JsonStore = function(c){
12006 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12007 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12008 reader: new Roo.data.JsonReader(c, c.fields)
12011 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12013 * Ext JS Library 1.1.1
12014 * Copyright(c) 2006-2007, Ext JS, LLC.
12016 * Originally Released Under LGPL - original licence link has changed is not relivant.
12019 * <script type="text/javascript">
12023 Roo.data.Field = function(config){
12024 if(typeof config == "string"){
12025 config = {name: config};
12027 Roo.apply(this, config);
12030 this.type = "auto";
12033 var st = Roo.data.SortTypes;
12034 // named sortTypes are supported, here we look them up
12035 if(typeof this.sortType == "string"){
12036 this.sortType = st[this.sortType];
12039 // set default sortType for strings and dates
12040 if(!this.sortType){
12043 this.sortType = st.asUCString;
12046 this.sortType = st.asDate;
12049 this.sortType = st.none;
12054 var stripRe = /[\$,%]/g;
12056 // prebuilt conversion function for this field, instead of
12057 // switching every time we're reading a value
12059 var cv, dateFormat = this.dateFormat;
12064 cv = function(v){ return v; };
12067 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12071 return v !== undefined && v !== null && v !== '' ?
12072 parseInt(String(v).replace(stripRe, ""), 10) : '';
12077 return v !== undefined && v !== null && v !== '' ?
12078 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12083 cv = function(v){ return v === true || v === "true" || v == 1; };
12090 if(v instanceof Date){
12094 if(dateFormat == "timestamp"){
12095 return new Date(v*1000);
12097 return Date.parseDate(v, dateFormat);
12099 var parsed = Date.parse(v);
12100 return parsed ? new Date(parsed) : null;
12109 Roo.data.Field.prototype = {
12117 * Ext JS Library 1.1.1
12118 * Copyright(c) 2006-2007, Ext JS, LLC.
12120 * Originally Released Under LGPL - original licence link has changed is not relivant.
12123 * <script type="text/javascript">
12126 // Base class for reading structured data from a data source. This class is intended to be
12127 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12130 * @class Roo.data.DataReader
12131 * Base class for reading structured data from a data source. This class is intended to be
12132 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12135 Roo.data.DataReader = function(meta, recordType){
12139 this.recordType = recordType instanceof Array ?
12140 Roo.data.Record.create(recordType) : recordType;
12143 Roo.data.DataReader.prototype = {
12145 * Create an empty record
12146 * @param {Object} data (optional) - overlay some values
12147 * @return {Roo.data.Record} record created.
12149 newRow : function(d) {
12151 this.recordType.prototype.fields.each(function(c) {
12153 case 'int' : da[c.name] = 0; break;
12154 case 'date' : da[c.name] = new Date(); break;
12155 case 'float' : da[c.name] = 0.0; break;
12156 case 'boolean' : da[c.name] = false; break;
12157 default : da[c.name] = ""; break;
12161 return new this.recordType(Roo.apply(da, d));
12166 * Ext JS Library 1.1.1
12167 * Copyright(c) 2006-2007, Ext JS, LLC.
12169 * Originally Released Under LGPL - original licence link has changed is not relivant.
12172 * <script type="text/javascript">
12176 * @class Roo.data.DataProxy
12177 * @extends Roo.data.Observable
12178 * This class is an abstract base class for implementations which provide retrieval of
12179 * unformatted data objects.<br>
12181 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12182 * (of the appropriate type which knows how to parse the data object) to provide a block of
12183 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12185 * Custom implementations must implement the load method as described in
12186 * {@link Roo.data.HttpProxy#load}.
12188 Roo.data.DataProxy = function(){
12191 * @event beforeload
12192 * Fires before a network request is made to retrieve a data object.
12193 * @param {Object} This DataProxy object.
12194 * @param {Object} params The params parameter to the load function.
12199 * Fires before the load method's callback is called.
12200 * @param {Object} This DataProxy object.
12201 * @param {Object} o The data object.
12202 * @param {Object} arg The callback argument object passed to the load function.
12206 * @event loadexception
12207 * Fires if an Exception occurs during data retrieval.
12208 * @param {Object} This DataProxy object.
12209 * @param {Object} o The data object.
12210 * @param {Object} arg The callback argument object passed to the load function.
12211 * @param {Object} e The Exception.
12213 loadexception : true
12215 Roo.data.DataProxy.superclass.constructor.call(this);
12218 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12221 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12225 * Ext JS Library 1.1.1
12226 * Copyright(c) 2006-2007, Ext JS, LLC.
12228 * Originally Released Under LGPL - original licence link has changed is not relivant.
12231 * <script type="text/javascript">
12234 * @class Roo.data.MemoryProxy
12235 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12236 * to the Reader when its load method is called.
12238 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12240 Roo.data.MemoryProxy = function(data){
12244 Roo.data.MemoryProxy.superclass.constructor.call(this);
12248 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12251 * Load data from the requested source (in this case an in-memory
12252 * data object passed to the constructor), read the data object into
12253 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12254 * process that block using the passed callback.
12255 * @param {Object} params This parameter is not used by the MemoryProxy class.
12256 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12257 * object into a block of Roo.data.Records.
12258 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12259 * The function must be passed <ul>
12260 * <li>The Record block object</li>
12261 * <li>The "arg" argument from the load function</li>
12262 * <li>A boolean success indicator</li>
12264 * @param {Object} scope The scope in which to call the callback
12265 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12267 load : function(params, reader, callback, scope, arg){
12268 params = params || {};
12271 result = reader.readRecords(this.data);
12273 this.fireEvent("loadexception", this, arg, null, e);
12274 callback.call(scope, null, arg, false);
12277 callback.call(scope, result, arg, true);
12281 update : function(params, records){
12286 * Ext JS Library 1.1.1
12287 * Copyright(c) 2006-2007, Ext JS, LLC.
12289 * Originally Released Under LGPL - original licence link has changed is not relivant.
12292 * <script type="text/javascript">
12295 * @class Roo.data.HttpProxy
12296 * @extends Roo.data.DataProxy
12297 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12298 * configured to reference a certain URL.<br><br>
12300 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12301 * from which the running page was served.<br><br>
12303 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12305 * Be aware that to enable the browser to parse an XML document, the server must set
12306 * the Content-Type header in the HTTP response to "text/xml".
12308 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12309 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12310 * will be used to make the request.
12312 Roo.data.HttpProxy = function(conn){
12313 Roo.data.HttpProxy.superclass.constructor.call(this);
12314 // is conn a conn config or a real conn?
12316 this.useAjax = !conn || !conn.events;
12320 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12321 // thse are take from connection...
12324 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12327 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12328 * extra parameters to each request made by this object. (defaults to undefined)
12331 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12332 * to each request made by this object. (defaults to undefined)
12335 * @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)
12338 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12341 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12347 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12351 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12352 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12353 * a finer-grained basis than the DataProxy events.
12355 getConnection : function(){
12356 return this.useAjax ? Roo.Ajax : this.conn;
12360 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12361 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12362 * process that block using the passed callback.
12363 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12364 * for the request to the remote server.
12365 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12366 * object into a block of Roo.data.Records.
12367 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12368 * The function must be passed <ul>
12369 * <li>The Record block object</li>
12370 * <li>The "arg" argument from the load function</li>
12371 * <li>A boolean success indicator</li>
12373 * @param {Object} scope The scope in which to call the callback
12374 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12376 load : function(params, reader, callback, scope, arg){
12377 if(this.fireEvent("beforeload", this, params) !== false){
12379 params : params || {},
12381 callback : callback,
12386 callback : this.loadResponse,
12390 Roo.applyIf(o, this.conn);
12391 if(this.activeRequest){
12392 Roo.Ajax.abort(this.activeRequest);
12394 this.activeRequest = Roo.Ajax.request(o);
12396 this.conn.request(o);
12399 callback.call(scope||this, null, arg, false);
12404 loadResponse : function(o, success, response){
12405 delete this.activeRequest;
12407 this.fireEvent("loadexception", this, o, response);
12408 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12413 result = o.reader.read(response);
12415 this.fireEvent("loadexception", this, o, response, e);
12416 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12420 this.fireEvent("load", this, o, o.request.arg);
12421 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12425 update : function(dataSet){
12430 updateResponse : function(dataSet){
12435 * Ext JS Library 1.1.1
12436 * Copyright(c) 2006-2007, Ext JS, LLC.
12438 * Originally Released Under LGPL - original licence link has changed is not relivant.
12441 * <script type="text/javascript">
12445 * @class Roo.data.ScriptTagProxy
12446 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12447 * other than the originating domain of the running page.<br><br>
12449 * <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
12450 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12452 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12453 * source code that is used as the source inside a <script> tag.<br><br>
12455 * In order for the browser to process the returned data, the server must wrap the data object
12456 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12457 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12458 * depending on whether the callback name was passed:
12461 boolean scriptTag = false;
12462 String cb = request.getParameter("callback");
12465 response.setContentType("text/javascript");
12467 response.setContentType("application/x-json");
12469 Writer out = response.getWriter();
12471 out.write(cb + "(");
12473 out.print(dataBlock.toJsonString());
12480 * @param {Object} config A configuration object.
12482 Roo.data.ScriptTagProxy = function(config){
12483 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12484 Roo.apply(this, config);
12485 this.head = document.getElementsByTagName("head")[0];
12488 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12490 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12492 * @cfg {String} url The URL from which to request the data object.
12495 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12499 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12500 * the server the name of the callback function set up by the load call to process the returned data object.
12501 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12502 * javascript output which calls this named function passing the data object as its only parameter.
12504 callbackParam : "callback",
12506 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12507 * name to the request.
12512 * Load data from the configured URL, read the data object into
12513 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12514 * process that block using the passed callback.
12515 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12516 * for the request to the remote server.
12517 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12518 * object into a block of Roo.data.Records.
12519 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12520 * The function must be passed <ul>
12521 * <li>The Record block object</li>
12522 * <li>The "arg" argument from the load function</li>
12523 * <li>A boolean success indicator</li>
12525 * @param {Object} scope The scope in which to call the callback
12526 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12528 load : function(params, reader, callback, scope, arg){
12529 if(this.fireEvent("beforeload", this, params) !== false){
12531 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12533 var url = this.url;
12534 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12536 url += "&_dc=" + (new Date().getTime());
12538 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12541 cb : "stcCallback"+transId,
12542 scriptId : "stcScript"+transId,
12546 callback : callback,
12552 window[trans.cb] = function(o){
12553 conn.handleResponse(o, trans);
12556 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12558 if(this.autoAbort !== false){
12562 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12564 var script = document.createElement("script");
12565 script.setAttribute("src", url);
12566 script.setAttribute("type", "text/javascript");
12567 script.setAttribute("id", trans.scriptId);
12568 this.head.appendChild(script);
12570 this.trans = trans;
12572 callback.call(scope||this, null, arg, false);
12577 isLoading : function(){
12578 return this.trans ? true : false;
12582 * Abort the current server request.
12584 abort : function(){
12585 if(this.isLoading()){
12586 this.destroyTrans(this.trans);
12591 destroyTrans : function(trans, isLoaded){
12592 this.head.removeChild(document.getElementById(trans.scriptId));
12593 clearTimeout(trans.timeoutId);
12595 window[trans.cb] = undefined;
12597 delete window[trans.cb];
12600 // if hasn't been loaded, wait for load to remove it to prevent script error
12601 window[trans.cb] = function(){
12602 window[trans.cb] = undefined;
12604 delete window[trans.cb];
12611 handleResponse : function(o, trans){
12612 this.trans = false;
12613 this.destroyTrans(trans, true);
12616 result = trans.reader.readRecords(o);
12618 this.fireEvent("loadexception", this, o, trans.arg, e);
12619 trans.callback.call(trans.scope||window, null, trans.arg, false);
12622 this.fireEvent("load", this, o, trans.arg);
12623 trans.callback.call(trans.scope||window, result, trans.arg, true);
12627 handleFailure : function(trans){
12628 this.trans = false;
12629 this.destroyTrans(trans, false);
12630 this.fireEvent("loadexception", this, null, trans.arg);
12631 trans.callback.call(trans.scope||window, null, trans.arg, false);
12635 * Ext JS Library 1.1.1
12636 * Copyright(c) 2006-2007, Ext JS, LLC.
12638 * Originally Released Under LGPL - original licence link has changed is not relivant.
12641 * <script type="text/javascript">
12645 * @class Roo.data.JsonReader
12646 * @extends Roo.data.DataReader
12647 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12648 * based on mappings in a provided Roo.data.Record constructor.
12650 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12651 * in the reply previously.
12656 var RecordDef = Roo.data.Record.create([
12657 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12658 {name: 'occupation'} // This field will use "occupation" as the mapping.
12660 var myReader = new Roo.data.JsonReader({
12661 totalProperty: "results", // The property which contains the total dataset size (optional)
12662 root: "rows", // The property which contains an Array of row objects
12663 id: "id" // The property within each row object that provides an ID for the record (optional)
12667 * This would consume a JSON file like this:
12669 { 'results': 2, 'rows': [
12670 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12671 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12674 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12675 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12676 * paged from the remote server.
12677 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12678 * @cfg {String} root name of the property which contains the Array of row objects.
12679 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12680 * @cfg {Array} fields Array of field definition objects
12682 * Create a new JsonReader
12683 * @param {Object} meta Metadata configuration options
12684 * @param {Object} recordType Either an Array of field definition objects,
12685 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12687 Roo.data.JsonReader = function(meta, recordType){
12690 // set some defaults:
12691 Roo.applyIf(meta, {
12692 totalProperty: 'total',
12693 successProperty : 'success',
12698 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12700 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12703 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12704 * Used by Store query builder to append _requestMeta to params.
12707 metaFromRemote : false,
12709 * This method is only used by a DataProxy which has retrieved data from a remote server.
12710 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12711 * @return {Object} data A data block which is used by an Roo.data.Store object as
12712 * a cache of Roo.data.Records.
12714 read : function(response){
12715 var json = response.responseText;
12717 var o = /* eval:var:o */ eval("("+json+")");
12719 throw {message: "JsonReader.read: Json object not found"};
12725 this.metaFromRemote = true;
12726 this.meta = o.metaData;
12727 this.recordType = Roo.data.Record.create(o.metaData.fields);
12728 this.onMetaChange(this.meta, this.recordType, o);
12730 return this.readRecords(o);
12733 // private function a store will implement
12734 onMetaChange : function(meta, recordType, o){
12741 simpleAccess: function(obj, subsc) {
12748 getJsonAccessor: function(){
12750 return function(expr) {
12752 return(re.test(expr))
12753 ? new Function("obj", "return obj." + expr)
12758 return Roo.emptyFn;
12763 * Create a data block containing Roo.data.Records from an XML document.
12764 * @param {Object} o An object which contains an Array of row objects in the property specified
12765 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12766 * which contains the total size of the dataset.
12767 * @return {Object} data A data block which is used by an Roo.data.Store object as
12768 * a cache of Roo.data.Records.
12770 readRecords : function(o){
12772 * After any data loads, the raw JSON data is available for further custom processing.
12776 var s = this.meta, Record = this.recordType,
12777 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12779 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12781 if(s.totalProperty) {
12782 this.getTotal = this.getJsonAccessor(s.totalProperty);
12784 if(s.successProperty) {
12785 this.getSuccess = this.getJsonAccessor(s.successProperty);
12787 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12789 var g = this.getJsonAccessor(s.id);
12790 this.getId = function(rec) {
12792 return (r === undefined || r === "") ? null : r;
12795 this.getId = function(){return null;};
12798 for(var jj = 0; jj < fl; jj++){
12800 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12801 this.ef[jj] = this.getJsonAccessor(map);
12805 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12806 if(s.totalProperty){
12807 var vt = parseInt(this.getTotal(o), 10);
12812 if(s.successProperty){
12813 var vs = this.getSuccess(o);
12814 if(vs === false || vs === 'false'){
12819 for(var i = 0; i < c; i++){
12822 var id = this.getId(n);
12823 for(var j = 0; j < fl; j++){
12825 var v = this.ef[j](n);
12827 Roo.log('missing convert for ' + f.name);
12831 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12833 var record = new Record(values, id);
12835 records[i] = record;
12841 totalRecords : totalRecords
12846 * Ext JS Library 1.1.1
12847 * Copyright(c) 2006-2007, Ext JS, LLC.
12849 * Originally Released Under LGPL - original licence link has changed is not relivant.
12852 * <script type="text/javascript">
12856 * @class Roo.data.ArrayReader
12857 * @extends Roo.data.DataReader
12858 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12859 * Each element of that Array represents a row of data fields. The
12860 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12861 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12865 var RecordDef = Roo.data.Record.create([
12866 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12867 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12869 var myReader = new Roo.data.ArrayReader({
12870 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12874 * This would consume an Array like this:
12876 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12878 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12880 * Create a new JsonReader
12881 * @param {Object} meta Metadata configuration options.
12882 * @param {Object} recordType Either an Array of field definition objects
12883 * as specified to {@link Roo.data.Record#create},
12884 * or an {@link Roo.data.Record} object
12885 * created using {@link Roo.data.Record#create}.
12887 Roo.data.ArrayReader = function(meta, recordType){
12888 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12891 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12893 * Create a data block containing Roo.data.Records from an XML document.
12894 * @param {Object} o An Array of row objects which represents the dataset.
12895 * @return {Object} data A data block which is used by an Roo.data.Store object as
12896 * a cache of Roo.data.Records.
12898 readRecords : function(o){
12899 var sid = this.meta ? this.meta.id : null;
12900 var recordType = this.recordType, fields = recordType.prototype.fields;
12903 for(var i = 0; i < root.length; i++){
12906 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12907 for(var j = 0, jlen = fields.length; j < jlen; j++){
12908 var f = fields.items[j];
12909 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12910 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12912 values[f.name] = v;
12914 var record = new recordType(values, id);
12916 records[records.length] = record;
12920 totalRecords : records.length
12929 * @class Roo.bootstrap.ComboBox
12930 * @extends Roo.bootstrap.TriggerField
12931 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12932 * @cfg {Boolean} append (true|false) default false
12933 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12934 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12935 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12936 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12937 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12938 * @cfg {Boolean} animate default true
12939 * @cfg {Boolean} emptyResultText only for touch device
12940 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12941 * @cfg {String} emptyTitle default ''
12943 * Create a new ComboBox.
12944 * @param {Object} config Configuration options
12946 Roo.bootstrap.ComboBox = function(config){
12947 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12951 * Fires when the dropdown list is expanded
12952 * @param {Roo.bootstrap.ComboBox} combo This combo box
12957 * Fires when the dropdown list is collapsed
12958 * @param {Roo.bootstrap.ComboBox} combo This combo box
12962 * @event beforeselect
12963 * Fires before a list item is selected. Return false to cancel the selection.
12964 * @param {Roo.bootstrap.ComboBox} combo This combo box
12965 * @param {Roo.data.Record} record The data record returned from the underlying store
12966 * @param {Number} index The index of the selected item in the dropdown list
12968 'beforeselect' : true,
12971 * Fires when a list item is selected
12972 * @param {Roo.bootstrap.ComboBox} combo This combo box
12973 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12974 * @param {Number} index The index of the selected item in the dropdown list
12978 * @event beforequery
12979 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12980 * The event object passed has these properties:
12981 * @param {Roo.bootstrap.ComboBox} combo This combo box
12982 * @param {String} query The query
12983 * @param {Boolean} forceAll true to force "all" query
12984 * @param {Boolean} cancel true to cancel the query
12985 * @param {Object} e The query event object
12987 'beforequery': true,
12990 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12991 * @param {Roo.bootstrap.ComboBox} combo This combo box
12996 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12997 * @param {Roo.bootstrap.ComboBox} combo This combo box
12998 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13003 * Fires when the remove value from the combobox array
13004 * @param {Roo.bootstrap.ComboBox} combo This combo box
13008 * @event afterremove
13009 * Fires when the remove value from the combobox array
13010 * @param {Roo.bootstrap.ComboBox} combo This combo box
13012 'afterremove' : true,
13014 * @event specialfilter
13015 * Fires when specialfilter
13016 * @param {Roo.bootstrap.ComboBox} combo This combo box
13018 'specialfilter' : true,
13021 * Fires when tick the element
13022 * @param {Roo.bootstrap.ComboBox} combo This combo box
13026 * @event touchviewdisplay
13027 * Fires when touch view require special display (default is using displayField)
13028 * @param {Roo.bootstrap.ComboBox} combo This combo box
13029 * @param {Object} cfg set html .
13031 'touchviewdisplay' : true
13036 this.tickItems = [];
13038 this.selectedIndex = -1;
13039 if(this.mode == 'local'){
13040 if(config.queryDelay === undefined){
13041 this.queryDelay = 10;
13043 if(config.minChars === undefined){
13049 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13052 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13053 * rendering into an Roo.Editor, defaults to false)
13056 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13057 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13060 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13063 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13064 * the dropdown list (defaults to undefined, with no header element)
13068 * @cfg {String/Roo.Template} tpl The template to use to render the output
13072 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13074 listWidth: undefined,
13076 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13077 * mode = 'remote' or 'text' if mode = 'local')
13079 displayField: undefined,
13082 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13083 * mode = 'remote' or 'value' if mode = 'local').
13084 * Note: use of a valueField requires the user make a selection
13085 * in order for a value to be mapped.
13087 valueField: undefined,
13089 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13094 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13095 * field's data value (defaults to the underlying DOM element's name)
13097 hiddenName: undefined,
13099 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13103 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13105 selectedClass: 'active',
13108 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13112 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13113 * anchor positions (defaults to 'tl-bl')
13115 listAlign: 'tl-bl?',
13117 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13121 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13122 * query specified by the allQuery config option (defaults to 'query')
13124 triggerAction: 'query',
13126 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13127 * (defaults to 4, does not apply if editable = false)
13131 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13132 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13136 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13137 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13141 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13142 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13146 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13147 * when editable = true (defaults to false)
13149 selectOnFocus:false,
13151 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13153 queryParam: 'query',
13155 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13156 * when mode = 'remote' (defaults to 'Loading...')
13158 loadingText: 'Loading...',
13160 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13164 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13168 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13169 * traditional select (defaults to true)
13173 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13177 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13181 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13182 * listWidth has a higher value)
13186 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13187 * allow the user to set arbitrary text into the field (defaults to false)
13189 forceSelection:false,
13191 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13192 * if typeAhead = true (defaults to 250)
13194 typeAheadDelay : 250,
13196 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13197 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13199 valueNotFoundText : undefined,
13201 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13203 blockFocus : false,
13206 * @cfg {Boolean} disableClear Disable showing of clear button.
13208 disableClear : false,
13210 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13212 alwaysQuery : false,
13215 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13220 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13222 invalidClass : "has-warning",
13225 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13227 validClass : "has-success",
13230 * @cfg {Boolean} specialFilter (true|false) special filter default false
13232 specialFilter : false,
13235 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13237 mobileTouchView : true,
13240 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13242 useNativeIOS : false,
13245 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13247 mobile_restrict_height : false,
13249 ios_options : false,
13261 btnPosition : 'right',
13262 triggerList : true,
13263 showToggleBtn : true,
13265 emptyResultText: 'Empty',
13266 triggerText : 'Select',
13269 // element that contains real text value.. (when hidden is used..)
13271 getAutoCreate : function()
13276 * Render classic select for iso
13279 if(Roo.isIOS && this.useNativeIOS){
13280 cfg = this.getAutoCreateNativeIOS();
13288 if(Roo.isTouch && this.mobileTouchView){
13289 cfg = this.getAutoCreateTouchView();
13296 if(!this.tickable){
13297 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13302 * ComboBox with tickable selections
13305 var align = this.labelAlign || this.parentLabelAlign();
13308 cls : 'form-group roo-combobox-tickable' //input-group
13311 var btn_text_select = '';
13312 var btn_text_done = '';
13313 var btn_text_cancel = '';
13315 if (this.btn_text_show) {
13316 btn_text_select = 'Select';
13317 btn_text_done = 'Done';
13318 btn_text_cancel = 'Cancel';
13323 cls : 'tickable-buttons',
13328 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13329 //html : this.triggerText
13330 html: btn_text_select
13336 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13338 html: btn_text_done
13344 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13346 html: btn_text_cancel
13352 buttons.cn.unshift({
13354 cls: 'roo-select2-search-field-input'
13360 Roo.each(buttons.cn, function(c){
13362 c.cls += ' btn-' + _this.size;
13365 if (_this.disabled) {
13376 cls: 'form-hidden-field'
13380 cls: 'roo-select2-choices',
13384 cls: 'roo-select2-search-field',
13395 cls: 'roo-select2-container input-group roo-select2-container-multi',
13401 // cls: 'typeahead typeahead-long dropdown-menu',
13402 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13407 if(this.hasFeedback && !this.allowBlank){
13411 cls: 'glyphicon form-control-feedback'
13414 combobox.cn.push(feedback);
13419 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13420 tooltip : 'This field is required'
13422 if (Roo.bootstrap.version == 4) {
13425 style : 'display:none'
13428 if (align ==='left' && this.fieldLabel.length) {
13430 cfg.cls += ' roo-form-group-label-left row';
13437 cls : 'control-label col-form-label',
13438 html : this.fieldLabel
13450 var labelCfg = cfg.cn[1];
13451 var contentCfg = cfg.cn[2];
13454 if(this.indicatorpos == 'right'){
13460 cls : 'control-label col-form-label',
13464 html : this.fieldLabel
13480 labelCfg = cfg.cn[0];
13481 contentCfg = cfg.cn[1];
13485 if(this.labelWidth > 12){
13486 labelCfg.style = "width: " + this.labelWidth + 'px';
13489 if(this.labelWidth < 13 && this.labelmd == 0){
13490 this.labelmd = this.labelWidth;
13493 if(this.labellg > 0){
13494 labelCfg.cls += ' col-lg-' + this.labellg;
13495 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13498 if(this.labelmd > 0){
13499 labelCfg.cls += ' col-md-' + this.labelmd;
13500 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13503 if(this.labelsm > 0){
13504 labelCfg.cls += ' col-sm-' + this.labelsm;
13505 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13508 if(this.labelxs > 0){
13509 labelCfg.cls += ' col-xs-' + this.labelxs;
13510 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13514 } else if ( this.fieldLabel.length) {
13515 // Roo.log(" label");
13520 //cls : 'input-group-addon',
13521 html : this.fieldLabel
13526 if(this.indicatorpos == 'right'){
13530 //cls : 'input-group-addon',
13531 html : this.fieldLabel
13541 // Roo.log(" no label && no align");
13548 ['xs','sm','md','lg'].map(function(size){
13549 if (settings[size]) {
13550 cfg.cls += ' col-' + size + '-' + settings[size];
13558 _initEventsCalled : false,
13561 initEvents: function()
13563 if (this._initEventsCalled) { // as we call render... prevent looping...
13566 this._initEventsCalled = true;
13569 throw "can not find store for combo";
13572 this.indicator = this.indicatorEl();
13574 this.store = Roo.factory(this.store, Roo.data);
13575 this.store.parent = this;
13577 // if we are building from html. then this element is so complex, that we can not really
13578 // use the rendered HTML.
13579 // so we have to trash and replace the previous code.
13580 if (Roo.XComponent.build_from_html) {
13581 // remove this element....
13582 var e = this.el.dom, k=0;
13583 while (e ) { e = e.previousSibling; ++k;}
13588 this.rendered = false;
13590 this.render(this.parent().getChildContainer(true), k);
13593 if(Roo.isIOS && this.useNativeIOS){
13594 this.initIOSView();
13602 if(Roo.isTouch && this.mobileTouchView){
13603 this.initTouchView();
13608 this.initTickableEvents();
13612 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13614 if(this.hiddenName){
13616 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13618 this.hiddenField.dom.value =
13619 this.hiddenValue !== undefined ? this.hiddenValue :
13620 this.value !== undefined ? this.value : '';
13622 // prevent input submission
13623 this.el.dom.removeAttribute('name');
13624 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13629 // this.el.dom.setAttribute('autocomplete', 'off');
13632 var cls = 'x-combo-list';
13634 //this.list = new Roo.Layer({
13635 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13641 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13642 _this.list.setWidth(lw);
13645 this.list.on('mouseover', this.onViewOver, this);
13646 this.list.on('mousemove', this.onViewMove, this);
13647 this.list.on('scroll', this.onViewScroll, this);
13650 this.list.swallowEvent('mousewheel');
13651 this.assetHeight = 0;
13654 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13655 this.assetHeight += this.header.getHeight();
13658 this.innerList = this.list.createChild({cls:cls+'-inner'});
13659 this.innerList.on('mouseover', this.onViewOver, this);
13660 this.innerList.on('mousemove', this.onViewMove, this);
13661 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13663 if(this.allowBlank && !this.pageSize && !this.disableClear){
13664 this.footer = this.list.createChild({cls:cls+'-ft'});
13665 this.pageTb = new Roo.Toolbar(this.footer);
13669 this.footer = this.list.createChild({cls:cls+'-ft'});
13670 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13671 {pageSize: this.pageSize});
13675 if (this.pageTb && this.allowBlank && !this.disableClear) {
13677 this.pageTb.add(new Roo.Toolbar.Fill(), {
13678 cls: 'x-btn-icon x-btn-clear',
13680 handler: function()
13683 _this.clearValue();
13684 _this.onSelect(false, -1);
13689 this.assetHeight += this.footer.getHeight();
13694 this.tpl = Roo.bootstrap.version == 4 ?
13695 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13696 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13699 this.view = new Roo.View(this.list, this.tpl, {
13700 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13702 //this.view.wrapEl.setDisplayed(false);
13703 this.view.on('click', this.onViewClick, this);
13706 this.store.on('beforeload', this.onBeforeLoad, this);
13707 this.store.on('load', this.onLoad, this);
13708 this.store.on('loadexception', this.onLoadException, this);
13710 if(this.resizable){
13711 this.resizer = new Roo.Resizable(this.list, {
13712 pinned:true, handles:'se'
13714 this.resizer.on('resize', function(r, w, h){
13715 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13716 this.listWidth = w;
13717 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13718 this.restrictHeight();
13720 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13723 if(!this.editable){
13724 this.editable = true;
13725 this.setEditable(false);
13730 if (typeof(this.events.add.listeners) != 'undefined') {
13732 this.addicon = this.wrap.createChild(
13733 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13735 this.addicon.on('click', function(e) {
13736 this.fireEvent('add', this);
13739 if (typeof(this.events.edit.listeners) != 'undefined') {
13741 this.editicon = this.wrap.createChild(
13742 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13743 if (this.addicon) {
13744 this.editicon.setStyle('margin-left', '40px');
13746 this.editicon.on('click', function(e) {
13748 // we fire even if inothing is selected..
13749 this.fireEvent('edit', this, this.lastData );
13755 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13756 "up" : function(e){
13757 this.inKeyMode = true;
13761 "down" : function(e){
13762 if(!this.isExpanded()){
13763 this.onTriggerClick();
13765 this.inKeyMode = true;
13770 "enter" : function(e){
13771 // this.onViewClick();
13775 if(this.fireEvent("specialkey", this, e)){
13776 this.onViewClick(false);
13782 "esc" : function(e){
13786 "tab" : function(e){
13789 if(this.fireEvent("specialkey", this, e)){
13790 this.onViewClick(false);
13798 doRelay : function(foo, bar, hname){
13799 if(hname == 'down' || this.scope.isExpanded()){
13800 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13809 this.queryDelay = Math.max(this.queryDelay || 10,
13810 this.mode == 'local' ? 10 : 250);
13813 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13815 if(this.typeAhead){
13816 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13818 if(this.editable !== false){
13819 this.inputEl().on("keyup", this.onKeyUp, this);
13821 if(this.forceSelection){
13822 this.inputEl().on('blur', this.doForce, this);
13826 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13827 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13831 initTickableEvents: function()
13835 if(this.hiddenName){
13837 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13839 this.hiddenField.dom.value =
13840 this.hiddenValue !== undefined ? this.hiddenValue :
13841 this.value !== undefined ? this.value : '';
13843 // prevent input submission
13844 this.el.dom.removeAttribute('name');
13845 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13850 // this.list = this.el.select('ul.dropdown-menu',true).first();
13852 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13853 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13854 if(this.triggerList){
13855 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13858 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13859 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13861 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13862 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13864 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13865 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13867 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13868 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13869 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13872 this.cancelBtn.hide();
13877 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13878 _this.list.setWidth(lw);
13881 this.list.on('mouseover', this.onViewOver, this);
13882 this.list.on('mousemove', this.onViewMove, this);
13884 this.list.on('scroll', this.onViewScroll, this);
13887 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13888 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13891 this.view = new Roo.View(this.list, this.tpl, {
13896 selectedClass: this.selectedClass
13899 //this.view.wrapEl.setDisplayed(false);
13900 this.view.on('click', this.onViewClick, this);
13904 this.store.on('beforeload', this.onBeforeLoad, this);
13905 this.store.on('load', this.onLoad, this);
13906 this.store.on('loadexception', this.onLoadException, this);
13909 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13910 "up" : function(e){
13911 this.inKeyMode = true;
13915 "down" : function(e){
13916 this.inKeyMode = true;
13920 "enter" : function(e){
13921 if(this.fireEvent("specialkey", this, e)){
13922 this.onViewClick(false);
13928 "esc" : function(e){
13929 this.onTickableFooterButtonClick(e, false, false);
13932 "tab" : function(e){
13933 this.fireEvent("specialkey", this, e);
13935 this.onTickableFooterButtonClick(e, false, false);
13942 doRelay : function(e, fn, key){
13943 if(this.scope.isExpanded()){
13944 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13953 this.queryDelay = Math.max(this.queryDelay || 10,
13954 this.mode == 'local' ? 10 : 250);
13957 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13959 if(this.typeAhead){
13960 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13963 if(this.editable !== false){
13964 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13967 this.indicator = this.indicatorEl();
13969 if(this.indicator){
13970 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13971 this.indicator.hide();
13976 onDestroy : function(){
13978 this.view.setStore(null);
13979 this.view.el.removeAllListeners();
13980 this.view.el.remove();
13981 this.view.purgeListeners();
13984 this.list.dom.innerHTML = '';
13988 this.store.un('beforeload', this.onBeforeLoad, this);
13989 this.store.un('load', this.onLoad, this);
13990 this.store.un('loadexception', this.onLoadException, this);
13992 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13996 fireKey : function(e){
13997 if(e.isNavKeyPress() && !this.list.isVisible()){
13998 this.fireEvent("specialkey", this, e);
14003 onResize: function(w, h){
14004 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14006 // if(typeof w != 'number'){
14007 // // we do not handle it!?!?
14010 // var tw = this.trigger.getWidth();
14011 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14012 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14014 // this.inputEl().setWidth( this.adjustWidth('input', x));
14016 // //this.trigger.setStyle('left', x+'px');
14018 // if(this.list && this.listWidth === undefined){
14019 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14020 // this.list.setWidth(lw);
14021 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14029 * Allow or prevent the user from directly editing the field text. If false is passed,
14030 * the user will only be able to select from the items defined in the dropdown list. This method
14031 * is the runtime equivalent of setting the 'editable' config option at config time.
14032 * @param {Boolean} value True to allow the user to directly edit the field text
14034 setEditable : function(value){
14035 if(value == this.editable){
14038 this.editable = value;
14040 this.inputEl().dom.setAttribute('readOnly', true);
14041 this.inputEl().on('mousedown', this.onTriggerClick, this);
14042 this.inputEl().addClass('x-combo-noedit');
14044 this.inputEl().dom.setAttribute('readOnly', false);
14045 this.inputEl().un('mousedown', this.onTriggerClick, this);
14046 this.inputEl().removeClass('x-combo-noedit');
14052 onBeforeLoad : function(combo,opts){
14053 if(!this.hasFocus){
14057 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14059 this.restrictHeight();
14060 this.selectedIndex = -1;
14064 onLoad : function(){
14066 this.hasQuery = false;
14068 if(!this.hasFocus){
14072 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14073 this.loading.hide();
14076 if(this.store.getCount() > 0){
14079 this.restrictHeight();
14080 if(this.lastQuery == this.allQuery){
14081 if(this.editable && !this.tickable){
14082 this.inputEl().dom.select();
14086 !this.selectByValue(this.value, true) &&
14089 !this.store.lastOptions ||
14090 typeof(this.store.lastOptions.add) == 'undefined' ||
14091 this.store.lastOptions.add != true
14094 this.select(0, true);
14097 if(this.autoFocus){
14100 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14101 this.taTask.delay(this.typeAheadDelay);
14105 this.onEmptyResults();
14111 onLoadException : function()
14113 this.hasQuery = false;
14115 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14116 this.loading.hide();
14119 if(this.tickable && this.editable){
14124 // only causes errors at present
14125 //Roo.log(this.store.reader.jsonData);
14126 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14128 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14134 onTypeAhead : function(){
14135 if(this.store.getCount() > 0){
14136 var r = this.store.getAt(0);
14137 var newValue = r.data[this.displayField];
14138 var len = newValue.length;
14139 var selStart = this.getRawValue().length;
14141 if(selStart != len){
14142 this.setRawValue(newValue);
14143 this.selectText(selStart, newValue.length);
14149 onSelect : function(record, index){
14151 if(this.fireEvent('beforeselect', this, record, index) !== false){
14153 this.setFromData(index > -1 ? record.data : false);
14156 this.fireEvent('select', this, record, index);
14161 * Returns the currently selected field value or empty string if no value is set.
14162 * @return {String} value The selected value
14164 getValue : function()
14166 if(Roo.isIOS && this.useNativeIOS){
14167 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14171 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14174 if(this.valueField){
14175 return typeof this.value != 'undefined' ? this.value : '';
14177 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14181 getRawValue : function()
14183 if(Roo.isIOS && this.useNativeIOS){
14184 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14187 var v = this.inputEl().getValue();
14193 * Clears any text/value currently set in the field
14195 clearValue : function(){
14197 if(this.hiddenField){
14198 this.hiddenField.dom.value = '';
14201 this.setRawValue('');
14202 this.lastSelectionText = '';
14203 this.lastData = false;
14205 var close = this.closeTriggerEl();
14216 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14217 * will be displayed in the field. If the value does not match the data value of an existing item,
14218 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14219 * Otherwise the field will be blank (although the value will still be set).
14220 * @param {String} value The value to match
14222 setValue : function(v)
14224 if(Roo.isIOS && this.useNativeIOS){
14225 this.setIOSValue(v);
14235 if(this.valueField){
14236 var r = this.findRecord(this.valueField, v);
14238 text = r.data[this.displayField];
14239 }else if(this.valueNotFoundText !== undefined){
14240 text = this.valueNotFoundText;
14243 this.lastSelectionText = text;
14244 if(this.hiddenField){
14245 this.hiddenField.dom.value = v;
14247 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14250 var close = this.closeTriggerEl();
14253 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14259 * @property {Object} the last set data for the element
14264 * Sets the value of the field based on a object which is related to the record format for the store.
14265 * @param {Object} value the value to set as. or false on reset?
14267 setFromData : function(o){
14274 var dv = ''; // display value
14275 var vv = ''; // value value..
14277 if (this.displayField) {
14278 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14280 // this is an error condition!!!
14281 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14284 if(this.valueField){
14285 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14288 var close = this.closeTriggerEl();
14291 if(dv.length || vv * 1 > 0){
14293 this.blockFocus=true;
14299 if(this.hiddenField){
14300 this.hiddenField.dom.value = vv;
14302 this.lastSelectionText = dv;
14303 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14307 // no hidden field.. - we store the value in 'value', but still display
14308 // display field!!!!
14309 this.lastSelectionText = dv;
14310 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14317 reset : function(){
14318 // overridden so that last data is reset..
14325 this.setValue(this.originalValue);
14326 //this.clearInvalid();
14327 this.lastData = false;
14329 this.view.clearSelections();
14335 findRecord : function(prop, value){
14337 if(this.store.getCount() > 0){
14338 this.store.each(function(r){
14339 if(r.data[prop] == value){
14349 getName: function()
14351 // returns hidden if it's set..
14352 if (!this.rendered) {return ''};
14353 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14357 onViewMove : function(e, t){
14358 this.inKeyMode = false;
14362 onViewOver : function(e, t){
14363 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14366 var item = this.view.findItemFromChild(t);
14369 var index = this.view.indexOf(item);
14370 this.select(index, false);
14375 onViewClick : function(view, doFocus, el, e)
14377 var index = this.view.getSelectedIndexes()[0];
14379 var r = this.store.getAt(index);
14383 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14390 Roo.each(this.tickItems, function(v,k){
14392 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14394 _this.tickItems.splice(k, 1);
14396 if(typeof(e) == 'undefined' && view == false){
14397 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14409 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14410 this.tickItems.push(r.data);
14413 if(typeof(e) == 'undefined' && view == false){
14414 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14421 this.onSelect(r, index);
14423 if(doFocus !== false && !this.blockFocus){
14424 this.inputEl().focus();
14429 restrictHeight : function(){
14430 //this.innerList.dom.style.height = '';
14431 //var inner = this.innerList.dom;
14432 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14433 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14434 //this.list.beginUpdate();
14435 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14436 this.list.alignTo(this.inputEl(), this.listAlign);
14437 this.list.alignTo(this.inputEl(), this.listAlign);
14438 //this.list.endUpdate();
14442 onEmptyResults : function(){
14444 if(this.tickable && this.editable){
14445 this.hasFocus = false;
14446 this.restrictHeight();
14454 * Returns true if the dropdown list is expanded, else false.
14456 isExpanded : function(){
14457 return this.list.isVisible();
14461 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14462 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14463 * @param {String} value The data value of the item to select
14464 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14465 * selected item if it is not currently in view (defaults to true)
14466 * @return {Boolean} True if the value matched an item in the list, else false
14468 selectByValue : function(v, scrollIntoView){
14469 if(v !== undefined && v !== null){
14470 var r = this.findRecord(this.valueField || this.displayField, v);
14472 this.select(this.store.indexOf(r), scrollIntoView);
14480 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14481 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14482 * @param {Number} index The zero-based index of the list item to select
14483 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14484 * selected item if it is not currently in view (defaults to true)
14486 select : function(index, scrollIntoView){
14487 this.selectedIndex = index;
14488 this.view.select(index);
14489 if(scrollIntoView !== false){
14490 var el = this.view.getNode(index);
14492 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14495 this.list.scrollChildIntoView(el, false);
14501 selectNext : function(){
14502 var ct = this.store.getCount();
14504 if(this.selectedIndex == -1){
14506 }else if(this.selectedIndex < ct-1){
14507 this.select(this.selectedIndex+1);
14513 selectPrev : function(){
14514 var ct = this.store.getCount();
14516 if(this.selectedIndex == -1){
14518 }else if(this.selectedIndex != 0){
14519 this.select(this.selectedIndex-1);
14525 onKeyUp : function(e){
14526 if(this.editable !== false && !e.isSpecialKey()){
14527 this.lastKey = e.getKey();
14528 this.dqTask.delay(this.queryDelay);
14533 validateBlur : function(){
14534 return !this.list || !this.list.isVisible();
14538 initQuery : function(){
14540 var v = this.getRawValue();
14542 if(this.tickable && this.editable){
14543 v = this.tickableInputEl().getValue();
14550 doForce : function(){
14551 if(this.inputEl().dom.value.length > 0){
14552 this.inputEl().dom.value =
14553 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14559 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14560 * query allowing the query action to be canceled if needed.
14561 * @param {String} query The SQL query to execute
14562 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14563 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14564 * saved in the current store (defaults to false)
14566 doQuery : function(q, forceAll){
14568 if(q === undefined || q === null){
14573 forceAll: forceAll,
14577 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14582 forceAll = qe.forceAll;
14583 if(forceAll === true || (q.length >= this.minChars)){
14585 this.hasQuery = true;
14587 if(this.lastQuery != q || this.alwaysQuery){
14588 this.lastQuery = q;
14589 if(this.mode == 'local'){
14590 this.selectedIndex = -1;
14592 this.store.clearFilter();
14595 if(this.specialFilter){
14596 this.fireEvent('specialfilter', this);
14601 this.store.filter(this.displayField, q);
14604 this.store.fireEvent("datachanged", this.store);
14611 this.store.baseParams[this.queryParam] = q;
14613 var options = {params : this.getParams(q)};
14616 options.add = true;
14617 options.params.start = this.page * this.pageSize;
14620 this.store.load(options);
14623 * this code will make the page width larger, at the beginning, the list not align correctly,
14624 * we should expand the list on onLoad
14625 * so command out it
14630 this.selectedIndex = -1;
14635 this.loadNext = false;
14639 getParams : function(q){
14641 //p[this.queryParam] = q;
14645 p.limit = this.pageSize;
14651 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14653 collapse : function(){
14654 if(!this.isExpanded()){
14660 this.hasFocus = false;
14664 this.cancelBtn.hide();
14665 this.trigger.show();
14668 this.tickableInputEl().dom.value = '';
14669 this.tickableInputEl().blur();
14674 Roo.get(document).un('mousedown', this.collapseIf, this);
14675 Roo.get(document).un('mousewheel', this.collapseIf, this);
14676 if (!this.editable) {
14677 Roo.get(document).un('keydown', this.listKeyPress, this);
14679 this.fireEvent('collapse', this);
14685 collapseIf : function(e){
14686 var in_combo = e.within(this.el);
14687 var in_list = e.within(this.list);
14688 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14690 if (in_combo || in_list || is_list) {
14691 //e.stopPropagation();
14696 this.onTickableFooterButtonClick(e, false, false);
14704 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14706 expand : function(){
14708 if(this.isExpanded() || !this.hasFocus){
14712 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14713 this.list.setWidth(lw);
14719 this.restrictHeight();
14723 this.tickItems = Roo.apply([], this.item);
14726 this.cancelBtn.show();
14727 this.trigger.hide();
14730 this.tickableInputEl().focus();
14735 Roo.get(document).on('mousedown', this.collapseIf, this);
14736 Roo.get(document).on('mousewheel', this.collapseIf, this);
14737 if (!this.editable) {
14738 Roo.get(document).on('keydown', this.listKeyPress, this);
14741 this.fireEvent('expand', this);
14745 // Implements the default empty TriggerField.onTriggerClick function
14746 onTriggerClick : function(e)
14748 Roo.log('trigger click');
14750 if(this.disabled || !this.triggerList){
14755 this.loadNext = false;
14757 if(this.isExpanded()){
14759 if (!this.blockFocus) {
14760 this.inputEl().focus();
14764 this.hasFocus = true;
14765 if(this.triggerAction == 'all') {
14766 this.doQuery(this.allQuery, true);
14768 this.doQuery(this.getRawValue());
14770 if (!this.blockFocus) {
14771 this.inputEl().focus();
14776 onTickableTriggerClick : function(e)
14783 this.loadNext = false;
14784 this.hasFocus = true;
14786 if(this.triggerAction == 'all') {
14787 this.doQuery(this.allQuery, true);
14789 this.doQuery(this.getRawValue());
14793 onSearchFieldClick : function(e)
14795 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14796 this.onTickableFooterButtonClick(e, false, false);
14800 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14805 this.loadNext = false;
14806 this.hasFocus = true;
14808 if(this.triggerAction == 'all') {
14809 this.doQuery(this.allQuery, true);
14811 this.doQuery(this.getRawValue());
14815 listKeyPress : function(e)
14817 //Roo.log('listkeypress');
14818 // scroll to first matching element based on key pres..
14819 if (e.isSpecialKey()) {
14822 var k = String.fromCharCode(e.getKey()).toUpperCase();
14825 var csel = this.view.getSelectedNodes();
14826 var cselitem = false;
14828 var ix = this.view.indexOf(csel[0]);
14829 cselitem = this.store.getAt(ix);
14830 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14836 this.store.each(function(v) {
14838 // start at existing selection.
14839 if (cselitem.id == v.id) {
14845 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14846 match = this.store.indexOf(v);
14852 if (match === false) {
14853 return true; // no more action?
14856 this.view.select(match);
14857 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14858 sn.scrollIntoView(sn.dom.parentNode, false);
14861 onViewScroll : function(e, t){
14863 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){
14867 this.hasQuery = true;
14869 this.loading = this.list.select('.loading', true).first();
14871 if(this.loading === null){
14872 this.list.createChild({
14874 cls: 'loading roo-select2-more-results roo-select2-active',
14875 html: 'Loading more results...'
14878 this.loading = this.list.select('.loading', true).first();
14880 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14882 this.loading.hide();
14885 this.loading.show();
14890 this.loadNext = true;
14892 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14897 addItem : function(o)
14899 var dv = ''; // display value
14901 if (this.displayField) {
14902 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14904 // this is an error condition!!!
14905 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14912 var choice = this.choices.createChild({
14914 cls: 'roo-select2-search-choice',
14923 cls: 'roo-select2-search-choice-close fa fa-times',
14928 }, this.searchField);
14930 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14932 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14940 this.inputEl().dom.value = '';
14945 onRemoveItem : function(e, _self, o)
14947 e.preventDefault();
14949 this.lastItem = Roo.apply([], this.item);
14951 var index = this.item.indexOf(o.data) * 1;
14954 Roo.log('not this item?!');
14958 this.item.splice(index, 1);
14963 this.fireEvent('remove', this, e);
14969 syncValue : function()
14971 if(!this.item.length){
14978 Roo.each(this.item, function(i){
14979 if(_this.valueField){
14980 value.push(i[_this.valueField]);
14987 this.value = value.join(',');
14989 if(this.hiddenField){
14990 this.hiddenField.dom.value = this.value;
14993 this.store.fireEvent("datachanged", this.store);
14998 clearItem : function()
15000 if(!this.multiple){
15006 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15014 if(this.tickable && !Roo.isTouch){
15015 this.view.refresh();
15019 inputEl: function ()
15021 if(Roo.isIOS && this.useNativeIOS){
15022 return this.el.select('select.roo-ios-select', true).first();
15025 if(Roo.isTouch && this.mobileTouchView){
15026 return this.el.select('input.form-control',true).first();
15030 return this.searchField;
15033 return this.el.select('input.form-control',true).first();
15036 onTickableFooterButtonClick : function(e, btn, el)
15038 e.preventDefault();
15040 this.lastItem = Roo.apply([], this.item);
15042 if(btn && btn.name == 'cancel'){
15043 this.tickItems = Roo.apply([], this.item);
15052 Roo.each(this.tickItems, function(o){
15060 validate : function()
15062 if(this.getVisibilityEl().hasClass('hidden')){
15066 var v = this.getRawValue();
15069 v = this.getValue();
15072 if(this.disabled || this.allowBlank || v.length){
15077 this.markInvalid();
15081 tickableInputEl : function()
15083 if(!this.tickable || !this.editable){
15084 return this.inputEl();
15087 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15091 getAutoCreateTouchView : function()
15096 cls: 'form-group' //input-group
15102 type : this.inputType,
15103 cls : 'form-control x-combo-noedit',
15104 autocomplete: 'new-password',
15105 placeholder : this.placeholder || '',
15110 input.name = this.name;
15114 input.cls += ' input-' + this.size;
15117 if (this.disabled) {
15118 input.disabled = true;
15129 inputblock.cls += ' input-group';
15131 inputblock.cn.unshift({
15133 cls : 'input-group-addon input-group-prepend input-group-text',
15138 if(this.removable && !this.multiple){
15139 inputblock.cls += ' roo-removable';
15141 inputblock.cn.push({
15144 cls : 'roo-combo-removable-btn close'
15148 if(this.hasFeedback && !this.allowBlank){
15150 inputblock.cls += ' has-feedback';
15152 inputblock.cn.push({
15154 cls: 'glyphicon form-control-feedback'
15161 inputblock.cls += (this.before) ? '' : ' input-group';
15163 inputblock.cn.push({
15165 cls : 'input-group-addon input-group-append input-group-text',
15171 var ibwrap = inputblock;
15176 cls: 'roo-select2-choices',
15180 cls: 'roo-select2-search-field',
15193 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15198 cls: 'form-hidden-field'
15204 if(!this.multiple && this.showToggleBtn){
15211 if (this.caret != false) {
15214 cls: 'fa fa-' + this.caret
15221 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15226 cls: 'combobox-clear',
15240 combobox.cls += ' roo-select2-container-multi';
15243 var align = this.labelAlign || this.parentLabelAlign();
15245 if (align ==='left' && this.fieldLabel.length) {
15250 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15251 tooltip : 'This field is required'
15255 cls : 'control-label col-form-label',
15256 html : this.fieldLabel
15267 var labelCfg = cfg.cn[1];
15268 var contentCfg = cfg.cn[2];
15271 if(this.indicatorpos == 'right'){
15276 cls : 'control-label col-form-label',
15280 html : this.fieldLabel
15284 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15285 tooltip : 'This field is required'
15298 labelCfg = cfg.cn[0];
15299 contentCfg = cfg.cn[1];
15304 if(this.labelWidth > 12){
15305 labelCfg.style = "width: " + this.labelWidth + 'px';
15308 if(this.labelWidth < 13 && this.labelmd == 0){
15309 this.labelmd = this.labelWidth;
15312 if(this.labellg > 0){
15313 labelCfg.cls += ' col-lg-' + this.labellg;
15314 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15317 if(this.labelmd > 0){
15318 labelCfg.cls += ' col-md-' + this.labelmd;
15319 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15322 if(this.labelsm > 0){
15323 labelCfg.cls += ' col-sm-' + this.labelsm;
15324 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15327 if(this.labelxs > 0){
15328 labelCfg.cls += ' col-xs-' + this.labelxs;
15329 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15333 } else if ( this.fieldLabel.length) {
15337 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15338 tooltip : 'This field is required'
15342 cls : 'control-label',
15343 html : this.fieldLabel
15354 if(this.indicatorpos == 'right'){
15358 cls : 'control-label',
15359 html : this.fieldLabel,
15363 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15364 tooltip : 'This field is required'
15381 var settings = this;
15383 ['xs','sm','md','lg'].map(function(size){
15384 if (settings[size]) {
15385 cfg.cls += ' col-' + size + '-' + settings[size];
15392 initTouchView : function()
15394 this.renderTouchView();
15396 this.touchViewEl.on('scroll', function(){
15397 this.el.dom.scrollTop = 0;
15400 this.originalValue = this.getValue();
15402 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15404 this.inputEl().on("click", this.showTouchView, this);
15405 if (this.triggerEl) {
15406 this.triggerEl.on("click", this.showTouchView, this);
15410 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15411 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15413 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15415 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15416 this.store.on('load', this.onTouchViewLoad, this);
15417 this.store.on('loadexception', this.onTouchViewLoadException, this);
15419 if(this.hiddenName){
15421 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15423 this.hiddenField.dom.value =
15424 this.hiddenValue !== undefined ? this.hiddenValue :
15425 this.value !== undefined ? this.value : '';
15427 this.el.dom.removeAttribute('name');
15428 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15432 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15433 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15436 if(this.removable && !this.multiple){
15437 var close = this.closeTriggerEl();
15439 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15440 close.on('click', this.removeBtnClick, this, close);
15444 * fix the bug in Safari iOS8
15446 this.inputEl().on("focus", function(e){
15447 document.activeElement.blur();
15450 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15457 renderTouchView : function()
15459 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15460 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15462 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15463 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15465 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15466 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15467 this.touchViewBodyEl.setStyle('overflow', 'auto');
15469 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15470 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15472 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15473 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15477 showTouchView : function()
15483 this.touchViewHeaderEl.hide();
15485 if(this.modalTitle.length){
15486 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15487 this.touchViewHeaderEl.show();
15490 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15491 this.touchViewEl.show();
15493 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15495 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15496 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15498 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15500 if(this.modalTitle.length){
15501 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15504 this.touchViewBodyEl.setHeight(bodyHeight);
15508 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15510 this.touchViewEl.addClass('in');
15513 if(this._touchViewMask){
15514 Roo.get(document.body).addClass("x-body-masked");
15515 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15516 this._touchViewMask.setStyle('z-index', 10000);
15517 this._touchViewMask.addClass('show');
15520 this.doTouchViewQuery();
15524 hideTouchView : function()
15526 this.touchViewEl.removeClass('in');
15530 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15532 this.touchViewEl.setStyle('display', 'none');
15535 if(this._touchViewMask){
15536 this._touchViewMask.removeClass('show');
15537 Roo.get(document.body).removeClass("x-body-masked");
15541 setTouchViewValue : function()
15548 Roo.each(this.tickItems, function(o){
15553 this.hideTouchView();
15556 doTouchViewQuery : function()
15565 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15569 if(!this.alwaysQuery || this.mode == 'local'){
15570 this.onTouchViewLoad();
15577 onTouchViewBeforeLoad : function(combo,opts)
15583 onTouchViewLoad : function()
15585 if(this.store.getCount() < 1){
15586 this.onTouchViewEmptyResults();
15590 this.clearTouchView();
15592 var rawValue = this.getRawValue();
15594 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15596 this.tickItems = [];
15598 this.store.data.each(function(d, rowIndex){
15599 var row = this.touchViewListGroup.createChild(template);
15601 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15602 row.addClass(d.data.cls);
15605 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15608 html : d.data[this.displayField]
15611 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15612 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15615 row.removeClass('selected');
15616 if(!this.multiple && this.valueField &&
15617 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15620 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15621 row.addClass('selected');
15624 if(this.multiple && this.valueField &&
15625 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15629 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15630 this.tickItems.push(d.data);
15633 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15637 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15639 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15641 if(this.modalTitle.length){
15642 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15645 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15647 if(this.mobile_restrict_height && listHeight < bodyHeight){
15648 this.touchViewBodyEl.setHeight(listHeight);
15653 if(firstChecked && listHeight > bodyHeight){
15654 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15659 onTouchViewLoadException : function()
15661 this.hideTouchView();
15664 onTouchViewEmptyResults : function()
15666 this.clearTouchView();
15668 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15670 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15674 clearTouchView : function()
15676 this.touchViewListGroup.dom.innerHTML = '';
15679 onTouchViewClick : function(e, el, o)
15681 e.preventDefault();
15684 var rowIndex = o.rowIndex;
15686 var r = this.store.getAt(rowIndex);
15688 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15690 if(!this.multiple){
15691 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15692 c.dom.removeAttribute('checked');
15695 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15697 this.setFromData(r.data);
15699 var close = this.closeTriggerEl();
15705 this.hideTouchView();
15707 this.fireEvent('select', this, r, rowIndex);
15712 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15713 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15714 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15718 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15719 this.addItem(r.data);
15720 this.tickItems.push(r.data);
15724 getAutoCreateNativeIOS : function()
15727 cls: 'form-group' //input-group,
15732 cls : 'roo-ios-select'
15736 combobox.name = this.name;
15739 if (this.disabled) {
15740 combobox.disabled = true;
15743 var settings = this;
15745 ['xs','sm','md','lg'].map(function(size){
15746 if (settings[size]) {
15747 cfg.cls += ' col-' + size + '-' + settings[size];
15757 initIOSView : function()
15759 this.store.on('load', this.onIOSViewLoad, this);
15764 onIOSViewLoad : function()
15766 if(this.store.getCount() < 1){
15770 this.clearIOSView();
15772 if(this.allowBlank) {
15774 var default_text = '-- SELECT --';
15776 if(this.placeholder.length){
15777 default_text = this.placeholder;
15780 if(this.emptyTitle.length){
15781 default_text += ' - ' + this.emptyTitle + ' -';
15784 var opt = this.inputEl().createChild({
15787 html : default_text
15791 o[this.valueField] = 0;
15792 o[this.displayField] = default_text;
15794 this.ios_options.push({
15801 this.store.data.each(function(d, rowIndex){
15805 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15806 html = d.data[this.displayField];
15811 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15812 value = d.data[this.valueField];
15821 if(this.value == d.data[this.valueField]){
15822 option['selected'] = true;
15825 var opt = this.inputEl().createChild(option);
15827 this.ios_options.push({
15834 this.inputEl().on('change', function(){
15835 this.fireEvent('select', this);
15840 clearIOSView: function()
15842 this.inputEl().dom.innerHTML = '';
15844 this.ios_options = [];
15847 setIOSValue: function(v)
15851 if(!this.ios_options){
15855 Roo.each(this.ios_options, function(opts){
15857 opts.el.dom.removeAttribute('selected');
15859 if(opts.data[this.valueField] != v){
15863 opts.el.dom.setAttribute('selected', true);
15869 * @cfg {Boolean} grow
15873 * @cfg {Number} growMin
15877 * @cfg {Number} growMax
15886 Roo.apply(Roo.bootstrap.ComboBox, {
15890 cls: 'modal-header',
15912 cls: 'list-group-item',
15916 cls: 'roo-combobox-list-group-item-value'
15920 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15934 listItemCheckbox : {
15936 cls: 'list-group-item',
15940 cls: 'roo-combobox-list-group-item-value'
15944 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15960 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15965 cls: 'modal-footer',
15973 cls: 'col-xs-6 text-left',
15976 cls: 'btn btn-danger roo-touch-view-cancel',
15982 cls: 'col-xs-6 text-right',
15985 cls: 'btn btn-success roo-touch-view-ok',
15996 Roo.apply(Roo.bootstrap.ComboBox, {
15998 touchViewTemplate : {
16000 cls: 'modal fade roo-combobox-touch-view',
16004 cls: 'modal-dialog',
16005 style : 'position:fixed', // we have to fix position....
16009 cls: 'modal-content',
16011 Roo.bootstrap.ComboBox.header,
16012 Roo.bootstrap.ComboBox.body,
16013 Roo.bootstrap.ComboBox.footer
16022 * Ext JS Library 1.1.1
16023 * Copyright(c) 2006-2007, Ext JS, LLC.
16025 * Originally Released Under LGPL - original licence link has changed is not relivant.
16028 * <script type="text/javascript">
16033 * @extends Roo.util.Observable
16034 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16035 * This class also supports single and multi selection modes. <br>
16036 * Create a data model bound view:
16038 var store = new Roo.data.Store(...);
16040 var view = new Roo.View({
16042 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16044 singleSelect: true,
16045 selectedClass: "ydataview-selected",
16049 // listen for node click?
16050 view.on("click", function(vw, index, node, e){
16051 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16055 dataModel.load("foobar.xml");
16057 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16059 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16060 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16062 * Note: old style constructor is still suported (container, template, config)
16065 * Create a new View
16066 * @param {Object} config The config object
16069 Roo.View = function(config, depreciated_tpl, depreciated_config){
16071 this.parent = false;
16073 if (typeof(depreciated_tpl) == 'undefined') {
16074 // new way.. - universal constructor.
16075 Roo.apply(this, config);
16076 this.el = Roo.get(this.el);
16079 this.el = Roo.get(config);
16080 this.tpl = depreciated_tpl;
16081 Roo.apply(this, depreciated_config);
16083 this.wrapEl = this.el.wrap().wrap();
16084 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16087 if(typeof(this.tpl) == "string"){
16088 this.tpl = new Roo.Template(this.tpl);
16090 // support xtype ctors..
16091 this.tpl = new Roo.factory(this.tpl, Roo);
16095 this.tpl.compile();
16100 * @event beforeclick
16101 * Fires before a click is processed. Returns false to cancel the default action.
16102 * @param {Roo.View} this
16103 * @param {Number} index The index of the target node
16104 * @param {HTMLElement} node The target node
16105 * @param {Roo.EventObject} e The raw event object
16107 "beforeclick" : true,
16110 * Fires when a template node is clicked.
16111 * @param {Roo.View} this
16112 * @param {Number} index The index of the target node
16113 * @param {HTMLElement} node The target node
16114 * @param {Roo.EventObject} e The raw event object
16119 * Fires when a template node is double clicked.
16120 * @param {Roo.View} this
16121 * @param {Number} index The index of the target node
16122 * @param {HTMLElement} node The target node
16123 * @param {Roo.EventObject} e The raw event object
16127 * @event contextmenu
16128 * Fires when a template node is right clicked.
16129 * @param {Roo.View} this
16130 * @param {Number} index The index of the target node
16131 * @param {HTMLElement} node The target node
16132 * @param {Roo.EventObject} e The raw event object
16134 "contextmenu" : true,
16136 * @event selectionchange
16137 * Fires when the selected nodes change.
16138 * @param {Roo.View} this
16139 * @param {Array} selections Array of the selected nodes
16141 "selectionchange" : true,
16144 * @event beforeselect
16145 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16146 * @param {Roo.View} this
16147 * @param {HTMLElement} node The node to be selected
16148 * @param {Array} selections Array of currently selected nodes
16150 "beforeselect" : true,
16152 * @event preparedata
16153 * Fires on every row to render, to allow you to change the data.
16154 * @param {Roo.View} this
16155 * @param {Object} data to be rendered (change this)
16157 "preparedata" : true
16165 "click": this.onClick,
16166 "dblclick": this.onDblClick,
16167 "contextmenu": this.onContextMenu,
16171 this.selections = [];
16173 this.cmp = new Roo.CompositeElementLite([]);
16175 this.store = Roo.factory(this.store, Roo.data);
16176 this.setStore(this.store, true);
16179 if ( this.footer && this.footer.xtype) {
16181 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16183 this.footer.dataSource = this.store;
16184 this.footer.container = fctr;
16185 this.footer = Roo.factory(this.footer, Roo);
16186 fctr.insertFirst(this.el);
16188 // this is a bit insane - as the paging toolbar seems to detach the el..
16189 // dom.parentNode.parentNode.parentNode
16190 // they get detached?
16194 Roo.View.superclass.constructor.call(this);
16199 Roo.extend(Roo.View, Roo.util.Observable, {
16202 * @cfg {Roo.data.Store} store Data store to load data from.
16207 * @cfg {String|Roo.Element} el The container element.
16212 * @cfg {String|Roo.Template} tpl The template used by this View
16216 * @cfg {String} dataName the named area of the template to use as the data area
16217 * Works with domtemplates roo-name="name"
16221 * @cfg {String} selectedClass The css class to add to selected nodes
16223 selectedClass : "x-view-selected",
16225 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16230 * @cfg {String} text to display on mask (default Loading)
16234 * @cfg {Boolean} multiSelect Allow multiple selection
16236 multiSelect : false,
16238 * @cfg {Boolean} singleSelect Allow single selection
16240 singleSelect: false,
16243 * @cfg {Boolean} toggleSelect - selecting
16245 toggleSelect : false,
16248 * @cfg {Boolean} tickable - selecting
16253 * Returns the element this view is bound to.
16254 * @return {Roo.Element}
16256 getEl : function(){
16257 return this.wrapEl;
16263 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16265 refresh : function(){
16266 //Roo.log('refresh');
16269 // if we are using something like 'domtemplate', then
16270 // the what gets used is:
16271 // t.applySubtemplate(NAME, data, wrapping data..)
16272 // the outer template then get' applied with
16273 // the store 'extra data'
16274 // and the body get's added to the
16275 // roo-name="data" node?
16276 // <span class='roo-tpl-{name}'></span> ?????
16280 this.clearSelections();
16281 this.el.update("");
16283 var records = this.store.getRange();
16284 if(records.length < 1) {
16286 // is this valid?? = should it render a template??
16288 this.el.update(this.emptyText);
16292 if (this.dataName) {
16293 this.el.update(t.apply(this.store.meta)); //????
16294 el = this.el.child('.roo-tpl-' + this.dataName);
16297 for(var i = 0, len = records.length; i < len; i++){
16298 var data = this.prepareData(records[i].data, i, records[i]);
16299 this.fireEvent("preparedata", this, data, i, records[i]);
16301 var d = Roo.apply({}, data);
16304 Roo.apply(d, {'roo-id' : Roo.id()});
16308 Roo.each(this.parent.item, function(item){
16309 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16312 Roo.apply(d, {'roo-data-checked' : 'checked'});
16316 html[html.length] = Roo.util.Format.trim(
16318 t.applySubtemplate(this.dataName, d, this.store.meta) :
16325 el.update(html.join(""));
16326 this.nodes = el.dom.childNodes;
16327 this.updateIndexes(0);
16332 * Function to override to reformat the data that is sent to
16333 * the template for each node.
16334 * DEPRICATED - use the preparedata event handler.
16335 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16336 * a JSON object for an UpdateManager bound view).
16338 prepareData : function(data, index, record)
16340 this.fireEvent("preparedata", this, data, index, record);
16344 onUpdate : function(ds, record){
16345 // Roo.log('on update');
16346 this.clearSelections();
16347 var index = this.store.indexOf(record);
16348 var n = this.nodes[index];
16349 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16350 n.parentNode.removeChild(n);
16351 this.updateIndexes(index, index);
16357 onAdd : function(ds, records, index)
16359 //Roo.log(['on Add', ds, records, index] );
16360 this.clearSelections();
16361 if(this.nodes.length == 0){
16365 var n = this.nodes[index];
16366 for(var i = 0, len = records.length; i < len; i++){
16367 var d = this.prepareData(records[i].data, i, records[i]);
16369 this.tpl.insertBefore(n, d);
16372 this.tpl.append(this.el, d);
16375 this.updateIndexes(index);
16378 onRemove : function(ds, record, index){
16379 // Roo.log('onRemove');
16380 this.clearSelections();
16381 var el = this.dataName ?
16382 this.el.child('.roo-tpl-' + this.dataName) :
16385 el.dom.removeChild(this.nodes[index]);
16386 this.updateIndexes(index);
16390 * Refresh an individual node.
16391 * @param {Number} index
16393 refreshNode : function(index){
16394 this.onUpdate(this.store, this.store.getAt(index));
16397 updateIndexes : function(startIndex, endIndex){
16398 var ns = this.nodes;
16399 startIndex = startIndex || 0;
16400 endIndex = endIndex || ns.length - 1;
16401 for(var i = startIndex; i <= endIndex; i++){
16402 ns[i].nodeIndex = i;
16407 * Changes the data store this view uses and refresh the view.
16408 * @param {Store} store
16410 setStore : function(store, initial){
16411 if(!initial && this.store){
16412 this.store.un("datachanged", this.refresh);
16413 this.store.un("add", this.onAdd);
16414 this.store.un("remove", this.onRemove);
16415 this.store.un("update", this.onUpdate);
16416 this.store.un("clear", this.refresh);
16417 this.store.un("beforeload", this.onBeforeLoad);
16418 this.store.un("load", this.onLoad);
16419 this.store.un("loadexception", this.onLoad);
16423 store.on("datachanged", this.refresh, this);
16424 store.on("add", this.onAdd, this);
16425 store.on("remove", this.onRemove, this);
16426 store.on("update", this.onUpdate, this);
16427 store.on("clear", this.refresh, this);
16428 store.on("beforeload", this.onBeforeLoad, this);
16429 store.on("load", this.onLoad, this);
16430 store.on("loadexception", this.onLoad, this);
16438 * onbeforeLoad - masks the loading area.
16441 onBeforeLoad : function(store,opts)
16443 //Roo.log('onBeforeLoad');
16445 this.el.update("");
16447 this.el.mask(this.mask ? this.mask : "Loading" );
16449 onLoad : function ()
16456 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16457 * @param {HTMLElement} node
16458 * @return {HTMLElement} The template node
16460 findItemFromChild : function(node){
16461 var el = this.dataName ?
16462 this.el.child('.roo-tpl-' + this.dataName,true) :
16465 if(!node || node.parentNode == el){
16468 var p = node.parentNode;
16469 while(p && p != el){
16470 if(p.parentNode == el){
16479 onClick : function(e){
16480 var item = this.findItemFromChild(e.getTarget());
16482 var index = this.indexOf(item);
16483 if(this.onItemClick(item, index, e) !== false){
16484 this.fireEvent("click", this, index, item, e);
16487 this.clearSelections();
16492 onContextMenu : function(e){
16493 var item = this.findItemFromChild(e.getTarget());
16495 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16500 onDblClick : function(e){
16501 var item = this.findItemFromChild(e.getTarget());
16503 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16507 onItemClick : function(item, index, e)
16509 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16512 if (this.toggleSelect) {
16513 var m = this.isSelected(item) ? 'unselect' : 'select';
16516 _t[m](item, true, false);
16519 if(this.multiSelect || this.singleSelect){
16520 if(this.multiSelect && e.shiftKey && this.lastSelection){
16521 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16523 this.select(item, this.multiSelect && e.ctrlKey);
16524 this.lastSelection = item;
16527 if(!this.tickable){
16528 e.preventDefault();
16536 * Get the number of selected nodes.
16539 getSelectionCount : function(){
16540 return this.selections.length;
16544 * Get the currently selected nodes.
16545 * @return {Array} An array of HTMLElements
16547 getSelectedNodes : function(){
16548 return this.selections;
16552 * Get the indexes of the selected nodes.
16555 getSelectedIndexes : function(){
16556 var indexes = [], s = this.selections;
16557 for(var i = 0, len = s.length; i < len; i++){
16558 indexes.push(s[i].nodeIndex);
16564 * Clear all selections
16565 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16567 clearSelections : function(suppressEvent){
16568 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16569 this.cmp.elements = this.selections;
16570 this.cmp.removeClass(this.selectedClass);
16571 this.selections = [];
16572 if(!suppressEvent){
16573 this.fireEvent("selectionchange", this, this.selections);
16579 * Returns true if the passed node is selected
16580 * @param {HTMLElement/Number} node The node or node index
16581 * @return {Boolean}
16583 isSelected : function(node){
16584 var s = this.selections;
16588 node = this.getNode(node);
16589 return s.indexOf(node) !== -1;
16594 * @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
16595 * @param {Boolean} keepExisting (optional) true to keep existing selections
16596 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16598 select : function(nodeInfo, keepExisting, suppressEvent){
16599 if(nodeInfo instanceof Array){
16601 this.clearSelections(true);
16603 for(var i = 0, len = nodeInfo.length; i < len; i++){
16604 this.select(nodeInfo[i], true, true);
16608 var node = this.getNode(nodeInfo);
16609 if(!node || this.isSelected(node)){
16610 return; // already selected.
16613 this.clearSelections(true);
16616 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16617 Roo.fly(node).addClass(this.selectedClass);
16618 this.selections.push(node);
16619 if(!suppressEvent){
16620 this.fireEvent("selectionchange", this, this.selections);
16628 * @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
16629 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16630 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16632 unselect : function(nodeInfo, keepExisting, suppressEvent)
16634 if(nodeInfo instanceof Array){
16635 Roo.each(this.selections, function(s) {
16636 this.unselect(s, nodeInfo);
16640 var node = this.getNode(nodeInfo);
16641 if(!node || !this.isSelected(node)){
16642 //Roo.log("not selected");
16643 return; // not selected.
16647 Roo.each(this.selections, function(s) {
16649 Roo.fly(node).removeClass(this.selectedClass);
16656 this.selections= ns;
16657 this.fireEvent("selectionchange", this, this.selections);
16661 * Gets a template node.
16662 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16663 * @return {HTMLElement} The node or null if it wasn't found
16665 getNode : function(nodeInfo){
16666 if(typeof nodeInfo == "string"){
16667 return document.getElementById(nodeInfo);
16668 }else if(typeof nodeInfo == "number"){
16669 return this.nodes[nodeInfo];
16675 * Gets a range template nodes.
16676 * @param {Number} startIndex
16677 * @param {Number} endIndex
16678 * @return {Array} An array of nodes
16680 getNodes : function(start, end){
16681 var ns = this.nodes;
16682 start = start || 0;
16683 end = typeof end == "undefined" ? ns.length - 1 : end;
16686 for(var i = start; i <= end; i++){
16690 for(var i = start; i >= end; i--){
16698 * Finds the index of the passed node
16699 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16700 * @return {Number} The index of the node or -1
16702 indexOf : function(node){
16703 node = this.getNode(node);
16704 if(typeof node.nodeIndex == "number"){
16705 return node.nodeIndex;
16707 var ns = this.nodes;
16708 for(var i = 0, len = ns.length; i < len; i++){
16719 * based on jquery fullcalendar
16723 Roo.bootstrap = Roo.bootstrap || {};
16725 * @class Roo.bootstrap.Calendar
16726 * @extends Roo.bootstrap.Component
16727 * Bootstrap Calendar class
16728 * @cfg {Boolean} loadMask (true|false) default false
16729 * @cfg {Object} header generate the user specific header of the calendar, default false
16732 * Create a new Container
16733 * @param {Object} config The config object
16738 Roo.bootstrap.Calendar = function(config){
16739 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16743 * Fires when a date is selected
16744 * @param {DatePicker} this
16745 * @param {Date} date The selected date
16749 * @event monthchange
16750 * Fires when the displayed month changes
16751 * @param {DatePicker} this
16752 * @param {Date} date The selected month
16754 'monthchange': true,
16756 * @event evententer
16757 * Fires when mouse over an event
16758 * @param {Calendar} this
16759 * @param {event} Event
16761 'evententer': true,
16763 * @event eventleave
16764 * Fires when the mouse leaves an
16765 * @param {Calendar} this
16768 'eventleave': true,
16770 * @event eventclick
16771 * Fires when the mouse click an
16772 * @param {Calendar} this
16781 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16784 * @cfg {Number} startDay
16785 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16793 getAutoCreate : function(){
16796 var fc_button = function(name, corner, style, content ) {
16797 return Roo.apply({},{
16799 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16801 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16804 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16815 style : 'width:100%',
16822 cls : 'fc-header-left',
16824 fc_button('prev', 'left', 'arrow', '‹' ),
16825 fc_button('next', 'right', 'arrow', '›' ),
16826 { tag: 'span', cls: 'fc-header-space' },
16827 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16835 cls : 'fc-header-center',
16839 cls: 'fc-header-title',
16842 html : 'month / year'
16850 cls : 'fc-header-right',
16852 /* fc_button('month', 'left', '', 'month' ),
16853 fc_button('week', '', '', 'week' ),
16854 fc_button('day', 'right', '', 'day' )
16866 header = this.header;
16869 var cal_heads = function() {
16871 // fixme - handle this.
16873 for (var i =0; i < Date.dayNames.length; i++) {
16874 var d = Date.dayNames[i];
16877 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16878 html : d.substring(0,3)
16882 ret[0].cls += ' fc-first';
16883 ret[6].cls += ' fc-last';
16886 var cal_cell = function(n) {
16889 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16894 cls: 'fc-day-number',
16898 cls: 'fc-day-content',
16902 style: 'position: relative;' // height: 17px;
16914 var cal_rows = function() {
16917 for (var r = 0; r < 6; r++) {
16924 for (var i =0; i < Date.dayNames.length; i++) {
16925 var d = Date.dayNames[i];
16926 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16929 row.cn[0].cls+=' fc-first';
16930 row.cn[0].cn[0].style = 'min-height:90px';
16931 row.cn[6].cls+=' fc-last';
16935 ret[0].cls += ' fc-first';
16936 ret[4].cls += ' fc-prev-last';
16937 ret[5].cls += ' fc-last';
16944 cls: 'fc-border-separate',
16945 style : 'width:100%',
16953 cls : 'fc-first fc-last',
16971 cls : 'fc-content',
16972 style : "position: relative;",
16975 cls : 'fc-view fc-view-month fc-grid',
16976 style : 'position: relative',
16977 unselectable : 'on',
16980 cls : 'fc-event-container',
16981 style : 'position:absolute;z-index:8;top:0;left:0;'
16999 initEvents : function()
17002 throw "can not find store for calendar";
17008 style: "text-align:center",
17012 style: "background-color:white;width:50%;margin:250 auto",
17016 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17027 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17029 var size = this.el.select('.fc-content', true).first().getSize();
17030 this.maskEl.setSize(size.width, size.height);
17031 this.maskEl.enableDisplayMode("block");
17032 if(!this.loadMask){
17033 this.maskEl.hide();
17036 this.store = Roo.factory(this.store, Roo.data);
17037 this.store.on('load', this.onLoad, this);
17038 this.store.on('beforeload', this.onBeforeLoad, this);
17042 this.cells = this.el.select('.fc-day',true);
17043 //Roo.log(this.cells);
17044 this.textNodes = this.el.query('.fc-day-number');
17045 this.cells.addClassOnOver('fc-state-hover');
17047 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17048 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17049 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17050 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17052 this.on('monthchange', this.onMonthChange, this);
17054 this.update(new Date().clearTime());
17057 resize : function() {
17058 var sz = this.el.getSize();
17060 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17061 this.el.select('.fc-day-content div',true).setHeight(34);
17066 showPrevMonth : function(e){
17067 this.update(this.activeDate.add("mo", -1));
17069 showToday : function(e){
17070 this.update(new Date().clearTime());
17073 showNextMonth : function(e){
17074 this.update(this.activeDate.add("mo", 1));
17078 showPrevYear : function(){
17079 this.update(this.activeDate.add("y", -1));
17083 showNextYear : function(){
17084 this.update(this.activeDate.add("y", 1));
17089 update : function(date)
17091 var vd = this.activeDate;
17092 this.activeDate = date;
17093 // if(vd && this.el){
17094 // var t = date.getTime();
17095 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17096 // Roo.log('using add remove');
17098 // this.fireEvent('monthchange', this, date);
17100 // this.cells.removeClass("fc-state-highlight");
17101 // this.cells.each(function(c){
17102 // if(c.dateValue == t){
17103 // c.addClass("fc-state-highlight");
17104 // setTimeout(function(){
17105 // try{c.dom.firstChild.focus();}catch(e){}
17115 var days = date.getDaysInMonth();
17117 var firstOfMonth = date.getFirstDateOfMonth();
17118 var startingPos = firstOfMonth.getDay()-this.startDay;
17120 if(startingPos < this.startDay){
17124 var pm = date.add(Date.MONTH, -1);
17125 var prevStart = pm.getDaysInMonth()-startingPos;
17127 this.cells = this.el.select('.fc-day',true);
17128 this.textNodes = this.el.query('.fc-day-number');
17129 this.cells.addClassOnOver('fc-state-hover');
17131 var cells = this.cells.elements;
17132 var textEls = this.textNodes;
17134 Roo.each(cells, function(cell){
17135 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17138 days += startingPos;
17140 // convert everything to numbers so it's fast
17141 var day = 86400000;
17142 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17145 //Roo.log(prevStart);
17147 var today = new Date().clearTime().getTime();
17148 var sel = date.clearTime().getTime();
17149 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17150 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17151 var ddMatch = this.disabledDatesRE;
17152 var ddText = this.disabledDatesText;
17153 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17154 var ddaysText = this.disabledDaysText;
17155 var format = this.format;
17157 var setCellClass = function(cal, cell){
17161 //Roo.log('set Cell Class');
17163 var t = d.getTime();
17167 cell.dateValue = t;
17169 cell.className += " fc-today";
17170 cell.className += " fc-state-highlight";
17171 cell.title = cal.todayText;
17174 // disable highlight in other month..
17175 //cell.className += " fc-state-highlight";
17180 cell.className = " fc-state-disabled";
17181 cell.title = cal.minText;
17185 cell.className = " fc-state-disabled";
17186 cell.title = cal.maxText;
17190 if(ddays.indexOf(d.getDay()) != -1){
17191 cell.title = ddaysText;
17192 cell.className = " fc-state-disabled";
17195 if(ddMatch && format){
17196 var fvalue = d.dateFormat(format);
17197 if(ddMatch.test(fvalue)){
17198 cell.title = ddText.replace("%0", fvalue);
17199 cell.className = " fc-state-disabled";
17203 if (!cell.initialClassName) {
17204 cell.initialClassName = cell.dom.className;
17207 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17212 for(; i < startingPos; i++) {
17213 textEls[i].innerHTML = (++prevStart);
17214 d.setDate(d.getDate()+1);
17216 cells[i].className = "fc-past fc-other-month";
17217 setCellClass(this, cells[i]);
17222 for(; i < days; i++){
17223 intDay = i - startingPos + 1;
17224 textEls[i].innerHTML = (intDay);
17225 d.setDate(d.getDate()+1);
17227 cells[i].className = ''; // "x-date-active";
17228 setCellClass(this, cells[i]);
17232 for(; i < 42; i++) {
17233 textEls[i].innerHTML = (++extraDays);
17234 d.setDate(d.getDate()+1);
17236 cells[i].className = "fc-future fc-other-month";
17237 setCellClass(this, cells[i]);
17240 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17242 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17244 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17245 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17247 if(totalRows != 6){
17248 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17249 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17252 this.fireEvent('monthchange', this, date);
17256 if(!this.internalRender){
17257 var main = this.el.dom.firstChild;
17258 var w = main.offsetWidth;
17259 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17260 Roo.fly(main).setWidth(w);
17261 this.internalRender = true;
17262 // opera does not respect the auto grow header center column
17263 // then, after it gets a width opera refuses to recalculate
17264 // without a second pass
17265 if(Roo.isOpera && !this.secondPass){
17266 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17267 this.secondPass = true;
17268 this.update.defer(10, this, [date]);
17275 findCell : function(dt) {
17276 dt = dt.clearTime().getTime();
17278 this.cells.each(function(c){
17279 //Roo.log("check " +c.dateValue + '?=' + dt);
17280 if(c.dateValue == dt){
17290 findCells : function(ev) {
17291 var s = ev.start.clone().clearTime().getTime();
17293 var e= ev.end.clone().clearTime().getTime();
17296 this.cells.each(function(c){
17297 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17299 if(c.dateValue > e){
17302 if(c.dateValue < s){
17311 // findBestRow: function(cells)
17315 // for (var i =0 ; i < cells.length;i++) {
17316 // ret = Math.max(cells[i].rows || 0,ret);
17323 addItem : function(ev)
17325 // look for vertical location slot in
17326 var cells = this.findCells(ev);
17328 // ev.row = this.findBestRow(cells);
17330 // work out the location.
17334 for(var i =0; i < cells.length; i++) {
17336 cells[i].row = cells[0].row;
17339 cells[i].row = cells[i].row + 1;
17349 if (crow.start.getY() == cells[i].getY()) {
17351 crow.end = cells[i];
17368 cells[0].events.push(ev);
17370 this.calevents.push(ev);
17373 clearEvents: function() {
17375 if(!this.calevents){
17379 Roo.each(this.cells.elements, function(c){
17385 Roo.each(this.calevents, function(e) {
17386 Roo.each(e.els, function(el) {
17387 el.un('mouseenter' ,this.onEventEnter, this);
17388 el.un('mouseleave' ,this.onEventLeave, this);
17393 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17399 renderEvents: function()
17403 this.cells.each(function(c) {
17412 if(c.row != c.events.length){
17413 r = 4 - (4 - (c.row - c.events.length));
17416 c.events = ev.slice(0, r);
17417 c.more = ev.slice(r);
17419 if(c.more.length && c.more.length == 1){
17420 c.events.push(c.more.pop());
17423 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17427 this.cells.each(function(c) {
17429 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17432 for (var e = 0; e < c.events.length; e++){
17433 var ev = c.events[e];
17434 var rows = ev.rows;
17436 for(var i = 0; i < rows.length; i++) {
17438 // how many rows should it span..
17441 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17442 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17444 unselectable : "on",
17447 cls: 'fc-event-inner',
17451 // cls: 'fc-event-time',
17452 // html : cells.length > 1 ? '' : ev.time
17456 cls: 'fc-event-title',
17457 html : String.format('{0}', ev.title)
17464 cls: 'ui-resizable-handle ui-resizable-e',
17465 html : '  '
17472 cfg.cls += ' fc-event-start';
17474 if ((i+1) == rows.length) {
17475 cfg.cls += ' fc-event-end';
17478 var ctr = _this.el.select('.fc-event-container',true).first();
17479 var cg = ctr.createChild(cfg);
17481 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17482 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17484 var r = (c.more.length) ? 1 : 0;
17485 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17486 cg.setWidth(ebox.right - sbox.x -2);
17488 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17489 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17490 cg.on('click', _this.onEventClick, _this, ev);
17501 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17502 style : 'position: absolute',
17503 unselectable : "on",
17506 cls: 'fc-event-inner',
17510 cls: 'fc-event-title',
17518 cls: 'ui-resizable-handle ui-resizable-e',
17519 html : '  '
17525 var ctr = _this.el.select('.fc-event-container',true).first();
17526 var cg = ctr.createChild(cfg);
17528 var sbox = c.select('.fc-day-content',true).first().getBox();
17529 var ebox = c.select('.fc-day-content',true).first().getBox();
17531 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17532 cg.setWidth(ebox.right - sbox.x -2);
17534 cg.on('click', _this.onMoreEventClick, _this, c.more);
17544 onEventEnter: function (e, el,event,d) {
17545 this.fireEvent('evententer', this, el, event);
17548 onEventLeave: function (e, el,event,d) {
17549 this.fireEvent('eventleave', this, el, event);
17552 onEventClick: function (e, el,event,d) {
17553 this.fireEvent('eventclick', this, el, event);
17556 onMonthChange: function () {
17560 onMoreEventClick: function(e, el, more)
17564 this.calpopover.placement = 'right';
17565 this.calpopover.setTitle('More');
17567 this.calpopover.setContent('');
17569 var ctr = this.calpopover.el.select('.popover-content', true).first();
17571 Roo.each(more, function(m){
17573 cls : 'fc-event-hori fc-event-draggable',
17576 var cg = ctr.createChild(cfg);
17578 cg.on('click', _this.onEventClick, _this, m);
17581 this.calpopover.show(el);
17586 onLoad: function ()
17588 this.calevents = [];
17591 if(this.store.getCount() > 0){
17592 this.store.data.each(function(d){
17595 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17596 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17597 time : d.data.start_time,
17598 title : d.data.title,
17599 description : d.data.description,
17600 venue : d.data.venue
17605 this.renderEvents();
17607 if(this.calevents.length && this.loadMask){
17608 this.maskEl.hide();
17612 onBeforeLoad: function()
17614 this.clearEvents();
17616 this.maskEl.show();
17630 * @class Roo.bootstrap.Popover
17631 * @extends Roo.bootstrap.Component
17632 * Bootstrap Popover class
17633 * @cfg {String} html contents of the popover (or false to use children..)
17634 * @cfg {String} title of popover (or false to hide)
17635 * @cfg {String} placement how it is placed
17636 * @cfg {String} trigger click || hover (or false to trigger manually)
17637 * @cfg {String} over what (parent or false to trigger manually.)
17638 * @cfg {Number} delay - delay before showing
17641 * Create a new Popover
17642 * @param {Object} config The config object
17645 Roo.bootstrap.Popover = function(config){
17646 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17652 * After the popover show
17654 * @param {Roo.bootstrap.Popover} this
17659 * After the popover hide
17661 * @param {Roo.bootstrap.Popover} this
17667 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17669 title: 'Fill in a title',
17672 placement : 'right',
17673 trigger : 'hover', // hover
17679 can_build_overlaid : false,
17681 getChildContainer : function()
17683 return this.el.select('.popover-content',true).first();
17686 getAutoCreate : function(){
17689 cls : 'popover roo-dynamic',
17690 style: 'display:block',
17696 cls : 'popover-inner',
17700 cls: 'popover-title popover-header',
17704 cls : 'popover-content popover-body',
17715 setTitle: function(str)
17718 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17720 setContent: function(str)
17723 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17725 // as it get's added to the bottom of the page.
17726 onRender : function(ct, position)
17728 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17730 var cfg = Roo.apply({}, this.getAutoCreate());
17734 cfg.cls += ' ' + this.cls;
17737 cfg.style = this.style;
17739 //Roo.log("adding to ");
17740 this.el = Roo.get(document.body).createChild(cfg, position);
17741 // Roo.log(this.el);
17746 initEvents : function()
17748 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17749 this.el.enableDisplayMode('block');
17751 if (this.over === false) {
17754 if (this.triggers === false) {
17757 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17758 var triggers = this.trigger ? this.trigger.split(' ') : [];
17759 Roo.each(triggers, function(trigger) {
17761 if (trigger == 'click') {
17762 on_el.on('click', this.toggle, this);
17763 } else if (trigger != 'manual') {
17764 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17765 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17767 on_el.on(eventIn ,this.enter, this);
17768 on_el.on(eventOut, this.leave, this);
17779 toggle : function () {
17780 this.hoverState == 'in' ? this.leave() : this.enter();
17783 enter : function () {
17785 clearTimeout(this.timeout);
17787 this.hoverState = 'in';
17789 if (!this.delay || !this.delay.show) {
17794 this.timeout = setTimeout(function () {
17795 if (_t.hoverState == 'in') {
17798 }, this.delay.show)
17801 leave : function() {
17802 clearTimeout(this.timeout);
17804 this.hoverState = 'out';
17806 if (!this.delay || !this.delay.hide) {
17811 this.timeout = setTimeout(function () {
17812 if (_t.hoverState == 'out') {
17815 }, this.delay.hide)
17818 show : function (on_el)
17821 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17825 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17826 if (this.html !== false) {
17827 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17829 this.el.removeClass([
17830 'fade','top','bottom', 'left', 'right','in',
17831 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17833 if (!this.title.length) {
17834 this.el.select('.popover-title',true).hide();
17837 var placement = typeof this.placement == 'function' ?
17838 this.placement.call(this, this.el, on_el) :
17841 var autoToken = /\s?auto?\s?/i;
17842 var autoPlace = autoToken.test(placement);
17844 placement = placement.replace(autoToken, '') || 'top';
17848 //this.el.setXY([0,0]);
17850 this.el.dom.style.display='block';
17851 this.el.addClass(placement);
17853 //this.el.appendTo(on_el);
17855 var p = this.getPosition();
17856 var box = this.el.getBox();
17861 var align = Roo.bootstrap.Popover.alignment[placement];
17864 this.el.alignTo(on_el, align[0],align[1]);
17865 //var arrow = this.el.select('.arrow',true).first();
17866 //arrow.set(align[2],
17868 this.el.addClass('in');
17871 if (this.el.hasClass('fade')) {
17875 this.hoverState = 'in';
17877 this.fireEvent('show', this);
17882 this.el.setXY([0,0]);
17883 this.el.removeClass('in');
17885 this.hoverState = null;
17887 this.fireEvent('hide', this);
17892 Roo.bootstrap.Popover.alignment = {
17893 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17894 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17895 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17896 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17907 * @class Roo.bootstrap.Progress
17908 * @extends Roo.bootstrap.Component
17909 * Bootstrap Progress class
17910 * @cfg {Boolean} striped striped of the progress bar
17911 * @cfg {Boolean} active animated of the progress bar
17915 * Create a new Progress
17916 * @param {Object} config The config object
17919 Roo.bootstrap.Progress = function(config){
17920 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17923 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17928 getAutoCreate : function(){
17936 cfg.cls += ' progress-striped';
17940 cfg.cls += ' active';
17959 * @class Roo.bootstrap.ProgressBar
17960 * @extends Roo.bootstrap.Component
17961 * Bootstrap ProgressBar class
17962 * @cfg {Number} aria_valuenow aria-value now
17963 * @cfg {Number} aria_valuemin aria-value min
17964 * @cfg {Number} aria_valuemax aria-value max
17965 * @cfg {String} label label for the progress bar
17966 * @cfg {String} panel (success | info | warning | danger )
17967 * @cfg {String} role role of the progress bar
17968 * @cfg {String} sr_only text
17972 * Create a new ProgressBar
17973 * @param {Object} config The config object
17976 Roo.bootstrap.ProgressBar = function(config){
17977 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17980 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17984 aria_valuemax : 100,
17990 getAutoCreate : function()
17995 cls: 'progress-bar',
17996 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18008 cfg.role = this.role;
18011 if(this.aria_valuenow){
18012 cfg['aria-valuenow'] = this.aria_valuenow;
18015 if(this.aria_valuemin){
18016 cfg['aria-valuemin'] = this.aria_valuemin;
18019 if(this.aria_valuemax){
18020 cfg['aria-valuemax'] = this.aria_valuemax;
18023 if(this.label && !this.sr_only){
18024 cfg.html = this.label;
18028 cfg.cls += ' progress-bar-' + this.panel;
18034 update : function(aria_valuenow)
18036 this.aria_valuenow = aria_valuenow;
18038 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18053 * @class Roo.bootstrap.TabGroup
18054 * @extends Roo.bootstrap.Column
18055 * Bootstrap Column class
18056 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18057 * @cfg {Boolean} carousel true to make the group behave like a carousel
18058 * @cfg {Boolean} bullets show bullets for the panels
18059 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18060 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18061 * @cfg {Boolean} showarrow (true|false) show arrow default true
18064 * Create a new TabGroup
18065 * @param {Object} config The config object
18068 Roo.bootstrap.TabGroup = function(config){
18069 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18071 this.navId = Roo.id();
18074 Roo.bootstrap.TabGroup.register(this);
18078 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18081 transition : false,
18086 slideOnTouch : false,
18089 getAutoCreate : function()
18091 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18093 cfg.cls += ' tab-content';
18095 if (this.carousel) {
18096 cfg.cls += ' carousel slide';
18099 cls : 'carousel-inner',
18103 if(this.bullets && !Roo.isTouch){
18106 cls : 'carousel-bullets',
18110 if(this.bullets_cls){
18111 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18118 cfg.cn[0].cn.push(bullets);
18121 if(this.showarrow){
18122 cfg.cn[0].cn.push({
18124 class : 'carousel-arrow',
18128 class : 'carousel-prev',
18132 class : 'fa fa-chevron-left'
18138 class : 'carousel-next',
18142 class : 'fa fa-chevron-right'
18155 initEvents: function()
18157 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18158 // this.el.on("touchstart", this.onTouchStart, this);
18161 if(this.autoslide){
18164 this.slideFn = window.setInterval(function() {
18165 _this.showPanelNext();
18169 if(this.showarrow){
18170 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18171 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18177 // onTouchStart : function(e, el, o)
18179 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18183 // this.showPanelNext();
18187 getChildContainer : function()
18189 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18193 * register a Navigation item
18194 * @param {Roo.bootstrap.NavItem} the navitem to add
18196 register : function(item)
18198 this.tabs.push( item);
18199 item.navId = this.navId; // not really needed..
18204 getActivePanel : function()
18207 Roo.each(this.tabs, function(t) {
18217 getPanelByName : function(n)
18220 Roo.each(this.tabs, function(t) {
18221 if (t.tabId == n) {
18229 indexOfPanel : function(p)
18232 Roo.each(this.tabs, function(t,i) {
18233 if (t.tabId == p.tabId) {
18242 * show a specific panel
18243 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18244 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18246 showPanel : function (pan)
18248 if(this.transition || typeof(pan) == 'undefined'){
18249 Roo.log("waiting for the transitionend");
18253 if (typeof(pan) == 'number') {
18254 pan = this.tabs[pan];
18257 if (typeof(pan) == 'string') {
18258 pan = this.getPanelByName(pan);
18261 var cur = this.getActivePanel();
18264 Roo.log('pan or acitve pan is undefined');
18268 if (pan.tabId == this.getActivePanel().tabId) {
18272 if (false === cur.fireEvent('beforedeactivate')) {
18276 if(this.bullets > 0 && !Roo.isTouch){
18277 this.setActiveBullet(this.indexOfPanel(pan));
18280 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18282 this.transition = true;
18283 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18284 var lr = dir == 'next' ? 'left' : 'right';
18285 pan.el.addClass(dir); // or prev
18286 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18287 cur.el.addClass(lr); // or right
18288 pan.el.addClass(lr);
18291 cur.el.on('transitionend', function() {
18292 Roo.log("trans end?");
18294 pan.el.removeClass([lr,dir]);
18295 pan.setActive(true);
18297 cur.el.removeClass([lr]);
18298 cur.setActive(false);
18300 _this.transition = false;
18302 }, this, { single: true } );
18307 cur.setActive(false);
18308 pan.setActive(true);
18313 showPanelNext : function()
18315 var i = this.indexOfPanel(this.getActivePanel());
18317 if (i >= this.tabs.length - 1 && !this.autoslide) {
18321 if (i >= this.tabs.length - 1 && this.autoslide) {
18325 this.showPanel(this.tabs[i+1]);
18328 showPanelPrev : function()
18330 var i = this.indexOfPanel(this.getActivePanel());
18332 if (i < 1 && !this.autoslide) {
18336 if (i < 1 && this.autoslide) {
18337 i = this.tabs.length;
18340 this.showPanel(this.tabs[i-1]);
18344 addBullet: function()
18346 if(!this.bullets || Roo.isTouch){
18349 var ctr = this.el.select('.carousel-bullets',true).first();
18350 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18351 var bullet = ctr.createChild({
18352 cls : 'bullet bullet-' + i
18353 },ctr.dom.lastChild);
18358 bullet.on('click', (function(e, el, o, ii, t){
18360 e.preventDefault();
18362 this.showPanel(ii);
18364 if(this.autoslide && this.slideFn){
18365 clearInterval(this.slideFn);
18366 this.slideFn = window.setInterval(function() {
18367 _this.showPanelNext();
18371 }).createDelegate(this, [i, bullet], true));
18376 setActiveBullet : function(i)
18382 Roo.each(this.el.select('.bullet', true).elements, function(el){
18383 el.removeClass('selected');
18386 var bullet = this.el.select('.bullet-' + i, true).first();
18392 bullet.addClass('selected');
18403 Roo.apply(Roo.bootstrap.TabGroup, {
18407 * register a Navigation Group
18408 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18410 register : function(navgrp)
18412 this.groups[navgrp.navId] = navgrp;
18416 * fetch a Navigation Group based on the navigation ID
18417 * if one does not exist , it will get created.
18418 * @param {string} the navgroup to add
18419 * @returns {Roo.bootstrap.NavGroup} the navgroup
18421 get: function(navId) {
18422 if (typeof(this.groups[navId]) == 'undefined') {
18423 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18425 return this.groups[navId] ;
18440 * @class Roo.bootstrap.TabPanel
18441 * @extends Roo.bootstrap.Component
18442 * Bootstrap TabPanel class
18443 * @cfg {Boolean} active panel active
18444 * @cfg {String} html panel content
18445 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18446 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18447 * @cfg {String} href click to link..
18451 * Create a new TabPanel
18452 * @param {Object} config The config object
18455 Roo.bootstrap.TabPanel = function(config){
18456 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18460 * Fires when the active status changes
18461 * @param {Roo.bootstrap.TabPanel} this
18462 * @param {Boolean} state the new state
18467 * @event beforedeactivate
18468 * Fires before a tab is de-activated - can be used to do validation on a form.
18469 * @param {Roo.bootstrap.TabPanel} this
18470 * @return {Boolean} false if there is an error
18473 'beforedeactivate': true
18476 this.tabId = this.tabId || Roo.id();
18480 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18488 getAutoCreate : function(){
18491 // item is needed for carousel - not sure if it has any effect otherwise
18492 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18493 html: this.html || ''
18497 cfg.cls += ' active';
18501 cfg.tabId = this.tabId;
18508 initEvents: function()
18510 var p = this.parent();
18512 this.navId = this.navId || p.navId;
18514 if (typeof(this.navId) != 'undefined') {
18515 // not really needed.. but just in case.. parent should be a NavGroup.
18516 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18520 var i = tg.tabs.length - 1;
18522 if(this.active && tg.bullets > 0 && i < tg.bullets){
18523 tg.setActiveBullet(i);
18527 this.el.on('click', this.onClick, this);
18530 this.el.on("touchstart", this.onTouchStart, this);
18531 this.el.on("touchmove", this.onTouchMove, this);
18532 this.el.on("touchend", this.onTouchEnd, this);
18537 onRender : function(ct, position)
18539 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18542 setActive : function(state)
18544 Roo.log("panel - set active " + this.tabId + "=" + state);
18546 this.active = state;
18548 this.el.removeClass('active');
18550 } else if (!this.el.hasClass('active')) {
18551 this.el.addClass('active');
18554 this.fireEvent('changed', this, state);
18557 onClick : function(e)
18559 e.preventDefault();
18561 if(!this.href.length){
18565 window.location.href = this.href;
18574 onTouchStart : function(e)
18576 this.swiping = false;
18578 this.startX = e.browserEvent.touches[0].clientX;
18579 this.startY = e.browserEvent.touches[0].clientY;
18582 onTouchMove : function(e)
18584 this.swiping = true;
18586 this.endX = e.browserEvent.touches[0].clientX;
18587 this.endY = e.browserEvent.touches[0].clientY;
18590 onTouchEnd : function(e)
18597 var tabGroup = this.parent();
18599 if(this.endX > this.startX){ // swiping right
18600 tabGroup.showPanelPrev();
18604 if(this.startX > this.endX){ // swiping left
18605 tabGroup.showPanelNext();
18624 * @class Roo.bootstrap.DateField
18625 * @extends Roo.bootstrap.Input
18626 * Bootstrap DateField class
18627 * @cfg {Number} weekStart default 0
18628 * @cfg {String} viewMode default empty, (months|years)
18629 * @cfg {String} minViewMode default empty, (months|years)
18630 * @cfg {Number} startDate default -Infinity
18631 * @cfg {Number} endDate default Infinity
18632 * @cfg {Boolean} todayHighlight default false
18633 * @cfg {Boolean} todayBtn default false
18634 * @cfg {Boolean} calendarWeeks default false
18635 * @cfg {Object} daysOfWeekDisabled default empty
18636 * @cfg {Boolean} singleMode default false (true | false)
18638 * @cfg {Boolean} keyboardNavigation default true
18639 * @cfg {String} language default en
18642 * Create a new DateField
18643 * @param {Object} config The config object
18646 Roo.bootstrap.DateField = function(config){
18647 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18651 * Fires when this field show.
18652 * @param {Roo.bootstrap.DateField} this
18653 * @param {Mixed} date The date value
18658 * Fires when this field hide.
18659 * @param {Roo.bootstrap.DateField} this
18660 * @param {Mixed} date The date value
18665 * Fires when select a date.
18666 * @param {Roo.bootstrap.DateField} this
18667 * @param {Mixed} date The date value
18671 * @event beforeselect
18672 * Fires when before select a date.
18673 * @param {Roo.bootstrap.DateField} this
18674 * @param {Mixed} date The date value
18676 beforeselect : true
18680 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18683 * @cfg {String} format
18684 * The default date format string which can be overriden for localization support. The format must be
18685 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18689 * @cfg {String} altFormats
18690 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18691 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18693 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18701 todayHighlight : false,
18707 keyboardNavigation: true,
18709 calendarWeeks: false,
18711 startDate: -Infinity,
18715 daysOfWeekDisabled: [],
18719 singleMode : false,
18721 UTCDate: function()
18723 return new Date(Date.UTC.apply(Date, arguments));
18726 UTCToday: function()
18728 var today = new Date();
18729 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18732 getDate: function() {
18733 var d = this.getUTCDate();
18734 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18737 getUTCDate: function() {
18741 setDate: function(d) {
18742 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18745 setUTCDate: function(d) {
18747 this.setValue(this.formatDate(this.date));
18750 onRender: function(ct, position)
18753 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18755 this.language = this.language || 'en';
18756 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18757 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18759 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18760 this.format = this.format || 'm/d/y';
18761 this.isInline = false;
18762 this.isInput = true;
18763 this.component = this.el.select('.add-on', true).first() || false;
18764 this.component = (this.component && this.component.length === 0) ? false : this.component;
18765 this.hasInput = this.component && this.inputEl().length;
18767 if (typeof(this.minViewMode === 'string')) {
18768 switch (this.minViewMode) {
18770 this.minViewMode = 1;
18773 this.minViewMode = 2;
18776 this.minViewMode = 0;
18781 if (typeof(this.viewMode === 'string')) {
18782 switch (this.viewMode) {
18795 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18797 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18799 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18801 this.picker().on('mousedown', this.onMousedown, this);
18802 this.picker().on('click', this.onClick, this);
18804 this.picker().addClass('datepicker-dropdown');
18806 this.startViewMode = this.viewMode;
18808 if(this.singleMode){
18809 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18810 v.setVisibilityMode(Roo.Element.DISPLAY);
18814 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18815 v.setStyle('width', '189px');
18819 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18820 if(!this.calendarWeeks){
18825 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18826 v.attr('colspan', function(i, val){
18827 return parseInt(val) + 1;
18832 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18834 this.setStartDate(this.startDate);
18835 this.setEndDate(this.endDate);
18837 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18844 if(this.isInline) {
18849 picker : function()
18851 return this.pickerEl;
18852 // return this.el.select('.datepicker', true).first();
18855 fillDow: function()
18857 var dowCnt = this.weekStart;
18866 if(this.calendarWeeks){
18874 while (dowCnt < this.weekStart + 7) {
18878 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18882 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18885 fillMonths: function()
18888 var months = this.picker().select('>.datepicker-months td', true).first();
18890 months.dom.innerHTML = '';
18896 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18899 months.createChild(month);
18906 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;
18908 if (this.date < this.startDate) {
18909 this.viewDate = new Date(this.startDate);
18910 } else if (this.date > this.endDate) {
18911 this.viewDate = new Date(this.endDate);
18913 this.viewDate = new Date(this.date);
18921 var d = new Date(this.viewDate),
18922 year = d.getUTCFullYear(),
18923 month = d.getUTCMonth(),
18924 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18925 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18926 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18927 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18928 currentDate = this.date && this.date.valueOf(),
18929 today = this.UTCToday();
18931 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18933 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18935 // this.picker.select('>tfoot th.today').
18936 // .text(dates[this.language].today)
18937 // .toggle(this.todayBtn !== false);
18939 this.updateNavArrows();
18942 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18944 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18946 prevMonth.setUTCDate(day);
18948 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18950 var nextMonth = new Date(prevMonth);
18952 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18954 nextMonth = nextMonth.valueOf();
18956 var fillMonths = false;
18958 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18960 while(prevMonth.valueOf() <= nextMonth) {
18963 if (prevMonth.getUTCDay() === this.weekStart) {
18965 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18973 if(this.calendarWeeks){
18974 // ISO 8601: First week contains first thursday.
18975 // ISO also states week starts on Monday, but we can be more abstract here.
18977 // Start of current week: based on weekstart/current date
18978 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18979 // Thursday of this week
18980 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18981 // First Thursday of year, year from thursday
18982 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18983 // Calendar week: ms between thursdays, div ms per day, div 7 days
18984 calWeek = (th - yth) / 864e5 / 7 + 1;
18986 fillMonths.cn.push({
18994 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18996 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18999 if (this.todayHighlight &&
19000 prevMonth.getUTCFullYear() == today.getFullYear() &&
19001 prevMonth.getUTCMonth() == today.getMonth() &&
19002 prevMonth.getUTCDate() == today.getDate()) {
19003 clsName += ' today';
19006 if (currentDate && prevMonth.valueOf() === currentDate) {
19007 clsName += ' active';
19010 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19011 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19012 clsName += ' disabled';
19015 fillMonths.cn.push({
19017 cls: 'day ' + clsName,
19018 html: prevMonth.getDate()
19021 prevMonth.setDate(prevMonth.getDate()+1);
19024 var currentYear = this.date && this.date.getUTCFullYear();
19025 var currentMonth = this.date && this.date.getUTCMonth();
19027 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19029 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19030 v.removeClass('active');
19032 if(currentYear === year && k === currentMonth){
19033 v.addClass('active');
19036 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19037 v.addClass('disabled');
19043 year = parseInt(year/10, 10) * 10;
19045 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19047 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19050 for (var i = -1; i < 11; i++) {
19051 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19053 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19061 showMode: function(dir)
19064 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19067 Roo.each(this.picker().select('>div',true).elements, function(v){
19068 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19071 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19076 if(this.isInline) {
19080 this.picker().removeClass(['bottom', 'top']);
19082 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19084 * place to the top of element!
19088 this.picker().addClass('top');
19089 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19094 this.picker().addClass('bottom');
19096 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19099 parseDate : function(value)
19101 if(!value || value instanceof Date){
19104 var v = Date.parseDate(value, this.format);
19105 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19106 v = Date.parseDate(value, 'Y-m-d');
19108 if(!v && this.altFormats){
19109 if(!this.altFormatsArray){
19110 this.altFormatsArray = this.altFormats.split("|");
19112 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19113 v = Date.parseDate(value, this.altFormatsArray[i]);
19119 formatDate : function(date, fmt)
19121 return (!date || !(date instanceof Date)) ?
19122 date : date.dateFormat(fmt || this.format);
19125 onFocus : function()
19127 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19131 onBlur : function()
19133 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19135 var d = this.inputEl().getValue();
19142 showPopup : function()
19144 this.picker().show();
19148 this.fireEvent('showpopup', this, this.date);
19151 hidePopup : function()
19153 if(this.isInline) {
19156 this.picker().hide();
19157 this.viewMode = this.startViewMode;
19160 this.fireEvent('hidepopup', this, this.date);
19164 onMousedown: function(e)
19166 e.stopPropagation();
19167 e.preventDefault();
19172 Roo.bootstrap.DateField.superclass.keyup.call(this);
19176 setValue: function(v)
19178 if(this.fireEvent('beforeselect', this, v) !== false){
19179 var d = new Date(this.parseDate(v) ).clearTime();
19181 if(isNaN(d.getTime())){
19182 this.date = this.viewDate = '';
19183 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19187 v = this.formatDate(d);
19189 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19191 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19195 this.fireEvent('select', this, this.date);
19199 getValue: function()
19201 return this.formatDate(this.date);
19204 fireKey: function(e)
19206 if (!this.picker().isVisible()){
19207 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19213 var dateChanged = false,
19215 newDate, newViewDate;
19220 e.preventDefault();
19224 if (!this.keyboardNavigation) {
19227 dir = e.keyCode == 37 ? -1 : 1;
19230 newDate = this.moveYear(this.date, dir);
19231 newViewDate = this.moveYear(this.viewDate, dir);
19232 } else if (e.shiftKey){
19233 newDate = this.moveMonth(this.date, dir);
19234 newViewDate = this.moveMonth(this.viewDate, dir);
19236 newDate = new Date(this.date);
19237 newDate.setUTCDate(this.date.getUTCDate() + dir);
19238 newViewDate = new Date(this.viewDate);
19239 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19241 if (this.dateWithinRange(newDate)){
19242 this.date = newDate;
19243 this.viewDate = newViewDate;
19244 this.setValue(this.formatDate(this.date));
19246 e.preventDefault();
19247 dateChanged = true;
19252 if (!this.keyboardNavigation) {
19255 dir = e.keyCode == 38 ? -1 : 1;
19257 newDate = this.moveYear(this.date, dir);
19258 newViewDate = this.moveYear(this.viewDate, dir);
19259 } else if (e.shiftKey){
19260 newDate = this.moveMonth(this.date, dir);
19261 newViewDate = this.moveMonth(this.viewDate, dir);
19263 newDate = new Date(this.date);
19264 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19265 newViewDate = new Date(this.viewDate);
19266 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19268 if (this.dateWithinRange(newDate)){
19269 this.date = newDate;
19270 this.viewDate = newViewDate;
19271 this.setValue(this.formatDate(this.date));
19273 e.preventDefault();
19274 dateChanged = true;
19278 this.setValue(this.formatDate(this.date));
19280 e.preventDefault();
19283 this.setValue(this.formatDate(this.date));
19297 onClick: function(e)
19299 e.stopPropagation();
19300 e.preventDefault();
19302 var target = e.getTarget();
19304 if(target.nodeName.toLowerCase() === 'i'){
19305 target = Roo.get(target).dom.parentNode;
19308 var nodeName = target.nodeName;
19309 var className = target.className;
19310 var html = target.innerHTML;
19311 //Roo.log(nodeName);
19313 switch(nodeName.toLowerCase()) {
19315 switch(className) {
19321 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19322 switch(this.viewMode){
19324 this.viewDate = this.moveMonth(this.viewDate, dir);
19328 this.viewDate = this.moveYear(this.viewDate, dir);
19334 var date = new Date();
19335 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19337 this.setValue(this.formatDate(this.date));
19344 if (className.indexOf('disabled') < 0) {
19345 this.viewDate.setUTCDate(1);
19346 if (className.indexOf('month') > -1) {
19347 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19349 var year = parseInt(html, 10) || 0;
19350 this.viewDate.setUTCFullYear(year);
19354 if(this.singleMode){
19355 this.setValue(this.formatDate(this.viewDate));
19366 //Roo.log(className);
19367 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19368 var day = parseInt(html, 10) || 1;
19369 var year = this.viewDate.getUTCFullYear(),
19370 month = this.viewDate.getUTCMonth();
19372 if (className.indexOf('old') > -1) {
19379 } else if (className.indexOf('new') > -1) {
19387 //Roo.log([year,month,day]);
19388 this.date = this.UTCDate(year, month, day,0,0,0,0);
19389 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19391 //Roo.log(this.formatDate(this.date));
19392 this.setValue(this.formatDate(this.date));
19399 setStartDate: function(startDate)
19401 this.startDate = startDate || -Infinity;
19402 if (this.startDate !== -Infinity) {
19403 this.startDate = this.parseDate(this.startDate);
19406 this.updateNavArrows();
19409 setEndDate: function(endDate)
19411 this.endDate = endDate || Infinity;
19412 if (this.endDate !== Infinity) {
19413 this.endDate = this.parseDate(this.endDate);
19416 this.updateNavArrows();
19419 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19421 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19422 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19423 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19425 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19426 return parseInt(d, 10);
19429 this.updateNavArrows();
19432 updateNavArrows: function()
19434 if(this.singleMode){
19438 var d = new Date(this.viewDate),
19439 year = d.getUTCFullYear(),
19440 month = d.getUTCMonth();
19442 Roo.each(this.picker().select('.prev', true).elements, function(v){
19444 switch (this.viewMode) {
19447 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19453 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19460 Roo.each(this.picker().select('.next', true).elements, function(v){
19462 switch (this.viewMode) {
19465 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19471 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19479 moveMonth: function(date, dir)
19484 var new_date = new Date(date.valueOf()),
19485 day = new_date.getUTCDate(),
19486 month = new_date.getUTCMonth(),
19487 mag = Math.abs(dir),
19489 dir = dir > 0 ? 1 : -1;
19492 // If going back one month, make sure month is not current month
19493 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19495 return new_date.getUTCMonth() == month;
19497 // If going forward one month, make sure month is as expected
19498 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19500 return new_date.getUTCMonth() != new_month;
19502 new_month = month + dir;
19503 new_date.setUTCMonth(new_month);
19504 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19505 if (new_month < 0 || new_month > 11) {
19506 new_month = (new_month + 12) % 12;
19509 // For magnitudes >1, move one month at a time...
19510 for (var i=0; i<mag; i++) {
19511 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19512 new_date = this.moveMonth(new_date, dir);
19514 // ...then reset the day, keeping it in the new month
19515 new_month = new_date.getUTCMonth();
19516 new_date.setUTCDate(day);
19518 return new_month != new_date.getUTCMonth();
19521 // Common date-resetting loop -- if date is beyond end of month, make it
19524 new_date.setUTCDate(--day);
19525 new_date.setUTCMonth(new_month);
19530 moveYear: function(date, dir)
19532 return this.moveMonth(date, dir*12);
19535 dateWithinRange: function(date)
19537 return date >= this.startDate && date <= this.endDate;
19543 this.picker().remove();
19546 validateValue : function(value)
19548 if(this.getVisibilityEl().hasClass('hidden')){
19552 if(value.length < 1) {
19553 if(this.allowBlank){
19559 if(value.length < this.minLength){
19562 if(value.length > this.maxLength){
19566 var vt = Roo.form.VTypes;
19567 if(!vt[this.vtype](value, this)){
19571 if(typeof this.validator == "function"){
19572 var msg = this.validator(value);
19578 if(this.regex && !this.regex.test(value)){
19582 if(typeof(this.parseDate(value)) == 'undefined'){
19586 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19590 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19600 this.date = this.viewDate = '';
19602 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19607 Roo.apply(Roo.bootstrap.DateField, {
19618 html: '<i class="fa fa-arrow-left"/>'
19628 html: '<i class="fa fa-arrow-right"/>'
19670 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19671 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19672 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19673 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19674 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19687 navFnc: 'FullYear',
19692 navFnc: 'FullYear',
19697 Roo.apply(Roo.bootstrap.DateField, {
19701 cls: 'datepicker dropdown-menu roo-dynamic',
19705 cls: 'datepicker-days',
19709 cls: 'table-condensed',
19711 Roo.bootstrap.DateField.head,
19715 Roo.bootstrap.DateField.footer
19722 cls: 'datepicker-months',
19726 cls: 'table-condensed',
19728 Roo.bootstrap.DateField.head,
19729 Roo.bootstrap.DateField.content,
19730 Roo.bootstrap.DateField.footer
19737 cls: 'datepicker-years',
19741 cls: 'table-condensed',
19743 Roo.bootstrap.DateField.head,
19744 Roo.bootstrap.DateField.content,
19745 Roo.bootstrap.DateField.footer
19764 * @class Roo.bootstrap.TimeField
19765 * @extends Roo.bootstrap.Input
19766 * Bootstrap DateField class
19770 * Create a new TimeField
19771 * @param {Object} config The config object
19774 Roo.bootstrap.TimeField = function(config){
19775 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19779 * Fires when this field show.
19780 * @param {Roo.bootstrap.DateField} thisthis
19781 * @param {Mixed} date The date value
19786 * Fires when this field hide.
19787 * @param {Roo.bootstrap.DateField} this
19788 * @param {Mixed} date The date value
19793 * Fires when select a date.
19794 * @param {Roo.bootstrap.DateField} this
19795 * @param {Mixed} date The date value
19801 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19804 * @cfg {String} format
19805 * The default time format string which can be overriden for localization support. The format must be
19806 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19810 onRender: function(ct, position)
19813 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19815 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19817 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19819 this.pop = this.picker().select('>.datepicker-time',true).first();
19820 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19822 this.picker().on('mousedown', this.onMousedown, this);
19823 this.picker().on('click', this.onClick, this);
19825 this.picker().addClass('datepicker-dropdown');
19830 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19831 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19832 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19833 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19834 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19835 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19839 fireKey: function(e){
19840 if (!this.picker().isVisible()){
19841 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19847 e.preventDefault();
19855 this.onTogglePeriod();
19858 this.onIncrementMinutes();
19861 this.onDecrementMinutes();
19870 onClick: function(e) {
19871 e.stopPropagation();
19872 e.preventDefault();
19875 picker : function()
19877 return this.el.select('.datepicker', true).first();
19880 fillTime: function()
19882 var time = this.pop.select('tbody', true).first();
19884 time.dom.innerHTML = '';
19899 cls: 'hours-up glyphicon glyphicon-chevron-up'
19919 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19940 cls: 'timepicker-hour',
19955 cls: 'timepicker-minute',
19970 cls: 'btn btn-primary period',
19992 cls: 'hours-down glyphicon glyphicon-chevron-down'
20012 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20030 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20037 var hours = this.time.getHours();
20038 var minutes = this.time.getMinutes();
20051 hours = hours - 12;
20055 hours = '0' + hours;
20059 minutes = '0' + minutes;
20062 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20063 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20064 this.pop.select('button', true).first().dom.innerHTML = period;
20070 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20072 var cls = ['bottom'];
20074 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20081 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20086 this.picker().addClass(cls.join('-'));
20090 Roo.each(cls, function(c){
20092 _this.picker().setTop(_this.inputEl().getHeight());
20096 _this.picker().setTop(0 - _this.picker().getHeight());
20101 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20105 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20112 onFocus : function()
20114 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20118 onBlur : function()
20120 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20126 this.picker().show();
20131 this.fireEvent('show', this, this.date);
20136 this.picker().hide();
20139 this.fireEvent('hide', this, this.date);
20142 setTime : function()
20145 this.setValue(this.time.format(this.format));
20147 this.fireEvent('select', this, this.date);
20152 onMousedown: function(e){
20153 e.stopPropagation();
20154 e.preventDefault();
20157 onIncrementHours: function()
20159 Roo.log('onIncrementHours');
20160 this.time = this.time.add(Date.HOUR, 1);
20165 onDecrementHours: function()
20167 Roo.log('onDecrementHours');
20168 this.time = this.time.add(Date.HOUR, -1);
20172 onIncrementMinutes: function()
20174 Roo.log('onIncrementMinutes');
20175 this.time = this.time.add(Date.MINUTE, 1);
20179 onDecrementMinutes: function()
20181 Roo.log('onDecrementMinutes');
20182 this.time = this.time.add(Date.MINUTE, -1);
20186 onTogglePeriod: function()
20188 Roo.log('onTogglePeriod');
20189 this.time = this.time.add(Date.HOUR, 12);
20196 Roo.apply(Roo.bootstrap.TimeField, {
20226 cls: 'btn btn-info ok',
20238 Roo.apply(Roo.bootstrap.TimeField, {
20242 cls: 'datepicker dropdown-menu',
20246 cls: 'datepicker-time',
20250 cls: 'table-condensed',
20252 Roo.bootstrap.TimeField.content,
20253 Roo.bootstrap.TimeField.footer
20272 * @class Roo.bootstrap.MonthField
20273 * @extends Roo.bootstrap.Input
20274 * Bootstrap MonthField class
20276 * @cfg {String} language default en
20279 * Create a new MonthField
20280 * @param {Object} config The config object
20283 Roo.bootstrap.MonthField = function(config){
20284 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20289 * Fires when this field show.
20290 * @param {Roo.bootstrap.MonthField} this
20291 * @param {Mixed} date The date value
20296 * Fires when this field hide.
20297 * @param {Roo.bootstrap.MonthField} this
20298 * @param {Mixed} date The date value
20303 * Fires when select a date.
20304 * @param {Roo.bootstrap.MonthField} this
20305 * @param {String} oldvalue The old value
20306 * @param {String} newvalue The new value
20312 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20314 onRender: function(ct, position)
20317 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20319 this.language = this.language || 'en';
20320 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20321 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20323 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20324 this.isInline = false;
20325 this.isInput = true;
20326 this.component = this.el.select('.add-on', true).first() || false;
20327 this.component = (this.component && this.component.length === 0) ? false : this.component;
20328 this.hasInput = this.component && this.inputEL().length;
20330 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20332 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20334 this.picker().on('mousedown', this.onMousedown, this);
20335 this.picker().on('click', this.onClick, this);
20337 this.picker().addClass('datepicker-dropdown');
20339 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20340 v.setStyle('width', '189px');
20347 if(this.isInline) {
20353 setValue: function(v, suppressEvent)
20355 var o = this.getValue();
20357 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20361 if(suppressEvent !== true){
20362 this.fireEvent('select', this, o, v);
20367 getValue: function()
20372 onClick: function(e)
20374 e.stopPropagation();
20375 e.preventDefault();
20377 var target = e.getTarget();
20379 if(target.nodeName.toLowerCase() === 'i'){
20380 target = Roo.get(target).dom.parentNode;
20383 var nodeName = target.nodeName;
20384 var className = target.className;
20385 var html = target.innerHTML;
20387 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20391 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20393 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20399 picker : function()
20401 return this.pickerEl;
20404 fillMonths: function()
20407 var months = this.picker().select('>.datepicker-months td', true).first();
20409 months.dom.innerHTML = '';
20415 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20418 months.createChild(month);
20427 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20428 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20431 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20432 e.removeClass('active');
20434 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20435 e.addClass('active');
20442 if(this.isInline) {
20446 this.picker().removeClass(['bottom', 'top']);
20448 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20450 * place to the top of element!
20454 this.picker().addClass('top');
20455 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20460 this.picker().addClass('bottom');
20462 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20465 onFocus : function()
20467 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20471 onBlur : function()
20473 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20475 var d = this.inputEl().getValue();
20484 this.picker().show();
20485 this.picker().select('>.datepicker-months', true).first().show();
20489 this.fireEvent('show', this, this.date);
20494 if(this.isInline) {
20497 this.picker().hide();
20498 this.fireEvent('hide', this, this.date);
20502 onMousedown: function(e)
20504 e.stopPropagation();
20505 e.preventDefault();
20510 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20514 fireKey: function(e)
20516 if (!this.picker().isVisible()){
20517 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20528 e.preventDefault();
20532 dir = e.keyCode == 37 ? -1 : 1;
20534 this.vIndex = this.vIndex + dir;
20536 if(this.vIndex < 0){
20540 if(this.vIndex > 11){
20544 if(isNaN(this.vIndex)){
20548 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20554 dir = e.keyCode == 38 ? -1 : 1;
20556 this.vIndex = this.vIndex + dir * 4;
20558 if(this.vIndex < 0){
20562 if(this.vIndex > 11){
20566 if(isNaN(this.vIndex)){
20570 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20575 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20576 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20580 e.preventDefault();
20583 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20584 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20600 this.picker().remove();
20605 Roo.apply(Roo.bootstrap.MonthField, {
20624 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20625 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20630 Roo.apply(Roo.bootstrap.MonthField, {
20634 cls: 'datepicker dropdown-menu roo-dynamic',
20638 cls: 'datepicker-months',
20642 cls: 'table-condensed',
20644 Roo.bootstrap.DateField.content
20664 * @class Roo.bootstrap.CheckBox
20665 * @extends Roo.bootstrap.Input
20666 * Bootstrap CheckBox class
20668 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20669 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20670 * @cfg {String} boxLabel The text that appears beside the checkbox
20671 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20672 * @cfg {Boolean} checked initnal the element
20673 * @cfg {Boolean} inline inline the element (default false)
20674 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20675 * @cfg {String} tooltip label tooltip
20678 * Create a new CheckBox
20679 * @param {Object} config The config object
20682 Roo.bootstrap.CheckBox = function(config){
20683 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20688 * Fires when the element is checked or unchecked.
20689 * @param {Roo.bootstrap.CheckBox} this This input
20690 * @param {Boolean} checked The new checked value
20695 * Fires when the element is click.
20696 * @param {Roo.bootstrap.CheckBox} this This input
20703 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20705 inputType: 'checkbox',
20714 getAutoCreate : function()
20716 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20722 cfg.cls = 'form-group ' + this.inputType; //input-group
20725 cfg.cls += ' ' + this.inputType + '-inline';
20731 type : this.inputType,
20732 value : this.inputValue,
20733 cls : 'roo-' + this.inputType, //'form-box',
20734 placeholder : this.placeholder || ''
20738 if(this.inputType != 'radio'){
20742 cls : 'roo-hidden-value',
20743 value : this.checked ? this.inputValue : this.valueOff
20748 if (this.weight) { // Validity check?
20749 cfg.cls += " " + this.inputType + "-" + this.weight;
20752 if (this.disabled) {
20753 input.disabled=true;
20757 input.checked = this.checked;
20762 input.name = this.name;
20764 if(this.inputType != 'radio'){
20765 hidden.name = this.name;
20766 input.name = '_hidden_' + this.name;
20771 input.cls += ' input-' + this.size;
20776 ['xs','sm','md','lg'].map(function(size){
20777 if (settings[size]) {
20778 cfg.cls += ' col-' + size + '-' + settings[size];
20782 var inputblock = input;
20784 if (this.before || this.after) {
20787 cls : 'input-group',
20792 inputblock.cn.push({
20794 cls : 'input-group-addon',
20799 inputblock.cn.push(input);
20801 if(this.inputType != 'radio'){
20802 inputblock.cn.push(hidden);
20806 inputblock.cn.push({
20808 cls : 'input-group-addon',
20815 if (align ==='left' && this.fieldLabel.length) {
20816 // Roo.log("left and has label");
20821 cls : 'control-label',
20822 html : this.fieldLabel
20832 if(this.labelWidth > 12){
20833 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20836 if(this.labelWidth < 13 && this.labelmd == 0){
20837 this.labelmd = this.labelWidth;
20840 if(this.labellg > 0){
20841 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20842 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20845 if(this.labelmd > 0){
20846 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20847 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20850 if(this.labelsm > 0){
20851 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20852 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20855 if(this.labelxs > 0){
20856 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20857 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20860 } else if ( this.fieldLabel.length) {
20861 // Roo.log(" label");
20865 tag: this.boxLabel ? 'span' : 'label',
20867 cls: 'control-label box-input-label',
20868 //cls : 'input-group-addon',
20869 html : this.fieldLabel
20878 // Roo.log(" no label && no align");
20879 cfg.cn = [ inputblock ] ;
20885 var boxLabelCfg = {
20887 //'for': id, // box label is handled by onclick - so no for...
20889 html: this.boxLabel
20893 boxLabelCfg.tooltip = this.tooltip;
20896 cfg.cn.push(boxLabelCfg);
20899 if(this.inputType != 'radio'){
20900 cfg.cn.push(hidden);
20908 * return the real input element.
20910 inputEl: function ()
20912 return this.el.select('input.roo-' + this.inputType,true).first();
20914 hiddenEl: function ()
20916 return this.el.select('input.roo-hidden-value',true).first();
20919 labelEl: function()
20921 return this.el.select('label.control-label',true).first();
20923 /* depricated... */
20927 return this.labelEl();
20930 boxLabelEl: function()
20932 return this.el.select('label.box-label',true).first();
20935 initEvents : function()
20937 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20939 this.inputEl().on('click', this.onClick, this);
20941 if (this.boxLabel) {
20942 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20945 this.startValue = this.getValue();
20948 Roo.bootstrap.CheckBox.register(this);
20952 onClick : function(e)
20954 if(this.fireEvent('click', this, e) !== false){
20955 this.setChecked(!this.checked);
20960 setChecked : function(state,suppressEvent)
20962 this.startValue = this.getValue();
20964 if(this.inputType == 'radio'){
20966 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20967 e.dom.checked = false;
20970 this.inputEl().dom.checked = true;
20972 this.inputEl().dom.value = this.inputValue;
20974 if(suppressEvent !== true){
20975 this.fireEvent('check', this, true);
20983 this.checked = state;
20985 this.inputEl().dom.checked = state;
20988 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20990 if(suppressEvent !== true){
20991 this.fireEvent('check', this, state);
20997 getValue : function()
20999 if(this.inputType == 'radio'){
21000 return this.getGroupValue();
21003 return this.hiddenEl().dom.value;
21007 getGroupValue : function()
21009 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21013 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21016 setValue : function(v,suppressEvent)
21018 if(this.inputType == 'radio'){
21019 this.setGroupValue(v, suppressEvent);
21023 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21028 setGroupValue : function(v, suppressEvent)
21030 this.startValue = this.getValue();
21032 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21033 e.dom.checked = false;
21035 if(e.dom.value == v){
21036 e.dom.checked = true;
21040 if(suppressEvent !== true){
21041 this.fireEvent('check', this, true);
21049 validate : function()
21051 if(this.getVisibilityEl().hasClass('hidden')){
21057 (this.inputType == 'radio' && this.validateRadio()) ||
21058 (this.inputType == 'checkbox' && this.validateCheckbox())
21064 this.markInvalid();
21068 validateRadio : function()
21070 if(this.getVisibilityEl().hasClass('hidden')){
21074 if(this.allowBlank){
21080 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21081 if(!e.dom.checked){
21093 validateCheckbox : function()
21096 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21097 //return (this.getValue() == this.inputValue) ? true : false;
21100 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21108 for(var i in group){
21109 if(group[i].el.isVisible(true)){
21117 for(var i in group){
21122 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21129 * Mark this field as valid
21131 markValid : function()
21135 this.fireEvent('valid', this);
21137 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21140 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21147 if(this.inputType == 'radio'){
21148 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21149 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21150 e.findParent('.form-group', false, true).addClass(_this.validClass);
21157 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21158 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21162 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21168 for(var i in group){
21169 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21170 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21175 * Mark this field as invalid
21176 * @param {String} msg The validation message
21178 markInvalid : function(msg)
21180 if(this.allowBlank){
21186 this.fireEvent('invalid', this, msg);
21188 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21191 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21195 label.markInvalid();
21198 if(this.inputType == 'radio'){
21199 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21200 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21201 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21208 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21209 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21213 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21219 for(var i in group){
21220 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21221 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21226 clearInvalid : function()
21228 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21230 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21232 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21234 if (label && label.iconEl) {
21235 label.iconEl.removeClass(label.validClass);
21236 label.iconEl.removeClass(label.invalidClass);
21240 disable : function()
21242 if(this.inputType != 'radio'){
21243 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21250 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21251 _this.getActionEl().addClass(this.disabledClass);
21252 e.dom.disabled = true;
21256 this.disabled = true;
21257 this.fireEvent("disable", this);
21261 enable : function()
21263 if(this.inputType != 'radio'){
21264 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21271 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21272 _this.getActionEl().removeClass(this.disabledClass);
21273 e.dom.disabled = false;
21277 this.disabled = false;
21278 this.fireEvent("enable", this);
21282 setBoxLabel : function(v)
21287 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21293 Roo.apply(Roo.bootstrap.CheckBox, {
21298 * register a CheckBox Group
21299 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21301 register : function(checkbox)
21303 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21304 this.groups[checkbox.groupId] = {};
21307 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21311 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21315 * fetch a CheckBox Group based on the group ID
21316 * @param {string} the group ID
21317 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21319 get: function(groupId) {
21320 if (typeof(this.groups[groupId]) == 'undefined') {
21324 return this.groups[groupId] ;
21337 * @class Roo.bootstrap.Radio
21338 * @extends Roo.bootstrap.Component
21339 * Bootstrap Radio class
21340 * @cfg {String} boxLabel - the label associated
21341 * @cfg {String} value - the value of radio
21344 * Create a new Radio
21345 * @param {Object} config The config object
21347 Roo.bootstrap.Radio = function(config){
21348 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21352 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21358 getAutoCreate : function()
21362 cls : 'form-group radio',
21367 html : this.boxLabel
21375 initEvents : function()
21377 this.parent().register(this);
21379 this.el.on('click', this.onClick, this);
21383 onClick : function(e)
21385 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21386 this.setChecked(true);
21390 setChecked : function(state, suppressEvent)
21392 this.parent().setValue(this.value, suppressEvent);
21396 setBoxLabel : function(v)
21401 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21416 * @class Roo.bootstrap.SecurePass
21417 * @extends Roo.bootstrap.Input
21418 * Bootstrap SecurePass class
21422 * Create a new SecurePass
21423 * @param {Object} config The config object
21426 Roo.bootstrap.SecurePass = function (config) {
21427 // these go here, so the translation tool can replace them..
21429 PwdEmpty: "Please type a password, and then retype it to confirm.",
21430 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21431 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21432 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21433 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21434 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21435 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21436 TooWeak: "Your password is Too Weak."
21438 this.meterLabel = "Password strength:";
21439 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21440 this.meterClass = [
21441 "roo-password-meter-tooweak",
21442 "roo-password-meter-weak",
21443 "roo-password-meter-medium",
21444 "roo-password-meter-strong",
21445 "roo-password-meter-grey"
21450 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21453 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21455 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21457 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21458 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21459 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21460 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21461 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21462 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21463 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21473 * @cfg {String/Object} Label for the strength meter (defaults to
21474 * 'Password strength:')
21479 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21480 * ['Weak', 'Medium', 'Strong'])
21483 pwdStrengths: false,
21496 initEvents: function ()
21498 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21500 if (this.el.is('input[type=password]') && Roo.isSafari) {
21501 this.el.on('keydown', this.SafariOnKeyDown, this);
21504 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21507 onRender: function (ct, position)
21509 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21510 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21511 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21513 this.trigger.createChild({
21518 cls: 'roo-password-meter-grey col-xs-12',
21521 //width: this.meterWidth + 'px'
21525 cls: 'roo-password-meter-text'
21531 if (this.hideTrigger) {
21532 this.trigger.setDisplayed(false);
21534 this.setSize(this.width || '', this.height || '');
21537 onDestroy: function ()
21539 if (this.trigger) {
21540 this.trigger.removeAllListeners();
21541 this.trigger.remove();
21544 this.wrap.remove();
21546 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21549 checkStrength: function ()
21551 var pwd = this.inputEl().getValue();
21552 if (pwd == this._lastPwd) {
21557 if (this.ClientSideStrongPassword(pwd)) {
21559 } else if (this.ClientSideMediumPassword(pwd)) {
21561 } else if (this.ClientSideWeakPassword(pwd)) {
21567 Roo.log('strength1: ' + strength);
21569 //var pm = this.trigger.child('div/div/div').dom;
21570 var pm = this.trigger.child('div/div');
21571 pm.removeClass(this.meterClass);
21572 pm.addClass(this.meterClass[strength]);
21575 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21577 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21579 this._lastPwd = pwd;
21583 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21585 this._lastPwd = '';
21587 var pm = this.trigger.child('div/div');
21588 pm.removeClass(this.meterClass);
21589 pm.addClass('roo-password-meter-grey');
21592 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21595 this.inputEl().dom.type='password';
21598 validateValue: function (value)
21601 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21604 if (value.length == 0) {
21605 if (this.allowBlank) {
21606 this.clearInvalid();
21610 this.markInvalid(this.errors.PwdEmpty);
21611 this.errorMsg = this.errors.PwdEmpty;
21619 if ('[\x21-\x7e]*'.match(value)) {
21620 this.markInvalid(this.errors.PwdBadChar);
21621 this.errorMsg = this.errors.PwdBadChar;
21624 if (value.length < 6) {
21625 this.markInvalid(this.errors.PwdShort);
21626 this.errorMsg = this.errors.PwdShort;
21629 if (value.length > 16) {
21630 this.markInvalid(this.errors.PwdLong);
21631 this.errorMsg = this.errors.PwdLong;
21635 if (this.ClientSideStrongPassword(value)) {
21637 } else if (this.ClientSideMediumPassword(value)) {
21639 } else if (this.ClientSideWeakPassword(value)) {
21646 if (strength < 2) {
21647 //this.markInvalid(this.errors.TooWeak);
21648 this.errorMsg = this.errors.TooWeak;
21653 console.log('strength2: ' + strength);
21655 //var pm = this.trigger.child('div/div/div').dom;
21657 var pm = this.trigger.child('div/div');
21658 pm.removeClass(this.meterClass);
21659 pm.addClass(this.meterClass[strength]);
21661 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21663 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21665 this.errorMsg = '';
21669 CharacterSetChecks: function (type)
21672 this.fResult = false;
21675 isctype: function (character, type)
21678 case this.kCapitalLetter:
21679 if (character >= 'A' && character <= 'Z') {
21684 case this.kSmallLetter:
21685 if (character >= 'a' && character <= 'z') {
21691 if (character >= '0' && character <= '9') {
21696 case this.kPunctuation:
21697 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21708 IsLongEnough: function (pwd, size)
21710 return !(pwd == null || isNaN(size) || pwd.length < size);
21713 SpansEnoughCharacterSets: function (word, nb)
21715 if (!this.IsLongEnough(word, nb))
21720 var characterSetChecks = new Array(
21721 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21722 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21725 for (var index = 0; index < word.length; ++index) {
21726 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21727 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21728 characterSetChecks[nCharSet].fResult = true;
21735 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21736 if (characterSetChecks[nCharSet].fResult) {
21741 if (nCharSets < nb) {
21747 ClientSideStrongPassword: function (pwd)
21749 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21752 ClientSideMediumPassword: function (pwd)
21754 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21757 ClientSideWeakPassword: function (pwd)
21759 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21762 })//<script type="text/javascript">
21765 * Based Ext JS Library 1.1.1
21766 * Copyright(c) 2006-2007, Ext JS, LLC.
21772 * @class Roo.HtmlEditorCore
21773 * @extends Roo.Component
21774 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21776 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21779 Roo.HtmlEditorCore = function(config){
21782 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21787 * @event initialize
21788 * Fires when the editor is fully initialized (including the iframe)
21789 * @param {Roo.HtmlEditorCore} this
21794 * Fires when the editor is first receives the focus. Any insertion must wait
21795 * until after this event.
21796 * @param {Roo.HtmlEditorCore} this
21800 * @event beforesync
21801 * Fires before the textarea is updated with content from the editor iframe. Return false
21802 * to cancel the sync.
21803 * @param {Roo.HtmlEditorCore} this
21804 * @param {String} html
21808 * @event beforepush
21809 * Fires before the iframe editor is updated with content from the textarea. Return false
21810 * to cancel the push.
21811 * @param {Roo.HtmlEditorCore} this
21812 * @param {String} html
21817 * Fires when the textarea is updated with content from the editor iframe.
21818 * @param {Roo.HtmlEditorCore} this
21819 * @param {String} html
21824 * Fires when the iframe editor is updated with content from the textarea.
21825 * @param {Roo.HtmlEditorCore} this
21826 * @param {String} html
21831 * @event editorevent
21832 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21833 * @param {Roo.HtmlEditorCore} this
21839 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21841 // defaults : white / black...
21842 this.applyBlacklists();
21849 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21853 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21859 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21864 * @cfg {Number} height (in pixels)
21868 * @cfg {Number} width (in pixels)
21873 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21876 stylesheets: false,
21881 // private properties
21882 validationEvent : false,
21884 initialized : false,
21886 sourceEditMode : false,
21887 onFocus : Roo.emptyFn,
21889 hideMode:'offsets',
21893 // blacklist + whitelisted elements..
21900 * Protected method that will not generally be called directly. It
21901 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21902 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21904 getDocMarkup : function(){
21908 // inherit styels from page...??
21909 if (this.stylesheets === false) {
21911 Roo.get(document.head).select('style').each(function(node) {
21912 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21915 Roo.get(document.head).select('link').each(function(node) {
21916 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21919 } else if (!this.stylesheets.length) {
21921 st = '<style type="text/css">' +
21922 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21925 st = '<style type="text/css">' +
21930 st += '<style type="text/css">' +
21931 'IMG { cursor: pointer } ' +
21934 var cls = 'roo-htmleditor-body';
21936 if(this.bodyCls.length){
21937 cls += ' ' + this.bodyCls;
21940 return '<html><head>' + st +
21941 //<style type="text/css">' +
21942 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21944 ' </head><body class="' + cls + '"></body></html>';
21948 onRender : function(ct, position)
21951 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21952 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21955 this.el.dom.style.border = '0 none';
21956 this.el.dom.setAttribute('tabIndex', -1);
21957 this.el.addClass('x-hidden hide');
21961 if(Roo.isIE){ // fix IE 1px bogus margin
21962 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21966 this.frameId = Roo.id();
21970 var iframe = this.owner.wrap.createChild({
21972 cls: 'form-control', // bootstrap..
21974 name: this.frameId,
21975 frameBorder : 'no',
21976 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21981 this.iframe = iframe.dom;
21983 this.assignDocWin();
21985 this.doc.designMode = 'on';
21988 this.doc.write(this.getDocMarkup());
21992 var task = { // must defer to wait for browser to be ready
21994 //console.log("run task?" + this.doc.readyState);
21995 this.assignDocWin();
21996 if(this.doc.body || this.doc.readyState == 'complete'){
21998 this.doc.designMode="on";
22002 Roo.TaskMgr.stop(task);
22003 this.initEditor.defer(10, this);
22010 Roo.TaskMgr.start(task);
22015 onResize : function(w, h)
22017 Roo.log('resize: ' +w + ',' + h );
22018 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22022 if(typeof w == 'number'){
22024 this.iframe.style.width = w + 'px';
22026 if(typeof h == 'number'){
22028 this.iframe.style.height = h + 'px';
22030 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22037 * Toggles the editor between standard and source edit mode.
22038 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22040 toggleSourceEdit : function(sourceEditMode){
22042 this.sourceEditMode = sourceEditMode === true;
22044 if(this.sourceEditMode){
22046 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22049 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22050 //this.iframe.className = '';
22053 //this.setSize(this.owner.wrap.getSize());
22054 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22061 * Protected method that will not generally be called directly. If you need/want
22062 * custom HTML cleanup, this is the method you should override.
22063 * @param {String} html The HTML to be cleaned
22064 * return {String} The cleaned HTML
22066 cleanHtml : function(html){
22067 html = String(html);
22068 if(html.length > 5){
22069 if(Roo.isSafari){ // strip safari nonsense
22070 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22073 if(html == ' '){
22080 * HTML Editor -> Textarea
22081 * Protected method that will not generally be called directly. Syncs the contents
22082 * of the editor iframe with the textarea.
22084 syncValue : function(){
22085 if(this.initialized){
22086 var bd = (this.doc.body || this.doc.documentElement);
22087 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22088 var html = bd.innerHTML;
22090 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22091 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22093 html = '<div style="'+m[0]+'">' + html + '</div>';
22096 html = this.cleanHtml(html);
22097 // fix up the special chars.. normaly like back quotes in word...
22098 // however we do not want to do this with chinese..
22099 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22100 var cc = b.charCodeAt();
22102 (cc >= 0x4E00 && cc < 0xA000 ) ||
22103 (cc >= 0x3400 && cc < 0x4E00 ) ||
22104 (cc >= 0xf900 && cc < 0xfb00 )
22110 if(this.owner.fireEvent('beforesync', this, html) !== false){
22111 this.el.dom.value = html;
22112 this.owner.fireEvent('sync', this, html);
22118 * Protected method that will not generally be called directly. Pushes the value of the textarea
22119 * into the iframe editor.
22121 pushValue : function(){
22122 if(this.initialized){
22123 var v = this.el.dom.value.trim();
22125 // if(v.length < 1){
22129 if(this.owner.fireEvent('beforepush', this, v) !== false){
22130 var d = (this.doc.body || this.doc.documentElement);
22132 this.cleanUpPaste();
22133 this.el.dom.value = d.innerHTML;
22134 this.owner.fireEvent('push', this, v);
22140 deferFocus : function(){
22141 this.focus.defer(10, this);
22145 focus : function(){
22146 if(this.win && !this.sourceEditMode){
22153 assignDocWin: function()
22155 var iframe = this.iframe;
22158 this.doc = iframe.contentWindow.document;
22159 this.win = iframe.contentWindow;
22161 // if (!Roo.get(this.frameId)) {
22164 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22165 // this.win = Roo.get(this.frameId).dom.contentWindow;
22167 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22171 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22172 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22177 initEditor : function(){
22178 //console.log("INIT EDITOR");
22179 this.assignDocWin();
22183 this.doc.designMode="on";
22185 this.doc.write(this.getDocMarkup());
22188 var dbody = (this.doc.body || this.doc.documentElement);
22189 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22190 // this copies styles from the containing element into thsi one..
22191 // not sure why we need all of this..
22192 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22194 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22195 //ss['background-attachment'] = 'fixed'; // w3c
22196 dbody.bgProperties = 'fixed'; // ie
22197 //Roo.DomHelper.applyStyles(dbody, ss);
22198 Roo.EventManager.on(this.doc, {
22199 //'mousedown': this.onEditorEvent,
22200 'mouseup': this.onEditorEvent,
22201 'dblclick': this.onEditorEvent,
22202 'click': this.onEditorEvent,
22203 'keyup': this.onEditorEvent,
22208 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22210 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22211 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22213 this.initialized = true;
22215 this.owner.fireEvent('initialize', this);
22220 onDestroy : function(){
22226 //for (var i =0; i < this.toolbars.length;i++) {
22227 // // fixme - ask toolbars for heights?
22228 // this.toolbars[i].onDestroy();
22231 //this.wrap.dom.innerHTML = '';
22232 //this.wrap.remove();
22237 onFirstFocus : function(){
22239 this.assignDocWin();
22242 this.activated = true;
22245 if(Roo.isGecko){ // prevent silly gecko errors
22247 var s = this.win.getSelection();
22248 if(!s.focusNode || s.focusNode.nodeType != 3){
22249 var r = s.getRangeAt(0);
22250 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22255 this.execCmd('useCSS', true);
22256 this.execCmd('styleWithCSS', false);
22259 this.owner.fireEvent('activate', this);
22263 adjustFont: function(btn){
22264 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22265 //if(Roo.isSafari){ // safari
22268 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22269 if(Roo.isSafari){ // safari
22270 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22271 v = (v < 10) ? 10 : v;
22272 v = (v > 48) ? 48 : v;
22273 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22278 v = Math.max(1, v+adjust);
22280 this.execCmd('FontSize', v );
22283 onEditorEvent : function(e)
22285 this.owner.fireEvent('editorevent', this, e);
22286 // this.updateToolbar();
22287 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22290 insertTag : function(tg)
22292 // could be a bit smarter... -> wrap the current selected tRoo..
22293 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22295 range = this.createRange(this.getSelection());
22296 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22297 wrappingNode.appendChild(range.extractContents());
22298 range.insertNode(wrappingNode);
22305 this.execCmd("formatblock", tg);
22309 insertText : function(txt)
22313 var range = this.createRange();
22314 range.deleteContents();
22315 //alert(Sender.getAttribute('label'));
22317 range.insertNode(this.doc.createTextNode(txt));
22323 * Executes a Midas editor command on the editor document and performs necessary focus and
22324 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22325 * @param {String} cmd The Midas command
22326 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22328 relayCmd : function(cmd, value){
22330 this.execCmd(cmd, value);
22331 this.owner.fireEvent('editorevent', this);
22332 //this.updateToolbar();
22333 this.owner.deferFocus();
22337 * Executes a Midas editor command directly on the editor document.
22338 * For visual commands, you should use {@link #relayCmd} instead.
22339 * <b>This should only be called after the editor is initialized.</b>
22340 * @param {String} cmd The Midas command
22341 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22343 execCmd : function(cmd, value){
22344 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22351 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22353 * @param {String} text | dom node..
22355 insertAtCursor : function(text)
22358 if(!this.activated){
22364 var r = this.doc.selection.createRange();
22375 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22379 // from jquery ui (MIT licenced)
22381 var win = this.win;
22383 if (win.getSelection && win.getSelection().getRangeAt) {
22384 range = win.getSelection().getRangeAt(0);
22385 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22386 range.insertNode(node);
22387 } else if (win.document.selection && win.document.selection.createRange) {
22388 // no firefox support
22389 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22390 win.document.selection.createRange().pasteHTML(txt);
22392 // no firefox support
22393 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22394 this.execCmd('InsertHTML', txt);
22403 mozKeyPress : function(e){
22405 var c = e.getCharCode(), cmd;
22408 c = String.fromCharCode(c).toLowerCase();
22422 this.cleanUpPaste.defer(100, this);
22430 e.preventDefault();
22438 fixKeys : function(){ // load time branching for fastest keydown performance
22440 return function(e){
22441 var k = e.getKey(), r;
22444 r = this.doc.selection.createRange();
22447 r.pasteHTML('    ');
22454 r = this.doc.selection.createRange();
22456 var target = r.parentElement();
22457 if(!target || target.tagName.toLowerCase() != 'li'){
22459 r.pasteHTML('<br />');
22465 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22466 this.cleanUpPaste.defer(100, this);
22472 }else if(Roo.isOpera){
22473 return function(e){
22474 var k = e.getKey();
22478 this.execCmd('InsertHTML','    ');
22481 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22482 this.cleanUpPaste.defer(100, this);
22487 }else if(Roo.isSafari){
22488 return function(e){
22489 var k = e.getKey();
22493 this.execCmd('InsertText','\t');
22497 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22498 this.cleanUpPaste.defer(100, this);
22506 getAllAncestors: function()
22508 var p = this.getSelectedNode();
22511 a.push(p); // push blank onto stack..
22512 p = this.getParentElement();
22516 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22520 a.push(this.doc.body);
22524 lastSelNode : false,
22527 getSelection : function()
22529 this.assignDocWin();
22530 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22533 getSelectedNode: function()
22535 // this may only work on Gecko!!!
22537 // should we cache this!!!!
22542 var range = this.createRange(this.getSelection()).cloneRange();
22545 var parent = range.parentElement();
22547 var testRange = range.duplicate();
22548 testRange.moveToElementText(parent);
22549 if (testRange.inRange(range)) {
22552 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22555 parent = parent.parentElement;
22560 // is ancestor a text element.
22561 var ac = range.commonAncestorContainer;
22562 if (ac.nodeType == 3) {
22563 ac = ac.parentNode;
22566 var ar = ac.childNodes;
22569 var other_nodes = [];
22570 var has_other_nodes = false;
22571 for (var i=0;i<ar.length;i++) {
22572 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22575 // fullly contained node.
22577 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22582 // probably selected..
22583 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22584 other_nodes.push(ar[i]);
22588 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22593 has_other_nodes = true;
22595 if (!nodes.length && other_nodes.length) {
22596 nodes= other_nodes;
22598 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22604 createRange: function(sel)
22606 // this has strange effects when using with
22607 // top toolbar - not sure if it's a great idea.
22608 //this.editor.contentWindow.focus();
22609 if (typeof sel != "undefined") {
22611 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22613 return this.doc.createRange();
22616 return this.doc.createRange();
22619 getParentElement: function()
22622 this.assignDocWin();
22623 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22625 var range = this.createRange(sel);
22628 var p = range.commonAncestorContainer;
22629 while (p.nodeType == 3) { // text node
22640 * Range intersection.. the hard stuff...
22644 * [ -- selected range --- ]
22648 * if end is before start or hits it. fail.
22649 * if start is after end or hits it fail.
22651 * if either hits (but other is outside. - then it's not
22657 // @see http://www.thismuchiknow.co.uk/?p=64.
22658 rangeIntersectsNode : function(range, node)
22660 var nodeRange = node.ownerDocument.createRange();
22662 nodeRange.selectNode(node);
22664 nodeRange.selectNodeContents(node);
22667 var rangeStartRange = range.cloneRange();
22668 rangeStartRange.collapse(true);
22670 var rangeEndRange = range.cloneRange();
22671 rangeEndRange.collapse(false);
22673 var nodeStartRange = nodeRange.cloneRange();
22674 nodeStartRange.collapse(true);
22676 var nodeEndRange = nodeRange.cloneRange();
22677 nodeEndRange.collapse(false);
22679 return rangeStartRange.compareBoundaryPoints(
22680 Range.START_TO_START, nodeEndRange) == -1 &&
22681 rangeEndRange.compareBoundaryPoints(
22682 Range.START_TO_START, nodeStartRange) == 1;
22686 rangeCompareNode : function(range, node)
22688 var nodeRange = node.ownerDocument.createRange();
22690 nodeRange.selectNode(node);
22692 nodeRange.selectNodeContents(node);
22696 range.collapse(true);
22698 nodeRange.collapse(true);
22700 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22701 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22703 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22705 var nodeIsBefore = ss == 1;
22706 var nodeIsAfter = ee == -1;
22708 if (nodeIsBefore && nodeIsAfter) {
22711 if (!nodeIsBefore && nodeIsAfter) {
22712 return 1; //right trailed.
22715 if (nodeIsBefore && !nodeIsAfter) {
22716 return 2; // left trailed.
22722 // private? - in a new class?
22723 cleanUpPaste : function()
22725 // cleans up the whole document..
22726 Roo.log('cleanuppaste');
22728 this.cleanUpChildren(this.doc.body);
22729 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22730 if (clean != this.doc.body.innerHTML) {
22731 this.doc.body.innerHTML = clean;
22736 cleanWordChars : function(input) {// change the chars to hex code
22737 var he = Roo.HtmlEditorCore;
22739 var output = input;
22740 Roo.each(he.swapCodes, function(sw) {
22741 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22743 output = output.replace(swapper, sw[1]);
22750 cleanUpChildren : function (n)
22752 if (!n.childNodes.length) {
22755 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22756 this.cleanUpChild(n.childNodes[i]);
22763 cleanUpChild : function (node)
22766 //console.log(node);
22767 if (node.nodeName == "#text") {
22768 // clean up silly Windows -- stuff?
22771 if (node.nodeName == "#comment") {
22772 node.parentNode.removeChild(node);
22773 // clean up silly Windows -- stuff?
22776 var lcname = node.tagName.toLowerCase();
22777 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22778 // whitelist of tags..
22780 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22782 node.parentNode.removeChild(node);
22787 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22789 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22790 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22792 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22793 // remove_keep_children = true;
22796 if (remove_keep_children) {
22797 this.cleanUpChildren(node);
22798 // inserts everything just before this node...
22799 while (node.childNodes.length) {
22800 var cn = node.childNodes[0];
22801 node.removeChild(cn);
22802 node.parentNode.insertBefore(cn, node);
22804 node.parentNode.removeChild(node);
22808 if (!node.attributes || !node.attributes.length) {
22809 this.cleanUpChildren(node);
22813 function cleanAttr(n,v)
22816 if (v.match(/^\./) || v.match(/^\//)) {
22819 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22822 if (v.match(/^#/)) {
22825 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22826 node.removeAttribute(n);
22830 var cwhite = this.cwhite;
22831 var cblack = this.cblack;
22833 function cleanStyle(n,v)
22835 if (v.match(/expression/)) { //XSS?? should we even bother..
22836 node.removeAttribute(n);
22840 var parts = v.split(/;/);
22843 Roo.each(parts, function(p) {
22844 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22848 var l = p.split(':').shift().replace(/\s+/g,'');
22849 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22851 if ( cwhite.length && cblack.indexOf(l) > -1) {
22852 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22853 //node.removeAttribute(n);
22857 // only allow 'c whitelisted system attributes'
22858 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22859 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22860 //node.removeAttribute(n);
22870 if (clean.length) {
22871 node.setAttribute(n, clean.join(';'));
22873 node.removeAttribute(n);
22879 for (var i = node.attributes.length-1; i > -1 ; i--) {
22880 var a = node.attributes[i];
22883 if (a.name.toLowerCase().substr(0,2)=='on') {
22884 node.removeAttribute(a.name);
22887 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22888 node.removeAttribute(a.name);
22891 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22892 cleanAttr(a.name,a.value); // fixme..
22895 if (a.name == 'style') {
22896 cleanStyle(a.name,a.value);
22899 /// clean up MS crap..
22900 // tecnically this should be a list of valid class'es..
22903 if (a.name == 'class') {
22904 if (a.value.match(/^Mso/)) {
22905 node.className = '';
22908 if (a.value.match(/^body$/)) {
22909 node.className = '';
22920 this.cleanUpChildren(node);
22926 * Clean up MS wordisms...
22928 cleanWord : function(node)
22933 this.cleanWord(this.doc.body);
22936 if (node.nodeName == "#text") {
22937 // clean up silly Windows -- stuff?
22940 if (node.nodeName == "#comment") {
22941 node.parentNode.removeChild(node);
22942 // clean up silly Windows -- stuff?
22946 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22947 node.parentNode.removeChild(node);
22951 // remove - but keep children..
22952 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22953 while (node.childNodes.length) {
22954 var cn = node.childNodes[0];
22955 node.removeChild(cn);
22956 node.parentNode.insertBefore(cn, node);
22958 node.parentNode.removeChild(node);
22959 this.iterateChildren(node, this.cleanWord);
22963 if (node.className.length) {
22965 var cn = node.className.split(/\W+/);
22967 Roo.each(cn, function(cls) {
22968 if (cls.match(/Mso[a-zA-Z]+/)) {
22973 node.className = cna.length ? cna.join(' ') : '';
22975 node.removeAttribute("class");
22979 if (node.hasAttribute("lang")) {
22980 node.removeAttribute("lang");
22983 if (node.hasAttribute("style")) {
22985 var styles = node.getAttribute("style").split(";");
22987 Roo.each(styles, function(s) {
22988 if (!s.match(/:/)) {
22991 var kv = s.split(":");
22992 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22995 // what ever is left... we allow.
22998 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22999 if (!nstyle.length) {
23000 node.removeAttribute('style');
23003 this.iterateChildren(node, this.cleanWord);
23009 * iterateChildren of a Node, calling fn each time, using this as the scole..
23010 * @param {DomNode} node node to iterate children of.
23011 * @param {Function} fn method of this class to call on each item.
23013 iterateChildren : function(node, fn)
23015 if (!node.childNodes.length) {
23018 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23019 fn.call(this, node.childNodes[i])
23025 * cleanTableWidths.
23027 * Quite often pasting from word etc.. results in tables with column and widths.
23028 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23031 cleanTableWidths : function(node)
23036 this.cleanTableWidths(this.doc.body);
23041 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23044 Roo.log(node.tagName);
23045 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23046 this.iterateChildren(node, this.cleanTableWidths);
23049 if (node.hasAttribute('width')) {
23050 node.removeAttribute('width');
23054 if (node.hasAttribute("style")) {
23057 var styles = node.getAttribute("style").split(";");
23059 Roo.each(styles, function(s) {
23060 if (!s.match(/:/)) {
23063 var kv = s.split(":");
23064 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23067 // what ever is left... we allow.
23070 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23071 if (!nstyle.length) {
23072 node.removeAttribute('style');
23076 this.iterateChildren(node, this.cleanTableWidths);
23084 domToHTML : function(currentElement, depth, nopadtext) {
23086 depth = depth || 0;
23087 nopadtext = nopadtext || false;
23089 if (!currentElement) {
23090 return this.domToHTML(this.doc.body);
23093 //Roo.log(currentElement);
23095 var allText = false;
23096 var nodeName = currentElement.nodeName;
23097 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23099 if (nodeName == '#text') {
23101 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23106 if (nodeName != 'BODY') {
23109 // Prints the node tagName, such as <A>, <IMG>, etc
23112 for(i = 0; i < currentElement.attributes.length;i++) {
23114 var aname = currentElement.attributes.item(i).name;
23115 if (!currentElement.attributes.item(i).value.length) {
23118 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23121 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23130 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23133 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23138 // Traverse the tree
23140 var currentElementChild = currentElement.childNodes.item(i);
23141 var allText = true;
23142 var innerHTML = '';
23144 while (currentElementChild) {
23145 // Formatting code (indent the tree so it looks nice on the screen)
23146 var nopad = nopadtext;
23147 if (lastnode == 'SPAN') {
23151 if (currentElementChild.nodeName == '#text') {
23152 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23153 toadd = nopadtext ? toadd : toadd.trim();
23154 if (!nopad && toadd.length > 80) {
23155 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23157 innerHTML += toadd;
23160 currentElementChild = currentElement.childNodes.item(i);
23166 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23168 // Recursively traverse the tree structure of the child node
23169 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23170 lastnode = currentElementChild.nodeName;
23172 currentElementChild=currentElement.childNodes.item(i);
23178 // The remaining code is mostly for formatting the tree
23179 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23184 ret+= "</"+tagName+">";
23190 applyBlacklists : function()
23192 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23193 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23197 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23198 if (b.indexOf(tag) > -1) {
23201 this.white.push(tag);
23205 Roo.each(w, function(tag) {
23206 if (b.indexOf(tag) > -1) {
23209 if (this.white.indexOf(tag) > -1) {
23212 this.white.push(tag);
23217 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23218 if (w.indexOf(tag) > -1) {
23221 this.black.push(tag);
23225 Roo.each(b, function(tag) {
23226 if (w.indexOf(tag) > -1) {
23229 if (this.black.indexOf(tag) > -1) {
23232 this.black.push(tag);
23237 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23238 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23242 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23243 if (b.indexOf(tag) > -1) {
23246 this.cwhite.push(tag);
23250 Roo.each(w, function(tag) {
23251 if (b.indexOf(tag) > -1) {
23254 if (this.cwhite.indexOf(tag) > -1) {
23257 this.cwhite.push(tag);
23262 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23263 if (w.indexOf(tag) > -1) {
23266 this.cblack.push(tag);
23270 Roo.each(b, function(tag) {
23271 if (w.indexOf(tag) > -1) {
23274 if (this.cblack.indexOf(tag) > -1) {
23277 this.cblack.push(tag);
23282 setStylesheets : function(stylesheets)
23284 if(typeof(stylesheets) == 'string'){
23285 Roo.get(this.iframe.contentDocument.head).createChild({
23287 rel : 'stylesheet',
23296 Roo.each(stylesheets, function(s) {
23301 Roo.get(_this.iframe.contentDocument.head).createChild({
23303 rel : 'stylesheet',
23312 removeStylesheets : function()
23316 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23321 setStyle : function(style)
23323 Roo.get(this.iframe.contentDocument.head).createChild({
23332 // hide stuff that is not compatible
23346 * @event specialkey
23350 * @cfg {String} fieldClass @hide
23353 * @cfg {String} focusClass @hide
23356 * @cfg {String} autoCreate @hide
23359 * @cfg {String} inputType @hide
23362 * @cfg {String} invalidClass @hide
23365 * @cfg {String} invalidText @hide
23368 * @cfg {String} msgFx @hide
23371 * @cfg {String} validateOnBlur @hide
23375 Roo.HtmlEditorCore.white = [
23376 'area', 'br', 'img', 'input', 'hr', 'wbr',
23378 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23379 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23380 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23381 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23382 'table', 'ul', 'xmp',
23384 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23387 'dir', 'menu', 'ol', 'ul', 'dl',
23393 Roo.HtmlEditorCore.black = [
23394 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23396 'base', 'basefont', 'bgsound', 'blink', 'body',
23397 'frame', 'frameset', 'head', 'html', 'ilayer',
23398 'iframe', 'layer', 'link', 'meta', 'object',
23399 'script', 'style' ,'title', 'xml' // clean later..
23401 Roo.HtmlEditorCore.clean = [
23402 'script', 'style', 'title', 'xml'
23404 Roo.HtmlEditorCore.remove = [
23409 Roo.HtmlEditorCore.ablack = [
23413 Roo.HtmlEditorCore.aclean = [
23414 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23418 Roo.HtmlEditorCore.pwhite= [
23419 'http', 'https', 'mailto'
23422 // white listed style attributes.
23423 Roo.HtmlEditorCore.cwhite= [
23424 // 'text-align', /// default is to allow most things..
23430 // black listed style attributes.
23431 Roo.HtmlEditorCore.cblack= [
23432 // 'font-size' -- this can be set by the project
23436 Roo.HtmlEditorCore.swapCodes =[
23455 * @class Roo.bootstrap.HtmlEditor
23456 * @extends Roo.bootstrap.TextArea
23457 * Bootstrap HtmlEditor class
23460 * Create a new HtmlEditor
23461 * @param {Object} config The config object
23464 Roo.bootstrap.HtmlEditor = function(config){
23465 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23466 if (!this.toolbars) {
23467 this.toolbars = [];
23470 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23473 * @event initialize
23474 * Fires when the editor is fully initialized (including the iframe)
23475 * @param {HtmlEditor} this
23480 * Fires when the editor is first receives the focus. Any insertion must wait
23481 * until after this event.
23482 * @param {HtmlEditor} this
23486 * @event beforesync
23487 * Fires before the textarea is updated with content from the editor iframe. Return false
23488 * to cancel the sync.
23489 * @param {HtmlEditor} this
23490 * @param {String} html
23494 * @event beforepush
23495 * Fires before the iframe editor is updated with content from the textarea. Return false
23496 * to cancel the push.
23497 * @param {HtmlEditor} this
23498 * @param {String} html
23503 * Fires when the textarea is updated with content from the editor iframe.
23504 * @param {HtmlEditor} this
23505 * @param {String} html
23510 * Fires when the iframe editor is updated with content from the textarea.
23511 * @param {HtmlEditor} this
23512 * @param {String} html
23516 * @event editmodechange
23517 * Fires when the editor switches edit modes
23518 * @param {HtmlEditor} this
23519 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23521 editmodechange: true,
23523 * @event editorevent
23524 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23525 * @param {HtmlEditor} this
23529 * @event firstfocus
23530 * Fires when on first focus - needed by toolbars..
23531 * @param {HtmlEditor} this
23536 * Auto save the htmlEditor value as a file into Events
23537 * @param {HtmlEditor} this
23541 * @event savedpreview
23542 * preview the saved version of htmlEditor
23543 * @param {HtmlEditor} this
23550 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23554 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23559 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23564 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23569 * @cfg {Number} height (in pixels)
23573 * @cfg {Number} width (in pixels)
23578 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23581 stylesheets: false,
23586 // private properties
23587 validationEvent : false,
23589 initialized : false,
23592 onFocus : Roo.emptyFn,
23594 hideMode:'offsets',
23596 tbContainer : false,
23600 toolbarContainer :function() {
23601 return this.wrap.select('.x-html-editor-tb',true).first();
23605 * Protected method that will not generally be called directly. It
23606 * is called when the editor creates its toolbar. Override this method if you need to
23607 * add custom toolbar buttons.
23608 * @param {HtmlEditor} editor
23610 createToolbar : function(){
23611 Roo.log('renewing');
23612 Roo.log("create toolbars");
23614 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23615 this.toolbars[0].render(this.toolbarContainer());
23619 // if (!editor.toolbars || !editor.toolbars.length) {
23620 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23623 // for (var i =0 ; i < editor.toolbars.length;i++) {
23624 // editor.toolbars[i] = Roo.factory(
23625 // typeof(editor.toolbars[i]) == 'string' ?
23626 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23627 // Roo.bootstrap.HtmlEditor);
23628 // editor.toolbars[i].init(editor);
23634 onRender : function(ct, position)
23636 // Roo.log("Call onRender: " + this.xtype);
23638 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23640 this.wrap = this.inputEl().wrap({
23641 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23644 this.editorcore.onRender(ct, position);
23646 if (this.resizable) {
23647 this.resizeEl = new Roo.Resizable(this.wrap, {
23651 minHeight : this.height,
23652 height: this.height,
23653 handles : this.resizable,
23656 resize : function(r, w, h) {
23657 _t.onResize(w,h); // -something
23663 this.createToolbar(this);
23666 if(!this.width && this.resizable){
23667 this.setSize(this.wrap.getSize());
23669 if (this.resizeEl) {
23670 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23671 // should trigger onReize..
23677 onResize : function(w, h)
23679 Roo.log('resize: ' +w + ',' + h );
23680 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23684 if(this.inputEl() ){
23685 if(typeof w == 'number'){
23686 var aw = w - this.wrap.getFrameWidth('lr');
23687 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23690 if(typeof h == 'number'){
23691 var tbh = -11; // fixme it needs to tool bar size!
23692 for (var i =0; i < this.toolbars.length;i++) {
23693 // fixme - ask toolbars for heights?
23694 tbh += this.toolbars[i].el.getHeight();
23695 //if (this.toolbars[i].footer) {
23696 // tbh += this.toolbars[i].footer.el.getHeight();
23704 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23705 ah -= 5; // knock a few pixes off for look..
23706 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23710 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23711 this.editorcore.onResize(ew,eh);
23716 * Toggles the editor between standard and source edit mode.
23717 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23719 toggleSourceEdit : function(sourceEditMode)
23721 this.editorcore.toggleSourceEdit(sourceEditMode);
23723 if(this.editorcore.sourceEditMode){
23724 Roo.log('editor - showing textarea');
23727 // Roo.log(this.syncValue());
23729 this.inputEl().removeClass(['hide', 'x-hidden']);
23730 this.inputEl().dom.removeAttribute('tabIndex');
23731 this.inputEl().focus();
23733 Roo.log('editor - hiding textarea');
23735 // Roo.log(this.pushValue());
23738 this.inputEl().addClass(['hide', 'x-hidden']);
23739 this.inputEl().dom.setAttribute('tabIndex', -1);
23740 //this.deferFocus();
23743 if(this.resizable){
23744 this.setSize(this.wrap.getSize());
23747 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23750 // private (for BoxComponent)
23751 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23753 // private (for BoxComponent)
23754 getResizeEl : function(){
23758 // private (for BoxComponent)
23759 getPositionEl : function(){
23764 initEvents : function(){
23765 this.originalValue = this.getValue();
23769 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23772 // markInvalid : Roo.emptyFn,
23774 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23777 // clearInvalid : Roo.emptyFn,
23779 setValue : function(v){
23780 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23781 this.editorcore.pushValue();
23786 deferFocus : function(){
23787 this.focus.defer(10, this);
23791 focus : function(){
23792 this.editorcore.focus();
23798 onDestroy : function(){
23804 for (var i =0; i < this.toolbars.length;i++) {
23805 // fixme - ask toolbars for heights?
23806 this.toolbars[i].onDestroy();
23809 this.wrap.dom.innerHTML = '';
23810 this.wrap.remove();
23815 onFirstFocus : function(){
23816 //Roo.log("onFirstFocus");
23817 this.editorcore.onFirstFocus();
23818 for (var i =0; i < this.toolbars.length;i++) {
23819 this.toolbars[i].onFirstFocus();
23825 syncValue : function()
23827 this.editorcore.syncValue();
23830 pushValue : function()
23832 this.editorcore.pushValue();
23836 // hide stuff that is not compatible
23850 * @event specialkey
23854 * @cfg {String} fieldClass @hide
23857 * @cfg {String} focusClass @hide
23860 * @cfg {String} autoCreate @hide
23863 * @cfg {String} inputType @hide
23866 * @cfg {String} invalidClass @hide
23869 * @cfg {String} invalidText @hide
23872 * @cfg {String} msgFx @hide
23875 * @cfg {String} validateOnBlur @hide
23884 Roo.namespace('Roo.bootstrap.htmleditor');
23886 * @class Roo.bootstrap.HtmlEditorToolbar1
23891 new Roo.bootstrap.HtmlEditor({
23894 new Roo.bootstrap.HtmlEditorToolbar1({
23895 disable : { fonts: 1 , format: 1, ..., ... , ...],
23901 * @cfg {Object} disable List of elements to disable..
23902 * @cfg {Array} btns List of additional buttons.
23906 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23909 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23912 Roo.apply(this, config);
23914 // default disabled, based on 'good practice'..
23915 this.disable = this.disable || {};
23916 Roo.applyIf(this.disable, {
23919 specialElements : true
23921 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23923 this.editor = config.editor;
23924 this.editorcore = config.editor.editorcore;
23926 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23928 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23929 // dont call parent... till later.
23931 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23936 editorcore : false,
23941 "h1","h2","h3","h4","h5","h6",
23943 "abbr", "acronym", "address", "cite", "samp", "var",
23947 onRender : function(ct, position)
23949 // Roo.log("Call onRender: " + this.xtype);
23951 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23953 this.el.dom.style.marginBottom = '0';
23955 var editorcore = this.editorcore;
23956 var editor= this.editor;
23959 var btn = function(id,cmd , toggle, handler, html){
23961 var event = toggle ? 'toggle' : 'click';
23966 xns: Roo.bootstrap,
23969 enableToggle:toggle !== false,
23971 pressed : toggle ? false : null,
23974 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23975 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23981 // var cb_box = function...
23986 xns: Roo.bootstrap,
23987 glyphicon : 'font',
23991 xns: Roo.bootstrap,
23995 Roo.each(this.formats, function(f) {
23996 style.menu.items.push({
23998 xns: Roo.bootstrap,
23999 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24004 editorcore.insertTag(this.tagname);
24011 children.push(style);
24013 btn('bold',false,true);
24014 btn('italic',false,true);
24015 btn('align-left', 'justifyleft',true);
24016 btn('align-center', 'justifycenter',true);
24017 btn('align-right' , 'justifyright',true);
24018 btn('link', false, false, function(btn) {
24019 //Roo.log("create link?");
24020 var url = prompt(this.createLinkText, this.defaultLinkValue);
24021 if(url && url != 'http:/'+'/'){
24022 this.editorcore.relayCmd('createlink', url);
24025 btn('list','insertunorderedlist',true);
24026 btn('pencil', false,true, function(btn){
24028 this.toggleSourceEdit(btn.pressed);
24031 if (this.editor.btns.length > 0) {
24032 for (var i = 0; i<this.editor.btns.length; i++) {
24033 children.push(this.editor.btns[i]);
24041 xns: Roo.bootstrap,
24046 xns: Roo.bootstrap,
24051 cog.menu.items.push({
24053 xns: Roo.bootstrap,
24054 html : Clean styles,
24059 editorcore.insertTag(this.tagname);
24068 this.xtype = 'NavSimplebar';
24070 for(var i=0;i< children.length;i++) {
24072 this.buttons.add(this.addxtypeChild(children[i]));
24076 editor.on('editorevent', this.updateToolbar, this);
24078 onBtnClick : function(id)
24080 this.editorcore.relayCmd(id);
24081 this.editorcore.focus();
24085 * Protected method that will not generally be called directly. It triggers
24086 * a toolbar update by reading the markup state of the current selection in the editor.
24088 updateToolbar: function(){
24090 if(!this.editorcore.activated){
24091 this.editor.onFirstFocus(); // is this neeed?
24095 var btns = this.buttons;
24096 var doc = this.editorcore.doc;
24097 btns.get('bold').setActive(doc.queryCommandState('bold'));
24098 btns.get('italic').setActive(doc.queryCommandState('italic'));
24099 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24101 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24102 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24103 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24105 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24106 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24109 var ans = this.editorcore.getAllAncestors();
24110 if (this.formatCombo) {
24113 var store = this.formatCombo.store;
24114 this.formatCombo.setValue("");
24115 for (var i =0; i < ans.length;i++) {
24116 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24118 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24126 // hides menus... - so this cant be on a menu...
24127 Roo.bootstrap.MenuMgr.hideAll();
24129 Roo.bootstrap.MenuMgr.hideAll();
24130 //this.editorsyncValue();
24132 onFirstFocus: function() {
24133 this.buttons.each(function(item){
24137 toggleSourceEdit : function(sourceEditMode){
24140 if(sourceEditMode){
24141 Roo.log("disabling buttons");
24142 this.buttons.each( function(item){
24143 if(item.cmd != 'pencil'){
24149 Roo.log("enabling buttons");
24150 if(this.editorcore.initialized){
24151 this.buttons.each( function(item){
24157 Roo.log("calling toggole on editor");
24158 // tell the editor that it's been pressed..
24159 this.editor.toggleSourceEdit(sourceEditMode);
24169 * @class Roo.bootstrap.Table.AbstractSelectionModel
24170 * @extends Roo.util.Observable
24171 * Abstract base class for grid SelectionModels. It provides the interface that should be
24172 * implemented by descendant classes. This class should not be directly instantiated.
24175 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24176 this.locked = false;
24177 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24181 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24182 /** @ignore Called by the grid automatically. Do not call directly. */
24183 init : function(grid){
24189 * Locks the selections.
24192 this.locked = true;
24196 * Unlocks the selections.
24198 unlock : function(){
24199 this.locked = false;
24203 * Returns true if the selections are locked.
24204 * @return {Boolean}
24206 isLocked : function(){
24207 return this.locked;
24211 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24212 * @class Roo.bootstrap.Table.RowSelectionModel
24213 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24214 * It supports multiple selections and keyboard selection/navigation.
24216 * @param {Object} config
24219 Roo.bootstrap.Table.RowSelectionModel = function(config){
24220 Roo.apply(this, config);
24221 this.selections = new Roo.util.MixedCollection(false, function(o){
24226 this.lastActive = false;
24230 * @event selectionchange
24231 * Fires when the selection changes
24232 * @param {SelectionModel} this
24234 "selectionchange" : true,
24236 * @event afterselectionchange
24237 * Fires after the selection changes (eg. by key press or clicking)
24238 * @param {SelectionModel} this
24240 "afterselectionchange" : true,
24242 * @event beforerowselect
24243 * Fires when a row is selected being selected, return false to cancel.
24244 * @param {SelectionModel} this
24245 * @param {Number} rowIndex The selected index
24246 * @param {Boolean} keepExisting False if other selections will be cleared
24248 "beforerowselect" : true,
24251 * Fires when a row is selected.
24252 * @param {SelectionModel} this
24253 * @param {Number} rowIndex The selected index
24254 * @param {Roo.data.Record} r The record
24256 "rowselect" : true,
24258 * @event rowdeselect
24259 * Fires when a row is deselected.
24260 * @param {SelectionModel} this
24261 * @param {Number} rowIndex The selected index
24263 "rowdeselect" : true
24265 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24266 this.locked = false;
24269 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24271 * @cfg {Boolean} singleSelect
24272 * True to allow selection of only one row at a time (defaults to false)
24274 singleSelect : false,
24277 initEvents : function()
24280 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24281 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24282 //}else{ // allow click to work like normal
24283 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24285 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24286 this.grid.on("rowclick", this.handleMouseDown, this);
24288 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24289 "up" : function(e){
24291 this.selectPrevious(e.shiftKey);
24292 }else if(this.last !== false && this.lastActive !== false){
24293 var last = this.last;
24294 this.selectRange(this.last, this.lastActive-1);
24295 this.grid.getView().focusRow(this.lastActive);
24296 if(last !== false){
24300 this.selectFirstRow();
24302 this.fireEvent("afterselectionchange", this);
24304 "down" : function(e){
24306 this.selectNext(e.shiftKey);
24307 }else if(this.last !== false && this.lastActive !== false){
24308 var last = this.last;
24309 this.selectRange(this.last, this.lastActive+1);
24310 this.grid.getView().focusRow(this.lastActive);
24311 if(last !== false){
24315 this.selectFirstRow();
24317 this.fireEvent("afterselectionchange", this);
24321 this.grid.store.on('load', function(){
24322 this.selections.clear();
24325 var view = this.grid.view;
24326 view.on("refresh", this.onRefresh, this);
24327 view.on("rowupdated", this.onRowUpdated, this);
24328 view.on("rowremoved", this.onRemove, this);
24333 onRefresh : function()
24335 var ds = this.grid.store, i, v = this.grid.view;
24336 var s = this.selections;
24337 s.each(function(r){
24338 if((i = ds.indexOfId(r.id)) != -1){
24347 onRemove : function(v, index, r){
24348 this.selections.remove(r);
24352 onRowUpdated : function(v, index, r){
24353 if(this.isSelected(r)){
24354 v.onRowSelect(index);
24360 * @param {Array} records The records to select
24361 * @param {Boolean} keepExisting (optional) True to keep existing selections
24363 selectRecords : function(records, keepExisting)
24366 this.clearSelections();
24368 var ds = this.grid.store;
24369 for(var i = 0, len = records.length; i < len; i++){
24370 this.selectRow(ds.indexOf(records[i]), true);
24375 * Gets the number of selected rows.
24378 getCount : function(){
24379 return this.selections.length;
24383 * Selects the first row in the grid.
24385 selectFirstRow : function(){
24390 * Select the last row.
24391 * @param {Boolean} keepExisting (optional) True to keep existing selections
24393 selectLastRow : function(keepExisting){
24394 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24395 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24399 * Selects the row immediately following the last selected row.
24400 * @param {Boolean} keepExisting (optional) True to keep existing selections
24402 selectNext : function(keepExisting)
24404 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24405 this.selectRow(this.last+1, keepExisting);
24406 this.grid.getView().focusRow(this.last);
24411 * Selects the row that precedes the last selected row.
24412 * @param {Boolean} keepExisting (optional) True to keep existing selections
24414 selectPrevious : function(keepExisting){
24416 this.selectRow(this.last-1, keepExisting);
24417 this.grid.getView().focusRow(this.last);
24422 * Returns the selected records
24423 * @return {Array} Array of selected records
24425 getSelections : function(){
24426 return [].concat(this.selections.items);
24430 * Returns the first selected record.
24433 getSelected : function(){
24434 return this.selections.itemAt(0);
24439 * Clears all selections.
24441 clearSelections : function(fast)
24447 var ds = this.grid.store;
24448 var s = this.selections;
24449 s.each(function(r){
24450 this.deselectRow(ds.indexOfId(r.id));
24454 this.selections.clear();
24461 * Selects all rows.
24463 selectAll : function(){
24467 this.selections.clear();
24468 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24469 this.selectRow(i, true);
24474 * Returns True if there is a selection.
24475 * @return {Boolean}
24477 hasSelection : function(){
24478 return this.selections.length > 0;
24482 * Returns True if the specified row is selected.
24483 * @param {Number/Record} record The record or index of the record to check
24484 * @return {Boolean}
24486 isSelected : function(index){
24487 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24488 return (r && this.selections.key(r.id) ? true : false);
24492 * Returns True if the specified record id is selected.
24493 * @param {String} id The id of record to check
24494 * @return {Boolean}
24496 isIdSelected : function(id){
24497 return (this.selections.key(id) ? true : false);
24502 handleMouseDBClick : function(e, t){
24506 handleMouseDown : function(e, t)
24508 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24509 if(this.isLocked() || rowIndex < 0 ){
24512 if(e.shiftKey && this.last !== false){
24513 var last = this.last;
24514 this.selectRange(last, rowIndex, e.ctrlKey);
24515 this.last = last; // reset the last
24519 var isSelected = this.isSelected(rowIndex);
24520 //Roo.log("select row:" + rowIndex);
24522 this.deselectRow(rowIndex);
24524 this.selectRow(rowIndex, true);
24528 if(e.button !== 0 && isSelected){
24529 alert('rowIndex 2: ' + rowIndex);
24530 view.focusRow(rowIndex);
24531 }else if(e.ctrlKey && isSelected){
24532 this.deselectRow(rowIndex);
24533 }else if(!isSelected){
24534 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24535 view.focusRow(rowIndex);
24539 this.fireEvent("afterselectionchange", this);
24542 handleDragableRowClick : function(grid, rowIndex, e)
24544 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24545 this.selectRow(rowIndex, false);
24546 grid.view.focusRow(rowIndex);
24547 this.fireEvent("afterselectionchange", this);
24552 * Selects multiple rows.
24553 * @param {Array} rows Array of the indexes of the row to select
24554 * @param {Boolean} keepExisting (optional) True to keep existing selections
24556 selectRows : function(rows, keepExisting){
24558 this.clearSelections();
24560 for(var i = 0, len = rows.length; i < len; i++){
24561 this.selectRow(rows[i], true);
24566 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24567 * @param {Number} startRow The index of the first row in the range
24568 * @param {Number} endRow The index of the last row in the range
24569 * @param {Boolean} keepExisting (optional) True to retain existing selections
24571 selectRange : function(startRow, endRow, keepExisting){
24576 this.clearSelections();
24578 if(startRow <= endRow){
24579 for(var i = startRow; i <= endRow; i++){
24580 this.selectRow(i, true);
24583 for(var i = startRow; i >= endRow; i--){
24584 this.selectRow(i, true);
24590 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24591 * @param {Number} startRow The index of the first row in the range
24592 * @param {Number} endRow The index of the last row in the range
24594 deselectRange : function(startRow, endRow, preventViewNotify){
24598 for(var i = startRow; i <= endRow; i++){
24599 this.deselectRow(i, preventViewNotify);
24605 * @param {Number} row The index of the row to select
24606 * @param {Boolean} keepExisting (optional) True to keep existing selections
24608 selectRow : function(index, keepExisting, preventViewNotify)
24610 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24613 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24614 if(!keepExisting || this.singleSelect){
24615 this.clearSelections();
24618 var r = this.grid.store.getAt(index);
24619 //console.log('selectRow - record id :' + r.id);
24621 this.selections.add(r);
24622 this.last = this.lastActive = index;
24623 if(!preventViewNotify){
24624 var proxy = new Roo.Element(
24625 this.grid.getRowDom(index)
24627 proxy.addClass('bg-info info');
24629 this.fireEvent("rowselect", this, index, r);
24630 this.fireEvent("selectionchange", this);
24636 * @param {Number} row The index of the row to deselect
24638 deselectRow : function(index, preventViewNotify)
24643 if(this.last == index){
24646 if(this.lastActive == index){
24647 this.lastActive = false;
24650 var r = this.grid.store.getAt(index);
24655 this.selections.remove(r);
24656 //.console.log('deselectRow - record id :' + r.id);
24657 if(!preventViewNotify){
24659 var proxy = new Roo.Element(
24660 this.grid.getRowDom(index)
24662 proxy.removeClass('bg-info info');
24664 this.fireEvent("rowdeselect", this, index);
24665 this.fireEvent("selectionchange", this);
24669 restoreLast : function(){
24671 this.last = this._last;
24676 acceptsNav : function(row, col, cm){
24677 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24681 onEditorKey : function(field, e){
24682 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24687 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24689 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24691 }else if(k == e.ENTER && !e.ctrlKey){
24695 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24697 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24699 }else if(k == e.ESC){
24703 g.startEditing(newCell[0], newCell[1]);
24709 * Ext JS Library 1.1.1
24710 * Copyright(c) 2006-2007, Ext JS, LLC.
24712 * Originally Released Under LGPL - original licence link has changed is not relivant.
24715 * <script type="text/javascript">
24719 * @class Roo.bootstrap.PagingToolbar
24720 * @extends Roo.bootstrap.NavSimplebar
24721 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24723 * Create a new PagingToolbar
24724 * @param {Object} config The config object
24725 * @param {Roo.data.Store} store
24727 Roo.bootstrap.PagingToolbar = function(config)
24729 // old args format still supported... - xtype is prefered..
24730 // created from xtype...
24732 this.ds = config.dataSource;
24734 if (config.store && !this.ds) {
24735 this.store= Roo.factory(config.store, Roo.data);
24736 this.ds = this.store;
24737 this.ds.xmodule = this.xmodule || false;
24740 this.toolbarItems = [];
24741 if (config.items) {
24742 this.toolbarItems = config.items;
24745 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24750 this.bind(this.ds);
24753 if (Roo.bootstrap.version == 4) {
24754 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24756 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24761 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24763 * @cfg {Roo.data.Store} dataSource
24764 * The underlying data store providing the paged data
24767 * @cfg {String/HTMLElement/Element} container
24768 * container The id or element that will contain the toolbar
24771 * @cfg {Boolean} displayInfo
24772 * True to display the displayMsg (defaults to false)
24775 * @cfg {Number} pageSize
24776 * The number of records to display per page (defaults to 20)
24780 * @cfg {String} displayMsg
24781 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24783 displayMsg : 'Displaying {0} - {1} of {2}',
24785 * @cfg {String} emptyMsg
24786 * The message to display when no records are found (defaults to "No data to display")
24788 emptyMsg : 'No data to display',
24790 * Customizable piece of the default paging text (defaults to "Page")
24793 beforePageText : "Page",
24795 * Customizable piece of the default paging text (defaults to "of %0")
24798 afterPageText : "of {0}",
24800 * Customizable piece of the default paging text (defaults to "First Page")
24803 firstText : "First Page",
24805 * Customizable piece of the default paging text (defaults to "Previous Page")
24808 prevText : "Previous Page",
24810 * Customizable piece of the default paging text (defaults to "Next Page")
24813 nextText : "Next Page",
24815 * Customizable piece of the default paging text (defaults to "Last Page")
24818 lastText : "Last Page",
24820 * Customizable piece of the default paging text (defaults to "Refresh")
24823 refreshText : "Refresh",
24827 onRender : function(ct, position)
24829 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24830 this.navgroup.parentId = this.id;
24831 this.navgroup.onRender(this.el, null);
24832 // add the buttons to the navgroup
24834 if(this.displayInfo){
24835 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24836 this.displayEl = this.el.select('.x-paging-info', true).first();
24837 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24838 // this.displayEl = navel.el.select('span',true).first();
24844 Roo.each(_this.buttons, function(e){ // this might need to use render????
24845 Roo.factory(e).render(_this.el);
24849 Roo.each(_this.toolbarItems, function(e) {
24850 _this.navgroup.addItem(e);
24854 this.first = this.navgroup.addItem({
24855 tooltip: this.firstText,
24856 cls: "prev btn-outline-secondary",
24857 html : ' <i class="fa fa-step-backward"></i>',
24859 preventDefault: true,
24860 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24863 this.prev = this.navgroup.addItem({
24864 tooltip: this.prevText,
24865 cls: "prev btn-outline-secondary",
24866 html : ' <i class="fa fa-backward"></i>',
24868 preventDefault: true,
24869 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24871 //this.addSeparator();
24874 var field = this.navgroup.addItem( {
24876 cls : 'x-paging-position btn-outline-secondary',
24878 html : this.beforePageText +
24879 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24880 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24883 this.field = field.el.select('input', true).first();
24884 this.field.on("keydown", this.onPagingKeydown, this);
24885 this.field.on("focus", function(){this.dom.select();});
24888 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24889 //this.field.setHeight(18);
24890 //this.addSeparator();
24891 this.next = this.navgroup.addItem({
24892 tooltip: this.nextText,
24893 cls: "next btn-outline-secondary",
24894 html : ' <i class="fa fa-forward"></i>',
24896 preventDefault: true,
24897 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24899 this.last = this.navgroup.addItem({
24900 tooltip: this.lastText,
24901 html : ' <i class="fa fa-step-forward"></i>',
24902 cls: "next btn-outline-secondary",
24904 preventDefault: true,
24905 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24907 //this.addSeparator();
24908 this.loading = this.navgroup.addItem({
24909 tooltip: this.refreshText,
24910 cls: "btn-outline-secondary",
24911 html : ' <i class="fa fa-refresh"></i>',
24912 preventDefault: true,
24913 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24919 updateInfo : function(){
24920 if(this.displayEl){
24921 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24922 var msg = count == 0 ?
24926 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24928 this.displayEl.update(msg);
24933 onLoad : function(ds, r, o)
24935 this.cursor = o.params.start ? o.params.start : 0;
24937 var d = this.getPageData(),
24942 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24943 this.field.dom.value = ap;
24944 this.first.setDisabled(ap == 1);
24945 this.prev.setDisabled(ap == 1);
24946 this.next.setDisabled(ap == ps);
24947 this.last.setDisabled(ap == ps);
24948 this.loading.enable();
24953 getPageData : function(){
24954 var total = this.ds.getTotalCount();
24957 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24958 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24963 onLoadError : function(){
24964 this.loading.enable();
24968 onPagingKeydown : function(e){
24969 var k = e.getKey();
24970 var d = this.getPageData();
24972 var v = this.field.dom.value, pageNum;
24973 if(!v || isNaN(pageNum = parseInt(v, 10))){
24974 this.field.dom.value = d.activePage;
24977 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24978 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24981 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))
24983 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24984 this.field.dom.value = pageNum;
24985 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24988 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24990 var v = this.field.dom.value, pageNum;
24991 var increment = (e.shiftKey) ? 10 : 1;
24992 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24995 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24996 this.field.dom.value = d.activePage;
24999 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25001 this.field.dom.value = parseInt(v, 10) + increment;
25002 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25003 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25010 beforeLoad : function(){
25012 this.loading.disable();
25017 onClick : function(which){
25026 ds.load({params:{start: 0, limit: this.pageSize}});
25029 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25032 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25035 var total = ds.getTotalCount();
25036 var extra = total % this.pageSize;
25037 var lastStart = extra ? (total - extra) : total-this.pageSize;
25038 ds.load({params:{start: lastStart, limit: this.pageSize}});
25041 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25047 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25048 * @param {Roo.data.Store} store The data store to unbind
25050 unbind : function(ds){
25051 ds.un("beforeload", this.beforeLoad, this);
25052 ds.un("load", this.onLoad, this);
25053 ds.un("loadexception", this.onLoadError, this);
25054 ds.un("remove", this.updateInfo, this);
25055 ds.un("add", this.updateInfo, this);
25056 this.ds = undefined;
25060 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25061 * @param {Roo.data.Store} store The data store to bind
25063 bind : function(ds){
25064 ds.on("beforeload", this.beforeLoad, this);
25065 ds.on("load", this.onLoad, this);
25066 ds.on("loadexception", this.onLoadError, this);
25067 ds.on("remove", this.updateInfo, this);
25068 ds.on("add", this.updateInfo, this);
25079 * @class Roo.bootstrap.MessageBar
25080 * @extends Roo.bootstrap.Component
25081 * Bootstrap MessageBar class
25082 * @cfg {String} html contents of the MessageBar
25083 * @cfg {String} weight (info | success | warning | danger) default info
25084 * @cfg {String} beforeClass insert the bar before the given class
25085 * @cfg {Boolean} closable (true | false) default false
25086 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25089 * Create a new Element
25090 * @param {Object} config The config object
25093 Roo.bootstrap.MessageBar = function(config){
25094 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25097 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25103 beforeClass: 'bootstrap-sticky-wrap',
25105 getAutoCreate : function(){
25109 cls: 'alert alert-dismissable alert-' + this.weight,
25114 html: this.html || ''
25120 cfg.cls += ' alert-messages-fixed';
25134 onRender : function(ct, position)
25136 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25139 var cfg = Roo.apply({}, this.getAutoCreate());
25143 cfg.cls += ' ' + this.cls;
25146 cfg.style = this.style;
25148 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25150 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25153 this.el.select('>button.close').on('click', this.hide, this);
25159 if (!this.rendered) {
25165 this.fireEvent('show', this);
25171 if (!this.rendered) {
25177 this.fireEvent('hide', this);
25180 update : function()
25182 // var e = this.el.dom.firstChild;
25184 // if(this.closable){
25185 // e = e.nextSibling;
25188 // e.data = this.html || '';
25190 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25206 * @class Roo.bootstrap.Graph
25207 * @extends Roo.bootstrap.Component
25208 * Bootstrap Graph class
25212 @cfg {String} graphtype bar | vbar | pie
25213 @cfg {number} g_x coodinator | centre x (pie)
25214 @cfg {number} g_y coodinator | centre y (pie)
25215 @cfg {number} g_r radius (pie)
25216 @cfg {number} g_height height of the chart (respected by all elements in the set)
25217 @cfg {number} g_width width of the chart (respected by all elements in the set)
25218 @cfg {Object} title The title of the chart
25221 -opts (object) options for the chart
25223 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25224 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25226 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.
25227 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25229 o stretch (boolean)
25231 -opts (object) options for the pie
25234 o startAngle (number)
25235 o endAngle (number)
25239 * Create a new Input
25240 * @param {Object} config The config object
25243 Roo.bootstrap.Graph = function(config){
25244 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25250 * The img click event for the img.
25251 * @param {Roo.EventObject} e
25257 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25268 //g_colors: this.colors,
25275 getAutoCreate : function(){
25286 onRender : function(ct,position){
25289 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25291 if (typeof(Raphael) == 'undefined') {
25292 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25296 this.raphael = Raphael(this.el.dom);
25298 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25299 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25300 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25301 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25303 r.text(160, 10, "Single Series Chart").attr(txtattr);
25304 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25305 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25306 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25308 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25309 r.barchart(330, 10, 300, 220, data1);
25310 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25311 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25314 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25315 // r.barchart(30, 30, 560, 250, xdata, {
25316 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25317 // axis : "0 0 1 1",
25318 // axisxlabels : xdata
25319 // //yvalues : cols,
25322 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25324 // this.load(null,xdata,{
25325 // axis : "0 0 1 1",
25326 // axisxlabels : xdata
25331 load : function(graphtype,xdata,opts)
25333 this.raphael.clear();
25335 graphtype = this.graphtype;
25340 var r = this.raphael,
25341 fin = function () {
25342 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25344 fout = function () {
25345 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25347 pfin = function() {
25348 this.sector.stop();
25349 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25352 this.label[0].stop();
25353 this.label[0].attr({ r: 7.5 });
25354 this.label[1].attr({ "font-weight": 800 });
25357 pfout = function() {
25358 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25361 this.label[0].animate({ r: 5 }, 500, "bounce");
25362 this.label[1].attr({ "font-weight": 400 });
25368 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25371 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25374 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25375 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25377 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25384 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25389 setTitle: function(o)
25394 initEvents: function() {
25397 this.el.on('click', this.onClick, this);
25401 onClick : function(e)
25403 Roo.log('img onclick');
25404 this.fireEvent('click', this, e);
25416 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25419 * @class Roo.bootstrap.dash.NumberBox
25420 * @extends Roo.bootstrap.Component
25421 * Bootstrap NumberBox class
25422 * @cfg {String} headline Box headline
25423 * @cfg {String} content Box content
25424 * @cfg {String} icon Box icon
25425 * @cfg {String} footer Footer text
25426 * @cfg {String} fhref Footer href
25429 * Create a new NumberBox
25430 * @param {Object} config The config object
25434 Roo.bootstrap.dash.NumberBox = function(config){
25435 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25439 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25448 getAutoCreate : function(){
25452 cls : 'small-box ',
25460 cls : 'roo-headline',
25461 html : this.headline
25465 cls : 'roo-content',
25466 html : this.content
25480 cls : 'ion ' + this.icon
25489 cls : 'small-box-footer',
25490 href : this.fhref || '#',
25494 cfg.cn.push(footer);
25501 onRender : function(ct,position){
25502 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25509 setHeadline: function (value)
25511 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25514 setFooter: function (value, href)
25516 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25519 this.el.select('a.small-box-footer',true).first().attr('href', href);
25524 setContent: function (value)
25526 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25529 initEvents: function()
25543 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25546 * @class Roo.bootstrap.dash.TabBox
25547 * @extends Roo.bootstrap.Component
25548 * Bootstrap TabBox class
25549 * @cfg {String} title Title of the TabBox
25550 * @cfg {String} icon Icon of the TabBox
25551 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25552 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25555 * Create a new TabBox
25556 * @param {Object} config The config object
25560 Roo.bootstrap.dash.TabBox = function(config){
25561 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25566 * When a pane is added
25567 * @param {Roo.bootstrap.dash.TabPane} pane
25571 * @event activatepane
25572 * When a pane is activated
25573 * @param {Roo.bootstrap.dash.TabPane} pane
25575 "activatepane" : true
25583 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25588 tabScrollable : false,
25590 getChildContainer : function()
25592 return this.el.select('.tab-content', true).first();
25595 getAutoCreate : function(){
25599 cls: 'pull-left header',
25607 cls: 'fa ' + this.icon
25613 cls: 'nav nav-tabs pull-right',
25619 if(this.tabScrollable){
25626 cls: 'nav nav-tabs pull-right',
25637 cls: 'nav-tabs-custom',
25642 cls: 'tab-content no-padding',
25650 initEvents : function()
25652 //Roo.log('add add pane handler');
25653 this.on('addpane', this.onAddPane, this);
25656 * Updates the box title
25657 * @param {String} html to set the title to.
25659 setTitle : function(value)
25661 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25663 onAddPane : function(pane)
25665 this.panes.push(pane);
25666 //Roo.log('addpane');
25668 // tabs are rendere left to right..
25669 if(!this.showtabs){
25673 var ctr = this.el.select('.nav-tabs', true).first();
25676 var existing = ctr.select('.nav-tab',true);
25677 var qty = existing.getCount();;
25680 var tab = ctr.createChild({
25682 cls : 'nav-tab' + (qty ? '' : ' active'),
25690 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25693 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25695 pane.el.addClass('active');
25700 onTabClick : function(ev,un,ob,pane)
25702 //Roo.log('tab - prev default');
25703 ev.preventDefault();
25706 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25707 pane.tab.addClass('active');
25708 //Roo.log(pane.title);
25709 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25710 // technically we should have a deactivate event.. but maybe add later.
25711 // and it should not de-activate the selected tab...
25712 this.fireEvent('activatepane', pane);
25713 pane.el.addClass('active');
25714 pane.fireEvent('activate');
25719 getActivePane : function()
25722 Roo.each(this.panes, function(p) {
25723 if(p.el.hasClass('active')){
25744 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25746 * @class Roo.bootstrap.TabPane
25747 * @extends Roo.bootstrap.Component
25748 * Bootstrap TabPane class
25749 * @cfg {Boolean} active (false | true) Default false
25750 * @cfg {String} title title of panel
25754 * Create a new TabPane
25755 * @param {Object} config The config object
25758 Roo.bootstrap.dash.TabPane = function(config){
25759 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25765 * When a pane is activated
25766 * @param {Roo.bootstrap.dash.TabPane} pane
25773 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25778 // the tabBox that this is attached to.
25781 getAutoCreate : function()
25789 cfg.cls += ' active';
25794 initEvents : function()
25796 //Roo.log('trigger add pane handler');
25797 this.parent().fireEvent('addpane', this)
25801 * Updates the tab title
25802 * @param {String} html to set the title to.
25804 setTitle: function(str)
25810 this.tab.select('a', true).first().dom.innerHTML = str;
25827 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25830 * @class Roo.bootstrap.menu.Menu
25831 * @extends Roo.bootstrap.Component
25832 * Bootstrap Menu class - container for Menu
25833 * @cfg {String} html Text of the menu
25834 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25835 * @cfg {String} icon Font awesome icon
25836 * @cfg {String} pos Menu align to (top | bottom) default bottom
25840 * Create a new Menu
25841 * @param {Object} config The config object
25845 Roo.bootstrap.menu.Menu = function(config){
25846 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25850 * @event beforeshow
25851 * Fires before this menu is displayed
25852 * @param {Roo.bootstrap.menu.Menu} this
25856 * @event beforehide
25857 * Fires before this menu is hidden
25858 * @param {Roo.bootstrap.menu.Menu} this
25863 * Fires after this menu is displayed
25864 * @param {Roo.bootstrap.menu.Menu} this
25869 * Fires after this menu is hidden
25870 * @param {Roo.bootstrap.menu.Menu} this
25875 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25876 * @param {Roo.bootstrap.menu.Menu} this
25877 * @param {Roo.EventObject} e
25884 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25888 weight : 'default',
25893 getChildContainer : function() {
25894 if(this.isSubMenu){
25898 return this.el.select('ul.dropdown-menu', true).first();
25901 getAutoCreate : function()
25906 cls : 'roo-menu-text',
25914 cls : 'fa ' + this.icon
25925 cls : 'dropdown-button btn btn-' + this.weight,
25930 cls : 'dropdown-toggle btn btn-' + this.weight,
25940 cls : 'dropdown-menu'
25946 if(this.pos == 'top'){
25947 cfg.cls += ' dropup';
25950 if(this.isSubMenu){
25953 cls : 'dropdown-menu'
25960 onRender : function(ct, position)
25962 this.isSubMenu = ct.hasClass('dropdown-submenu');
25964 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25967 initEvents : function()
25969 if(this.isSubMenu){
25973 this.hidden = true;
25975 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25976 this.triggerEl.on('click', this.onTriggerPress, this);
25978 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25979 this.buttonEl.on('click', this.onClick, this);
25985 if(this.isSubMenu){
25989 return this.el.select('ul.dropdown-menu', true).first();
25992 onClick : function(e)
25994 this.fireEvent("click", this, e);
25997 onTriggerPress : function(e)
25999 if (this.isVisible()) {
26006 isVisible : function(){
26007 return !this.hidden;
26012 this.fireEvent("beforeshow", this);
26014 this.hidden = false;
26015 this.el.addClass('open');
26017 Roo.get(document).on("mouseup", this.onMouseUp, this);
26019 this.fireEvent("show", this);
26026 this.fireEvent("beforehide", this);
26028 this.hidden = true;
26029 this.el.removeClass('open');
26031 Roo.get(document).un("mouseup", this.onMouseUp);
26033 this.fireEvent("hide", this);
26036 onMouseUp : function()
26050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26053 * @class Roo.bootstrap.menu.Item
26054 * @extends Roo.bootstrap.Component
26055 * Bootstrap MenuItem class
26056 * @cfg {Boolean} submenu (true | false) default false
26057 * @cfg {String} html text of the item
26058 * @cfg {String} href the link
26059 * @cfg {Boolean} disable (true | false) default false
26060 * @cfg {Boolean} preventDefault (true | false) default true
26061 * @cfg {String} icon Font awesome icon
26062 * @cfg {String} pos Submenu align to (left | right) default right
26066 * Create a new Item
26067 * @param {Object} config The config object
26071 Roo.bootstrap.menu.Item = function(config){
26072 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26076 * Fires when the mouse is hovering over this menu
26077 * @param {Roo.bootstrap.menu.Item} this
26078 * @param {Roo.EventObject} e
26083 * Fires when the mouse exits this menu
26084 * @param {Roo.bootstrap.menu.Item} this
26085 * @param {Roo.EventObject} e
26091 * The raw click event for the entire grid.
26092 * @param {Roo.EventObject} e
26098 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26103 preventDefault: true,
26108 getAutoCreate : function()
26113 cls : 'roo-menu-item-text',
26121 cls : 'fa ' + this.icon
26130 href : this.href || '#',
26137 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26141 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26143 if(this.pos == 'left'){
26144 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26151 initEvents : function()
26153 this.el.on('mouseover', this.onMouseOver, this);
26154 this.el.on('mouseout', this.onMouseOut, this);
26156 this.el.select('a', true).first().on('click', this.onClick, this);
26160 onClick : function(e)
26162 if(this.preventDefault){
26163 e.preventDefault();
26166 this.fireEvent("click", this, e);
26169 onMouseOver : function(e)
26171 if(this.submenu && this.pos == 'left'){
26172 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26175 this.fireEvent("mouseover", this, e);
26178 onMouseOut : function(e)
26180 this.fireEvent("mouseout", this, e);
26192 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26195 * @class Roo.bootstrap.menu.Separator
26196 * @extends Roo.bootstrap.Component
26197 * Bootstrap Separator class
26200 * Create a new Separator
26201 * @param {Object} config The config object
26205 Roo.bootstrap.menu.Separator = function(config){
26206 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26209 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26211 getAutoCreate : function(){
26232 * @class Roo.bootstrap.Tooltip
26233 * Bootstrap Tooltip class
26234 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26235 * to determine which dom element triggers the tooltip.
26237 * It needs to add support for additional attributes like tooltip-position
26240 * Create a new Toolti
26241 * @param {Object} config The config object
26244 Roo.bootstrap.Tooltip = function(config){
26245 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26247 this.alignment = Roo.bootstrap.Tooltip.alignment;
26249 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26250 this.alignment = config.alignment;
26255 Roo.apply(Roo.bootstrap.Tooltip, {
26257 * @function init initialize tooltip monitoring.
26261 currentTip : false,
26262 currentRegion : false,
26268 Roo.get(document).on('mouseover', this.enter ,this);
26269 Roo.get(document).on('mouseout', this.leave, this);
26272 this.currentTip = new Roo.bootstrap.Tooltip();
26275 enter : function(ev)
26277 var dom = ev.getTarget();
26279 //Roo.log(['enter',dom]);
26280 var el = Roo.fly(dom);
26281 if (this.currentEl) {
26283 //Roo.log(this.currentEl);
26284 //Roo.log(this.currentEl.contains(dom));
26285 if (this.currentEl == el) {
26288 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26294 if (this.currentTip.el) {
26295 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26299 if(!el || el.dom == document){
26305 // you can not look for children, as if el is the body.. then everythign is the child..
26306 if (!el.attr('tooltip')) { //
26307 if (!el.select("[tooltip]").elements.length) {
26310 // is the mouse over this child...?
26311 bindEl = el.select("[tooltip]").first();
26312 var xy = ev.getXY();
26313 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26314 //Roo.log("not in region.");
26317 //Roo.log("child element over..");
26320 this.currentEl = bindEl;
26321 this.currentTip.bind(bindEl);
26322 this.currentRegion = Roo.lib.Region.getRegion(dom);
26323 this.currentTip.enter();
26326 leave : function(ev)
26328 var dom = ev.getTarget();
26329 //Roo.log(['leave',dom]);
26330 if (!this.currentEl) {
26335 if (dom != this.currentEl.dom) {
26338 var xy = ev.getXY();
26339 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26342 // only activate leave if mouse cursor is outside... bounding box..
26347 if (this.currentTip) {
26348 this.currentTip.leave();
26350 //Roo.log('clear currentEl');
26351 this.currentEl = false;
26356 'left' : ['r-l', [-2,0], 'right'],
26357 'right' : ['l-r', [2,0], 'left'],
26358 'bottom' : ['t-b', [0,2], 'top'],
26359 'top' : [ 'b-t', [0,-2], 'bottom']
26365 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26370 delay : null, // can be { show : 300 , hide: 500}
26374 hoverState : null, //???
26376 placement : 'bottom',
26380 getAutoCreate : function(){
26387 cls : 'tooltip-arrow'
26390 cls : 'tooltip-inner'
26397 bind : function(el)
26403 enter : function () {
26405 if (this.timeout != null) {
26406 clearTimeout(this.timeout);
26409 this.hoverState = 'in';
26410 //Roo.log("enter - show");
26411 if (!this.delay || !this.delay.show) {
26416 this.timeout = setTimeout(function () {
26417 if (_t.hoverState == 'in') {
26420 }, this.delay.show);
26424 clearTimeout(this.timeout);
26426 this.hoverState = 'out';
26427 if (!this.delay || !this.delay.hide) {
26433 this.timeout = setTimeout(function () {
26434 //Roo.log("leave - timeout");
26436 if (_t.hoverState == 'out') {
26438 Roo.bootstrap.Tooltip.currentEl = false;
26443 show : function (msg)
26446 this.render(document.body);
26449 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26451 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26453 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26455 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26457 var placement = typeof this.placement == 'function' ?
26458 this.placement.call(this, this.el, on_el) :
26461 var autoToken = /\s?auto?\s?/i;
26462 var autoPlace = autoToken.test(placement);
26464 placement = placement.replace(autoToken, '') || 'top';
26468 //this.el.setXY([0,0]);
26470 //this.el.dom.style.display='block';
26472 //this.el.appendTo(on_el);
26474 var p = this.getPosition();
26475 var box = this.el.getBox();
26481 var align = this.alignment[placement];
26483 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26485 if(placement == 'top' || placement == 'bottom'){
26487 placement = 'right';
26490 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26491 placement = 'left';
26494 var scroll = Roo.select('body', true).first().getScroll();
26496 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26500 align = this.alignment[placement];
26503 this.el.alignTo(this.bindEl, align[0],align[1]);
26504 //var arrow = this.el.select('.arrow',true).first();
26505 //arrow.set(align[2],
26507 this.el.addClass(placement);
26509 this.el.addClass('in fade');
26511 this.hoverState = null;
26513 if (this.el.hasClass('fade')) {
26524 //this.el.setXY([0,0]);
26525 this.el.removeClass('in');
26541 * @class Roo.bootstrap.LocationPicker
26542 * @extends Roo.bootstrap.Component
26543 * Bootstrap LocationPicker class
26544 * @cfg {Number} latitude Position when init default 0
26545 * @cfg {Number} longitude Position when init default 0
26546 * @cfg {Number} zoom default 15
26547 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26548 * @cfg {Boolean} mapTypeControl default false
26549 * @cfg {Boolean} disableDoubleClickZoom default false
26550 * @cfg {Boolean} scrollwheel default true
26551 * @cfg {Boolean} streetViewControl default false
26552 * @cfg {Number} radius default 0
26553 * @cfg {String} locationName
26554 * @cfg {Boolean} draggable default true
26555 * @cfg {Boolean} enableAutocomplete default false
26556 * @cfg {Boolean} enableReverseGeocode default true
26557 * @cfg {String} markerTitle
26560 * Create a new LocationPicker
26561 * @param {Object} config The config object
26565 Roo.bootstrap.LocationPicker = function(config){
26567 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26572 * Fires when the picker initialized.
26573 * @param {Roo.bootstrap.LocationPicker} this
26574 * @param {Google Location} location
26578 * @event positionchanged
26579 * Fires when the picker position changed.
26580 * @param {Roo.bootstrap.LocationPicker} this
26581 * @param {Google Location} location
26583 positionchanged : true,
26586 * Fires when the map resize.
26587 * @param {Roo.bootstrap.LocationPicker} this
26592 * Fires when the map show.
26593 * @param {Roo.bootstrap.LocationPicker} this
26598 * Fires when the map hide.
26599 * @param {Roo.bootstrap.LocationPicker} this
26604 * Fires when click the map.
26605 * @param {Roo.bootstrap.LocationPicker} this
26606 * @param {Map event} e
26610 * @event mapRightClick
26611 * Fires when right click the map.
26612 * @param {Roo.bootstrap.LocationPicker} this
26613 * @param {Map event} e
26615 mapRightClick : true,
26617 * @event markerClick
26618 * Fires when click the marker.
26619 * @param {Roo.bootstrap.LocationPicker} this
26620 * @param {Map event} e
26622 markerClick : true,
26624 * @event markerRightClick
26625 * Fires when right click the marker.
26626 * @param {Roo.bootstrap.LocationPicker} this
26627 * @param {Map event} e
26629 markerRightClick : true,
26631 * @event OverlayViewDraw
26632 * Fires when OverlayView Draw
26633 * @param {Roo.bootstrap.LocationPicker} this
26635 OverlayViewDraw : true,
26637 * @event OverlayViewOnAdd
26638 * Fires when OverlayView Draw
26639 * @param {Roo.bootstrap.LocationPicker} this
26641 OverlayViewOnAdd : true,
26643 * @event OverlayViewOnRemove
26644 * Fires when OverlayView Draw
26645 * @param {Roo.bootstrap.LocationPicker} this
26647 OverlayViewOnRemove : true,
26649 * @event OverlayViewShow
26650 * Fires when OverlayView Draw
26651 * @param {Roo.bootstrap.LocationPicker} this
26652 * @param {Pixel} cpx
26654 OverlayViewShow : true,
26656 * @event OverlayViewHide
26657 * Fires when OverlayView Draw
26658 * @param {Roo.bootstrap.LocationPicker} this
26660 OverlayViewHide : true,
26662 * @event loadexception
26663 * Fires when load google lib failed.
26664 * @param {Roo.bootstrap.LocationPicker} this
26666 loadexception : true
26671 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26673 gMapContext: false,
26679 mapTypeControl: false,
26680 disableDoubleClickZoom: false,
26682 streetViewControl: false,
26686 enableAutocomplete: false,
26687 enableReverseGeocode: true,
26690 getAutoCreate: function()
26695 cls: 'roo-location-picker'
26701 initEvents: function(ct, position)
26703 if(!this.el.getWidth() || this.isApplied()){
26707 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26712 initial: function()
26714 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26715 this.fireEvent('loadexception', this);
26719 if(!this.mapTypeId){
26720 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26723 this.gMapContext = this.GMapContext();
26725 this.initOverlayView();
26727 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26731 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26732 _this.setPosition(_this.gMapContext.marker.position);
26735 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26736 _this.fireEvent('mapClick', this, event);
26740 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26741 _this.fireEvent('mapRightClick', this, event);
26745 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26746 _this.fireEvent('markerClick', this, event);
26750 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26751 _this.fireEvent('markerRightClick', this, event);
26755 this.setPosition(this.gMapContext.location);
26757 this.fireEvent('initial', this, this.gMapContext.location);
26760 initOverlayView: function()
26764 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26768 _this.fireEvent('OverlayViewDraw', _this);
26773 _this.fireEvent('OverlayViewOnAdd', _this);
26776 onRemove: function()
26778 _this.fireEvent('OverlayViewOnRemove', _this);
26781 show: function(cpx)
26783 _this.fireEvent('OverlayViewShow', _this, cpx);
26788 _this.fireEvent('OverlayViewHide', _this);
26794 fromLatLngToContainerPixel: function(event)
26796 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26799 isApplied: function()
26801 return this.getGmapContext() == false ? false : true;
26804 getGmapContext: function()
26806 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26809 GMapContext: function()
26811 var position = new google.maps.LatLng(this.latitude, this.longitude);
26813 var _map = new google.maps.Map(this.el.dom, {
26816 mapTypeId: this.mapTypeId,
26817 mapTypeControl: this.mapTypeControl,
26818 disableDoubleClickZoom: this.disableDoubleClickZoom,
26819 scrollwheel: this.scrollwheel,
26820 streetViewControl: this.streetViewControl,
26821 locationName: this.locationName,
26822 draggable: this.draggable,
26823 enableAutocomplete: this.enableAutocomplete,
26824 enableReverseGeocode: this.enableReverseGeocode
26827 var _marker = new google.maps.Marker({
26828 position: position,
26830 title: this.markerTitle,
26831 draggable: this.draggable
26838 location: position,
26839 radius: this.radius,
26840 locationName: this.locationName,
26841 addressComponents: {
26842 formatted_address: null,
26843 addressLine1: null,
26844 addressLine2: null,
26846 streetNumber: null,
26850 stateOrProvince: null
26853 domContainer: this.el.dom,
26854 geodecoder: new google.maps.Geocoder()
26858 drawCircle: function(center, radius, options)
26860 if (this.gMapContext.circle != null) {
26861 this.gMapContext.circle.setMap(null);
26865 options = Roo.apply({}, options, {
26866 strokeColor: "#0000FF",
26867 strokeOpacity: .35,
26869 fillColor: "#0000FF",
26873 options.map = this.gMapContext.map;
26874 options.radius = radius;
26875 options.center = center;
26876 this.gMapContext.circle = new google.maps.Circle(options);
26877 return this.gMapContext.circle;
26883 setPosition: function(location)
26885 this.gMapContext.location = location;
26886 this.gMapContext.marker.setPosition(location);
26887 this.gMapContext.map.panTo(location);
26888 this.drawCircle(location, this.gMapContext.radius, {});
26892 if (this.gMapContext.settings.enableReverseGeocode) {
26893 this.gMapContext.geodecoder.geocode({
26894 latLng: this.gMapContext.location
26895 }, function(results, status) {
26897 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26898 _this.gMapContext.locationName = results[0].formatted_address;
26899 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26901 _this.fireEvent('positionchanged', this, location);
26908 this.fireEvent('positionchanged', this, location);
26913 google.maps.event.trigger(this.gMapContext.map, "resize");
26915 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26917 this.fireEvent('resize', this);
26920 setPositionByLatLng: function(latitude, longitude)
26922 this.setPosition(new google.maps.LatLng(latitude, longitude));
26925 getCurrentPosition: function()
26928 latitude: this.gMapContext.location.lat(),
26929 longitude: this.gMapContext.location.lng()
26933 getAddressName: function()
26935 return this.gMapContext.locationName;
26938 getAddressComponents: function()
26940 return this.gMapContext.addressComponents;
26943 address_component_from_google_geocode: function(address_components)
26947 for (var i = 0; i < address_components.length; i++) {
26948 var component = address_components[i];
26949 if (component.types.indexOf("postal_code") >= 0) {
26950 result.postalCode = component.short_name;
26951 } else if (component.types.indexOf("street_number") >= 0) {
26952 result.streetNumber = component.short_name;
26953 } else if (component.types.indexOf("route") >= 0) {
26954 result.streetName = component.short_name;
26955 } else if (component.types.indexOf("neighborhood") >= 0) {
26956 result.city = component.short_name;
26957 } else if (component.types.indexOf("locality") >= 0) {
26958 result.city = component.short_name;
26959 } else if (component.types.indexOf("sublocality") >= 0) {
26960 result.district = component.short_name;
26961 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26962 result.stateOrProvince = component.short_name;
26963 } else if (component.types.indexOf("country") >= 0) {
26964 result.country = component.short_name;
26968 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26969 result.addressLine2 = "";
26973 setZoomLevel: function(zoom)
26975 this.gMapContext.map.setZoom(zoom);
26988 this.fireEvent('show', this);
26999 this.fireEvent('hide', this);
27004 Roo.apply(Roo.bootstrap.LocationPicker, {
27006 OverlayView : function(map, options)
27008 options = options || {};
27022 * @class Roo.bootstrap.Alert
27023 * @extends Roo.bootstrap.Component
27024 * Bootstrap Alert class
27025 * @cfg {String} title The title of alert
27026 * @cfg {String} html The content of alert
27027 * @cfg {String} weight ( success | info | warning | danger )
27028 * @cfg {String} faicon font-awesomeicon
27031 * Create a new alert
27032 * @param {Object} config The config object
27036 Roo.bootstrap.Alert = function(config){
27037 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27041 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27048 getAutoCreate : function()
27057 cls : 'roo-alert-icon'
27062 cls : 'roo-alert-title',
27067 cls : 'roo-alert-text',
27074 cfg.cn[0].cls += ' fa ' + this.faicon;
27078 cfg.cls += ' alert-' + this.weight;
27084 initEvents: function()
27086 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27089 setTitle : function(str)
27091 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27094 setText : function(str)
27096 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27099 setWeight : function(weight)
27102 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27105 this.weight = weight;
27107 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27110 setIcon : function(icon)
27113 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27116 this.faicon = icon;
27118 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27139 * @class Roo.bootstrap.UploadCropbox
27140 * @extends Roo.bootstrap.Component
27141 * Bootstrap UploadCropbox class
27142 * @cfg {String} emptyText show when image has been loaded
27143 * @cfg {String} rotateNotify show when image too small to rotate
27144 * @cfg {Number} errorTimeout default 3000
27145 * @cfg {Number} minWidth default 300
27146 * @cfg {Number} minHeight default 300
27147 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27148 * @cfg {Boolean} isDocument (true|false) default false
27149 * @cfg {String} url action url
27150 * @cfg {String} paramName default 'imageUpload'
27151 * @cfg {String} method default POST
27152 * @cfg {Boolean} loadMask (true|false) default true
27153 * @cfg {Boolean} loadingText default 'Loading...'
27156 * Create a new UploadCropbox
27157 * @param {Object} config The config object
27160 Roo.bootstrap.UploadCropbox = function(config){
27161 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27165 * @event beforeselectfile
27166 * Fire before select file
27167 * @param {Roo.bootstrap.UploadCropbox} this
27169 "beforeselectfile" : true,
27172 * Fire after initEvent
27173 * @param {Roo.bootstrap.UploadCropbox} this
27178 * Fire after initEvent
27179 * @param {Roo.bootstrap.UploadCropbox} this
27180 * @param {String} data
27185 * Fire when preparing the file data
27186 * @param {Roo.bootstrap.UploadCropbox} this
27187 * @param {Object} file
27192 * Fire when get exception
27193 * @param {Roo.bootstrap.UploadCropbox} this
27194 * @param {XMLHttpRequest} xhr
27196 "exception" : true,
27198 * @event beforeloadcanvas
27199 * Fire before load the canvas
27200 * @param {Roo.bootstrap.UploadCropbox} this
27201 * @param {String} src
27203 "beforeloadcanvas" : true,
27206 * Fire when trash image
27207 * @param {Roo.bootstrap.UploadCropbox} this
27212 * Fire when download the image
27213 * @param {Roo.bootstrap.UploadCropbox} this
27217 * @event footerbuttonclick
27218 * Fire when footerbuttonclick
27219 * @param {Roo.bootstrap.UploadCropbox} this
27220 * @param {String} type
27222 "footerbuttonclick" : true,
27226 * @param {Roo.bootstrap.UploadCropbox} this
27231 * Fire when rotate the image
27232 * @param {Roo.bootstrap.UploadCropbox} this
27233 * @param {String} pos
27238 * Fire when inspect the file
27239 * @param {Roo.bootstrap.UploadCropbox} this
27240 * @param {Object} file
27245 * Fire when xhr upload the file
27246 * @param {Roo.bootstrap.UploadCropbox} this
27247 * @param {Object} data
27252 * Fire when arrange the file data
27253 * @param {Roo.bootstrap.UploadCropbox} this
27254 * @param {Object} formData
27259 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27262 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27264 emptyText : 'Click to upload image',
27265 rotateNotify : 'Image is too small to rotate',
27266 errorTimeout : 3000,
27280 cropType : 'image/jpeg',
27282 canvasLoaded : false,
27283 isDocument : false,
27285 paramName : 'imageUpload',
27287 loadingText : 'Loading...',
27290 getAutoCreate : function()
27294 cls : 'roo-upload-cropbox',
27298 cls : 'roo-upload-cropbox-selector',
27303 cls : 'roo-upload-cropbox-body',
27304 style : 'cursor:pointer',
27308 cls : 'roo-upload-cropbox-preview'
27312 cls : 'roo-upload-cropbox-thumb'
27316 cls : 'roo-upload-cropbox-empty-notify',
27317 html : this.emptyText
27321 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27322 html : this.rotateNotify
27328 cls : 'roo-upload-cropbox-footer',
27331 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27341 onRender : function(ct, position)
27343 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27345 if (this.buttons.length) {
27347 Roo.each(this.buttons, function(bb) {
27349 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27351 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27357 this.maskEl = this.el;
27361 initEvents : function()
27363 this.urlAPI = (window.createObjectURL && window) ||
27364 (window.URL && URL.revokeObjectURL && URL) ||
27365 (window.webkitURL && webkitURL);
27367 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27368 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27370 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27371 this.selectorEl.hide();
27373 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27374 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27376 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27377 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27378 this.thumbEl.hide();
27380 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27381 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27383 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27384 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27385 this.errorEl.hide();
27387 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27388 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27389 this.footerEl.hide();
27391 this.setThumbBoxSize();
27397 this.fireEvent('initial', this);
27404 window.addEventListener("resize", function() { _this.resize(); } );
27406 this.bodyEl.on('click', this.beforeSelectFile, this);
27409 this.bodyEl.on('touchstart', this.onTouchStart, this);
27410 this.bodyEl.on('touchmove', this.onTouchMove, this);
27411 this.bodyEl.on('touchend', this.onTouchEnd, this);
27415 this.bodyEl.on('mousedown', this.onMouseDown, this);
27416 this.bodyEl.on('mousemove', this.onMouseMove, this);
27417 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27418 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27419 Roo.get(document).on('mouseup', this.onMouseUp, this);
27422 this.selectorEl.on('change', this.onFileSelected, this);
27428 this.baseScale = 1;
27430 this.baseRotate = 1;
27431 this.dragable = false;
27432 this.pinching = false;
27435 this.cropData = false;
27436 this.notifyEl.dom.innerHTML = this.emptyText;
27438 this.selectorEl.dom.value = '';
27442 resize : function()
27444 if(this.fireEvent('resize', this) != false){
27445 this.setThumbBoxPosition();
27446 this.setCanvasPosition();
27450 onFooterButtonClick : function(e, el, o, type)
27453 case 'rotate-left' :
27454 this.onRotateLeft(e);
27456 case 'rotate-right' :
27457 this.onRotateRight(e);
27460 this.beforeSelectFile(e);
27475 this.fireEvent('footerbuttonclick', this, type);
27478 beforeSelectFile : function(e)
27480 e.preventDefault();
27482 if(this.fireEvent('beforeselectfile', this) != false){
27483 this.selectorEl.dom.click();
27487 onFileSelected : function(e)
27489 e.preventDefault();
27491 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27495 var file = this.selectorEl.dom.files[0];
27497 if(this.fireEvent('inspect', this, file) != false){
27498 this.prepare(file);
27503 trash : function(e)
27505 this.fireEvent('trash', this);
27508 download : function(e)
27510 this.fireEvent('download', this);
27513 loadCanvas : function(src)
27515 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27519 this.imageEl = document.createElement('img');
27523 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27525 this.imageEl.src = src;
27529 onLoadCanvas : function()
27531 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27532 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27534 this.bodyEl.un('click', this.beforeSelectFile, this);
27536 this.notifyEl.hide();
27537 this.thumbEl.show();
27538 this.footerEl.show();
27540 this.baseRotateLevel();
27542 if(this.isDocument){
27543 this.setThumbBoxSize();
27546 this.setThumbBoxPosition();
27548 this.baseScaleLevel();
27554 this.canvasLoaded = true;
27557 this.maskEl.unmask();
27562 setCanvasPosition : function()
27564 if(!this.canvasEl){
27568 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27569 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27571 this.previewEl.setLeft(pw);
27572 this.previewEl.setTop(ph);
27576 onMouseDown : function(e)
27580 this.dragable = true;
27581 this.pinching = false;
27583 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27584 this.dragable = false;
27588 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27589 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27593 onMouseMove : function(e)
27597 if(!this.canvasLoaded){
27601 if (!this.dragable){
27605 var minX = Math.ceil(this.thumbEl.getLeft(true));
27606 var minY = Math.ceil(this.thumbEl.getTop(true));
27608 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27609 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27611 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27612 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27614 x = x - this.mouseX;
27615 y = y - this.mouseY;
27617 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27618 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27620 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27621 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27623 this.previewEl.setLeft(bgX);
27624 this.previewEl.setTop(bgY);
27626 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27627 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27630 onMouseUp : function(e)
27634 this.dragable = false;
27637 onMouseWheel : function(e)
27641 this.startScale = this.scale;
27643 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27645 if(!this.zoomable()){
27646 this.scale = this.startScale;
27655 zoomable : function()
27657 var minScale = this.thumbEl.getWidth() / this.minWidth;
27659 if(this.minWidth < this.minHeight){
27660 minScale = this.thumbEl.getHeight() / this.minHeight;
27663 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27664 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27668 (this.rotate == 0 || this.rotate == 180) &&
27670 width > this.imageEl.OriginWidth ||
27671 height > this.imageEl.OriginHeight ||
27672 (width < this.minWidth && height < this.minHeight)
27680 (this.rotate == 90 || this.rotate == 270) &&
27682 width > this.imageEl.OriginWidth ||
27683 height > this.imageEl.OriginHeight ||
27684 (width < this.minHeight && height < this.minWidth)
27691 !this.isDocument &&
27692 (this.rotate == 0 || this.rotate == 180) &&
27694 width < this.minWidth ||
27695 width > this.imageEl.OriginWidth ||
27696 height < this.minHeight ||
27697 height > this.imageEl.OriginHeight
27704 !this.isDocument &&
27705 (this.rotate == 90 || this.rotate == 270) &&
27707 width < this.minHeight ||
27708 width > this.imageEl.OriginWidth ||
27709 height < this.minWidth ||
27710 height > this.imageEl.OriginHeight
27720 onRotateLeft : function(e)
27722 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27724 var minScale = this.thumbEl.getWidth() / this.minWidth;
27726 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27727 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27729 this.startScale = this.scale;
27731 while (this.getScaleLevel() < minScale){
27733 this.scale = this.scale + 1;
27735 if(!this.zoomable()){
27740 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27741 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27746 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27753 this.scale = this.startScale;
27755 this.onRotateFail();
27760 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27762 if(this.isDocument){
27763 this.setThumbBoxSize();
27764 this.setThumbBoxPosition();
27765 this.setCanvasPosition();
27770 this.fireEvent('rotate', this, 'left');
27774 onRotateRight : function(e)
27776 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27778 var minScale = this.thumbEl.getWidth() / this.minWidth;
27780 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27781 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27783 this.startScale = this.scale;
27785 while (this.getScaleLevel() < minScale){
27787 this.scale = this.scale + 1;
27789 if(!this.zoomable()){
27794 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27795 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27800 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27807 this.scale = this.startScale;
27809 this.onRotateFail();
27814 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27816 if(this.isDocument){
27817 this.setThumbBoxSize();
27818 this.setThumbBoxPosition();
27819 this.setCanvasPosition();
27824 this.fireEvent('rotate', this, 'right');
27827 onRotateFail : function()
27829 this.errorEl.show(true);
27833 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27838 this.previewEl.dom.innerHTML = '';
27840 var canvasEl = document.createElement("canvas");
27842 var contextEl = canvasEl.getContext("2d");
27844 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27845 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27846 var center = this.imageEl.OriginWidth / 2;
27848 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27849 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27850 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27851 center = this.imageEl.OriginHeight / 2;
27854 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27856 contextEl.translate(center, center);
27857 contextEl.rotate(this.rotate * Math.PI / 180);
27859 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27861 this.canvasEl = document.createElement("canvas");
27863 this.contextEl = this.canvasEl.getContext("2d");
27865 switch (this.rotate) {
27868 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27869 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27871 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27876 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27877 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27879 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27880 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);
27884 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27889 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27890 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27892 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27893 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);
27897 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);
27902 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27903 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27905 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27906 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27910 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);
27917 this.previewEl.appendChild(this.canvasEl);
27919 this.setCanvasPosition();
27924 if(!this.canvasLoaded){
27928 var imageCanvas = document.createElement("canvas");
27930 var imageContext = imageCanvas.getContext("2d");
27932 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27933 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27935 var center = imageCanvas.width / 2;
27937 imageContext.translate(center, center);
27939 imageContext.rotate(this.rotate * Math.PI / 180);
27941 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27943 var canvas = document.createElement("canvas");
27945 var context = canvas.getContext("2d");
27947 canvas.width = this.minWidth;
27948 canvas.height = this.minHeight;
27950 switch (this.rotate) {
27953 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27954 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27956 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27957 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27959 var targetWidth = this.minWidth - 2 * x;
27960 var targetHeight = this.minHeight - 2 * y;
27964 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27965 scale = targetWidth / width;
27968 if(x > 0 && y == 0){
27969 scale = targetHeight / height;
27972 if(x > 0 && y > 0){
27973 scale = targetWidth / width;
27975 if(width < height){
27976 scale = targetHeight / height;
27980 context.scale(scale, scale);
27982 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27983 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27985 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27986 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27988 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27993 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27994 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27996 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27997 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27999 var targetWidth = this.minWidth - 2 * x;
28000 var targetHeight = this.minHeight - 2 * y;
28004 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28005 scale = targetWidth / width;
28008 if(x > 0 && y == 0){
28009 scale = targetHeight / height;
28012 if(x > 0 && y > 0){
28013 scale = targetWidth / width;
28015 if(width < height){
28016 scale = targetHeight / height;
28020 context.scale(scale, scale);
28022 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28023 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28025 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28026 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28028 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28030 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28035 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28036 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28038 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28039 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28041 var targetWidth = this.minWidth - 2 * x;
28042 var targetHeight = this.minHeight - 2 * y;
28046 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28047 scale = targetWidth / width;
28050 if(x > 0 && y == 0){
28051 scale = targetHeight / height;
28054 if(x > 0 && y > 0){
28055 scale = targetWidth / width;
28057 if(width < height){
28058 scale = targetHeight / height;
28062 context.scale(scale, scale);
28064 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28065 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28067 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28068 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28070 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28071 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28073 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28078 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28079 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28081 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28082 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28084 var targetWidth = this.minWidth - 2 * x;
28085 var targetHeight = this.minHeight - 2 * y;
28089 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28090 scale = targetWidth / width;
28093 if(x > 0 && y == 0){
28094 scale = targetHeight / height;
28097 if(x > 0 && y > 0){
28098 scale = targetWidth / width;
28100 if(width < height){
28101 scale = targetHeight / height;
28105 context.scale(scale, scale);
28107 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28108 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28110 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28111 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28113 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28115 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28122 this.cropData = canvas.toDataURL(this.cropType);
28124 if(this.fireEvent('crop', this, this.cropData) !== false){
28125 this.process(this.file, this.cropData);
28132 setThumbBoxSize : function()
28136 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28137 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28138 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28140 this.minWidth = width;
28141 this.minHeight = height;
28143 if(this.rotate == 90 || this.rotate == 270){
28144 this.minWidth = height;
28145 this.minHeight = width;
28150 width = Math.ceil(this.minWidth * height / this.minHeight);
28152 if(this.minWidth > this.minHeight){
28154 height = Math.ceil(this.minHeight * width / this.minWidth);
28157 this.thumbEl.setStyle({
28158 width : width + 'px',
28159 height : height + 'px'
28166 setThumbBoxPosition : function()
28168 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28169 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28171 this.thumbEl.setLeft(x);
28172 this.thumbEl.setTop(y);
28176 baseRotateLevel : function()
28178 this.baseRotate = 1;
28181 typeof(this.exif) != 'undefined' &&
28182 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28183 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28185 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28188 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28192 baseScaleLevel : function()
28196 if(this.isDocument){
28198 if(this.baseRotate == 6 || this.baseRotate == 8){
28200 height = this.thumbEl.getHeight();
28201 this.baseScale = height / this.imageEl.OriginWidth;
28203 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28204 width = this.thumbEl.getWidth();
28205 this.baseScale = width / this.imageEl.OriginHeight;
28211 height = this.thumbEl.getHeight();
28212 this.baseScale = height / this.imageEl.OriginHeight;
28214 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28215 width = this.thumbEl.getWidth();
28216 this.baseScale = width / this.imageEl.OriginWidth;
28222 if(this.baseRotate == 6 || this.baseRotate == 8){
28224 width = this.thumbEl.getHeight();
28225 this.baseScale = width / this.imageEl.OriginHeight;
28227 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28228 height = this.thumbEl.getWidth();
28229 this.baseScale = height / this.imageEl.OriginHeight;
28232 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28233 height = this.thumbEl.getWidth();
28234 this.baseScale = height / this.imageEl.OriginHeight;
28236 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28237 width = this.thumbEl.getHeight();
28238 this.baseScale = width / this.imageEl.OriginWidth;
28245 width = this.thumbEl.getWidth();
28246 this.baseScale = width / this.imageEl.OriginWidth;
28248 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28249 height = this.thumbEl.getHeight();
28250 this.baseScale = height / this.imageEl.OriginHeight;
28253 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28255 height = this.thumbEl.getHeight();
28256 this.baseScale = height / this.imageEl.OriginHeight;
28258 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28259 width = this.thumbEl.getWidth();
28260 this.baseScale = width / this.imageEl.OriginWidth;
28268 getScaleLevel : function()
28270 return this.baseScale * Math.pow(1.1, this.scale);
28273 onTouchStart : function(e)
28275 if(!this.canvasLoaded){
28276 this.beforeSelectFile(e);
28280 var touches = e.browserEvent.touches;
28286 if(touches.length == 1){
28287 this.onMouseDown(e);
28291 if(touches.length != 2){
28297 for(var i = 0, finger; finger = touches[i]; i++){
28298 coords.push(finger.pageX, finger.pageY);
28301 var x = Math.pow(coords[0] - coords[2], 2);
28302 var y = Math.pow(coords[1] - coords[3], 2);
28304 this.startDistance = Math.sqrt(x + y);
28306 this.startScale = this.scale;
28308 this.pinching = true;
28309 this.dragable = false;
28313 onTouchMove : function(e)
28315 if(!this.pinching && !this.dragable){
28319 var touches = e.browserEvent.touches;
28326 this.onMouseMove(e);
28332 for(var i = 0, finger; finger = touches[i]; i++){
28333 coords.push(finger.pageX, finger.pageY);
28336 var x = Math.pow(coords[0] - coords[2], 2);
28337 var y = Math.pow(coords[1] - coords[3], 2);
28339 this.endDistance = Math.sqrt(x + y);
28341 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28343 if(!this.zoomable()){
28344 this.scale = this.startScale;
28352 onTouchEnd : function(e)
28354 this.pinching = false;
28355 this.dragable = false;
28359 process : function(file, crop)
28362 this.maskEl.mask(this.loadingText);
28365 this.xhr = new XMLHttpRequest();
28367 file.xhr = this.xhr;
28369 this.xhr.open(this.method, this.url, true);
28372 "Accept": "application/json",
28373 "Cache-Control": "no-cache",
28374 "X-Requested-With": "XMLHttpRequest"
28377 for (var headerName in headers) {
28378 var headerValue = headers[headerName];
28380 this.xhr.setRequestHeader(headerName, headerValue);
28386 this.xhr.onload = function()
28388 _this.xhrOnLoad(_this.xhr);
28391 this.xhr.onerror = function()
28393 _this.xhrOnError(_this.xhr);
28396 var formData = new FormData();
28398 formData.append('returnHTML', 'NO');
28401 formData.append('crop', crop);
28404 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28405 formData.append(this.paramName, file, file.name);
28408 if(typeof(file.filename) != 'undefined'){
28409 formData.append('filename', file.filename);
28412 if(typeof(file.mimetype) != 'undefined'){
28413 formData.append('mimetype', file.mimetype);
28416 if(this.fireEvent('arrange', this, formData) != false){
28417 this.xhr.send(formData);
28421 xhrOnLoad : function(xhr)
28424 this.maskEl.unmask();
28427 if (xhr.readyState !== 4) {
28428 this.fireEvent('exception', this, xhr);
28432 var response = Roo.decode(xhr.responseText);
28434 if(!response.success){
28435 this.fireEvent('exception', this, xhr);
28439 var response = Roo.decode(xhr.responseText);
28441 this.fireEvent('upload', this, response);
28445 xhrOnError : function()
28448 this.maskEl.unmask();
28451 Roo.log('xhr on error');
28453 var response = Roo.decode(xhr.responseText);
28459 prepare : function(file)
28462 this.maskEl.mask(this.loadingText);
28468 if(typeof(file) === 'string'){
28469 this.loadCanvas(file);
28473 if(!file || !this.urlAPI){
28478 this.cropType = file.type;
28482 if(this.fireEvent('prepare', this, this.file) != false){
28484 var reader = new FileReader();
28486 reader.onload = function (e) {
28487 if (e.target.error) {
28488 Roo.log(e.target.error);
28492 var buffer = e.target.result,
28493 dataView = new DataView(buffer),
28495 maxOffset = dataView.byteLength - 4,
28499 if (dataView.getUint16(0) === 0xffd8) {
28500 while (offset < maxOffset) {
28501 markerBytes = dataView.getUint16(offset);
28503 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28504 markerLength = dataView.getUint16(offset + 2) + 2;
28505 if (offset + markerLength > dataView.byteLength) {
28506 Roo.log('Invalid meta data: Invalid segment size.');
28510 if(markerBytes == 0xffe1){
28511 _this.parseExifData(
28518 offset += markerLength;
28528 var url = _this.urlAPI.createObjectURL(_this.file);
28530 _this.loadCanvas(url);
28535 reader.readAsArrayBuffer(this.file);
28541 parseExifData : function(dataView, offset, length)
28543 var tiffOffset = offset + 10,
28547 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28548 // No Exif data, might be XMP data instead
28552 // Check for the ASCII code for "Exif" (0x45786966):
28553 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28554 // No Exif data, might be XMP data instead
28557 if (tiffOffset + 8 > dataView.byteLength) {
28558 Roo.log('Invalid Exif data: Invalid segment size.');
28561 // Check for the two null bytes:
28562 if (dataView.getUint16(offset + 8) !== 0x0000) {
28563 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28566 // Check the byte alignment:
28567 switch (dataView.getUint16(tiffOffset)) {
28569 littleEndian = true;
28572 littleEndian = false;
28575 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28578 // Check for the TIFF tag marker (0x002A):
28579 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28580 Roo.log('Invalid Exif data: Missing TIFF marker.');
28583 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28584 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28586 this.parseExifTags(
28589 tiffOffset + dirOffset,
28594 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28599 if (dirOffset + 6 > dataView.byteLength) {
28600 Roo.log('Invalid Exif data: Invalid directory offset.');
28603 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28604 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28605 if (dirEndOffset + 4 > dataView.byteLength) {
28606 Roo.log('Invalid Exif data: Invalid directory size.');
28609 for (i = 0; i < tagsNumber; i += 1) {
28613 dirOffset + 2 + 12 * i, // tag offset
28617 // Return the offset to the next directory:
28618 return dataView.getUint32(dirEndOffset, littleEndian);
28621 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28623 var tag = dataView.getUint16(offset, littleEndian);
28625 this.exif[tag] = this.getExifValue(
28629 dataView.getUint16(offset + 2, littleEndian), // tag type
28630 dataView.getUint32(offset + 4, littleEndian), // tag length
28635 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28637 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28646 Roo.log('Invalid Exif data: Invalid tag type.');
28650 tagSize = tagType.size * length;
28651 // Determine if the value is contained in the dataOffset bytes,
28652 // or if the value at the dataOffset is a pointer to the actual data:
28653 dataOffset = tagSize > 4 ?
28654 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28655 if (dataOffset + tagSize > dataView.byteLength) {
28656 Roo.log('Invalid Exif data: Invalid data offset.');
28659 if (length === 1) {
28660 return tagType.getValue(dataView, dataOffset, littleEndian);
28663 for (i = 0; i < length; i += 1) {
28664 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28667 if (tagType.ascii) {
28669 // Concatenate the chars:
28670 for (i = 0; i < values.length; i += 1) {
28672 // Ignore the terminating NULL byte(s):
28673 if (c === '\u0000') {
28685 Roo.apply(Roo.bootstrap.UploadCropbox, {
28687 'Orientation': 0x0112
28691 1: 0, //'top-left',
28693 3: 180, //'bottom-right',
28694 // 4: 'bottom-left',
28696 6: 90, //'right-top',
28697 // 7: 'right-bottom',
28698 8: 270 //'left-bottom'
28702 // byte, 8-bit unsigned int:
28704 getValue: function (dataView, dataOffset) {
28705 return dataView.getUint8(dataOffset);
28709 // ascii, 8-bit byte:
28711 getValue: function (dataView, dataOffset) {
28712 return String.fromCharCode(dataView.getUint8(dataOffset));
28717 // short, 16 bit int:
28719 getValue: function (dataView, dataOffset, littleEndian) {
28720 return dataView.getUint16(dataOffset, littleEndian);
28724 // long, 32 bit int:
28726 getValue: function (dataView, dataOffset, littleEndian) {
28727 return dataView.getUint32(dataOffset, littleEndian);
28731 // rational = two long values, first is numerator, second is denominator:
28733 getValue: function (dataView, dataOffset, littleEndian) {
28734 return dataView.getUint32(dataOffset, littleEndian) /
28735 dataView.getUint32(dataOffset + 4, littleEndian);
28739 // slong, 32 bit signed int:
28741 getValue: function (dataView, dataOffset, littleEndian) {
28742 return dataView.getInt32(dataOffset, littleEndian);
28746 // srational, two slongs, first is numerator, second is denominator:
28748 getValue: function (dataView, dataOffset, littleEndian) {
28749 return dataView.getInt32(dataOffset, littleEndian) /
28750 dataView.getInt32(dataOffset + 4, littleEndian);
28760 cls : 'btn-group roo-upload-cropbox-rotate-left',
28761 action : 'rotate-left',
28765 cls : 'btn btn-default',
28766 html : '<i class="fa fa-undo"></i>'
28772 cls : 'btn-group roo-upload-cropbox-picture',
28773 action : 'picture',
28777 cls : 'btn btn-default',
28778 html : '<i class="fa fa-picture-o"></i>'
28784 cls : 'btn-group roo-upload-cropbox-rotate-right',
28785 action : 'rotate-right',
28789 cls : 'btn btn-default',
28790 html : '<i class="fa fa-repeat"></i>'
28798 cls : 'btn-group roo-upload-cropbox-rotate-left',
28799 action : 'rotate-left',
28803 cls : 'btn btn-default',
28804 html : '<i class="fa fa-undo"></i>'
28810 cls : 'btn-group roo-upload-cropbox-download',
28811 action : 'download',
28815 cls : 'btn btn-default',
28816 html : '<i class="fa fa-download"></i>'
28822 cls : 'btn-group roo-upload-cropbox-crop',
28827 cls : 'btn btn-default',
28828 html : '<i class="fa fa-crop"></i>'
28834 cls : 'btn-group roo-upload-cropbox-trash',
28839 cls : 'btn btn-default',
28840 html : '<i class="fa fa-trash"></i>'
28846 cls : 'btn-group roo-upload-cropbox-rotate-right',
28847 action : 'rotate-right',
28851 cls : 'btn btn-default',
28852 html : '<i class="fa fa-repeat"></i>'
28860 cls : 'btn-group roo-upload-cropbox-rotate-left',
28861 action : 'rotate-left',
28865 cls : 'btn btn-default',
28866 html : '<i class="fa fa-undo"></i>'
28872 cls : 'btn-group roo-upload-cropbox-rotate-right',
28873 action : 'rotate-right',
28877 cls : 'btn btn-default',
28878 html : '<i class="fa fa-repeat"></i>'
28891 * @class Roo.bootstrap.DocumentManager
28892 * @extends Roo.bootstrap.Component
28893 * Bootstrap DocumentManager class
28894 * @cfg {String} paramName default 'imageUpload'
28895 * @cfg {String} toolTipName default 'filename'
28896 * @cfg {String} method default POST
28897 * @cfg {String} url action url
28898 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28899 * @cfg {Boolean} multiple multiple upload default true
28900 * @cfg {Number} thumbSize default 300
28901 * @cfg {String} fieldLabel
28902 * @cfg {Number} labelWidth default 4
28903 * @cfg {String} labelAlign (left|top) default left
28904 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28905 * @cfg {Number} labellg set the width of label (1-12)
28906 * @cfg {Number} labelmd set the width of label (1-12)
28907 * @cfg {Number} labelsm set the width of label (1-12)
28908 * @cfg {Number} labelxs set the width of label (1-12)
28911 * Create a new DocumentManager
28912 * @param {Object} config The config object
28915 Roo.bootstrap.DocumentManager = function(config){
28916 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28919 this.delegates = [];
28924 * Fire when initial the DocumentManager
28925 * @param {Roo.bootstrap.DocumentManager} this
28930 * inspect selected file
28931 * @param {Roo.bootstrap.DocumentManager} this
28932 * @param {File} file
28937 * Fire when xhr load exception
28938 * @param {Roo.bootstrap.DocumentManager} this
28939 * @param {XMLHttpRequest} xhr
28941 "exception" : true,
28943 * @event afterupload
28944 * Fire when xhr load exception
28945 * @param {Roo.bootstrap.DocumentManager} this
28946 * @param {XMLHttpRequest} xhr
28948 "afterupload" : true,
28951 * prepare the form data
28952 * @param {Roo.bootstrap.DocumentManager} this
28953 * @param {Object} formData
28958 * Fire when remove the file
28959 * @param {Roo.bootstrap.DocumentManager} this
28960 * @param {Object} file
28965 * Fire after refresh the file
28966 * @param {Roo.bootstrap.DocumentManager} this
28971 * Fire after click the image
28972 * @param {Roo.bootstrap.DocumentManager} this
28973 * @param {Object} file
28978 * Fire when upload a image and editable set to true
28979 * @param {Roo.bootstrap.DocumentManager} this
28980 * @param {Object} file
28984 * @event beforeselectfile
28985 * Fire before select file
28986 * @param {Roo.bootstrap.DocumentManager} this
28988 "beforeselectfile" : true,
28991 * Fire before process file
28992 * @param {Roo.bootstrap.DocumentManager} this
28993 * @param {Object} file
28997 * @event previewrendered
28998 * Fire when preview rendered
28999 * @param {Roo.bootstrap.DocumentManager} this
29000 * @param {Object} file
29002 "previewrendered" : true,
29005 "previewResize" : true
29010 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29019 paramName : 'imageUpload',
29020 toolTipName : 'filename',
29023 labelAlign : 'left',
29033 getAutoCreate : function()
29035 var managerWidget = {
29037 cls : 'roo-document-manager',
29041 cls : 'roo-document-manager-selector',
29046 cls : 'roo-document-manager-uploader',
29050 cls : 'roo-document-manager-upload-btn',
29051 html : '<i class="fa fa-plus"></i>'
29062 cls : 'column col-md-12',
29067 if(this.fieldLabel.length){
29072 cls : 'column col-md-12',
29073 html : this.fieldLabel
29077 cls : 'column col-md-12',
29082 if(this.labelAlign == 'left'){
29087 html : this.fieldLabel
29096 if(this.labelWidth > 12){
29097 content[0].style = "width: " + this.labelWidth + 'px';
29100 if(this.labelWidth < 13 && this.labelmd == 0){
29101 this.labelmd = this.labelWidth;
29104 if(this.labellg > 0){
29105 content[0].cls += ' col-lg-' + this.labellg;
29106 content[1].cls += ' col-lg-' + (12 - this.labellg);
29109 if(this.labelmd > 0){
29110 content[0].cls += ' col-md-' + this.labelmd;
29111 content[1].cls += ' col-md-' + (12 - this.labelmd);
29114 if(this.labelsm > 0){
29115 content[0].cls += ' col-sm-' + this.labelsm;
29116 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29119 if(this.labelxs > 0){
29120 content[0].cls += ' col-xs-' + this.labelxs;
29121 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29129 cls : 'row clearfix',
29137 initEvents : function()
29139 this.managerEl = this.el.select('.roo-document-manager', true).first();
29140 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29142 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29143 this.selectorEl.hide();
29146 this.selectorEl.attr('multiple', 'multiple');
29149 this.selectorEl.on('change', this.onFileSelected, this);
29151 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29152 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29154 this.uploader.on('click', this.onUploaderClick, this);
29156 this.renderProgressDialog();
29160 window.addEventListener("resize", function() { _this.refresh(); } );
29162 this.fireEvent('initial', this);
29165 renderProgressDialog : function()
29169 this.progressDialog = new Roo.bootstrap.Modal({
29170 cls : 'roo-document-manager-progress-dialog',
29171 allow_close : false,
29181 btnclick : function() {
29182 _this.uploadCancel();
29188 this.progressDialog.render(Roo.get(document.body));
29190 this.progress = new Roo.bootstrap.Progress({
29191 cls : 'roo-document-manager-progress',
29196 this.progress.render(this.progressDialog.getChildContainer());
29198 this.progressBar = new Roo.bootstrap.ProgressBar({
29199 cls : 'roo-document-manager-progress-bar',
29202 aria_valuemax : 12,
29206 this.progressBar.render(this.progress.getChildContainer());
29209 onUploaderClick : function(e)
29211 e.preventDefault();
29213 if(this.fireEvent('beforeselectfile', this) != false){
29214 this.selectorEl.dom.click();
29219 onFileSelected : function(e)
29221 e.preventDefault();
29223 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29227 Roo.each(this.selectorEl.dom.files, function(file){
29228 if(this.fireEvent('inspect', this, file) != false){
29229 this.files.push(file);
29239 this.selectorEl.dom.value = '';
29241 if(!this.files || !this.files.length){
29245 if(this.boxes > 0 && this.files.length > this.boxes){
29246 this.files = this.files.slice(0, this.boxes);
29249 this.uploader.show();
29251 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29252 this.uploader.hide();
29261 Roo.each(this.files, function(file){
29263 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29264 var f = this.renderPreview(file);
29269 if(file.type.indexOf('image') != -1){
29270 this.delegates.push(
29272 _this.process(file);
29273 }).createDelegate(this)
29281 _this.process(file);
29282 }).createDelegate(this)
29287 this.files = files;
29289 this.delegates = this.delegates.concat(docs);
29291 if(!this.delegates.length){
29296 this.progressBar.aria_valuemax = this.delegates.length;
29303 arrange : function()
29305 if(!this.delegates.length){
29306 this.progressDialog.hide();
29311 var delegate = this.delegates.shift();
29313 this.progressDialog.show();
29315 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29317 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29322 refresh : function()
29324 this.uploader.show();
29326 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29327 this.uploader.hide();
29330 Roo.isTouch ? this.closable(false) : this.closable(true);
29332 this.fireEvent('refresh', this);
29335 onRemove : function(e, el, o)
29337 e.preventDefault();
29339 this.fireEvent('remove', this, o);
29343 remove : function(o)
29347 Roo.each(this.files, function(file){
29348 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29357 this.files = files;
29364 Roo.each(this.files, function(file){
29369 file.target.remove();
29378 onClick : function(e, el, o)
29380 e.preventDefault();
29382 this.fireEvent('click', this, o);
29386 closable : function(closable)
29388 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29390 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29402 xhrOnLoad : function(xhr)
29404 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29408 if (xhr.readyState !== 4) {
29410 this.fireEvent('exception', this, xhr);
29414 var response = Roo.decode(xhr.responseText);
29416 if(!response.success){
29418 this.fireEvent('exception', this, xhr);
29422 var file = this.renderPreview(response.data);
29424 this.files.push(file);
29428 this.fireEvent('afterupload', this, xhr);
29432 xhrOnError : function(xhr)
29434 Roo.log('xhr on error');
29436 var response = Roo.decode(xhr.responseText);
29443 process : function(file)
29445 if(this.fireEvent('process', this, file) !== false){
29446 if(this.editable && file.type.indexOf('image') != -1){
29447 this.fireEvent('edit', this, file);
29451 this.uploadStart(file, false);
29458 uploadStart : function(file, crop)
29460 this.xhr = new XMLHttpRequest();
29462 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29467 file.xhr = this.xhr;
29469 this.managerEl.createChild({
29471 cls : 'roo-document-manager-loading',
29475 tooltip : file.name,
29476 cls : 'roo-document-manager-thumb',
29477 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29483 this.xhr.open(this.method, this.url, true);
29486 "Accept": "application/json",
29487 "Cache-Control": "no-cache",
29488 "X-Requested-With": "XMLHttpRequest"
29491 for (var headerName in headers) {
29492 var headerValue = headers[headerName];
29494 this.xhr.setRequestHeader(headerName, headerValue);
29500 this.xhr.onload = function()
29502 _this.xhrOnLoad(_this.xhr);
29505 this.xhr.onerror = function()
29507 _this.xhrOnError(_this.xhr);
29510 var formData = new FormData();
29512 formData.append('returnHTML', 'NO');
29515 formData.append('crop', crop);
29518 formData.append(this.paramName, file, file.name);
29525 if(this.fireEvent('prepare', this, formData, options) != false){
29527 if(options.manually){
29531 this.xhr.send(formData);
29535 this.uploadCancel();
29538 uploadCancel : function()
29544 this.delegates = [];
29546 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29553 renderPreview : function(file)
29555 if(typeof(file.target) != 'undefined' && file.target){
29559 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29561 var previewEl = this.managerEl.createChild({
29563 cls : 'roo-document-manager-preview',
29567 tooltip : file[this.toolTipName],
29568 cls : 'roo-document-manager-thumb',
29569 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29574 html : '<i class="fa fa-times-circle"></i>'
29579 var close = previewEl.select('button.close', true).first();
29581 close.on('click', this.onRemove, this, file);
29583 file.target = previewEl;
29585 var image = previewEl.select('img', true).first();
29589 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29591 image.on('click', this.onClick, this, file);
29593 this.fireEvent('previewrendered', this, file);
29599 onPreviewLoad : function(file, image)
29601 if(typeof(file.target) == 'undefined' || !file.target){
29605 var width = image.dom.naturalWidth || image.dom.width;
29606 var height = image.dom.naturalHeight || image.dom.height;
29608 if(!this.previewResize) {
29612 if(width > height){
29613 file.target.addClass('wide');
29617 file.target.addClass('tall');
29622 uploadFromSource : function(file, crop)
29624 this.xhr = new XMLHttpRequest();
29626 this.managerEl.createChild({
29628 cls : 'roo-document-manager-loading',
29632 tooltip : file.name,
29633 cls : 'roo-document-manager-thumb',
29634 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29640 this.xhr.open(this.method, this.url, true);
29643 "Accept": "application/json",
29644 "Cache-Control": "no-cache",
29645 "X-Requested-With": "XMLHttpRequest"
29648 for (var headerName in headers) {
29649 var headerValue = headers[headerName];
29651 this.xhr.setRequestHeader(headerName, headerValue);
29657 this.xhr.onload = function()
29659 _this.xhrOnLoad(_this.xhr);
29662 this.xhr.onerror = function()
29664 _this.xhrOnError(_this.xhr);
29667 var formData = new FormData();
29669 formData.append('returnHTML', 'NO');
29671 formData.append('crop', crop);
29673 if(typeof(file.filename) != 'undefined'){
29674 formData.append('filename', file.filename);
29677 if(typeof(file.mimetype) != 'undefined'){
29678 formData.append('mimetype', file.mimetype);
29683 if(this.fireEvent('prepare', this, formData) != false){
29684 this.xhr.send(formData);
29694 * @class Roo.bootstrap.DocumentViewer
29695 * @extends Roo.bootstrap.Component
29696 * Bootstrap DocumentViewer class
29697 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29698 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29701 * Create a new DocumentViewer
29702 * @param {Object} config The config object
29705 Roo.bootstrap.DocumentViewer = function(config){
29706 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29711 * Fire after initEvent
29712 * @param {Roo.bootstrap.DocumentViewer} this
29718 * @param {Roo.bootstrap.DocumentViewer} this
29723 * Fire after download button
29724 * @param {Roo.bootstrap.DocumentViewer} this
29729 * Fire after trash button
29730 * @param {Roo.bootstrap.DocumentViewer} this
29737 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29739 showDownload : true,
29743 getAutoCreate : function()
29747 cls : 'roo-document-viewer',
29751 cls : 'roo-document-viewer-body',
29755 cls : 'roo-document-viewer-thumb',
29759 cls : 'roo-document-viewer-image'
29767 cls : 'roo-document-viewer-footer',
29770 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29774 cls : 'btn-group roo-document-viewer-download',
29778 cls : 'btn btn-default',
29779 html : '<i class="fa fa-download"></i>'
29785 cls : 'btn-group roo-document-viewer-trash',
29789 cls : 'btn btn-default',
29790 html : '<i class="fa fa-trash"></i>'
29803 initEvents : function()
29805 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29806 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29808 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29809 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29811 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29812 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29814 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29815 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29817 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29818 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29820 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29821 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29823 this.bodyEl.on('click', this.onClick, this);
29824 this.downloadBtn.on('click', this.onDownload, this);
29825 this.trashBtn.on('click', this.onTrash, this);
29827 this.downloadBtn.hide();
29828 this.trashBtn.hide();
29830 if(this.showDownload){
29831 this.downloadBtn.show();
29834 if(this.showTrash){
29835 this.trashBtn.show();
29838 if(!this.showDownload && !this.showTrash) {
29839 this.footerEl.hide();
29844 initial : function()
29846 this.fireEvent('initial', this);
29850 onClick : function(e)
29852 e.preventDefault();
29854 this.fireEvent('click', this);
29857 onDownload : function(e)
29859 e.preventDefault();
29861 this.fireEvent('download', this);
29864 onTrash : function(e)
29866 e.preventDefault();
29868 this.fireEvent('trash', this);
29880 * @class Roo.bootstrap.NavProgressBar
29881 * @extends Roo.bootstrap.Component
29882 * Bootstrap NavProgressBar class
29885 * Create a new nav progress bar
29886 * @param {Object} config The config object
29889 Roo.bootstrap.NavProgressBar = function(config){
29890 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29892 this.bullets = this.bullets || [];
29894 // Roo.bootstrap.NavProgressBar.register(this);
29898 * Fires when the active item changes
29899 * @param {Roo.bootstrap.NavProgressBar} this
29900 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29901 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29908 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29913 getAutoCreate : function()
29915 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29919 cls : 'roo-navigation-bar-group',
29923 cls : 'roo-navigation-top-bar'
29927 cls : 'roo-navigation-bullets-bar',
29931 cls : 'roo-navigation-bar'
29938 cls : 'roo-navigation-bottom-bar'
29948 initEvents: function()
29953 onRender : function(ct, position)
29955 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29957 if(this.bullets.length){
29958 Roo.each(this.bullets, function(b){
29967 addItem : function(cfg)
29969 var item = new Roo.bootstrap.NavProgressItem(cfg);
29971 item.parentId = this.id;
29972 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29975 var top = new Roo.bootstrap.Element({
29977 cls : 'roo-navigation-bar-text'
29980 var bottom = new Roo.bootstrap.Element({
29982 cls : 'roo-navigation-bar-text'
29985 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29986 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29988 var topText = new Roo.bootstrap.Element({
29990 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29993 var bottomText = new Roo.bootstrap.Element({
29995 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29998 topText.onRender(top.el, null);
29999 bottomText.onRender(bottom.el, null);
30002 item.bottomEl = bottom;
30005 this.barItems.push(item);
30010 getActive : function()
30012 var active = false;
30014 Roo.each(this.barItems, function(v){
30016 if (!v.isActive()) {
30028 setActiveItem : function(item)
30032 Roo.each(this.barItems, function(v){
30033 if (v.rid == item.rid) {
30037 if (v.isActive()) {
30038 v.setActive(false);
30043 item.setActive(true);
30045 this.fireEvent('changed', this, item, prev);
30048 getBarItem: function(rid)
30052 Roo.each(this.barItems, function(e) {
30053 if (e.rid != rid) {
30064 indexOfItem : function(item)
30068 Roo.each(this.barItems, function(v, i){
30070 if (v.rid != item.rid) {
30081 setActiveNext : function()
30083 var i = this.indexOfItem(this.getActive());
30085 if (i > this.barItems.length) {
30089 this.setActiveItem(this.barItems[i+1]);
30092 setActivePrev : function()
30094 var i = this.indexOfItem(this.getActive());
30100 this.setActiveItem(this.barItems[i-1]);
30103 format : function()
30105 if(!this.barItems.length){
30109 var width = 100 / this.barItems.length;
30111 Roo.each(this.barItems, function(i){
30112 i.el.setStyle('width', width + '%');
30113 i.topEl.el.setStyle('width', width + '%');
30114 i.bottomEl.el.setStyle('width', width + '%');
30123 * Nav Progress Item
30128 * @class Roo.bootstrap.NavProgressItem
30129 * @extends Roo.bootstrap.Component
30130 * Bootstrap NavProgressItem class
30131 * @cfg {String} rid the reference id
30132 * @cfg {Boolean} active (true|false) Is item active default false
30133 * @cfg {Boolean} disabled (true|false) Is item active default false
30134 * @cfg {String} html
30135 * @cfg {String} position (top|bottom) text position default bottom
30136 * @cfg {String} icon show icon instead of number
30139 * Create a new NavProgressItem
30140 * @param {Object} config The config object
30142 Roo.bootstrap.NavProgressItem = function(config){
30143 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30148 * The raw click event for the entire grid.
30149 * @param {Roo.bootstrap.NavProgressItem} this
30150 * @param {Roo.EventObject} e
30157 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30163 position : 'bottom',
30166 getAutoCreate : function()
30168 var iconCls = 'roo-navigation-bar-item-icon';
30170 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30174 cls: 'roo-navigation-bar-item',
30184 cfg.cls += ' active';
30187 cfg.cls += ' disabled';
30193 disable : function()
30195 this.setDisabled(true);
30198 enable : function()
30200 this.setDisabled(false);
30203 initEvents: function()
30205 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30207 this.iconEl.on('click', this.onClick, this);
30210 onClick : function(e)
30212 e.preventDefault();
30218 if(this.fireEvent('click', this, e) === false){
30222 this.parent().setActiveItem(this);
30225 isActive: function ()
30227 return this.active;
30230 setActive : function(state)
30232 if(this.active == state){
30236 this.active = state;
30239 this.el.addClass('active');
30243 this.el.removeClass('active');
30248 setDisabled : function(state)
30250 if(this.disabled == state){
30254 this.disabled = state;
30257 this.el.addClass('disabled');
30261 this.el.removeClass('disabled');
30264 tooltipEl : function()
30266 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30279 * @class Roo.bootstrap.FieldLabel
30280 * @extends Roo.bootstrap.Component
30281 * Bootstrap FieldLabel class
30282 * @cfg {String} html contents of the element
30283 * @cfg {String} tag tag of the element default label
30284 * @cfg {String} cls class of the element
30285 * @cfg {String} target label target
30286 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30287 * @cfg {String} invalidClass default "text-warning"
30288 * @cfg {String} validClass default "text-success"
30289 * @cfg {String} iconTooltip default "This field is required"
30290 * @cfg {String} indicatorpos (left|right) default left
30293 * Create a new FieldLabel
30294 * @param {Object} config The config object
30297 Roo.bootstrap.FieldLabel = function(config){
30298 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30303 * Fires after the field has been marked as invalid.
30304 * @param {Roo.form.FieldLabel} this
30305 * @param {String} msg The validation message
30310 * Fires after the field has been validated with no errors.
30311 * @param {Roo.form.FieldLabel} this
30317 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30324 invalidClass : 'has-warning',
30325 validClass : 'has-success',
30326 iconTooltip : 'This field is required',
30327 indicatorpos : 'left',
30329 getAutoCreate : function(){
30332 if (!this.allowBlank) {
30338 cls : 'roo-bootstrap-field-label ' + this.cls,
30343 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30344 tooltip : this.iconTooltip
30353 if(this.indicatorpos == 'right'){
30356 cls : 'roo-bootstrap-field-label ' + this.cls,
30365 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30366 tooltip : this.iconTooltip
30375 initEvents: function()
30377 Roo.bootstrap.Element.superclass.initEvents.call(this);
30379 this.indicator = this.indicatorEl();
30381 if(this.indicator){
30382 this.indicator.removeClass('visible');
30383 this.indicator.addClass('invisible');
30386 Roo.bootstrap.FieldLabel.register(this);
30389 indicatorEl : function()
30391 var indicator = this.el.select('i.roo-required-indicator',true).first();
30402 * Mark this field as valid
30404 markValid : function()
30406 if(this.indicator){
30407 this.indicator.removeClass('visible');
30408 this.indicator.addClass('invisible');
30411 this.el.removeClass(this.invalidClass);
30413 this.el.addClass(this.validClass);
30415 this.fireEvent('valid', this);
30419 * Mark this field as invalid
30420 * @param {String} msg The validation message
30422 markInvalid : function(msg)
30424 if(this.indicator){
30425 this.indicator.removeClass('invisible');
30426 this.indicator.addClass('visible');
30429 this.el.removeClass(this.validClass);
30431 this.el.addClass(this.invalidClass);
30433 this.fireEvent('invalid', this, msg);
30439 Roo.apply(Roo.bootstrap.FieldLabel, {
30444 * register a FieldLabel Group
30445 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30447 register : function(label)
30449 if(this.groups.hasOwnProperty(label.target)){
30453 this.groups[label.target] = label;
30457 * fetch a FieldLabel Group based on the target
30458 * @param {string} target
30459 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30461 get: function(target) {
30462 if (typeof(this.groups[target]) == 'undefined') {
30466 return this.groups[target] ;
30475 * page DateSplitField.
30481 * @class Roo.bootstrap.DateSplitField
30482 * @extends Roo.bootstrap.Component
30483 * Bootstrap DateSplitField class
30484 * @cfg {string} fieldLabel - the label associated
30485 * @cfg {Number} labelWidth set the width of label (0-12)
30486 * @cfg {String} labelAlign (top|left)
30487 * @cfg {Boolean} dayAllowBlank (true|false) default false
30488 * @cfg {Boolean} monthAllowBlank (true|false) default false
30489 * @cfg {Boolean} yearAllowBlank (true|false) default false
30490 * @cfg {string} dayPlaceholder
30491 * @cfg {string} monthPlaceholder
30492 * @cfg {string} yearPlaceholder
30493 * @cfg {string} dayFormat default 'd'
30494 * @cfg {string} monthFormat default 'm'
30495 * @cfg {string} yearFormat default 'Y'
30496 * @cfg {Number} labellg set the width of label (1-12)
30497 * @cfg {Number} labelmd set the width of label (1-12)
30498 * @cfg {Number} labelsm set the width of label (1-12)
30499 * @cfg {Number} labelxs set the width of label (1-12)
30503 * Create a new DateSplitField
30504 * @param {Object} config The config object
30507 Roo.bootstrap.DateSplitField = function(config){
30508 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30514 * getting the data of years
30515 * @param {Roo.bootstrap.DateSplitField} this
30516 * @param {Object} years
30521 * getting the data of days
30522 * @param {Roo.bootstrap.DateSplitField} this
30523 * @param {Object} days
30528 * Fires after the field has been marked as invalid.
30529 * @param {Roo.form.Field} this
30530 * @param {String} msg The validation message
30535 * Fires after the field has been validated with no errors.
30536 * @param {Roo.form.Field} this
30542 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30545 labelAlign : 'top',
30547 dayAllowBlank : false,
30548 monthAllowBlank : false,
30549 yearAllowBlank : false,
30550 dayPlaceholder : '',
30551 monthPlaceholder : '',
30552 yearPlaceholder : '',
30556 isFormField : true,
30562 getAutoCreate : function()
30566 cls : 'row roo-date-split-field-group',
30571 cls : 'form-hidden-field roo-date-split-field-group-value',
30577 var labelCls = 'col-md-12';
30578 var contentCls = 'col-md-4';
30580 if(this.fieldLabel){
30584 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30588 html : this.fieldLabel
30593 if(this.labelAlign == 'left'){
30595 if(this.labelWidth > 12){
30596 label.style = "width: " + this.labelWidth + 'px';
30599 if(this.labelWidth < 13 && this.labelmd == 0){
30600 this.labelmd = this.labelWidth;
30603 if(this.labellg > 0){
30604 labelCls = ' col-lg-' + this.labellg;
30605 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30608 if(this.labelmd > 0){
30609 labelCls = ' col-md-' + this.labelmd;
30610 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30613 if(this.labelsm > 0){
30614 labelCls = ' col-sm-' + this.labelsm;
30615 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30618 if(this.labelxs > 0){
30619 labelCls = ' col-xs-' + this.labelxs;
30620 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30624 label.cls += ' ' + labelCls;
30626 cfg.cn.push(label);
30629 Roo.each(['day', 'month', 'year'], function(t){
30632 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30639 inputEl: function ()
30641 return this.el.select('.roo-date-split-field-group-value', true).first();
30644 onRender : function(ct, position)
30648 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30650 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30652 this.dayField = new Roo.bootstrap.ComboBox({
30653 allowBlank : this.dayAllowBlank,
30654 alwaysQuery : true,
30655 displayField : 'value',
30658 forceSelection : true,
30660 placeholder : this.dayPlaceholder,
30661 selectOnFocus : true,
30662 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30663 triggerAction : 'all',
30665 valueField : 'value',
30666 store : new Roo.data.SimpleStore({
30667 data : (function() {
30669 _this.fireEvent('days', _this, days);
30672 fields : [ 'value' ]
30675 select : function (_self, record, index)
30677 _this.setValue(_this.getValue());
30682 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30684 this.monthField = new Roo.bootstrap.MonthField({
30685 after : '<i class=\"fa fa-calendar\"></i>',
30686 allowBlank : this.monthAllowBlank,
30687 placeholder : this.monthPlaceholder,
30690 render : function (_self)
30692 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30693 e.preventDefault();
30697 select : function (_self, oldvalue, newvalue)
30699 _this.setValue(_this.getValue());
30704 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30706 this.yearField = new Roo.bootstrap.ComboBox({
30707 allowBlank : this.yearAllowBlank,
30708 alwaysQuery : true,
30709 displayField : 'value',
30712 forceSelection : true,
30714 placeholder : this.yearPlaceholder,
30715 selectOnFocus : true,
30716 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30717 triggerAction : 'all',
30719 valueField : 'value',
30720 store : new Roo.data.SimpleStore({
30721 data : (function() {
30723 _this.fireEvent('years', _this, years);
30726 fields : [ 'value' ]
30729 select : function (_self, record, index)
30731 _this.setValue(_this.getValue());
30736 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30739 setValue : function(v, format)
30741 this.inputEl.dom.value = v;
30743 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30745 var d = Date.parseDate(v, f);
30752 this.setDay(d.format(this.dayFormat));
30753 this.setMonth(d.format(this.monthFormat));
30754 this.setYear(d.format(this.yearFormat));
30761 setDay : function(v)
30763 this.dayField.setValue(v);
30764 this.inputEl.dom.value = this.getValue();
30769 setMonth : function(v)
30771 this.monthField.setValue(v, true);
30772 this.inputEl.dom.value = this.getValue();
30777 setYear : function(v)
30779 this.yearField.setValue(v);
30780 this.inputEl.dom.value = this.getValue();
30785 getDay : function()
30787 return this.dayField.getValue();
30790 getMonth : function()
30792 return this.monthField.getValue();
30795 getYear : function()
30797 return this.yearField.getValue();
30800 getValue : function()
30802 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30804 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30814 this.inputEl.dom.value = '';
30819 validate : function()
30821 var d = this.dayField.validate();
30822 var m = this.monthField.validate();
30823 var y = this.yearField.validate();
30828 (!this.dayAllowBlank && !d) ||
30829 (!this.monthAllowBlank && !m) ||
30830 (!this.yearAllowBlank && !y)
30835 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30844 this.markInvalid();
30849 markValid : function()
30852 var label = this.el.select('label', true).first();
30853 var icon = this.el.select('i.fa-star', true).first();
30859 this.fireEvent('valid', this);
30863 * Mark this field as invalid
30864 * @param {String} msg The validation message
30866 markInvalid : function(msg)
30869 var label = this.el.select('label', true).first();
30870 var icon = this.el.select('i.fa-star', true).first();
30872 if(label && !icon){
30873 this.el.select('.roo-date-split-field-label', true).createChild({
30875 cls : 'text-danger fa fa-lg fa-star',
30876 tooltip : 'This field is required',
30877 style : 'margin-right:5px;'
30881 this.fireEvent('invalid', this, msg);
30884 clearInvalid : function()
30886 var label = this.el.select('label', true).first();
30887 var icon = this.el.select('i.fa-star', true).first();
30893 this.fireEvent('valid', this);
30896 getName: function()
30906 * http://masonry.desandro.com
30908 * The idea is to render all the bricks based on vertical width...
30910 * The original code extends 'outlayer' - we might need to use that....
30916 * @class Roo.bootstrap.LayoutMasonry
30917 * @extends Roo.bootstrap.Component
30918 * Bootstrap Layout Masonry class
30921 * Create a new Element
30922 * @param {Object} config The config object
30925 Roo.bootstrap.LayoutMasonry = function(config){
30927 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30931 Roo.bootstrap.LayoutMasonry.register(this);
30937 * Fire after layout the items
30938 * @param {Roo.bootstrap.LayoutMasonry} this
30939 * @param {Roo.EventObject} e
30946 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30949 * @cfg {Boolean} isLayoutInstant = no animation?
30951 isLayoutInstant : false, // needed?
30954 * @cfg {Number} boxWidth width of the columns
30959 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30964 * @cfg {Number} padWidth padding below box..
30969 * @cfg {Number} gutter gutter width..
30974 * @cfg {Number} maxCols maximum number of columns
30980 * @cfg {Boolean} isAutoInitial defalut true
30982 isAutoInitial : true,
30987 * @cfg {Boolean} isHorizontal defalut false
30989 isHorizontal : false,
30991 currentSize : null,
30997 bricks: null, //CompositeElement
31001 _isLayoutInited : false,
31003 // isAlternative : false, // only use for vertical layout...
31006 * @cfg {Number} alternativePadWidth padding below box..
31008 alternativePadWidth : 50,
31010 selectedBrick : [],
31012 getAutoCreate : function(){
31014 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31018 cls: 'blog-masonary-wrapper ' + this.cls,
31020 cls : 'mas-boxes masonary'
31027 getChildContainer: function( )
31029 if (this.boxesEl) {
31030 return this.boxesEl;
31033 this.boxesEl = this.el.select('.mas-boxes').first();
31035 return this.boxesEl;
31039 initEvents : function()
31043 if(this.isAutoInitial){
31044 Roo.log('hook children rendered');
31045 this.on('childrenrendered', function() {
31046 Roo.log('children rendered');
31052 initial : function()
31054 this.selectedBrick = [];
31056 this.currentSize = this.el.getBox(true);
31058 Roo.EventManager.onWindowResize(this.resize, this);
31060 if(!this.isAutoInitial){
31068 //this.layout.defer(500,this);
31072 resize : function()
31074 var cs = this.el.getBox(true);
31077 this.currentSize.width == cs.width &&
31078 this.currentSize.x == cs.x &&
31079 this.currentSize.height == cs.height &&
31080 this.currentSize.y == cs.y
31082 Roo.log("no change in with or X or Y");
31086 this.currentSize = cs;
31092 layout : function()
31094 this._resetLayout();
31096 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31098 this.layoutItems( isInstant );
31100 this._isLayoutInited = true;
31102 this.fireEvent('layout', this);
31106 _resetLayout : function()
31108 if(this.isHorizontal){
31109 this.horizontalMeasureColumns();
31113 this.verticalMeasureColumns();
31117 verticalMeasureColumns : function()
31119 this.getContainerWidth();
31121 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31122 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31126 var boxWidth = this.boxWidth + this.padWidth;
31128 if(this.containerWidth < this.boxWidth){
31129 boxWidth = this.containerWidth
31132 var containerWidth = this.containerWidth;
31134 var cols = Math.floor(containerWidth / boxWidth);
31136 this.cols = Math.max( cols, 1 );
31138 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31140 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31142 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31144 this.colWidth = boxWidth + avail - this.padWidth;
31146 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31147 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31150 horizontalMeasureColumns : function()
31152 this.getContainerWidth();
31154 var boxWidth = this.boxWidth;
31156 if(this.containerWidth < boxWidth){
31157 boxWidth = this.containerWidth;
31160 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31162 this.el.setHeight(boxWidth);
31166 getContainerWidth : function()
31168 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31171 layoutItems : function( isInstant )
31173 Roo.log(this.bricks);
31175 var items = Roo.apply([], this.bricks);
31177 if(this.isHorizontal){
31178 this._horizontalLayoutItems( items , isInstant );
31182 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31183 // this._verticalAlternativeLayoutItems( items , isInstant );
31187 this._verticalLayoutItems( items , isInstant );
31191 _verticalLayoutItems : function ( items , isInstant)
31193 if ( !items || !items.length ) {
31198 ['xs', 'xs', 'xs', 'tall'],
31199 ['xs', 'xs', 'tall'],
31200 ['xs', 'xs', 'sm'],
31201 ['xs', 'xs', 'xs'],
31207 ['sm', 'xs', 'xs'],
31211 ['tall', 'xs', 'xs', 'xs'],
31212 ['tall', 'xs', 'xs'],
31224 Roo.each(items, function(item, k){
31226 switch (item.size) {
31227 // these layouts take up a full box,
31238 boxes.push([item]);
31261 var filterPattern = function(box, length)
31269 var pattern = box.slice(0, length);
31273 Roo.each(pattern, function(i){
31274 format.push(i.size);
31277 Roo.each(standard, function(s){
31279 if(String(s) != String(format)){
31288 if(!match && length == 1){
31293 filterPattern(box, length - 1);
31297 queue.push(pattern);
31299 box = box.slice(length, box.length);
31301 filterPattern(box, 4);
31307 Roo.each(boxes, function(box, k){
31313 if(box.length == 1){
31318 filterPattern(box, 4);
31322 this._processVerticalLayoutQueue( queue, isInstant );
31326 // _verticalAlternativeLayoutItems : function( items , isInstant )
31328 // if ( !items || !items.length ) {
31332 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31336 _horizontalLayoutItems : function ( items , isInstant)
31338 if ( !items || !items.length || items.length < 3) {
31344 var eItems = items.slice(0, 3);
31346 items = items.slice(3, items.length);
31349 ['xs', 'xs', 'xs', 'wide'],
31350 ['xs', 'xs', 'wide'],
31351 ['xs', 'xs', 'sm'],
31352 ['xs', 'xs', 'xs'],
31358 ['sm', 'xs', 'xs'],
31362 ['wide', 'xs', 'xs', 'xs'],
31363 ['wide', 'xs', 'xs'],
31376 Roo.each(items, function(item, k){
31378 switch (item.size) {
31389 boxes.push([item]);
31413 var filterPattern = function(box, length)
31421 var pattern = box.slice(0, length);
31425 Roo.each(pattern, function(i){
31426 format.push(i.size);
31429 Roo.each(standard, function(s){
31431 if(String(s) != String(format)){
31440 if(!match && length == 1){
31445 filterPattern(box, length - 1);
31449 queue.push(pattern);
31451 box = box.slice(length, box.length);
31453 filterPattern(box, 4);
31459 Roo.each(boxes, function(box, k){
31465 if(box.length == 1){
31470 filterPattern(box, 4);
31477 var pos = this.el.getBox(true);
31481 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31483 var hit_end = false;
31485 Roo.each(queue, function(box){
31489 Roo.each(box, function(b){
31491 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31501 Roo.each(box, function(b){
31503 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31506 mx = Math.max(mx, b.x);
31510 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31514 Roo.each(box, function(b){
31516 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31530 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31533 /** Sets position of item in DOM
31534 * @param {Element} item
31535 * @param {Number} x - horizontal position
31536 * @param {Number} y - vertical position
31537 * @param {Boolean} isInstant - disables transitions
31539 _processVerticalLayoutQueue : function( queue, isInstant )
31541 var pos = this.el.getBox(true);
31546 for (var i = 0; i < this.cols; i++){
31550 Roo.each(queue, function(box, k){
31552 var col = k % this.cols;
31554 Roo.each(box, function(b,kk){
31556 b.el.position('absolute');
31558 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31559 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31561 if(b.size == 'md-left' || b.size == 'md-right'){
31562 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31563 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31566 b.el.setWidth(width);
31567 b.el.setHeight(height);
31569 b.el.select('iframe',true).setSize(width,height);
31573 for (var i = 0; i < this.cols; i++){
31575 if(maxY[i] < maxY[col]){
31580 col = Math.min(col, i);
31584 x = pos.x + col * (this.colWidth + this.padWidth);
31588 var positions = [];
31590 switch (box.length){
31592 positions = this.getVerticalOneBoxColPositions(x, y, box);
31595 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31598 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31601 positions = this.getVerticalFourBoxColPositions(x, y, box);
31607 Roo.each(box, function(b,kk){
31609 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31611 var sz = b.el.getSize();
31613 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31621 for (var i = 0; i < this.cols; i++){
31622 mY = Math.max(mY, maxY[i]);
31625 this.el.setHeight(mY - pos.y);
31629 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31631 // var pos = this.el.getBox(true);
31634 // var maxX = pos.right;
31636 // var maxHeight = 0;
31638 // Roo.each(items, function(item, k){
31642 // item.el.position('absolute');
31644 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31646 // item.el.setWidth(width);
31648 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31650 // item.el.setHeight(height);
31653 // item.el.setXY([x, y], isInstant ? false : true);
31655 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31658 // y = y + height + this.alternativePadWidth;
31660 // maxHeight = maxHeight + height + this.alternativePadWidth;
31664 // this.el.setHeight(maxHeight);
31668 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31670 var pos = this.el.getBox(true);
31675 var maxX = pos.right;
31677 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31679 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31681 Roo.each(queue, function(box, k){
31683 Roo.each(box, function(b, kk){
31685 b.el.position('absolute');
31687 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31688 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31690 if(b.size == 'md-left' || b.size == 'md-right'){
31691 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31692 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31695 b.el.setWidth(width);
31696 b.el.setHeight(height);
31704 var positions = [];
31706 switch (box.length){
31708 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31711 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31714 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31717 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31723 Roo.each(box, function(b,kk){
31725 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31727 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31735 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31737 Roo.each(eItems, function(b,k){
31739 b.size = (k == 0) ? 'sm' : 'xs';
31740 b.x = (k == 0) ? 2 : 1;
31741 b.y = (k == 0) ? 2 : 1;
31743 b.el.position('absolute');
31745 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31747 b.el.setWidth(width);
31749 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31751 b.el.setHeight(height);
31755 var positions = [];
31758 x : maxX - this.unitWidth * 2 - this.gutter,
31763 x : maxX - this.unitWidth,
31764 y : minY + (this.unitWidth + this.gutter) * 2
31768 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31772 Roo.each(eItems, function(b,k){
31774 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31780 getVerticalOneBoxColPositions : function(x, y, box)
31784 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31786 if(box[0].size == 'md-left'){
31790 if(box[0].size == 'md-right'){
31795 x : x + (this.unitWidth + this.gutter) * rand,
31802 getVerticalTwoBoxColPositions : function(x, y, box)
31806 if(box[0].size == 'xs'){
31810 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31814 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31828 x : x + (this.unitWidth + this.gutter) * 2,
31829 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31836 getVerticalThreeBoxColPositions : function(x, y, box)
31840 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31848 x : x + (this.unitWidth + this.gutter) * 1,
31853 x : x + (this.unitWidth + this.gutter) * 2,
31861 if(box[0].size == 'xs' && box[1].size == 'xs'){
31870 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31874 x : x + (this.unitWidth + this.gutter) * 1,
31888 x : x + (this.unitWidth + this.gutter) * 2,
31893 x : x + (this.unitWidth + this.gutter) * 2,
31894 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31901 getVerticalFourBoxColPositions : function(x, y, box)
31905 if(box[0].size == 'xs'){
31914 y : y + (this.unitHeight + this.gutter) * 1
31919 y : y + (this.unitHeight + this.gutter) * 2
31923 x : x + (this.unitWidth + this.gutter) * 1,
31937 x : x + (this.unitWidth + this.gutter) * 2,
31942 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31943 y : y + (this.unitHeight + this.gutter) * 1
31947 x : x + (this.unitWidth + this.gutter) * 2,
31948 y : y + (this.unitWidth + this.gutter) * 2
31955 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31959 if(box[0].size == 'md-left'){
31961 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31968 if(box[0].size == 'md-right'){
31970 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31971 y : minY + (this.unitWidth + this.gutter) * 1
31977 var rand = Math.floor(Math.random() * (4 - box[0].y));
31980 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31981 y : minY + (this.unitWidth + this.gutter) * rand
31988 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31992 if(box[0].size == 'xs'){
31995 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32009 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32014 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32015 y : minY + (this.unitWidth + this.gutter) * 2
32022 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32026 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32029 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32034 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32035 y : minY + (this.unitWidth + this.gutter) * 1
32039 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32040 y : minY + (this.unitWidth + this.gutter) * 2
32047 if(box[0].size == 'xs' && box[1].size == 'xs'){
32050 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32055 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32060 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32061 y : minY + (this.unitWidth + this.gutter) * 1
32069 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32074 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32075 y : minY + (this.unitWidth + this.gutter) * 2
32079 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32080 y : minY + (this.unitWidth + this.gutter) * 2
32087 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32091 if(box[0].size == 'xs'){
32094 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32099 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32104 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),
32109 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32110 y : minY + (this.unitWidth + this.gutter) * 1
32118 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32123 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32124 y : minY + (this.unitWidth + this.gutter) * 2
32128 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32129 y : minY + (this.unitWidth + this.gutter) * 2
32133 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),
32134 y : minY + (this.unitWidth + this.gutter) * 2
32142 * remove a Masonry Brick
32143 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32145 removeBrick : function(brick_id)
32151 for (var i = 0; i<this.bricks.length; i++) {
32152 if (this.bricks[i].id == brick_id) {
32153 this.bricks.splice(i,1);
32154 this.el.dom.removeChild(Roo.get(brick_id).dom);
32161 * adds a Masonry Brick
32162 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32164 addBrick : function(cfg)
32166 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32167 //this.register(cn);
32168 cn.parentId = this.id;
32169 cn.render(this.el);
32174 * register a Masonry Brick
32175 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32178 register : function(brick)
32180 this.bricks.push(brick);
32181 brick.masonryId = this.id;
32185 * clear all the Masonry Brick
32187 clearAll : function()
32190 //this.getChildContainer().dom.innerHTML = "";
32191 this.el.dom.innerHTML = '';
32194 getSelected : function()
32196 if (!this.selectedBrick) {
32200 return this.selectedBrick;
32204 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32208 * register a Masonry Layout
32209 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32212 register : function(layout)
32214 this.groups[layout.id] = layout;
32217 * fetch a Masonry Layout based on the masonry layout ID
32218 * @param {string} the masonry layout to add
32219 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32222 get: function(layout_id) {
32223 if (typeof(this.groups[layout_id]) == 'undefined') {
32226 return this.groups[layout_id] ;
32238 * http://masonry.desandro.com
32240 * The idea is to render all the bricks based on vertical width...
32242 * The original code extends 'outlayer' - we might need to use that....
32248 * @class Roo.bootstrap.LayoutMasonryAuto
32249 * @extends Roo.bootstrap.Component
32250 * Bootstrap Layout Masonry class
32253 * Create a new Element
32254 * @param {Object} config The config object
32257 Roo.bootstrap.LayoutMasonryAuto = function(config){
32258 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32261 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32264 * @cfg {Boolean} isFitWidth - resize the width..
32266 isFitWidth : false, // options..
32268 * @cfg {Boolean} isOriginLeft = left align?
32270 isOriginLeft : true,
32272 * @cfg {Boolean} isOriginTop = top align?
32274 isOriginTop : false,
32276 * @cfg {Boolean} isLayoutInstant = no animation?
32278 isLayoutInstant : false, // needed?
32280 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32282 isResizingContainer : true,
32284 * @cfg {Number} columnWidth width of the columns
32290 * @cfg {Number} maxCols maximum number of columns
32295 * @cfg {Number} padHeight padding below box..
32301 * @cfg {Boolean} isAutoInitial defalut true
32304 isAutoInitial : true,
32310 initialColumnWidth : 0,
32311 currentSize : null,
32313 colYs : null, // array.
32320 bricks: null, //CompositeElement
32321 cols : 0, // array?
32322 // element : null, // wrapped now this.el
32323 _isLayoutInited : null,
32326 getAutoCreate : function(){
32330 cls: 'blog-masonary-wrapper ' + this.cls,
32332 cls : 'mas-boxes masonary'
32339 getChildContainer: function( )
32341 if (this.boxesEl) {
32342 return this.boxesEl;
32345 this.boxesEl = this.el.select('.mas-boxes').first();
32347 return this.boxesEl;
32351 initEvents : function()
32355 if(this.isAutoInitial){
32356 Roo.log('hook children rendered');
32357 this.on('childrenrendered', function() {
32358 Roo.log('children rendered');
32365 initial : function()
32367 this.reloadItems();
32369 this.currentSize = this.el.getBox(true);
32371 /// was window resize... - let's see if this works..
32372 Roo.EventManager.onWindowResize(this.resize, this);
32374 if(!this.isAutoInitial){
32379 this.layout.defer(500,this);
32382 reloadItems: function()
32384 this.bricks = this.el.select('.masonry-brick', true);
32386 this.bricks.each(function(b) {
32387 //Roo.log(b.getSize());
32388 if (!b.attr('originalwidth')) {
32389 b.attr('originalwidth', b.getSize().width);
32394 Roo.log(this.bricks.elements.length);
32397 resize : function()
32400 var cs = this.el.getBox(true);
32402 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32403 Roo.log("no change in with or X");
32406 this.currentSize = cs;
32410 layout : function()
32413 this._resetLayout();
32414 //this._manageStamps();
32416 // don't animate first layout
32417 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32418 this.layoutItems( isInstant );
32420 // flag for initalized
32421 this._isLayoutInited = true;
32424 layoutItems : function( isInstant )
32426 //var items = this._getItemsForLayout( this.items );
32427 // original code supports filtering layout items.. we just ignore it..
32429 this._layoutItems( this.bricks , isInstant );
32431 this._postLayout();
32433 _layoutItems : function ( items , isInstant)
32435 //this.fireEvent( 'layout', this, items );
32438 if ( !items || !items.elements.length ) {
32439 // no items, emit event with empty array
32444 items.each(function(item) {
32445 Roo.log("layout item");
32447 // get x/y object from method
32448 var position = this._getItemLayoutPosition( item );
32450 position.item = item;
32451 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32452 queue.push( position );
32455 this._processLayoutQueue( queue );
32457 /** Sets position of item in DOM
32458 * @param {Element} item
32459 * @param {Number} x - horizontal position
32460 * @param {Number} y - vertical position
32461 * @param {Boolean} isInstant - disables transitions
32463 _processLayoutQueue : function( queue )
32465 for ( var i=0, len = queue.length; i < len; i++ ) {
32466 var obj = queue[i];
32467 obj.item.position('absolute');
32468 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32474 * Any logic you want to do after each layout,
32475 * i.e. size the container
32477 _postLayout : function()
32479 this.resizeContainer();
32482 resizeContainer : function()
32484 if ( !this.isResizingContainer ) {
32487 var size = this._getContainerSize();
32489 this.el.setSize(size.width,size.height);
32490 this.boxesEl.setSize(size.width,size.height);
32496 _resetLayout : function()
32498 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32499 this.colWidth = this.el.getWidth();
32500 //this.gutter = this.el.getWidth();
32502 this.measureColumns();
32508 this.colYs.push( 0 );
32514 measureColumns : function()
32516 this.getContainerWidth();
32517 // if columnWidth is 0, default to outerWidth of first item
32518 if ( !this.columnWidth ) {
32519 var firstItem = this.bricks.first();
32520 Roo.log(firstItem);
32521 this.columnWidth = this.containerWidth;
32522 if (firstItem && firstItem.attr('originalwidth') ) {
32523 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32525 // columnWidth fall back to item of first element
32526 Roo.log("set column width?");
32527 this.initialColumnWidth = this.columnWidth ;
32529 // if first elem has no width, default to size of container
32534 if (this.initialColumnWidth) {
32535 this.columnWidth = this.initialColumnWidth;
32540 // column width is fixed at the top - however if container width get's smaller we should
32543 // this bit calcs how man columns..
32545 var columnWidth = this.columnWidth += this.gutter;
32547 // calculate columns
32548 var containerWidth = this.containerWidth + this.gutter;
32550 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32551 // fix rounding errors, typically with gutters
32552 var excess = columnWidth - containerWidth % columnWidth;
32555 // if overshoot is less than a pixel, round up, otherwise floor it
32556 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32557 cols = Math[ mathMethod ]( cols );
32558 this.cols = Math.max( cols, 1 );
32559 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32561 // padding positioning..
32562 var totalColWidth = this.cols * this.columnWidth;
32563 var padavail = this.containerWidth - totalColWidth;
32564 // so for 2 columns - we need 3 'pads'
32566 var padNeeded = (1+this.cols) * this.padWidth;
32568 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32570 this.columnWidth += padExtra
32571 //this.padWidth = Math.floor(padavail / ( this.cols));
32573 // adjust colum width so that padding is fixed??
32575 // we have 3 columns ... total = width * 3
32576 // we have X left over... that should be used by
32578 //if (this.expandC) {
32586 getContainerWidth : function()
32588 /* // container is parent if fit width
32589 var container = this.isFitWidth ? this.element.parentNode : this.element;
32590 // check that this.size and size are there
32591 // IE8 triggers resize on body size change, so they might not be
32593 var size = getSize( container ); //FIXME
32594 this.containerWidth = size && size.innerWidth; //FIXME
32597 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32601 _getItemLayoutPosition : function( item ) // what is item?
32603 // we resize the item to our columnWidth..
32605 item.setWidth(this.columnWidth);
32606 item.autoBoxAdjust = false;
32608 var sz = item.getSize();
32610 // how many columns does this brick span
32611 var remainder = this.containerWidth % this.columnWidth;
32613 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32614 // round if off by 1 pixel, otherwise use ceil
32615 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32616 colSpan = Math.min( colSpan, this.cols );
32618 // normally this should be '1' as we dont' currently allow multi width columns..
32620 var colGroup = this._getColGroup( colSpan );
32621 // get the minimum Y value from the columns
32622 var minimumY = Math.min.apply( Math, colGroup );
32623 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32625 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32627 // position the brick
32629 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32630 y: this.currentSize.y + minimumY + this.padHeight
32634 // apply setHeight to necessary columns
32635 var setHeight = minimumY + sz.height + this.padHeight;
32636 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32638 var setSpan = this.cols + 1 - colGroup.length;
32639 for ( var i = 0; i < setSpan; i++ ) {
32640 this.colYs[ shortColIndex + i ] = setHeight ;
32647 * @param {Number} colSpan - number of columns the element spans
32648 * @returns {Array} colGroup
32650 _getColGroup : function( colSpan )
32652 if ( colSpan < 2 ) {
32653 // if brick spans only one column, use all the column Ys
32658 // how many different places could this brick fit horizontally
32659 var groupCount = this.cols + 1 - colSpan;
32660 // for each group potential horizontal position
32661 for ( var i = 0; i < groupCount; i++ ) {
32662 // make an array of colY values for that one group
32663 var groupColYs = this.colYs.slice( i, i + colSpan );
32664 // and get the max value of the array
32665 colGroup[i] = Math.max.apply( Math, groupColYs );
32670 _manageStamp : function( stamp )
32672 var stampSize = stamp.getSize();
32673 var offset = stamp.getBox();
32674 // get the columns that this stamp affects
32675 var firstX = this.isOriginLeft ? offset.x : offset.right;
32676 var lastX = firstX + stampSize.width;
32677 var firstCol = Math.floor( firstX / this.columnWidth );
32678 firstCol = Math.max( 0, firstCol );
32680 var lastCol = Math.floor( lastX / this.columnWidth );
32681 // lastCol should not go over if multiple of columnWidth #425
32682 lastCol -= lastX % this.columnWidth ? 0 : 1;
32683 lastCol = Math.min( this.cols - 1, lastCol );
32685 // set colYs to bottom of the stamp
32686 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32689 for ( var i = firstCol; i <= lastCol; i++ ) {
32690 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32695 _getContainerSize : function()
32697 this.maxY = Math.max.apply( Math, this.colYs );
32702 if ( this.isFitWidth ) {
32703 size.width = this._getContainerFitWidth();
32709 _getContainerFitWidth : function()
32711 var unusedCols = 0;
32712 // count unused columns
32715 if ( this.colYs[i] !== 0 ) {
32720 // fit container to columns that have been used
32721 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32724 needsResizeLayout : function()
32726 var previousWidth = this.containerWidth;
32727 this.getContainerWidth();
32728 return previousWidth !== this.containerWidth;
32743 * @class Roo.bootstrap.MasonryBrick
32744 * @extends Roo.bootstrap.Component
32745 * Bootstrap MasonryBrick class
32748 * Create a new MasonryBrick
32749 * @param {Object} config The config object
32752 Roo.bootstrap.MasonryBrick = function(config){
32754 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32756 Roo.bootstrap.MasonryBrick.register(this);
32762 * When a MasonryBrick is clcik
32763 * @param {Roo.bootstrap.MasonryBrick} this
32764 * @param {Roo.EventObject} e
32770 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32773 * @cfg {String} title
32777 * @cfg {String} html
32781 * @cfg {String} bgimage
32785 * @cfg {String} videourl
32789 * @cfg {String} cls
32793 * @cfg {String} href
32797 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32802 * @cfg {String} placetitle (center|bottom)
32807 * @cfg {Boolean} isFitContainer defalut true
32809 isFitContainer : true,
32812 * @cfg {Boolean} preventDefault defalut false
32814 preventDefault : false,
32817 * @cfg {Boolean} inverse defalut false
32819 maskInverse : false,
32821 getAutoCreate : function()
32823 if(!this.isFitContainer){
32824 return this.getSplitAutoCreate();
32827 var cls = 'masonry-brick masonry-brick-full';
32829 if(this.href.length){
32830 cls += ' masonry-brick-link';
32833 if(this.bgimage.length){
32834 cls += ' masonry-brick-image';
32837 if(this.maskInverse){
32838 cls += ' mask-inverse';
32841 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32842 cls += ' enable-mask';
32846 cls += ' masonry-' + this.size + '-brick';
32849 if(this.placetitle.length){
32851 switch (this.placetitle) {
32853 cls += ' masonry-center-title';
32856 cls += ' masonry-bottom-title';
32863 if(!this.html.length && !this.bgimage.length){
32864 cls += ' masonry-center-title';
32867 if(!this.html.length && this.bgimage.length){
32868 cls += ' masonry-bottom-title';
32873 cls += ' ' + this.cls;
32877 tag: (this.href.length) ? 'a' : 'div',
32882 cls: 'masonry-brick-mask'
32886 cls: 'masonry-brick-paragraph',
32892 if(this.href.length){
32893 cfg.href = this.href;
32896 var cn = cfg.cn[1].cn;
32898 if(this.title.length){
32901 cls: 'masonry-brick-title',
32906 if(this.html.length){
32909 cls: 'masonry-brick-text',
32914 if (!this.title.length && !this.html.length) {
32915 cfg.cn[1].cls += ' hide';
32918 if(this.bgimage.length){
32921 cls: 'masonry-brick-image-view',
32926 if(this.videourl.length){
32927 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32928 // youtube support only?
32931 cls: 'masonry-brick-image-view',
32934 allowfullscreen : true
32942 getSplitAutoCreate : function()
32944 var cls = 'masonry-brick masonry-brick-split';
32946 if(this.href.length){
32947 cls += ' masonry-brick-link';
32950 if(this.bgimage.length){
32951 cls += ' masonry-brick-image';
32955 cls += ' masonry-' + this.size + '-brick';
32958 switch (this.placetitle) {
32960 cls += ' masonry-center-title';
32963 cls += ' masonry-bottom-title';
32966 if(!this.bgimage.length){
32967 cls += ' masonry-center-title';
32970 if(this.bgimage.length){
32971 cls += ' masonry-bottom-title';
32977 cls += ' ' + this.cls;
32981 tag: (this.href.length) ? 'a' : 'div',
32986 cls: 'masonry-brick-split-head',
32990 cls: 'masonry-brick-paragraph',
32997 cls: 'masonry-brick-split-body',
33003 if(this.href.length){
33004 cfg.href = this.href;
33007 if(this.title.length){
33008 cfg.cn[0].cn[0].cn.push({
33010 cls: 'masonry-brick-title',
33015 if(this.html.length){
33016 cfg.cn[1].cn.push({
33018 cls: 'masonry-brick-text',
33023 if(this.bgimage.length){
33024 cfg.cn[0].cn.push({
33026 cls: 'masonry-brick-image-view',
33031 if(this.videourl.length){
33032 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33033 // youtube support only?
33034 cfg.cn[0].cn.cn.push({
33036 cls: 'masonry-brick-image-view',
33039 allowfullscreen : true
33046 initEvents: function()
33048 switch (this.size) {
33081 this.el.on('touchstart', this.onTouchStart, this);
33082 this.el.on('touchmove', this.onTouchMove, this);
33083 this.el.on('touchend', this.onTouchEnd, this);
33084 this.el.on('contextmenu', this.onContextMenu, this);
33086 this.el.on('mouseenter' ,this.enter, this);
33087 this.el.on('mouseleave', this.leave, this);
33088 this.el.on('click', this.onClick, this);
33091 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33092 this.parent().bricks.push(this);
33097 onClick: function(e, el)
33099 var time = this.endTimer - this.startTimer;
33100 // Roo.log(e.preventDefault());
33103 e.preventDefault();
33108 if(!this.preventDefault){
33112 e.preventDefault();
33114 if (this.activeClass != '') {
33115 this.selectBrick();
33118 this.fireEvent('click', this, e);
33121 enter: function(e, el)
33123 e.preventDefault();
33125 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33129 if(this.bgimage.length && this.html.length){
33130 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33134 leave: function(e, el)
33136 e.preventDefault();
33138 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33142 if(this.bgimage.length && this.html.length){
33143 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33147 onTouchStart: function(e, el)
33149 // e.preventDefault();
33151 this.touchmoved = false;
33153 if(!this.isFitContainer){
33157 if(!this.bgimage.length || !this.html.length){
33161 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33163 this.timer = new Date().getTime();
33167 onTouchMove: function(e, el)
33169 this.touchmoved = true;
33172 onContextMenu : function(e,el)
33174 e.preventDefault();
33175 e.stopPropagation();
33179 onTouchEnd: function(e, el)
33181 // e.preventDefault();
33183 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33190 if(!this.bgimage.length || !this.html.length){
33192 if(this.href.length){
33193 window.location.href = this.href;
33199 if(!this.isFitContainer){
33203 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33205 window.location.href = this.href;
33208 //selection on single brick only
33209 selectBrick : function() {
33211 if (!this.parentId) {
33215 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33216 var index = m.selectedBrick.indexOf(this.id);
33219 m.selectedBrick.splice(index,1);
33220 this.el.removeClass(this.activeClass);
33224 for(var i = 0; i < m.selectedBrick.length; i++) {
33225 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33226 b.el.removeClass(b.activeClass);
33229 m.selectedBrick = [];
33231 m.selectedBrick.push(this.id);
33232 this.el.addClass(this.activeClass);
33236 isSelected : function(){
33237 return this.el.hasClass(this.activeClass);
33242 Roo.apply(Roo.bootstrap.MasonryBrick, {
33245 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33247 * register a Masonry Brick
33248 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33251 register : function(brick)
33253 //this.groups[brick.id] = brick;
33254 this.groups.add(brick.id, brick);
33257 * fetch a masonry brick based on the masonry brick ID
33258 * @param {string} the masonry brick to add
33259 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33262 get: function(brick_id)
33264 // if (typeof(this.groups[brick_id]) == 'undefined') {
33267 // return this.groups[brick_id] ;
33269 if(this.groups.key(brick_id)) {
33270 return this.groups.key(brick_id);
33288 * @class Roo.bootstrap.Brick
33289 * @extends Roo.bootstrap.Component
33290 * Bootstrap Brick class
33293 * Create a new Brick
33294 * @param {Object} config The config object
33297 Roo.bootstrap.Brick = function(config){
33298 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33304 * When a Brick is click
33305 * @param {Roo.bootstrap.Brick} this
33306 * @param {Roo.EventObject} e
33312 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33315 * @cfg {String} title
33319 * @cfg {String} html
33323 * @cfg {String} bgimage
33327 * @cfg {String} cls
33331 * @cfg {String} href
33335 * @cfg {String} video
33339 * @cfg {Boolean} square
33343 getAutoCreate : function()
33345 var cls = 'roo-brick';
33347 if(this.href.length){
33348 cls += ' roo-brick-link';
33351 if(this.bgimage.length){
33352 cls += ' roo-brick-image';
33355 if(!this.html.length && !this.bgimage.length){
33356 cls += ' roo-brick-center-title';
33359 if(!this.html.length && this.bgimage.length){
33360 cls += ' roo-brick-bottom-title';
33364 cls += ' ' + this.cls;
33368 tag: (this.href.length) ? 'a' : 'div',
33373 cls: 'roo-brick-paragraph',
33379 if(this.href.length){
33380 cfg.href = this.href;
33383 var cn = cfg.cn[0].cn;
33385 if(this.title.length){
33388 cls: 'roo-brick-title',
33393 if(this.html.length){
33396 cls: 'roo-brick-text',
33403 if(this.bgimage.length){
33406 cls: 'roo-brick-image-view',
33414 initEvents: function()
33416 if(this.title.length || this.html.length){
33417 this.el.on('mouseenter' ,this.enter, this);
33418 this.el.on('mouseleave', this.leave, this);
33421 Roo.EventManager.onWindowResize(this.resize, this);
33423 if(this.bgimage.length){
33424 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33425 this.imageEl.on('load', this.onImageLoad, this);
33432 onImageLoad : function()
33437 resize : function()
33439 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33441 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33443 if(this.bgimage.length){
33444 var image = this.el.select('.roo-brick-image-view', true).first();
33446 image.setWidth(paragraph.getWidth());
33449 image.setHeight(paragraph.getWidth());
33452 this.el.setHeight(image.getHeight());
33453 paragraph.setHeight(image.getHeight());
33459 enter: function(e, el)
33461 e.preventDefault();
33463 if(this.bgimage.length){
33464 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33465 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33469 leave: function(e, el)
33471 e.preventDefault();
33473 if(this.bgimage.length){
33474 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33475 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33490 * @class Roo.bootstrap.NumberField
33491 * @extends Roo.bootstrap.Input
33492 * Bootstrap NumberField class
33498 * Create a new NumberField
33499 * @param {Object} config The config object
33502 Roo.bootstrap.NumberField = function(config){
33503 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33506 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33509 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33511 allowDecimals : true,
33513 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33515 decimalSeparator : ".",
33517 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33519 decimalPrecision : 2,
33521 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33523 allowNegative : true,
33526 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33530 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33532 minValue : Number.NEGATIVE_INFINITY,
33534 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33536 maxValue : Number.MAX_VALUE,
33538 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33540 minText : "The minimum value for this field is {0}",
33542 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33544 maxText : "The maximum value for this field is {0}",
33546 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33547 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33549 nanText : "{0} is not a valid number",
33551 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33553 thousandsDelimiter : false,
33555 * @cfg {String} valueAlign alignment of value
33557 valueAlign : "left",
33559 getAutoCreate : function()
33561 var hiddenInput = {
33565 cls: 'hidden-number-input'
33569 hiddenInput.name = this.name;
33574 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33576 this.name = hiddenInput.name;
33578 if(cfg.cn.length > 0) {
33579 cfg.cn.push(hiddenInput);
33586 initEvents : function()
33588 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33590 var allowed = "0123456789";
33592 if(this.allowDecimals){
33593 allowed += this.decimalSeparator;
33596 if(this.allowNegative){
33600 if(this.thousandsDelimiter) {
33604 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33606 var keyPress = function(e){
33608 var k = e.getKey();
33610 var c = e.getCharCode();
33613 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33614 allowed.indexOf(String.fromCharCode(c)) === -1
33620 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33624 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33629 this.el.on("keypress", keyPress, this);
33632 validateValue : function(value)
33635 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33639 var num = this.parseValue(value);
33642 this.markInvalid(String.format(this.nanText, value));
33646 if(num < this.minValue){
33647 this.markInvalid(String.format(this.minText, this.minValue));
33651 if(num > this.maxValue){
33652 this.markInvalid(String.format(this.maxText, this.maxValue));
33659 getValue : function()
33661 var v = this.hiddenEl().getValue();
33663 return this.fixPrecision(this.parseValue(v));
33666 parseValue : function(value)
33668 if(this.thousandsDelimiter) {
33670 r = new RegExp(",", "g");
33671 value = value.replace(r, "");
33674 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33675 return isNaN(value) ? '' : value;
33678 fixPrecision : function(value)
33680 if(this.thousandsDelimiter) {
33682 r = new RegExp(",", "g");
33683 value = value.replace(r, "");
33686 var nan = isNaN(value);
33688 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33689 return nan ? '' : value;
33691 return parseFloat(value).toFixed(this.decimalPrecision);
33694 setValue : function(v)
33696 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33702 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33704 this.inputEl().dom.value = (v == '') ? '' :
33705 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33707 if(!this.allowZero && v === '0') {
33708 this.hiddenEl().dom.value = '';
33709 this.inputEl().dom.value = '';
33716 decimalPrecisionFcn : function(v)
33718 return Math.floor(v);
33721 beforeBlur : function()
33723 var v = this.parseValue(this.getRawValue());
33725 if(v || v === 0 || v === ''){
33730 hiddenEl : function()
33732 return this.el.select('input.hidden-number-input',true).first();
33744 * @class Roo.bootstrap.DocumentSlider
33745 * @extends Roo.bootstrap.Component
33746 * Bootstrap DocumentSlider class
33749 * Create a new DocumentViewer
33750 * @param {Object} config The config object
33753 Roo.bootstrap.DocumentSlider = function(config){
33754 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33761 * Fire after initEvent
33762 * @param {Roo.bootstrap.DocumentSlider} this
33767 * Fire after update
33768 * @param {Roo.bootstrap.DocumentSlider} this
33774 * @param {Roo.bootstrap.DocumentSlider} this
33780 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33786 getAutoCreate : function()
33790 cls : 'roo-document-slider',
33794 cls : 'roo-document-slider-header',
33798 cls : 'roo-document-slider-header-title'
33804 cls : 'roo-document-slider-body',
33808 cls : 'roo-document-slider-prev',
33812 cls : 'fa fa-chevron-left'
33818 cls : 'roo-document-slider-thumb',
33822 cls : 'roo-document-slider-image'
33828 cls : 'roo-document-slider-next',
33832 cls : 'fa fa-chevron-right'
33844 initEvents : function()
33846 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33847 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33849 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33850 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33852 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33853 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33855 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33856 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33858 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33859 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33861 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33862 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33864 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33865 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33867 this.thumbEl.on('click', this.onClick, this);
33869 this.prevIndicator.on('click', this.prev, this);
33871 this.nextIndicator.on('click', this.next, this);
33875 initial : function()
33877 if(this.files.length){
33878 this.indicator = 1;
33882 this.fireEvent('initial', this);
33885 update : function()
33887 this.imageEl.attr('src', this.files[this.indicator - 1]);
33889 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33891 this.prevIndicator.show();
33893 if(this.indicator == 1){
33894 this.prevIndicator.hide();
33897 this.nextIndicator.show();
33899 if(this.indicator == this.files.length){
33900 this.nextIndicator.hide();
33903 this.thumbEl.scrollTo('top');
33905 this.fireEvent('update', this);
33908 onClick : function(e)
33910 e.preventDefault();
33912 this.fireEvent('click', this);
33917 e.preventDefault();
33919 this.indicator = Math.max(1, this.indicator - 1);
33926 e.preventDefault();
33928 this.indicator = Math.min(this.files.length, this.indicator + 1);
33942 * @class Roo.bootstrap.RadioSet
33943 * @extends Roo.bootstrap.Input
33944 * Bootstrap RadioSet class
33945 * @cfg {String} indicatorpos (left|right) default left
33946 * @cfg {Boolean} inline (true|false) inline the element (default true)
33947 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33949 * Create a new RadioSet
33950 * @param {Object} config The config object
33953 Roo.bootstrap.RadioSet = function(config){
33955 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33959 Roo.bootstrap.RadioSet.register(this);
33964 * Fires when the element is checked or unchecked.
33965 * @param {Roo.bootstrap.RadioSet} this This radio
33966 * @param {Roo.bootstrap.Radio} item The checked item
33971 * Fires when the element is click.
33972 * @param {Roo.bootstrap.RadioSet} this This radio set
33973 * @param {Roo.bootstrap.Radio} item The checked item
33974 * @param {Roo.EventObject} e The event object
33981 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33989 indicatorpos : 'left',
33991 getAutoCreate : function()
33995 cls : 'roo-radio-set-label',
33999 html : this.fieldLabel
34004 if(this.indicatorpos == 'left'){
34007 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34008 tooltip : 'This field is required'
34013 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34014 tooltip : 'This field is required'
34020 cls : 'roo-radio-set-items'
34023 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34025 if (align === 'left' && this.fieldLabel.length) {
34028 cls : "roo-radio-set-right",
34034 if(this.labelWidth > 12){
34035 label.style = "width: " + this.labelWidth + 'px';
34038 if(this.labelWidth < 13 && this.labelmd == 0){
34039 this.labelmd = this.labelWidth;
34042 if(this.labellg > 0){
34043 label.cls += ' col-lg-' + this.labellg;
34044 items.cls += ' col-lg-' + (12 - this.labellg);
34047 if(this.labelmd > 0){
34048 label.cls += ' col-md-' + this.labelmd;
34049 items.cls += ' col-md-' + (12 - this.labelmd);
34052 if(this.labelsm > 0){
34053 label.cls += ' col-sm-' + this.labelsm;
34054 items.cls += ' col-sm-' + (12 - this.labelsm);
34057 if(this.labelxs > 0){
34058 label.cls += ' col-xs-' + this.labelxs;
34059 items.cls += ' col-xs-' + (12 - this.labelxs);
34065 cls : 'roo-radio-set',
34069 cls : 'roo-radio-set-input',
34072 value : this.value ? this.value : ''
34079 if(this.weight.length){
34080 cfg.cls += ' roo-radio-' + this.weight;
34084 cfg.cls += ' roo-radio-set-inline';
34088 ['xs','sm','md','lg'].map(function(size){
34089 if (settings[size]) {
34090 cfg.cls += ' col-' + size + '-' + settings[size];
34098 initEvents : function()
34100 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34101 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34103 if(!this.fieldLabel.length){
34104 this.labelEl.hide();
34107 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34108 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34110 this.indicator = this.indicatorEl();
34112 if(this.indicator){
34113 this.indicator.addClass('invisible');
34116 this.originalValue = this.getValue();
34120 inputEl: function ()
34122 return this.el.select('.roo-radio-set-input', true).first();
34125 getChildContainer : function()
34127 return this.itemsEl;
34130 register : function(item)
34132 this.radioes.push(item);
34136 validate : function()
34138 if(this.getVisibilityEl().hasClass('hidden')){
34144 Roo.each(this.radioes, function(i){
34153 if(this.allowBlank) {
34157 if(this.disabled || valid){
34162 this.markInvalid();
34167 markValid : function()
34169 if(this.labelEl.isVisible(true)){
34170 this.indicatorEl().removeClass('visible');
34171 this.indicatorEl().addClass('invisible');
34174 this.el.removeClass([this.invalidClass, this.validClass]);
34175 this.el.addClass(this.validClass);
34177 this.fireEvent('valid', this);
34180 markInvalid : function(msg)
34182 if(this.allowBlank || this.disabled){
34186 if(this.labelEl.isVisible(true)){
34187 this.indicatorEl().removeClass('invisible');
34188 this.indicatorEl().addClass('visible');
34191 this.el.removeClass([this.invalidClass, this.validClass]);
34192 this.el.addClass(this.invalidClass);
34194 this.fireEvent('invalid', this, msg);
34198 setValue : function(v, suppressEvent)
34200 if(this.value === v){
34207 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34210 Roo.each(this.radioes, function(i){
34212 i.el.removeClass('checked');
34215 Roo.each(this.radioes, function(i){
34217 if(i.value === v || i.value.toString() === v.toString()){
34219 i.el.addClass('checked');
34221 if(suppressEvent !== true){
34222 this.fireEvent('check', this, i);
34233 clearInvalid : function(){
34235 if(!this.el || this.preventMark){
34239 this.el.removeClass([this.invalidClass]);
34241 this.fireEvent('valid', this);
34246 Roo.apply(Roo.bootstrap.RadioSet, {
34250 register : function(set)
34252 this.groups[set.name] = set;
34255 get: function(name)
34257 if (typeof(this.groups[name]) == 'undefined') {
34261 return this.groups[name] ;
34267 * Ext JS Library 1.1.1
34268 * Copyright(c) 2006-2007, Ext JS, LLC.
34270 * Originally Released Under LGPL - original licence link has changed is not relivant.
34273 * <script type="text/javascript">
34278 * @class Roo.bootstrap.SplitBar
34279 * @extends Roo.util.Observable
34280 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34284 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34285 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34286 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34287 split.minSize = 100;
34288 split.maxSize = 600;
34289 split.animate = true;
34290 split.on('moved', splitterMoved);
34293 * Create a new SplitBar
34294 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34295 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34296 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34297 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34298 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34299 position of the SplitBar).
34301 Roo.bootstrap.SplitBar = function(cfg){
34306 // dragElement : elm
34307 // resizingElement: el,
34309 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34310 // placement : Roo.bootstrap.SplitBar.LEFT ,
34311 // existingProxy ???
34314 this.el = Roo.get(cfg.dragElement, true);
34315 this.el.dom.unselectable = "on";
34317 this.resizingEl = Roo.get(cfg.resizingElement, true);
34321 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34322 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34325 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34328 * The minimum size of the resizing element. (Defaults to 0)
34334 * The maximum size of the resizing element. (Defaults to 2000)
34337 this.maxSize = 2000;
34340 * Whether to animate the transition to the new size
34343 this.animate = false;
34346 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34349 this.useShim = false;
34354 if(!cfg.existingProxy){
34356 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34358 this.proxy = Roo.get(cfg.existingProxy).dom;
34361 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34364 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34367 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34370 this.dragSpecs = {};
34373 * @private The adapter to use to positon and resize elements
34375 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34376 this.adapter.init(this);
34378 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34380 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34381 this.el.addClass("roo-splitbar-h");
34384 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34385 this.el.addClass("roo-splitbar-v");
34391 * Fires when the splitter is moved (alias for {@link #event-moved})
34392 * @param {Roo.bootstrap.SplitBar} this
34393 * @param {Number} newSize the new width or height
34398 * Fires when the splitter is moved
34399 * @param {Roo.bootstrap.SplitBar} this
34400 * @param {Number} newSize the new width or height
34404 * @event beforeresize
34405 * Fires before the splitter is dragged
34406 * @param {Roo.bootstrap.SplitBar} this
34408 "beforeresize" : true,
34410 "beforeapply" : true
34413 Roo.util.Observable.call(this);
34416 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34417 onStartProxyDrag : function(x, y){
34418 this.fireEvent("beforeresize", this);
34420 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34422 o.enableDisplayMode("block");
34423 // all splitbars share the same overlay
34424 Roo.bootstrap.SplitBar.prototype.overlay = o;
34426 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34427 this.overlay.show();
34428 Roo.get(this.proxy).setDisplayed("block");
34429 var size = this.adapter.getElementSize(this);
34430 this.activeMinSize = this.getMinimumSize();;
34431 this.activeMaxSize = this.getMaximumSize();;
34432 var c1 = size - this.activeMinSize;
34433 var c2 = Math.max(this.activeMaxSize - size, 0);
34434 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34435 this.dd.resetConstraints();
34436 this.dd.setXConstraint(
34437 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34438 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34440 this.dd.setYConstraint(0, 0);
34442 this.dd.resetConstraints();
34443 this.dd.setXConstraint(0, 0);
34444 this.dd.setYConstraint(
34445 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34446 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34449 this.dragSpecs.startSize = size;
34450 this.dragSpecs.startPoint = [x, y];
34451 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34455 * @private Called after the drag operation by the DDProxy
34457 onEndProxyDrag : function(e){
34458 Roo.get(this.proxy).setDisplayed(false);
34459 var endPoint = Roo.lib.Event.getXY(e);
34461 this.overlay.hide();
34464 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34465 newSize = this.dragSpecs.startSize +
34466 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34467 endPoint[0] - this.dragSpecs.startPoint[0] :
34468 this.dragSpecs.startPoint[0] - endPoint[0]
34471 newSize = this.dragSpecs.startSize +
34472 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34473 endPoint[1] - this.dragSpecs.startPoint[1] :
34474 this.dragSpecs.startPoint[1] - endPoint[1]
34477 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34478 if(newSize != this.dragSpecs.startSize){
34479 if(this.fireEvent('beforeapply', this, newSize) !== false){
34480 this.adapter.setElementSize(this, newSize);
34481 this.fireEvent("moved", this, newSize);
34482 this.fireEvent("resize", this, newSize);
34488 * Get the adapter this SplitBar uses
34489 * @return The adapter object
34491 getAdapter : function(){
34492 return this.adapter;
34496 * Set the adapter this SplitBar uses
34497 * @param {Object} adapter A SplitBar adapter object
34499 setAdapter : function(adapter){
34500 this.adapter = adapter;
34501 this.adapter.init(this);
34505 * Gets the minimum size for the resizing element
34506 * @return {Number} The minimum size
34508 getMinimumSize : function(){
34509 return this.minSize;
34513 * Sets the minimum size for the resizing element
34514 * @param {Number} minSize The minimum size
34516 setMinimumSize : function(minSize){
34517 this.minSize = minSize;
34521 * Gets the maximum size for the resizing element
34522 * @return {Number} The maximum size
34524 getMaximumSize : function(){
34525 return this.maxSize;
34529 * Sets the maximum size for the resizing element
34530 * @param {Number} maxSize The maximum size
34532 setMaximumSize : function(maxSize){
34533 this.maxSize = maxSize;
34537 * Sets the initialize size for the resizing element
34538 * @param {Number} size The initial size
34540 setCurrentSize : function(size){
34541 var oldAnimate = this.animate;
34542 this.animate = false;
34543 this.adapter.setElementSize(this, size);
34544 this.animate = oldAnimate;
34548 * Destroy this splitbar.
34549 * @param {Boolean} removeEl True to remove the element
34551 destroy : function(removeEl){
34553 this.shim.remove();
34556 this.proxy.parentNode.removeChild(this.proxy);
34564 * @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.
34566 Roo.bootstrap.SplitBar.createProxy = function(dir){
34567 var proxy = new Roo.Element(document.createElement("div"));
34568 proxy.unselectable();
34569 var cls = 'roo-splitbar-proxy';
34570 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34571 document.body.appendChild(proxy.dom);
34576 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34577 * Default Adapter. It assumes the splitter and resizing element are not positioned
34578 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34580 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34583 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34584 // do nothing for now
34585 init : function(s){
34589 * Called before drag operations to get the current size of the resizing element.
34590 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34592 getElementSize : function(s){
34593 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34594 return s.resizingEl.getWidth();
34596 return s.resizingEl.getHeight();
34601 * Called after drag operations to set the size of the resizing element.
34602 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34603 * @param {Number} newSize The new size to set
34604 * @param {Function} onComplete A function to be invoked when resizing is complete
34606 setElementSize : function(s, newSize, onComplete){
34607 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34609 s.resizingEl.setWidth(newSize);
34611 onComplete(s, newSize);
34614 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34619 s.resizingEl.setHeight(newSize);
34621 onComplete(s, newSize);
34624 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34631 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34632 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34633 * Adapter that moves the splitter element to align with the resized sizing element.
34634 * Used with an absolute positioned SplitBar.
34635 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34636 * document.body, make sure you assign an id to the body element.
34638 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34639 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34640 this.container = Roo.get(container);
34643 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34644 init : function(s){
34645 this.basic.init(s);
34648 getElementSize : function(s){
34649 return this.basic.getElementSize(s);
34652 setElementSize : function(s, newSize, onComplete){
34653 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34656 moveSplitter : function(s){
34657 var yes = Roo.bootstrap.SplitBar;
34658 switch(s.placement){
34660 s.el.setX(s.resizingEl.getRight());
34663 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34666 s.el.setY(s.resizingEl.getBottom());
34669 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34676 * Orientation constant - Create a vertical SplitBar
34680 Roo.bootstrap.SplitBar.VERTICAL = 1;
34683 * Orientation constant - Create a horizontal SplitBar
34687 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34690 * Placement constant - The resizing element is to the left of the splitter element
34694 Roo.bootstrap.SplitBar.LEFT = 1;
34697 * Placement constant - The resizing element is to the right of the splitter element
34701 Roo.bootstrap.SplitBar.RIGHT = 2;
34704 * Placement constant - The resizing element is positioned above the splitter element
34708 Roo.bootstrap.SplitBar.TOP = 3;
34711 * Placement constant - The resizing element is positioned under splitter element
34715 Roo.bootstrap.SplitBar.BOTTOM = 4;
34716 Roo.namespace("Roo.bootstrap.layout");/*
34718 * Ext JS Library 1.1.1
34719 * Copyright(c) 2006-2007, Ext JS, LLC.
34721 * Originally Released Under LGPL - original licence link has changed is not relivant.
34724 * <script type="text/javascript">
34728 * @class Roo.bootstrap.layout.Manager
34729 * @extends Roo.bootstrap.Component
34730 * Base class for layout managers.
34732 Roo.bootstrap.layout.Manager = function(config)
34734 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34740 /** false to disable window resize monitoring @type Boolean */
34741 this.monitorWindowResize = true;
34746 * Fires when a layout is performed.
34747 * @param {Roo.LayoutManager} this
34751 * @event regionresized
34752 * Fires when the user resizes a region.
34753 * @param {Roo.LayoutRegion} region The resized region
34754 * @param {Number} newSize The new size (width for east/west, height for north/south)
34756 "regionresized" : true,
34758 * @event regioncollapsed
34759 * Fires when a region is collapsed.
34760 * @param {Roo.LayoutRegion} region The collapsed region
34762 "regioncollapsed" : true,
34764 * @event regionexpanded
34765 * Fires when a region is expanded.
34766 * @param {Roo.LayoutRegion} region The expanded region
34768 "regionexpanded" : true
34770 this.updating = false;
34773 this.el = Roo.get(config.el);
34779 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34784 monitorWindowResize : true,
34790 onRender : function(ct, position)
34793 this.el = Roo.get(ct);
34796 //this.fireEvent('render',this);
34800 initEvents: function()
34804 // ie scrollbar fix
34805 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34806 document.body.scroll = "no";
34807 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34808 this.el.position('relative');
34810 this.id = this.el.id;
34811 this.el.addClass("roo-layout-container");
34812 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34813 if(this.el.dom != document.body ) {
34814 this.el.on('resize', this.layout,this);
34815 this.el.on('show', this.layout,this);
34821 * Returns true if this layout is currently being updated
34822 * @return {Boolean}
34824 isUpdating : function(){
34825 return this.updating;
34829 * Suspend the LayoutManager from doing auto-layouts while
34830 * making multiple add or remove calls
34832 beginUpdate : function(){
34833 this.updating = true;
34837 * Restore auto-layouts and optionally disable the manager from performing a layout
34838 * @param {Boolean} noLayout true to disable a layout update
34840 endUpdate : function(noLayout){
34841 this.updating = false;
34847 layout: function(){
34851 onRegionResized : function(region, newSize){
34852 this.fireEvent("regionresized", region, newSize);
34856 onRegionCollapsed : function(region){
34857 this.fireEvent("regioncollapsed", region);
34860 onRegionExpanded : function(region){
34861 this.fireEvent("regionexpanded", region);
34865 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34866 * performs box-model adjustments.
34867 * @return {Object} The size as an object {width: (the width), height: (the height)}
34869 getViewSize : function()
34872 if(this.el.dom != document.body){
34873 size = this.el.getSize();
34875 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34877 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34878 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34883 * Returns the Element this layout is bound to.
34884 * @return {Roo.Element}
34886 getEl : function(){
34891 * Returns the specified region.
34892 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34893 * @return {Roo.LayoutRegion}
34895 getRegion : function(target){
34896 return this.regions[target.toLowerCase()];
34899 onWindowResize : function(){
34900 if(this.monitorWindowResize){
34907 * Ext JS Library 1.1.1
34908 * Copyright(c) 2006-2007, Ext JS, LLC.
34910 * Originally Released Under LGPL - original licence link has changed is not relivant.
34913 * <script type="text/javascript">
34916 * @class Roo.bootstrap.layout.Border
34917 * @extends Roo.bootstrap.layout.Manager
34918 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34919 * please see: examples/bootstrap/nested.html<br><br>
34921 <b>The container the layout is rendered into can be either the body element or any other element.
34922 If it is not the body element, the container needs to either be an absolute positioned element,
34923 or you will need to add "position:relative" to the css of the container. You will also need to specify
34924 the container size if it is not the body element.</b>
34927 * Create a new Border
34928 * @param {Object} config Configuration options
34930 Roo.bootstrap.layout.Border = function(config){
34931 config = config || {};
34932 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34936 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34937 if(config[region]){
34938 config[region].region = region;
34939 this.addRegion(config[region]);
34945 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34947 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34949 * Creates and adds a new region if it doesn't already exist.
34950 * @param {String} target The target region key (north, south, east, west or center).
34951 * @param {Object} config The regions config object
34952 * @return {BorderLayoutRegion} The new region
34954 addRegion : function(config)
34956 if(!this.regions[config.region]){
34957 var r = this.factory(config);
34958 this.bindRegion(r);
34960 return this.regions[config.region];
34964 bindRegion : function(r){
34965 this.regions[r.config.region] = r;
34967 r.on("visibilitychange", this.layout, this);
34968 r.on("paneladded", this.layout, this);
34969 r.on("panelremoved", this.layout, this);
34970 r.on("invalidated", this.layout, this);
34971 r.on("resized", this.onRegionResized, this);
34972 r.on("collapsed", this.onRegionCollapsed, this);
34973 r.on("expanded", this.onRegionExpanded, this);
34977 * Performs a layout update.
34979 layout : function()
34981 if(this.updating) {
34985 // render all the rebions if they have not been done alreayd?
34986 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34987 if(this.regions[region] && !this.regions[region].bodyEl){
34988 this.regions[region].onRender(this.el)
34992 var size = this.getViewSize();
34993 var w = size.width;
34994 var h = size.height;
34999 //var x = 0, y = 0;
35001 var rs = this.regions;
35002 var north = rs["north"];
35003 var south = rs["south"];
35004 var west = rs["west"];
35005 var east = rs["east"];
35006 var center = rs["center"];
35007 //if(this.hideOnLayout){ // not supported anymore
35008 //c.el.setStyle("display", "none");
35010 if(north && north.isVisible()){
35011 var b = north.getBox();
35012 var m = north.getMargins();
35013 b.width = w - (m.left+m.right);
35016 centerY = b.height + b.y + m.bottom;
35017 centerH -= centerY;
35018 north.updateBox(this.safeBox(b));
35020 if(south && south.isVisible()){
35021 var b = south.getBox();
35022 var m = south.getMargins();
35023 b.width = w - (m.left+m.right);
35025 var totalHeight = (b.height + m.top + m.bottom);
35026 b.y = h - totalHeight + m.top;
35027 centerH -= totalHeight;
35028 south.updateBox(this.safeBox(b));
35030 if(west && west.isVisible()){
35031 var b = west.getBox();
35032 var m = west.getMargins();
35033 b.height = centerH - (m.top+m.bottom);
35035 b.y = centerY + m.top;
35036 var totalWidth = (b.width + m.left + m.right);
35037 centerX += totalWidth;
35038 centerW -= totalWidth;
35039 west.updateBox(this.safeBox(b));
35041 if(east && east.isVisible()){
35042 var b = east.getBox();
35043 var m = east.getMargins();
35044 b.height = centerH - (m.top+m.bottom);
35045 var totalWidth = (b.width + m.left + m.right);
35046 b.x = w - totalWidth + m.left;
35047 b.y = centerY + m.top;
35048 centerW -= totalWidth;
35049 east.updateBox(this.safeBox(b));
35052 var m = center.getMargins();
35054 x: centerX + m.left,
35055 y: centerY + m.top,
35056 width: centerW - (m.left+m.right),
35057 height: centerH - (m.top+m.bottom)
35059 //if(this.hideOnLayout){
35060 //center.el.setStyle("display", "block");
35062 center.updateBox(this.safeBox(centerBox));
35065 this.fireEvent("layout", this);
35069 safeBox : function(box){
35070 box.width = Math.max(0, box.width);
35071 box.height = Math.max(0, box.height);
35076 * Adds a ContentPanel (or subclass) to this layout.
35077 * @param {String} target The target region key (north, south, east, west or center).
35078 * @param {Roo.ContentPanel} panel The panel to add
35079 * @return {Roo.ContentPanel} The added panel
35081 add : function(target, panel){
35083 target = target.toLowerCase();
35084 return this.regions[target].add(panel);
35088 * Remove a ContentPanel (or subclass) to this layout.
35089 * @param {String} target The target region key (north, south, east, west or center).
35090 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35091 * @return {Roo.ContentPanel} The removed panel
35093 remove : function(target, panel){
35094 target = target.toLowerCase();
35095 return this.regions[target].remove(panel);
35099 * Searches all regions for a panel with the specified id
35100 * @param {String} panelId
35101 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35103 findPanel : function(panelId){
35104 var rs = this.regions;
35105 for(var target in rs){
35106 if(typeof rs[target] != "function"){
35107 var p = rs[target].getPanel(panelId);
35117 * Searches all regions for a panel with the specified id and activates (shows) it.
35118 * @param {String/ContentPanel} panelId The panels id or the panel itself
35119 * @return {Roo.ContentPanel} The shown panel or null
35121 showPanel : function(panelId) {
35122 var rs = this.regions;
35123 for(var target in rs){
35124 var r = rs[target];
35125 if(typeof r != "function"){
35126 if(r.hasPanel(panelId)){
35127 return r.showPanel(panelId);
35135 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35136 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35139 restoreState : function(provider){
35141 provider = Roo.state.Manager;
35143 var sm = new Roo.LayoutStateManager();
35144 sm.init(this, provider);
35150 * Adds a xtype elements to the layout.
35154 xtype : 'ContentPanel',
35161 xtype : 'NestedLayoutPanel',
35167 items : [ ... list of content panels or nested layout panels.. ]
35171 * @param {Object} cfg Xtype definition of item to add.
35173 addxtype : function(cfg)
35175 // basically accepts a pannel...
35176 // can accept a layout region..!?!?
35177 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35180 // theory? children can only be panels??
35182 //if (!cfg.xtype.match(/Panel$/)) {
35187 if (typeof(cfg.region) == 'undefined') {
35188 Roo.log("Failed to add Panel, region was not set");
35192 var region = cfg.region;
35198 xitems = cfg.items;
35205 case 'Content': // ContentPanel (el, cfg)
35206 case 'Scroll': // ContentPanel (el, cfg)
35208 cfg.autoCreate = true;
35209 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35211 // var el = this.el.createChild();
35212 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35215 this.add(region, ret);
35219 case 'TreePanel': // our new panel!
35220 cfg.el = this.el.createChild();
35221 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35222 this.add(region, ret);
35227 // create a new Layout (which is a Border Layout...
35229 var clayout = cfg.layout;
35230 clayout.el = this.el.createChild();
35231 clayout.items = clayout.items || [];
35235 // replace this exitems with the clayout ones..
35236 xitems = clayout.items;
35238 // force background off if it's in center...
35239 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35240 cfg.background = false;
35242 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35245 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35246 //console.log('adding nested layout panel ' + cfg.toSource());
35247 this.add(region, ret);
35248 nb = {}; /// find first...
35253 // needs grid and region
35255 //var el = this.getRegion(region).el.createChild();
35257 *var el = this.el.createChild();
35258 // create the grid first...
35259 cfg.grid.container = el;
35260 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35263 if (region == 'center' && this.active ) {
35264 cfg.background = false;
35267 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35269 this.add(region, ret);
35271 if (cfg.background) {
35272 // render grid on panel activation (if panel background)
35273 ret.on('activate', function(gp) {
35274 if (!gp.grid.rendered) {
35275 // gp.grid.render(el);
35279 // cfg.grid.render(el);
35285 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35286 // it was the old xcomponent building that caused this before.
35287 // espeically if border is the top element in the tree.
35297 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35299 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35300 this.add(region, ret);
35304 throw "Can not add '" + cfg.xtype + "' to Border";
35310 this.beginUpdate();
35314 Roo.each(xitems, function(i) {
35315 region = nb && i.region ? i.region : false;
35317 var add = ret.addxtype(i);
35320 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35321 if (!i.background) {
35322 abn[region] = nb[region] ;
35329 // make the last non-background panel active..
35330 //if (nb) { Roo.log(abn); }
35333 for(var r in abn) {
35334 region = this.getRegion(r);
35336 // tried using nb[r], but it does not work..
35338 region.showPanel(abn[r]);
35349 factory : function(cfg)
35352 var validRegions = Roo.bootstrap.layout.Border.regions;
35354 var target = cfg.region;
35357 var r = Roo.bootstrap.layout;
35361 return new r.North(cfg);
35363 return new r.South(cfg);
35365 return new r.East(cfg);
35367 return new r.West(cfg);
35369 return new r.Center(cfg);
35371 throw 'Layout region "'+target+'" not supported.';
35378 * Ext JS Library 1.1.1
35379 * Copyright(c) 2006-2007, Ext JS, LLC.
35381 * Originally Released Under LGPL - original licence link has changed is not relivant.
35384 * <script type="text/javascript">
35388 * @class Roo.bootstrap.layout.Basic
35389 * @extends Roo.util.Observable
35390 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35391 * and does not have a titlebar, tabs or any other features. All it does is size and position
35392 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35393 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35394 * @cfg {string} region the region that it inhabits..
35395 * @cfg {bool} skipConfig skip config?
35399 Roo.bootstrap.layout.Basic = function(config){
35401 this.mgr = config.mgr;
35403 this.position = config.region;
35405 var skipConfig = config.skipConfig;
35409 * @scope Roo.BasicLayoutRegion
35413 * @event beforeremove
35414 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35415 * @param {Roo.LayoutRegion} this
35416 * @param {Roo.ContentPanel} panel The panel
35417 * @param {Object} e The cancel event object
35419 "beforeremove" : true,
35421 * @event invalidated
35422 * Fires when the layout for this region is changed.
35423 * @param {Roo.LayoutRegion} this
35425 "invalidated" : true,
35427 * @event visibilitychange
35428 * Fires when this region is shown or hidden
35429 * @param {Roo.LayoutRegion} this
35430 * @param {Boolean} visibility true or false
35432 "visibilitychange" : true,
35434 * @event paneladded
35435 * Fires when a panel is added.
35436 * @param {Roo.LayoutRegion} this
35437 * @param {Roo.ContentPanel} panel The panel
35439 "paneladded" : true,
35441 * @event panelremoved
35442 * Fires when a panel is removed.
35443 * @param {Roo.LayoutRegion} this
35444 * @param {Roo.ContentPanel} panel The panel
35446 "panelremoved" : true,
35448 * @event beforecollapse
35449 * Fires when this region before collapse.
35450 * @param {Roo.LayoutRegion} this
35452 "beforecollapse" : true,
35455 * Fires when this region is collapsed.
35456 * @param {Roo.LayoutRegion} this
35458 "collapsed" : true,
35461 * Fires when this region is expanded.
35462 * @param {Roo.LayoutRegion} this
35467 * Fires when this region is slid into view.
35468 * @param {Roo.LayoutRegion} this
35470 "slideshow" : true,
35473 * Fires when this region slides out of view.
35474 * @param {Roo.LayoutRegion} this
35476 "slidehide" : true,
35478 * @event panelactivated
35479 * Fires when a panel is activated.
35480 * @param {Roo.LayoutRegion} this
35481 * @param {Roo.ContentPanel} panel The activated panel
35483 "panelactivated" : true,
35486 * Fires when the user resizes this region.
35487 * @param {Roo.LayoutRegion} this
35488 * @param {Number} newSize The new size (width for east/west, height for north/south)
35492 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35493 this.panels = new Roo.util.MixedCollection();
35494 this.panels.getKey = this.getPanelId.createDelegate(this);
35496 this.activePanel = null;
35497 // ensure listeners are added...
35499 if (config.listeners || config.events) {
35500 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35501 listeners : config.listeners || {},
35502 events : config.events || {}
35506 if(skipConfig !== true){
35507 this.applyConfig(config);
35511 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35513 getPanelId : function(p){
35517 applyConfig : function(config){
35518 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35519 this.config = config;
35524 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35525 * the width, for horizontal (north, south) the height.
35526 * @param {Number} newSize The new width or height
35528 resizeTo : function(newSize){
35529 var el = this.el ? this.el :
35530 (this.activePanel ? this.activePanel.getEl() : null);
35532 switch(this.position){
35535 el.setWidth(newSize);
35536 this.fireEvent("resized", this, newSize);
35540 el.setHeight(newSize);
35541 this.fireEvent("resized", this, newSize);
35547 getBox : function(){
35548 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35551 getMargins : function(){
35552 return this.margins;
35555 updateBox : function(box){
35557 var el = this.activePanel.getEl();
35558 el.dom.style.left = box.x + "px";
35559 el.dom.style.top = box.y + "px";
35560 this.activePanel.setSize(box.width, box.height);
35564 * Returns the container element for this region.
35565 * @return {Roo.Element}
35567 getEl : function(){
35568 return this.activePanel;
35572 * Returns true if this region is currently visible.
35573 * @return {Boolean}
35575 isVisible : function(){
35576 return this.activePanel ? true : false;
35579 setActivePanel : function(panel){
35580 panel = this.getPanel(panel);
35581 if(this.activePanel && this.activePanel != panel){
35582 this.activePanel.setActiveState(false);
35583 this.activePanel.getEl().setLeftTop(-10000,-10000);
35585 this.activePanel = panel;
35586 panel.setActiveState(true);
35588 panel.setSize(this.box.width, this.box.height);
35590 this.fireEvent("panelactivated", this, panel);
35591 this.fireEvent("invalidated");
35595 * Show the specified panel.
35596 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35597 * @return {Roo.ContentPanel} The shown panel or null
35599 showPanel : function(panel){
35600 panel = this.getPanel(panel);
35602 this.setActivePanel(panel);
35608 * Get the active panel for this region.
35609 * @return {Roo.ContentPanel} The active panel or null
35611 getActivePanel : function(){
35612 return this.activePanel;
35616 * Add the passed ContentPanel(s)
35617 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35618 * @return {Roo.ContentPanel} The panel added (if only one was added)
35620 add : function(panel){
35621 if(arguments.length > 1){
35622 for(var i = 0, len = arguments.length; i < len; i++) {
35623 this.add(arguments[i]);
35627 if(this.hasPanel(panel)){
35628 this.showPanel(panel);
35631 var el = panel.getEl();
35632 if(el.dom.parentNode != this.mgr.el.dom){
35633 this.mgr.el.dom.appendChild(el.dom);
35635 if(panel.setRegion){
35636 panel.setRegion(this);
35638 this.panels.add(panel);
35639 el.setStyle("position", "absolute");
35640 if(!panel.background){
35641 this.setActivePanel(panel);
35642 if(this.config.initialSize && this.panels.getCount()==1){
35643 this.resizeTo(this.config.initialSize);
35646 this.fireEvent("paneladded", this, panel);
35651 * Returns true if the panel is in this region.
35652 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35653 * @return {Boolean}
35655 hasPanel : function(panel){
35656 if(typeof panel == "object"){ // must be panel obj
35657 panel = panel.getId();
35659 return this.getPanel(panel) ? true : false;
35663 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35664 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35665 * @param {Boolean} preservePanel Overrides the config preservePanel option
35666 * @return {Roo.ContentPanel} The panel that was removed
35668 remove : function(panel, preservePanel){
35669 panel = this.getPanel(panel);
35674 this.fireEvent("beforeremove", this, panel, e);
35675 if(e.cancel === true){
35678 var panelId = panel.getId();
35679 this.panels.removeKey(panelId);
35684 * Returns the panel specified or null if it's not in this region.
35685 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35686 * @return {Roo.ContentPanel}
35688 getPanel : function(id){
35689 if(typeof id == "object"){ // must be panel obj
35692 return this.panels.get(id);
35696 * Returns this regions position (north/south/east/west/center).
35699 getPosition: function(){
35700 return this.position;
35704 * Ext JS Library 1.1.1
35705 * Copyright(c) 2006-2007, Ext JS, LLC.
35707 * Originally Released Under LGPL - original licence link has changed is not relivant.
35710 * <script type="text/javascript">
35714 * @class Roo.bootstrap.layout.Region
35715 * @extends Roo.bootstrap.layout.Basic
35716 * This class represents a region in a layout manager.
35718 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35719 * @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})
35720 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35721 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35722 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35723 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35724 * @cfg {String} title The title for the region (overrides panel titles)
35725 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35726 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35727 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35728 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35729 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35730 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35731 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35732 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35733 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35734 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35736 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35737 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35738 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35739 * @cfg {Number} width For East/West panels
35740 * @cfg {Number} height For North/South panels
35741 * @cfg {Boolean} split To show the splitter
35742 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35744 * @cfg {string} cls Extra CSS classes to add to region
35746 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35747 * @cfg {string} region the region that it inhabits..
35750 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35751 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35753 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35754 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35755 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35757 Roo.bootstrap.layout.Region = function(config)
35759 this.applyConfig(config);
35761 var mgr = config.mgr;
35762 var pos = config.region;
35763 config.skipConfig = true;
35764 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35767 this.onRender(mgr.el);
35770 this.visible = true;
35771 this.collapsed = false;
35772 this.unrendered_panels = [];
35775 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35777 position: '', // set by wrapper (eg. north/south etc..)
35778 unrendered_panels : null, // unrendered panels.
35779 createBody : function(){
35780 /** This region's body element
35781 * @type Roo.Element */
35782 this.bodyEl = this.el.createChild({
35784 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35788 onRender: function(ctr, pos)
35790 var dh = Roo.DomHelper;
35791 /** This region's container element
35792 * @type Roo.Element */
35793 this.el = dh.append(ctr.dom, {
35795 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35797 /** This region's title element
35798 * @type Roo.Element */
35800 this.titleEl = dh.append(this.el.dom,
35803 unselectable: "on",
35804 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35806 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35807 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35810 this.titleEl.enableDisplayMode();
35811 /** This region's title text element
35812 * @type HTMLElement */
35813 this.titleTextEl = this.titleEl.dom.firstChild;
35814 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35816 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35817 this.closeBtn.enableDisplayMode();
35818 this.closeBtn.on("click", this.closeClicked, this);
35819 this.closeBtn.hide();
35821 this.createBody(this.config);
35822 if(this.config.hideWhenEmpty){
35824 this.on("paneladded", this.validateVisibility, this);
35825 this.on("panelremoved", this.validateVisibility, this);
35827 if(this.autoScroll){
35828 this.bodyEl.setStyle("overflow", "auto");
35830 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35832 //if(c.titlebar !== false){
35833 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35834 this.titleEl.hide();
35836 this.titleEl.show();
35837 if(this.config.title){
35838 this.titleTextEl.innerHTML = this.config.title;
35842 if(this.config.collapsed){
35843 this.collapse(true);
35845 if(this.config.hidden){
35849 if (this.unrendered_panels && this.unrendered_panels.length) {
35850 for (var i =0;i< this.unrendered_panels.length; i++) {
35851 this.add(this.unrendered_panels[i]);
35853 this.unrendered_panels = null;
35859 applyConfig : function(c)
35862 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35863 var dh = Roo.DomHelper;
35864 if(c.titlebar !== false){
35865 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35866 this.collapseBtn.on("click", this.collapse, this);
35867 this.collapseBtn.enableDisplayMode();
35869 if(c.showPin === true || this.showPin){
35870 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35871 this.stickBtn.enableDisplayMode();
35872 this.stickBtn.on("click", this.expand, this);
35873 this.stickBtn.hide();
35878 /** This region's collapsed element
35879 * @type Roo.Element */
35882 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35883 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35886 if(c.floatable !== false){
35887 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35888 this.collapsedEl.on("click", this.collapseClick, this);
35891 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35892 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35893 id: "message", unselectable: "on", style:{"float":"left"}});
35894 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35896 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35897 this.expandBtn.on("click", this.expand, this);
35901 if(this.collapseBtn){
35902 this.collapseBtn.setVisible(c.collapsible == true);
35905 this.cmargins = c.cmargins || this.cmargins ||
35906 (this.position == "west" || this.position == "east" ?
35907 {top: 0, left: 2, right:2, bottom: 0} :
35908 {top: 2, left: 0, right:0, bottom: 2});
35910 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35913 this.bottomTabs = c.tabPosition != "top";
35915 this.autoScroll = c.autoScroll || false;
35920 this.duration = c.duration || .30;
35921 this.slideDuration = c.slideDuration || .45;
35926 * Returns true if this region is currently visible.
35927 * @return {Boolean}
35929 isVisible : function(){
35930 return this.visible;
35934 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35935 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35937 //setCollapsedTitle : function(title){
35938 // title = title || " ";
35939 // if(this.collapsedTitleTextEl){
35940 // this.collapsedTitleTextEl.innerHTML = title;
35944 getBox : function(){
35946 // if(!this.collapsed){
35947 b = this.el.getBox(false, true);
35949 // b = this.collapsedEl.getBox(false, true);
35954 getMargins : function(){
35955 return this.margins;
35956 //return this.collapsed ? this.cmargins : this.margins;
35959 highlight : function(){
35960 this.el.addClass("x-layout-panel-dragover");
35963 unhighlight : function(){
35964 this.el.removeClass("x-layout-panel-dragover");
35967 updateBox : function(box)
35969 if (!this.bodyEl) {
35970 return; // not rendered yet..
35974 if(!this.collapsed){
35975 this.el.dom.style.left = box.x + "px";
35976 this.el.dom.style.top = box.y + "px";
35977 this.updateBody(box.width, box.height);
35979 this.collapsedEl.dom.style.left = box.x + "px";
35980 this.collapsedEl.dom.style.top = box.y + "px";
35981 this.collapsedEl.setSize(box.width, box.height);
35984 this.tabs.autoSizeTabs();
35988 updateBody : function(w, h)
35991 this.el.setWidth(w);
35992 w -= this.el.getBorderWidth("rl");
35993 if(this.config.adjustments){
35994 w += this.config.adjustments[0];
35997 if(h !== null && h > 0){
35998 this.el.setHeight(h);
35999 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36000 h -= this.el.getBorderWidth("tb");
36001 if(this.config.adjustments){
36002 h += this.config.adjustments[1];
36004 this.bodyEl.setHeight(h);
36006 h = this.tabs.syncHeight(h);
36009 if(this.panelSize){
36010 w = w !== null ? w : this.panelSize.width;
36011 h = h !== null ? h : this.panelSize.height;
36013 if(this.activePanel){
36014 var el = this.activePanel.getEl();
36015 w = w !== null ? w : el.getWidth();
36016 h = h !== null ? h : el.getHeight();
36017 this.panelSize = {width: w, height: h};
36018 this.activePanel.setSize(w, h);
36020 if(Roo.isIE && this.tabs){
36021 this.tabs.el.repaint();
36026 * Returns the container element for this region.
36027 * @return {Roo.Element}
36029 getEl : function(){
36034 * Hides this region.
36037 //if(!this.collapsed){
36038 this.el.dom.style.left = "-2000px";
36041 // this.collapsedEl.dom.style.left = "-2000px";
36042 // this.collapsedEl.hide();
36044 this.visible = false;
36045 this.fireEvent("visibilitychange", this, false);
36049 * Shows this region if it was previously hidden.
36052 //if(!this.collapsed){
36055 // this.collapsedEl.show();
36057 this.visible = true;
36058 this.fireEvent("visibilitychange", this, true);
36061 closeClicked : function(){
36062 if(this.activePanel){
36063 this.remove(this.activePanel);
36067 collapseClick : function(e){
36069 e.stopPropagation();
36072 e.stopPropagation();
36078 * Collapses this region.
36079 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36082 collapse : function(skipAnim, skipCheck = false){
36083 if(this.collapsed) {
36087 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36089 this.collapsed = true;
36091 this.split.el.hide();
36093 if(this.config.animate && skipAnim !== true){
36094 this.fireEvent("invalidated", this);
36095 this.animateCollapse();
36097 this.el.setLocation(-20000,-20000);
36099 this.collapsedEl.show();
36100 this.fireEvent("collapsed", this);
36101 this.fireEvent("invalidated", this);
36107 animateCollapse : function(){
36112 * Expands this region if it was previously collapsed.
36113 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36114 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36117 expand : function(e, skipAnim){
36119 e.stopPropagation();
36121 if(!this.collapsed || this.el.hasActiveFx()) {
36125 this.afterSlideIn();
36128 this.collapsed = false;
36129 if(this.config.animate && skipAnim !== true){
36130 this.animateExpand();
36134 this.split.el.show();
36136 this.collapsedEl.setLocation(-2000,-2000);
36137 this.collapsedEl.hide();
36138 this.fireEvent("invalidated", this);
36139 this.fireEvent("expanded", this);
36143 animateExpand : function(){
36147 initTabs : function()
36149 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36151 var ts = new Roo.bootstrap.panel.Tabs({
36152 el: this.bodyEl.dom,
36153 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36154 disableTooltips: this.config.disableTabTips,
36155 toolbar : this.config.toolbar
36158 if(this.config.hideTabs){
36159 ts.stripWrap.setDisplayed(false);
36162 ts.resizeTabs = this.config.resizeTabs === true;
36163 ts.minTabWidth = this.config.minTabWidth || 40;
36164 ts.maxTabWidth = this.config.maxTabWidth || 250;
36165 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36166 ts.monitorResize = false;
36167 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36168 ts.bodyEl.addClass('roo-layout-tabs-body');
36169 this.panels.each(this.initPanelAsTab, this);
36172 initPanelAsTab : function(panel){
36173 var ti = this.tabs.addTab(
36177 this.config.closeOnTab && panel.isClosable(),
36180 if(panel.tabTip !== undefined){
36181 ti.setTooltip(panel.tabTip);
36183 ti.on("activate", function(){
36184 this.setActivePanel(panel);
36187 if(this.config.closeOnTab){
36188 ti.on("beforeclose", function(t, e){
36190 this.remove(panel);
36194 panel.tabItem = ti;
36199 updatePanelTitle : function(panel, title)
36201 if(this.activePanel == panel){
36202 this.updateTitle(title);
36205 var ti = this.tabs.getTab(panel.getEl().id);
36207 if(panel.tabTip !== undefined){
36208 ti.setTooltip(panel.tabTip);
36213 updateTitle : function(title){
36214 if(this.titleTextEl && !this.config.title){
36215 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36219 setActivePanel : function(panel)
36221 panel = this.getPanel(panel);
36222 if(this.activePanel && this.activePanel != panel){
36223 if(this.activePanel.setActiveState(false) === false){
36227 this.activePanel = panel;
36228 panel.setActiveState(true);
36229 if(this.panelSize){
36230 panel.setSize(this.panelSize.width, this.panelSize.height);
36233 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36235 this.updateTitle(panel.getTitle());
36237 this.fireEvent("invalidated", this);
36239 this.fireEvent("panelactivated", this, panel);
36243 * Shows the specified panel.
36244 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36245 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36247 showPanel : function(panel)
36249 panel = this.getPanel(panel);
36252 var tab = this.tabs.getTab(panel.getEl().id);
36253 if(tab.isHidden()){
36254 this.tabs.unhideTab(tab.id);
36258 this.setActivePanel(panel);
36265 * Get the active panel for this region.
36266 * @return {Roo.ContentPanel} The active panel or null
36268 getActivePanel : function(){
36269 return this.activePanel;
36272 validateVisibility : function(){
36273 if(this.panels.getCount() < 1){
36274 this.updateTitle(" ");
36275 this.closeBtn.hide();
36278 if(!this.isVisible()){
36285 * Adds the passed ContentPanel(s) to this region.
36286 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36287 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36289 add : function(panel)
36291 if(arguments.length > 1){
36292 for(var i = 0, len = arguments.length; i < len; i++) {
36293 this.add(arguments[i]);
36298 // if we have not been rendered yet, then we can not really do much of this..
36299 if (!this.bodyEl) {
36300 this.unrendered_panels.push(panel);
36307 if(this.hasPanel(panel)){
36308 this.showPanel(panel);
36311 panel.setRegion(this);
36312 this.panels.add(panel);
36313 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36314 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36315 // and hide them... ???
36316 this.bodyEl.dom.appendChild(panel.getEl().dom);
36317 if(panel.background !== true){
36318 this.setActivePanel(panel);
36320 this.fireEvent("paneladded", this, panel);
36327 this.initPanelAsTab(panel);
36331 if(panel.background !== true){
36332 this.tabs.activate(panel.getEl().id);
36334 this.fireEvent("paneladded", this, panel);
36339 * Hides the tab for the specified panel.
36340 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36342 hidePanel : function(panel){
36343 if(this.tabs && (panel = this.getPanel(panel))){
36344 this.tabs.hideTab(panel.getEl().id);
36349 * Unhides the tab for a previously hidden panel.
36350 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36352 unhidePanel : function(panel){
36353 if(this.tabs && (panel = this.getPanel(panel))){
36354 this.tabs.unhideTab(panel.getEl().id);
36358 clearPanels : function(){
36359 while(this.panels.getCount() > 0){
36360 this.remove(this.panels.first());
36365 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36366 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36367 * @param {Boolean} preservePanel Overrides the config preservePanel option
36368 * @return {Roo.ContentPanel} The panel that was removed
36370 remove : function(panel, preservePanel)
36372 panel = this.getPanel(panel);
36377 this.fireEvent("beforeremove", this, panel, e);
36378 if(e.cancel === true){
36381 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36382 var panelId = panel.getId();
36383 this.panels.removeKey(panelId);
36385 document.body.appendChild(panel.getEl().dom);
36388 this.tabs.removeTab(panel.getEl().id);
36389 }else if (!preservePanel){
36390 this.bodyEl.dom.removeChild(panel.getEl().dom);
36392 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36393 var p = this.panels.first();
36394 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36395 tempEl.appendChild(p.getEl().dom);
36396 this.bodyEl.update("");
36397 this.bodyEl.dom.appendChild(p.getEl().dom);
36399 this.updateTitle(p.getTitle());
36401 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36402 this.setActivePanel(p);
36404 panel.setRegion(null);
36405 if(this.activePanel == panel){
36406 this.activePanel = null;
36408 if(this.config.autoDestroy !== false && preservePanel !== true){
36409 try{panel.destroy();}catch(e){}
36411 this.fireEvent("panelremoved", this, panel);
36416 * Returns the TabPanel component used by this region
36417 * @return {Roo.TabPanel}
36419 getTabs : function(){
36423 createTool : function(parentEl, className){
36424 var btn = Roo.DomHelper.append(parentEl, {
36426 cls: "x-layout-tools-button",
36429 cls: "roo-layout-tools-button-inner " + className,
36433 btn.addClassOnOver("roo-layout-tools-button-over");
36438 * Ext JS Library 1.1.1
36439 * Copyright(c) 2006-2007, Ext JS, LLC.
36441 * Originally Released Under LGPL - original licence link has changed is not relivant.
36444 * <script type="text/javascript">
36450 * @class Roo.SplitLayoutRegion
36451 * @extends Roo.LayoutRegion
36452 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36454 Roo.bootstrap.layout.Split = function(config){
36455 this.cursor = config.cursor;
36456 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36459 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36461 splitTip : "Drag to resize.",
36462 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36463 useSplitTips : false,
36465 applyConfig : function(config){
36466 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36469 onRender : function(ctr,pos) {
36471 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36472 if(!this.config.split){
36477 var splitEl = Roo.DomHelper.append(ctr.dom, {
36479 id: this.el.id + "-split",
36480 cls: "roo-layout-split roo-layout-split-"+this.position,
36483 /** The SplitBar for this region
36484 * @type Roo.SplitBar */
36485 // does not exist yet...
36486 Roo.log([this.position, this.orientation]);
36488 this.split = new Roo.bootstrap.SplitBar({
36489 dragElement : splitEl,
36490 resizingElement: this.el,
36491 orientation : this.orientation
36494 this.split.on("moved", this.onSplitMove, this);
36495 this.split.useShim = this.config.useShim === true;
36496 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36497 if(this.useSplitTips){
36498 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36500 //if(config.collapsible){
36501 // this.split.el.on("dblclick", this.collapse, this);
36504 if(typeof this.config.minSize != "undefined"){
36505 this.split.minSize = this.config.minSize;
36507 if(typeof this.config.maxSize != "undefined"){
36508 this.split.maxSize = this.config.maxSize;
36510 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36511 this.hideSplitter();
36516 getHMaxSize : function(){
36517 var cmax = this.config.maxSize || 10000;
36518 var center = this.mgr.getRegion("center");
36519 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36522 getVMaxSize : function(){
36523 var cmax = this.config.maxSize || 10000;
36524 var center = this.mgr.getRegion("center");
36525 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36528 onSplitMove : function(split, newSize){
36529 this.fireEvent("resized", this, newSize);
36533 * Returns the {@link Roo.SplitBar} for this region.
36534 * @return {Roo.SplitBar}
36536 getSplitBar : function(){
36541 this.hideSplitter();
36542 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36545 hideSplitter : function(){
36547 this.split.el.setLocation(-2000,-2000);
36548 this.split.el.hide();
36554 this.split.el.show();
36556 Roo.bootstrap.layout.Split.superclass.show.call(this);
36559 beforeSlide: function(){
36560 if(Roo.isGecko){// firefox overflow auto bug workaround
36561 this.bodyEl.clip();
36563 this.tabs.bodyEl.clip();
36565 if(this.activePanel){
36566 this.activePanel.getEl().clip();
36568 if(this.activePanel.beforeSlide){
36569 this.activePanel.beforeSlide();
36575 afterSlide : function(){
36576 if(Roo.isGecko){// firefox overflow auto bug workaround
36577 this.bodyEl.unclip();
36579 this.tabs.bodyEl.unclip();
36581 if(this.activePanel){
36582 this.activePanel.getEl().unclip();
36583 if(this.activePanel.afterSlide){
36584 this.activePanel.afterSlide();
36590 initAutoHide : function(){
36591 if(this.autoHide !== false){
36592 if(!this.autoHideHd){
36593 var st = new Roo.util.DelayedTask(this.slideIn, this);
36594 this.autoHideHd = {
36595 "mouseout": function(e){
36596 if(!e.within(this.el, true)){
36600 "mouseover" : function(e){
36606 this.el.on(this.autoHideHd);
36610 clearAutoHide : function(){
36611 if(this.autoHide !== false){
36612 this.el.un("mouseout", this.autoHideHd.mouseout);
36613 this.el.un("mouseover", this.autoHideHd.mouseover);
36617 clearMonitor : function(){
36618 Roo.get(document).un("click", this.slideInIf, this);
36621 // these names are backwards but not changed for compat
36622 slideOut : function(){
36623 if(this.isSlid || this.el.hasActiveFx()){
36626 this.isSlid = true;
36627 if(this.collapseBtn){
36628 this.collapseBtn.hide();
36630 this.closeBtnState = this.closeBtn.getStyle('display');
36631 this.closeBtn.hide();
36633 this.stickBtn.show();
36636 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36637 this.beforeSlide();
36638 this.el.setStyle("z-index", 10001);
36639 this.el.slideIn(this.getSlideAnchor(), {
36640 callback: function(){
36642 this.initAutoHide();
36643 Roo.get(document).on("click", this.slideInIf, this);
36644 this.fireEvent("slideshow", this);
36651 afterSlideIn : function(){
36652 this.clearAutoHide();
36653 this.isSlid = false;
36654 this.clearMonitor();
36655 this.el.setStyle("z-index", "");
36656 if(this.collapseBtn){
36657 this.collapseBtn.show();
36659 this.closeBtn.setStyle('display', this.closeBtnState);
36661 this.stickBtn.hide();
36663 this.fireEvent("slidehide", this);
36666 slideIn : function(cb){
36667 if(!this.isSlid || this.el.hasActiveFx()){
36671 this.isSlid = false;
36672 this.beforeSlide();
36673 this.el.slideOut(this.getSlideAnchor(), {
36674 callback: function(){
36675 this.el.setLeftTop(-10000, -10000);
36677 this.afterSlideIn();
36685 slideInIf : function(e){
36686 if(!e.within(this.el)){
36691 animateCollapse : function(){
36692 this.beforeSlide();
36693 this.el.setStyle("z-index", 20000);
36694 var anchor = this.getSlideAnchor();
36695 this.el.slideOut(anchor, {
36696 callback : function(){
36697 this.el.setStyle("z-index", "");
36698 this.collapsedEl.slideIn(anchor, {duration:.3});
36700 this.el.setLocation(-10000,-10000);
36702 this.fireEvent("collapsed", this);
36709 animateExpand : function(){
36710 this.beforeSlide();
36711 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36712 this.el.setStyle("z-index", 20000);
36713 this.collapsedEl.hide({
36716 this.el.slideIn(this.getSlideAnchor(), {
36717 callback : function(){
36718 this.el.setStyle("z-index", "");
36721 this.split.el.show();
36723 this.fireEvent("invalidated", this);
36724 this.fireEvent("expanded", this);
36752 getAnchor : function(){
36753 return this.anchors[this.position];
36756 getCollapseAnchor : function(){
36757 return this.canchors[this.position];
36760 getSlideAnchor : function(){
36761 return this.sanchors[this.position];
36764 getAlignAdj : function(){
36765 var cm = this.cmargins;
36766 switch(this.position){
36782 getExpandAdj : function(){
36783 var c = this.collapsedEl, cm = this.cmargins;
36784 switch(this.position){
36786 return [-(cm.right+c.getWidth()+cm.left), 0];
36789 return [cm.right+c.getWidth()+cm.left, 0];
36792 return [0, -(cm.top+cm.bottom+c.getHeight())];
36795 return [0, cm.top+cm.bottom+c.getHeight()];
36801 * Ext JS Library 1.1.1
36802 * Copyright(c) 2006-2007, Ext JS, LLC.
36804 * Originally Released Under LGPL - original licence link has changed is not relivant.
36807 * <script type="text/javascript">
36810 * These classes are private internal classes
36812 Roo.bootstrap.layout.Center = function(config){
36813 config.region = "center";
36814 Roo.bootstrap.layout.Region.call(this, config);
36815 this.visible = true;
36816 this.minWidth = config.minWidth || 20;
36817 this.minHeight = config.minHeight || 20;
36820 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36822 // center panel can't be hidden
36826 // center panel can't be hidden
36829 getMinWidth: function(){
36830 return this.minWidth;
36833 getMinHeight: function(){
36834 return this.minHeight;
36847 Roo.bootstrap.layout.North = function(config)
36849 config.region = 'north';
36850 config.cursor = 'n-resize';
36852 Roo.bootstrap.layout.Split.call(this, config);
36856 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36857 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36858 this.split.el.addClass("roo-layout-split-v");
36860 var size = config.initialSize || config.height;
36861 if(typeof size != "undefined"){
36862 this.el.setHeight(size);
36865 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36867 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36871 getBox : function(){
36872 if(this.collapsed){
36873 return this.collapsedEl.getBox();
36875 var box = this.el.getBox();
36877 box.height += this.split.el.getHeight();
36882 updateBox : function(box){
36883 if(this.split && !this.collapsed){
36884 box.height -= this.split.el.getHeight();
36885 this.split.el.setLeft(box.x);
36886 this.split.el.setTop(box.y+box.height);
36887 this.split.el.setWidth(box.width);
36889 if(this.collapsed){
36890 this.updateBody(box.width, null);
36892 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36900 Roo.bootstrap.layout.South = function(config){
36901 config.region = 'south';
36902 config.cursor = 's-resize';
36903 Roo.bootstrap.layout.Split.call(this, config);
36905 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36906 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36907 this.split.el.addClass("roo-layout-split-v");
36909 var size = config.initialSize || config.height;
36910 if(typeof size != "undefined"){
36911 this.el.setHeight(size);
36915 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36916 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36917 getBox : function(){
36918 if(this.collapsed){
36919 return this.collapsedEl.getBox();
36921 var box = this.el.getBox();
36923 var sh = this.split.el.getHeight();
36930 updateBox : function(box){
36931 if(this.split && !this.collapsed){
36932 var sh = this.split.el.getHeight();
36935 this.split.el.setLeft(box.x);
36936 this.split.el.setTop(box.y-sh);
36937 this.split.el.setWidth(box.width);
36939 if(this.collapsed){
36940 this.updateBody(box.width, null);
36942 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36946 Roo.bootstrap.layout.East = function(config){
36947 config.region = "east";
36948 config.cursor = "e-resize";
36949 Roo.bootstrap.layout.Split.call(this, config);
36951 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36952 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36953 this.split.el.addClass("roo-layout-split-h");
36955 var size = config.initialSize || config.width;
36956 if(typeof size != "undefined"){
36957 this.el.setWidth(size);
36960 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36961 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36962 getBox : function(){
36963 if(this.collapsed){
36964 return this.collapsedEl.getBox();
36966 var box = this.el.getBox();
36968 var sw = this.split.el.getWidth();
36975 updateBox : function(box){
36976 if(this.split && !this.collapsed){
36977 var sw = this.split.el.getWidth();
36979 this.split.el.setLeft(box.x);
36980 this.split.el.setTop(box.y);
36981 this.split.el.setHeight(box.height);
36984 if(this.collapsed){
36985 this.updateBody(null, box.height);
36987 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36991 Roo.bootstrap.layout.West = function(config){
36992 config.region = "west";
36993 config.cursor = "w-resize";
36995 Roo.bootstrap.layout.Split.call(this, config);
36997 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36998 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36999 this.split.el.addClass("roo-layout-split-h");
37003 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37004 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37006 onRender: function(ctr, pos)
37008 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37009 var size = this.config.initialSize || this.config.width;
37010 if(typeof size != "undefined"){
37011 this.el.setWidth(size);
37015 getBox : function(){
37016 if(this.collapsed){
37017 return this.collapsedEl.getBox();
37019 var box = this.el.getBox();
37021 box.width += this.split.el.getWidth();
37026 updateBox : function(box){
37027 if(this.split && !this.collapsed){
37028 var sw = this.split.el.getWidth();
37030 this.split.el.setLeft(box.x+box.width);
37031 this.split.el.setTop(box.y);
37032 this.split.el.setHeight(box.height);
37034 if(this.collapsed){
37035 this.updateBody(null, box.height);
37037 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37040 Roo.namespace("Roo.bootstrap.panel");/*
37042 * Ext JS Library 1.1.1
37043 * Copyright(c) 2006-2007, Ext JS, LLC.
37045 * Originally Released Under LGPL - original licence link has changed is not relivant.
37048 * <script type="text/javascript">
37051 * @class Roo.ContentPanel
37052 * @extends Roo.util.Observable
37053 * A basic ContentPanel element.
37054 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37055 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37056 * @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
37057 * @cfg {Boolean} closable True if the panel can be closed/removed
37058 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37059 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37060 * @cfg {Toolbar} toolbar A toolbar for this panel
37061 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37062 * @cfg {String} title The title for this panel
37063 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37064 * @cfg {String} url Calls {@link #setUrl} with this value
37065 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37066 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37067 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37068 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37069 * @cfg {Boolean} badges render the badges
37072 * Create a new ContentPanel.
37073 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37074 * @param {String/Object} config A string to set only the title or a config object
37075 * @param {String} content (optional) Set the HTML content for this panel
37076 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37078 Roo.bootstrap.panel.Content = function( config){
37080 this.tpl = config.tpl || false;
37082 var el = config.el;
37083 var content = config.content;
37085 if(config.autoCreate){ // xtype is available if this is called from factory
37088 this.el = Roo.get(el);
37089 if(!this.el && config && config.autoCreate){
37090 if(typeof config.autoCreate == "object"){
37091 if(!config.autoCreate.id){
37092 config.autoCreate.id = config.id||el;
37094 this.el = Roo.DomHelper.append(document.body,
37095 config.autoCreate, true);
37097 var elcfg = { tag: "div",
37098 cls: "roo-layout-inactive-content",
37102 elcfg.html = config.html;
37106 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37109 this.closable = false;
37110 this.loaded = false;
37111 this.active = false;
37114 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37116 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37118 this.wrapEl = this.el; //this.el.wrap();
37120 if (config.toolbar.items) {
37121 ti = config.toolbar.items ;
37122 delete config.toolbar.items ;
37126 this.toolbar.render(this.wrapEl, 'before');
37127 for(var i =0;i < ti.length;i++) {
37128 // Roo.log(['add child', items[i]]);
37129 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37131 this.toolbar.items = nitems;
37132 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37133 delete config.toolbar;
37137 // xtype created footer. - not sure if will work as we normally have to render first..
37138 if (this.footer && !this.footer.el && this.footer.xtype) {
37139 if (!this.wrapEl) {
37140 this.wrapEl = this.el.wrap();
37143 this.footer.container = this.wrapEl.createChild();
37145 this.footer = Roo.factory(this.footer, Roo);
37150 if(typeof config == "string"){
37151 this.title = config;
37153 Roo.apply(this, config);
37157 this.resizeEl = Roo.get(this.resizeEl, true);
37159 this.resizeEl = this.el;
37161 // handle view.xtype
37169 * Fires when this panel is activated.
37170 * @param {Roo.ContentPanel} this
37174 * @event deactivate
37175 * Fires when this panel is activated.
37176 * @param {Roo.ContentPanel} this
37178 "deactivate" : true,
37182 * Fires when this panel is resized if fitToFrame is true.
37183 * @param {Roo.ContentPanel} this
37184 * @param {Number} width The width after any component adjustments
37185 * @param {Number} height The height after any component adjustments
37191 * Fires when this tab is created
37192 * @param {Roo.ContentPanel} this
37203 if(this.autoScroll){
37204 this.resizeEl.setStyle("overflow", "auto");
37206 // fix randome scrolling
37207 //this.el.on('scroll', function() {
37208 // Roo.log('fix random scolling');
37209 // this.scrollTo('top',0);
37212 content = content || this.content;
37214 this.setContent(content);
37216 if(config && config.url){
37217 this.setUrl(this.url, this.params, this.loadOnce);
37222 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37224 if (this.view && typeof(this.view.xtype) != 'undefined') {
37225 this.view.el = this.el.appendChild(document.createElement("div"));
37226 this.view = Roo.factory(this.view);
37227 this.view.render && this.view.render(false, '');
37231 this.fireEvent('render', this);
37234 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37238 setRegion : function(region){
37239 this.region = region;
37240 this.setActiveClass(region && !this.background);
37244 setActiveClass: function(state)
37247 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37248 this.el.setStyle('position','relative');
37250 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37251 this.el.setStyle('position', 'absolute');
37256 * Returns the toolbar for this Panel if one was configured.
37257 * @return {Roo.Toolbar}
37259 getToolbar : function(){
37260 return this.toolbar;
37263 setActiveState : function(active)
37265 this.active = active;
37266 this.setActiveClass(active);
37268 if(this.fireEvent("deactivate", this) === false){
37273 this.fireEvent("activate", this);
37277 * Updates this panel's element
37278 * @param {String} content The new content
37279 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37281 setContent : function(content, loadScripts){
37282 this.el.update(content, loadScripts);
37285 ignoreResize : function(w, h){
37286 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37289 this.lastSize = {width: w, height: h};
37294 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37295 * @return {Roo.UpdateManager} The UpdateManager
37297 getUpdateManager : function(){
37298 return this.el.getUpdateManager();
37301 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37302 * @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:
37305 url: "your-url.php",
37306 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37307 callback: yourFunction,
37308 scope: yourObject, //(optional scope)
37311 text: "Loading...",
37316 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37317 * 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.
37318 * @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}
37319 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37320 * @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.
37321 * @return {Roo.ContentPanel} this
37324 var um = this.el.getUpdateManager();
37325 um.update.apply(um, arguments);
37331 * 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.
37332 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37333 * @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)
37334 * @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)
37335 * @return {Roo.UpdateManager} The UpdateManager
37337 setUrl : function(url, params, loadOnce){
37338 if(this.refreshDelegate){
37339 this.removeListener("activate", this.refreshDelegate);
37341 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37342 this.on("activate", this.refreshDelegate);
37343 return this.el.getUpdateManager();
37346 _handleRefresh : function(url, params, loadOnce){
37347 if(!loadOnce || !this.loaded){
37348 var updater = this.el.getUpdateManager();
37349 updater.update(url, params, this._setLoaded.createDelegate(this));
37353 _setLoaded : function(){
37354 this.loaded = true;
37358 * Returns this panel's id
37361 getId : function(){
37366 * Returns this panel's element - used by regiosn to add.
37367 * @return {Roo.Element}
37369 getEl : function(){
37370 return this.wrapEl || this.el;
37375 adjustForComponents : function(width, height)
37377 //Roo.log('adjustForComponents ');
37378 if(this.resizeEl != this.el){
37379 width -= this.el.getFrameWidth('lr');
37380 height -= this.el.getFrameWidth('tb');
37383 var te = this.toolbar.getEl();
37384 te.setWidth(width);
37385 height -= te.getHeight();
37388 var te = this.footer.getEl();
37389 te.setWidth(width);
37390 height -= te.getHeight();
37394 if(this.adjustments){
37395 width += this.adjustments[0];
37396 height += this.adjustments[1];
37398 return {"width": width, "height": height};
37401 setSize : function(width, height){
37402 if(this.fitToFrame && !this.ignoreResize(width, height)){
37403 if(this.fitContainer && this.resizeEl != this.el){
37404 this.el.setSize(width, height);
37406 var size = this.adjustForComponents(width, height);
37407 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37408 this.fireEvent('resize', this, size.width, size.height);
37413 * Returns this panel's title
37416 getTitle : function(){
37418 if (typeof(this.title) != 'object') {
37423 for (var k in this.title) {
37424 if (!this.title.hasOwnProperty(k)) {
37428 if (k.indexOf('-') >= 0) {
37429 var s = k.split('-');
37430 for (var i = 0; i<s.length; i++) {
37431 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37434 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37441 * Set this panel's title
37442 * @param {String} title
37444 setTitle : function(title){
37445 this.title = title;
37447 this.region.updatePanelTitle(this, title);
37452 * Returns true is this panel was configured to be closable
37453 * @return {Boolean}
37455 isClosable : function(){
37456 return this.closable;
37459 beforeSlide : function(){
37461 this.resizeEl.clip();
37464 afterSlide : function(){
37466 this.resizeEl.unclip();
37470 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37471 * Will fail silently if the {@link #setUrl} method has not been called.
37472 * This does not activate the panel, just updates its content.
37474 refresh : function(){
37475 if(this.refreshDelegate){
37476 this.loaded = false;
37477 this.refreshDelegate();
37482 * Destroys this panel
37484 destroy : function(){
37485 this.el.removeAllListeners();
37486 var tempEl = document.createElement("span");
37487 tempEl.appendChild(this.el.dom);
37488 tempEl.innerHTML = "";
37494 * form - if the content panel contains a form - this is a reference to it.
37495 * @type {Roo.form.Form}
37499 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37500 * This contains a reference to it.
37506 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37516 * @param {Object} cfg Xtype definition of item to add.
37520 getChildContainer: function () {
37521 return this.getEl();
37526 var ret = new Roo.factory(cfg);
37531 if (cfg.xtype.match(/^Form$/)) {
37534 //if (this.footer) {
37535 // el = this.footer.container.insertSibling(false, 'before');
37537 el = this.el.createChild();
37540 this.form = new Roo.form.Form(cfg);
37543 if ( this.form.allItems.length) {
37544 this.form.render(el.dom);
37548 // should only have one of theses..
37549 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37550 // views.. should not be just added - used named prop 'view''
37552 cfg.el = this.el.appendChild(document.createElement("div"));
37555 var ret = new Roo.factory(cfg);
37557 ret.render && ret.render(false, ''); // render blank..
37567 * @class Roo.bootstrap.panel.Grid
37568 * @extends Roo.bootstrap.panel.Content
37570 * Create a new GridPanel.
37571 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37572 * @param {Object} config A the config object
37578 Roo.bootstrap.panel.Grid = function(config)
37582 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37583 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37585 config.el = this.wrapper;
37586 //this.el = this.wrapper;
37588 if (config.container) {
37589 // ctor'ed from a Border/panel.grid
37592 this.wrapper.setStyle("overflow", "hidden");
37593 this.wrapper.addClass('roo-grid-container');
37598 if(config.toolbar){
37599 var tool_el = this.wrapper.createChild();
37600 this.toolbar = Roo.factory(config.toolbar);
37602 if (config.toolbar.items) {
37603 ti = config.toolbar.items ;
37604 delete config.toolbar.items ;
37608 this.toolbar.render(tool_el);
37609 for(var i =0;i < ti.length;i++) {
37610 // Roo.log(['add child', items[i]]);
37611 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37613 this.toolbar.items = nitems;
37615 delete config.toolbar;
37618 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37619 config.grid.scrollBody = true;;
37620 config.grid.monitorWindowResize = false; // turn off autosizing
37621 config.grid.autoHeight = false;
37622 config.grid.autoWidth = false;
37624 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37626 if (config.background) {
37627 // render grid on panel activation (if panel background)
37628 this.on('activate', function(gp) {
37629 if (!gp.grid.rendered) {
37630 gp.grid.render(this.wrapper);
37631 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37636 this.grid.render(this.wrapper);
37637 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37640 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37641 // ??? needed ??? config.el = this.wrapper;
37646 // xtype created footer. - not sure if will work as we normally have to render first..
37647 if (this.footer && !this.footer.el && this.footer.xtype) {
37649 var ctr = this.grid.getView().getFooterPanel(true);
37650 this.footer.dataSource = this.grid.dataSource;
37651 this.footer = Roo.factory(this.footer, Roo);
37652 this.footer.render(ctr);
37662 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37663 getId : function(){
37664 return this.grid.id;
37668 * Returns the grid for this panel
37669 * @return {Roo.bootstrap.Table}
37671 getGrid : function(){
37675 setSize : function(width, height){
37676 if(!this.ignoreResize(width, height)){
37677 var grid = this.grid;
37678 var size = this.adjustForComponents(width, height);
37679 var gridel = grid.getGridEl();
37680 gridel.setSize(size.width, size.height);
37682 var thd = grid.getGridEl().select('thead',true).first();
37683 var tbd = grid.getGridEl().select('tbody', true).first();
37685 tbd.setSize(width, height - thd.getHeight());
37694 beforeSlide : function(){
37695 this.grid.getView().scroller.clip();
37698 afterSlide : function(){
37699 this.grid.getView().scroller.unclip();
37702 destroy : function(){
37703 this.grid.destroy();
37705 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37710 * @class Roo.bootstrap.panel.Nest
37711 * @extends Roo.bootstrap.panel.Content
37713 * Create a new Panel, that can contain a layout.Border.
37716 * @param {Roo.BorderLayout} layout The layout for this panel
37717 * @param {String/Object} config A string to set only the title or a config object
37719 Roo.bootstrap.panel.Nest = function(config)
37721 // construct with only one argument..
37722 /* FIXME - implement nicer consturctors
37723 if (layout.layout) {
37725 layout = config.layout;
37726 delete config.layout;
37728 if (layout.xtype && !layout.getEl) {
37729 // then layout needs constructing..
37730 layout = Roo.factory(layout, Roo);
37734 config.el = config.layout.getEl();
37736 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37738 config.layout.monitorWindowResize = false; // turn off autosizing
37739 this.layout = config.layout;
37740 this.layout.getEl().addClass("roo-layout-nested-layout");
37747 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37749 setSize : function(width, height){
37750 if(!this.ignoreResize(width, height)){
37751 var size = this.adjustForComponents(width, height);
37752 var el = this.layout.getEl();
37753 if (size.height < 1) {
37754 el.setWidth(size.width);
37756 el.setSize(size.width, size.height);
37758 var touch = el.dom.offsetWidth;
37759 this.layout.layout();
37760 // ie requires a double layout on the first pass
37761 if(Roo.isIE && !this.initialized){
37762 this.initialized = true;
37763 this.layout.layout();
37768 // activate all subpanels if not currently active..
37770 setActiveState : function(active){
37771 this.active = active;
37772 this.setActiveClass(active);
37775 this.fireEvent("deactivate", this);
37779 this.fireEvent("activate", this);
37780 // not sure if this should happen before or after..
37781 if (!this.layout) {
37782 return; // should not happen..
37785 for (var r in this.layout.regions) {
37786 reg = this.layout.getRegion(r);
37787 if (reg.getActivePanel()) {
37788 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37789 reg.setActivePanel(reg.getActivePanel());
37792 if (!reg.panels.length) {
37795 reg.showPanel(reg.getPanel(0));
37804 * Returns the nested BorderLayout for this panel
37805 * @return {Roo.BorderLayout}
37807 getLayout : function(){
37808 return this.layout;
37812 * Adds a xtype elements to the layout of the nested panel
37816 xtype : 'ContentPanel',
37823 xtype : 'NestedLayoutPanel',
37829 items : [ ... list of content panels or nested layout panels.. ]
37833 * @param {Object} cfg Xtype definition of item to add.
37835 addxtype : function(cfg) {
37836 return this.layout.addxtype(cfg);
37841 * Ext JS Library 1.1.1
37842 * Copyright(c) 2006-2007, Ext JS, LLC.
37844 * Originally Released Under LGPL - original licence link has changed is not relivant.
37847 * <script type="text/javascript">
37850 * @class Roo.TabPanel
37851 * @extends Roo.util.Observable
37852 * A lightweight tab container.
37856 // basic tabs 1, built from existing content
37857 var tabs = new Roo.TabPanel("tabs1");
37858 tabs.addTab("script", "View Script");
37859 tabs.addTab("markup", "View Markup");
37860 tabs.activate("script");
37862 // more advanced tabs, built from javascript
37863 var jtabs = new Roo.TabPanel("jtabs");
37864 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37866 // set up the UpdateManager
37867 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37868 var updater = tab2.getUpdateManager();
37869 updater.setDefaultUrl("ajax1.htm");
37870 tab2.on('activate', updater.refresh, updater, true);
37872 // Use setUrl for Ajax loading
37873 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37874 tab3.setUrl("ajax2.htm", null, true);
37877 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37880 jtabs.activate("jtabs-1");
37883 * Create a new TabPanel.
37884 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37885 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37887 Roo.bootstrap.panel.Tabs = function(config){
37889 * The container element for this TabPanel.
37890 * @type Roo.Element
37892 this.el = Roo.get(config.el);
37895 if(typeof config == "boolean"){
37896 this.tabPosition = config ? "bottom" : "top";
37898 Roo.apply(this, config);
37902 if(this.tabPosition == "bottom"){
37903 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37904 this.el.addClass("roo-tabs-bottom");
37906 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37907 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37908 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37910 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37912 if(this.tabPosition != "bottom"){
37913 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37914 * @type Roo.Element
37916 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37917 this.el.addClass("roo-tabs-top");
37921 this.bodyEl.setStyle("position", "relative");
37923 this.active = null;
37924 this.activateDelegate = this.activate.createDelegate(this);
37929 * Fires when the active tab changes
37930 * @param {Roo.TabPanel} this
37931 * @param {Roo.TabPanelItem} activePanel The new active tab
37935 * @event beforetabchange
37936 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37937 * @param {Roo.TabPanel} this
37938 * @param {Object} e Set cancel to true on this object to cancel the tab change
37939 * @param {Roo.TabPanelItem} tab The tab being changed to
37941 "beforetabchange" : true
37944 Roo.EventManager.onWindowResize(this.onResize, this);
37945 this.cpad = this.el.getPadding("lr");
37946 this.hiddenCount = 0;
37949 // toolbar on the tabbar support...
37950 if (this.toolbar) {
37951 alert("no toolbar support yet");
37952 this.toolbar = false;
37954 var tcfg = this.toolbar;
37955 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37956 this.toolbar = new Roo.Toolbar(tcfg);
37957 if (Roo.isSafari) {
37958 var tbl = tcfg.container.child('table', true);
37959 tbl.setAttribute('width', '100%');
37967 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37970 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37972 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37974 tabPosition : "top",
37976 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37978 currentTabWidth : 0,
37980 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37984 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37988 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37990 preferredTabWidth : 175,
37992 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37994 resizeTabs : false,
37996 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37998 monitorResize : true,
38000 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38005 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38006 * @param {String} id The id of the div to use <b>or create</b>
38007 * @param {String} text The text for the tab
38008 * @param {String} content (optional) Content to put in the TabPanelItem body
38009 * @param {Boolean} closable (optional) True to create a close icon on the tab
38010 * @return {Roo.TabPanelItem} The created TabPanelItem
38012 addTab : function(id, text, content, closable, tpl)
38014 var item = new Roo.bootstrap.panel.TabItem({
38018 closable : closable,
38021 this.addTabItem(item);
38023 item.setContent(content);
38029 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38030 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38031 * @return {Roo.TabPanelItem}
38033 getTab : function(id){
38034 return this.items[id];
38038 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38039 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38041 hideTab : function(id){
38042 var t = this.items[id];
38045 this.hiddenCount++;
38046 this.autoSizeTabs();
38051 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38052 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38054 unhideTab : function(id){
38055 var t = this.items[id];
38057 t.setHidden(false);
38058 this.hiddenCount--;
38059 this.autoSizeTabs();
38064 * Adds an existing {@link Roo.TabPanelItem}.
38065 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38067 addTabItem : function(item){
38068 this.items[item.id] = item;
38069 this.items.push(item);
38070 // if(this.resizeTabs){
38071 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38072 // this.autoSizeTabs();
38074 // item.autoSize();
38079 * Removes a {@link Roo.TabPanelItem}.
38080 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38082 removeTab : function(id){
38083 var items = this.items;
38084 var tab = items[id];
38085 if(!tab) { return; }
38086 var index = items.indexOf(tab);
38087 if(this.active == tab && items.length > 1){
38088 var newTab = this.getNextAvailable(index);
38093 this.stripEl.dom.removeChild(tab.pnode.dom);
38094 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38095 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38097 items.splice(index, 1);
38098 delete this.items[tab.id];
38099 tab.fireEvent("close", tab);
38100 tab.purgeListeners();
38101 this.autoSizeTabs();
38104 getNextAvailable : function(start){
38105 var items = this.items;
38107 // look for a next tab that will slide over to
38108 // replace the one being removed
38109 while(index < items.length){
38110 var item = items[++index];
38111 if(item && !item.isHidden()){
38115 // if one isn't found select the previous tab (on the left)
38118 var item = items[--index];
38119 if(item && !item.isHidden()){
38127 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38128 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38130 disableTab : function(id){
38131 var tab = this.items[id];
38132 if(tab && this.active != tab){
38138 * Enables a {@link Roo.TabPanelItem} that is disabled.
38139 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38141 enableTab : function(id){
38142 var tab = this.items[id];
38147 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38148 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38149 * @return {Roo.TabPanelItem} The TabPanelItem.
38151 activate : function(id){
38152 var tab = this.items[id];
38156 if(tab == this.active || tab.disabled){
38160 this.fireEvent("beforetabchange", this, e, tab);
38161 if(e.cancel !== true && !tab.disabled){
38163 this.active.hide();
38165 this.active = this.items[id];
38166 this.active.show();
38167 this.fireEvent("tabchange", this, this.active);
38173 * Gets the active {@link Roo.TabPanelItem}.
38174 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38176 getActiveTab : function(){
38177 return this.active;
38181 * Updates the tab body element to fit the height of the container element
38182 * for overflow scrolling
38183 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38185 syncHeight : function(targetHeight){
38186 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38187 var bm = this.bodyEl.getMargins();
38188 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38189 this.bodyEl.setHeight(newHeight);
38193 onResize : function(){
38194 if(this.monitorResize){
38195 this.autoSizeTabs();
38200 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38202 beginUpdate : function(){
38203 this.updating = true;
38207 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38209 endUpdate : function(){
38210 this.updating = false;
38211 this.autoSizeTabs();
38215 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38217 autoSizeTabs : function(){
38218 var count = this.items.length;
38219 var vcount = count - this.hiddenCount;
38220 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38223 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38224 var availWidth = Math.floor(w / vcount);
38225 var b = this.stripBody;
38226 if(b.getWidth() > w){
38227 var tabs = this.items;
38228 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38229 if(availWidth < this.minTabWidth){
38230 /*if(!this.sleft){ // incomplete scrolling code
38231 this.createScrollButtons();
38234 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38237 if(this.currentTabWidth < this.preferredTabWidth){
38238 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38244 * Returns the number of tabs in this TabPanel.
38247 getCount : function(){
38248 return this.items.length;
38252 * Resizes all the tabs to the passed width
38253 * @param {Number} The new width
38255 setTabWidth : function(width){
38256 this.currentTabWidth = width;
38257 for(var i = 0, len = this.items.length; i < len; i++) {
38258 if(!this.items[i].isHidden()) {
38259 this.items[i].setWidth(width);
38265 * Destroys this TabPanel
38266 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38268 destroy : function(removeEl){
38269 Roo.EventManager.removeResizeListener(this.onResize, this);
38270 for(var i = 0, len = this.items.length; i < len; i++){
38271 this.items[i].purgeListeners();
38273 if(removeEl === true){
38274 this.el.update("");
38279 createStrip : function(container)
38281 var strip = document.createElement("nav");
38282 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38283 container.appendChild(strip);
38287 createStripList : function(strip)
38289 // div wrapper for retard IE
38290 // returns the "tr" element.
38291 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38292 //'<div class="x-tabs-strip-wrap">'+
38293 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38294 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38295 return strip.firstChild; //.firstChild.firstChild.firstChild;
38297 createBody : function(container)
38299 var body = document.createElement("div");
38300 Roo.id(body, "tab-body");
38301 //Roo.fly(body).addClass("x-tabs-body");
38302 Roo.fly(body).addClass("tab-content");
38303 container.appendChild(body);
38306 createItemBody :function(bodyEl, id){
38307 var body = Roo.getDom(id);
38309 body = document.createElement("div");
38312 //Roo.fly(body).addClass("x-tabs-item-body");
38313 Roo.fly(body).addClass("tab-pane");
38314 bodyEl.insertBefore(body, bodyEl.firstChild);
38318 createStripElements : function(stripEl, text, closable, tpl)
38320 var td = document.createElement("li"); // was td..
38323 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38326 stripEl.appendChild(td);
38328 td.className = "x-tabs-closable";
38329 if(!this.closeTpl){
38330 this.closeTpl = new Roo.Template(
38331 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38332 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38333 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38336 var el = this.closeTpl.overwrite(td, {"text": text});
38337 var close = el.getElementsByTagName("div")[0];
38338 var inner = el.getElementsByTagName("em")[0];
38339 return {"el": el, "close": close, "inner": inner};
38342 // not sure what this is..
38343 // if(!this.tabTpl){
38344 //this.tabTpl = new Roo.Template(
38345 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38346 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38348 // this.tabTpl = new Roo.Template(
38349 // '<a href="#">' +
38350 // '<span unselectable="on"' +
38351 // (this.disableTooltips ? '' : ' title="{text}"') +
38352 // ' >{text}</span></a>'
38358 var template = tpl || this.tabTpl || false;
38362 template = new Roo.Template(
38364 '<span unselectable="on"' +
38365 (this.disableTooltips ? '' : ' title="{text}"') +
38366 ' >{text}</span></a>'
38370 switch (typeof(template)) {
38374 template = new Roo.Template(template);
38380 var el = template.overwrite(td, {"text": text});
38382 var inner = el.getElementsByTagName("span")[0];
38384 return {"el": el, "inner": inner};
38392 * @class Roo.TabPanelItem
38393 * @extends Roo.util.Observable
38394 * Represents an individual item (tab plus body) in a TabPanel.
38395 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38396 * @param {String} id The id of this TabPanelItem
38397 * @param {String} text The text for the tab of this TabPanelItem
38398 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38400 Roo.bootstrap.panel.TabItem = function(config){
38402 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38403 * @type Roo.TabPanel
38405 this.tabPanel = config.panel;
38407 * The id for this TabPanelItem
38410 this.id = config.id;
38412 this.disabled = false;
38414 this.text = config.text;
38416 this.loaded = false;
38417 this.closable = config.closable;
38420 * The body element for this TabPanelItem.
38421 * @type Roo.Element
38423 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38424 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38425 this.bodyEl.setStyle("display", "block");
38426 this.bodyEl.setStyle("zoom", "1");
38427 //this.hideAction();
38429 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38431 this.el = Roo.get(els.el);
38432 this.inner = Roo.get(els.inner, true);
38433 this.textEl = Roo.get(this.el.dom.firstChild, true);
38434 this.pnode = Roo.get(els.el.parentNode, true);
38435 // this.el.on("mousedown", this.onTabMouseDown, this);
38436 this.el.on("click", this.onTabClick, this);
38438 if(config.closable){
38439 var c = Roo.get(els.close, true);
38440 c.dom.title = this.closeText;
38441 c.addClassOnOver("close-over");
38442 c.on("click", this.closeClick, this);
38448 * Fires when this tab becomes the active tab.
38449 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38450 * @param {Roo.TabPanelItem} this
38454 * @event beforeclose
38455 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38456 * @param {Roo.TabPanelItem} this
38457 * @param {Object} e Set cancel to true on this object to cancel the close.
38459 "beforeclose": true,
38462 * Fires when this tab is closed.
38463 * @param {Roo.TabPanelItem} this
38467 * @event deactivate
38468 * Fires when this tab is no longer the active tab.
38469 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38470 * @param {Roo.TabPanelItem} this
38472 "deactivate" : true
38474 this.hidden = false;
38476 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38479 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38481 purgeListeners : function(){
38482 Roo.util.Observable.prototype.purgeListeners.call(this);
38483 this.el.removeAllListeners();
38486 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38489 this.pnode.addClass("active");
38492 this.tabPanel.stripWrap.repaint();
38494 this.fireEvent("activate", this.tabPanel, this);
38498 * Returns true if this tab is the active tab.
38499 * @return {Boolean}
38501 isActive : function(){
38502 return this.tabPanel.getActiveTab() == this;
38506 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38509 this.pnode.removeClass("active");
38511 this.fireEvent("deactivate", this.tabPanel, this);
38514 hideAction : function(){
38515 this.bodyEl.hide();
38516 this.bodyEl.setStyle("position", "absolute");
38517 this.bodyEl.setLeft("-20000px");
38518 this.bodyEl.setTop("-20000px");
38521 showAction : function(){
38522 this.bodyEl.setStyle("position", "relative");
38523 this.bodyEl.setTop("");
38524 this.bodyEl.setLeft("");
38525 this.bodyEl.show();
38529 * Set the tooltip for the tab.
38530 * @param {String} tooltip The tab's tooltip
38532 setTooltip : function(text){
38533 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38534 this.textEl.dom.qtip = text;
38535 this.textEl.dom.removeAttribute('title');
38537 this.textEl.dom.title = text;
38541 onTabClick : function(e){
38542 e.preventDefault();
38543 this.tabPanel.activate(this.id);
38546 onTabMouseDown : function(e){
38547 e.preventDefault();
38548 this.tabPanel.activate(this.id);
38551 getWidth : function(){
38552 return this.inner.getWidth();
38555 setWidth : function(width){
38556 var iwidth = width - this.pnode.getPadding("lr");
38557 this.inner.setWidth(iwidth);
38558 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38559 this.pnode.setWidth(width);
38563 * Show or hide the tab
38564 * @param {Boolean} hidden True to hide or false to show.
38566 setHidden : function(hidden){
38567 this.hidden = hidden;
38568 this.pnode.setStyle("display", hidden ? "none" : "");
38572 * Returns true if this tab is "hidden"
38573 * @return {Boolean}
38575 isHidden : function(){
38576 return this.hidden;
38580 * Returns the text for this tab
38583 getText : function(){
38587 autoSize : function(){
38588 //this.el.beginMeasure();
38589 this.textEl.setWidth(1);
38591 * #2804 [new] Tabs in Roojs
38592 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38594 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38595 //this.el.endMeasure();
38599 * Sets the text for the tab (Note: this also sets the tooltip text)
38600 * @param {String} text The tab's text and tooltip
38602 setText : function(text){
38604 this.textEl.update(text);
38605 this.setTooltip(text);
38606 //if(!this.tabPanel.resizeTabs){
38607 // this.autoSize();
38611 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38613 activate : function(){
38614 this.tabPanel.activate(this.id);
38618 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38620 disable : function(){
38621 if(this.tabPanel.active != this){
38622 this.disabled = true;
38623 this.pnode.addClass("disabled");
38628 * Enables this TabPanelItem if it was previously disabled.
38630 enable : function(){
38631 this.disabled = false;
38632 this.pnode.removeClass("disabled");
38636 * Sets the content for this TabPanelItem.
38637 * @param {String} content The content
38638 * @param {Boolean} loadScripts true to look for and load scripts
38640 setContent : function(content, loadScripts){
38641 this.bodyEl.update(content, loadScripts);
38645 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38646 * @return {Roo.UpdateManager} The UpdateManager
38648 getUpdateManager : function(){
38649 return this.bodyEl.getUpdateManager();
38653 * Set a URL to be used to load the content for this TabPanelItem.
38654 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38655 * @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)
38656 * @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)
38657 * @return {Roo.UpdateManager} The UpdateManager
38659 setUrl : function(url, params, loadOnce){
38660 if(this.refreshDelegate){
38661 this.un('activate', this.refreshDelegate);
38663 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38664 this.on("activate", this.refreshDelegate);
38665 return this.bodyEl.getUpdateManager();
38669 _handleRefresh : function(url, params, loadOnce){
38670 if(!loadOnce || !this.loaded){
38671 var updater = this.bodyEl.getUpdateManager();
38672 updater.update(url, params, this._setLoaded.createDelegate(this));
38677 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38678 * Will fail silently if the setUrl method has not been called.
38679 * This does not activate the panel, just updates its content.
38681 refresh : function(){
38682 if(this.refreshDelegate){
38683 this.loaded = false;
38684 this.refreshDelegate();
38689 _setLoaded : function(){
38690 this.loaded = true;
38694 closeClick : function(e){
38697 this.fireEvent("beforeclose", this, o);
38698 if(o.cancel !== true){
38699 this.tabPanel.removeTab(this.id);
38703 * The text displayed in the tooltip for the close icon.
38706 closeText : "Close this tab"
38709 * This script refer to:
38710 * Title: International Telephone Input
38711 * Author: Jack O'Connor
38712 * Code version: v12.1.12
38713 * Availability: https://github.com/jackocnr/intl-tel-input.git
38716 Roo.bootstrap.PhoneInputData = function() {
38719 "Afghanistan (افغانستان)",
38724 "Albania (Shqipëri)",
38729 "Algeria (الجزائر)",
38754 "Antigua and Barbuda",
38764 "Armenia (Հայաստան)",
38780 "Austria (Österreich)",
38785 "Azerbaijan (Azərbaycan)",
38795 "Bahrain (البحرين)",
38800 "Bangladesh (বাংলাদেশ)",
38810 "Belarus (Беларусь)",
38815 "Belgium (België)",
38845 "Bosnia and Herzegovina (Босна и Херцеговина)",
38860 "British Indian Ocean Territory",
38865 "British Virgin Islands",
38875 "Bulgaria (България)",
38885 "Burundi (Uburundi)",
38890 "Cambodia (កម្ពុជា)",
38895 "Cameroon (Cameroun)",
38904 ["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"]
38907 "Cape Verde (Kabu Verdi)",
38912 "Caribbean Netherlands",
38923 "Central African Republic (République centrafricaine)",
38943 "Christmas Island",
38949 "Cocos (Keeling) Islands",
38960 "Comoros (جزر القمر)",
38965 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38970 "Congo (Republic) (Congo-Brazzaville)",
38990 "Croatia (Hrvatska)",
39011 "Czech Republic (Česká republika)",
39016 "Denmark (Danmark)",
39031 "Dominican Republic (República Dominicana)",
39035 ["809", "829", "849"]
39053 "Equatorial Guinea (Guinea Ecuatorial)",
39073 "Falkland Islands (Islas Malvinas)",
39078 "Faroe Islands (Føroyar)",
39099 "French Guiana (Guyane française)",
39104 "French Polynesia (Polynésie française)",
39119 "Georgia (საქართველო)",
39124 "Germany (Deutschland)",
39144 "Greenland (Kalaallit Nunaat)",
39181 "Guinea-Bissau (Guiné Bissau)",
39206 "Hungary (Magyarország)",
39211 "Iceland (Ísland)",
39231 "Iraq (العراق)",
39247 "Israel (ישראל)",
39274 "Jordan (الأردن)",
39279 "Kazakhstan (Казахстан)",
39300 "Kuwait (الكويت)",
39305 "Kyrgyzstan (Кыргызстан)",
39315 "Latvia (Latvija)",
39320 "Lebanon (لبنان)",
39335 "Libya (ليبيا)",
39345 "Lithuania (Lietuva)",
39360 "Macedonia (FYROM) (Македонија)",
39365 "Madagascar (Madagasikara)",
39395 "Marshall Islands",
39405 "Mauritania (موريتانيا)",
39410 "Mauritius (Moris)",
39431 "Moldova (Republica Moldova)",
39441 "Mongolia (Монгол)",
39446 "Montenegro (Crna Gora)",
39456 "Morocco (المغرب)",
39462 "Mozambique (Moçambique)",
39467 "Myanmar (Burma) (မြန်မာ)",
39472 "Namibia (Namibië)",
39487 "Netherlands (Nederland)",
39492 "New Caledonia (Nouvelle-Calédonie)",
39527 "North Korea (조선 민주주의 인민 공화국)",
39532 "Northern Mariana Islands",
39548 "Pakistan (پاکستان)",
39558 "Palestine (فلسطين)",
39568 "Papua New Guinea",
39610 "Réunion (La Réunion)",
39616 "Romania (România)",
39632 "Saint Barthélemy",
39643 "Saint Kitts and Nevis",
39653 "Saint Martin (Saint-Martin (partie française))",
39659 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39664 "Saint Vincent and the Grenadines",
39679 "São Tomé and Príncipe (São Tomé e Príncipe)",
39684 "Saudi Arabia (المملكة العربية السعودية)",
39689 "Senegal (Sénégal)",
39719 "Slovakia (Slovensko)",
39724 "Slovenia (Slovenija)",
39734 "Somalia (Soomaaliya)",
39744 "South Korea (대한민국)",
39749 "South Sudan (جنوب السودان)",
39759 "Sri Lanka (ශ්රී ලංකාව)",
39764 "Sudan (السودان)",
39774 "Svalbard and Jan Mayen",
39785 "Sweden (Sverige)",
39790 "Switzerland (Schweiz)",
39795 "Syria (سوريا)",
39840 "Trinidad and Tobago",
39845 "Tunisia (تونس)",
39850 "Turkey (Türkiye)",
39860 "Turks and Caicos Islands",
39870 "U.S. Virgin Islands",
39880 "Ukraine (Україна)",
39885 "United Arab Emirates (الإمارات العربية المتحدة)",
39907 "Uzbekistan (Oʻzbekiston)",
39917 "Vatican City (Città del Vaticano)",
39928 "Vietnam (Việt Nam)",
39933 "Wallis and Futuna (Wallis-et-Futuna)",
39938 "Western Sahara (الصحراء الغربية)",
39944 "Yemen (اليمن)",
39968 * This script refer to:
39969 * Title: International Telephone Input
39970 * Author: Jack O'Connor
39971 * Code version: v12.1.12
39972 * Availability: https://github.com/jackocnr/intl-tel-input.git
39976 * @class Roo.bootstrap.PhoneInput
39977 * @extends Roo.bootstrap.TriggerField
39978 * An input with International dial-code selection
39980 * @cfg {String} defaultDialCode default '+852'
39981 * @cfg {Array} preferedCountries default []
39984 * Create a new PhoneInput.
39985 * @param {Object} config Configuration options
39988 Roo.bootstrap.PhoneInput = function(config) {
39989 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39992 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39994 listWidth: undefined,
39996 selectedClass: 'active',
39998 invalidClass : "has-warning",
40000 validClass: 'has-success',
40002 allowed: '0123456789',
40007 * @cfg {String} defaultDialCode The default dial code when initializing the input
40009 defaultDialCode: '+852',
40012 * @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
40014 preferedCountries: false,
40016 getAutoCreate : function()
40018 var data = Roo.bootstrap.PhoneInputData();
40019 var align = this.labelAlign || this.parentLabelAlign();
40022 this.allCountries = [];
40023 this.dialCodeMapping = [];
40025 for (var i = 0; i < data.length; i++) {
40027 this.allCountries[i] = {
40031 priority: c[3] || 0,
40032 areaCodes: c[4] || null
40034 this.dialCodeMapping[c[2]] = {
40037 priority: c[3] || 0,
40038 areaCodes: c[4] || null
40050 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40051 maxlength: this.max_length,
40052 cls : 'form-control tel-input',
40053 autocomplete: 'new-password'
40056 var hiddenInput = {
40059 cls: 'hidden-tel-input'
40063 hiddenInput.name = this.name;
40066 if (this.disabled) {
40067 input.disabled = true;
40070 var flag_container = {
40087 cls: this.hasFeedback ? 'has-feedback' : '',
40093 cls: 'dial-code-holder',
40100 cls: 'roo-select2-container input-group',
40107 if (this.fieldLabel.length) {
40110 tooltip: 'This field is required'
40116 cls: 'control-label',
40122 html: this.fieldLabel
40125 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40131 if(this.indicatorpos == 'right') {
40132 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40139 if(align == 'left') {
40147 if(this.labelWidth > 12){
40148 label.style = "width: " + this.labelWidth + 'px';
40150 if(this.labelWidth < 13 && this.labelmd == 0){
40151 this.labelmd = this.labelWidth;
40153 if(this.labellg > 0){
40154 label.cls += ' col-lg-' + this.labellg;
40155 input.cls += ' col-lg-' + (12 - this.labellg);
40157 if(this.labelmd > 0){
40158 label.cls += ' col-md-' + this.labelmd;
40159 container.cls += ' col-md-' + (12 - this.labelmd);
40161 if(this.labelsm > 0){
40162 label.cls += ' col-sm-' + this.labelsm;
40163 container.cls += ' col-sm-' + (12 - this.labelsm);
40165 if(this.labelxs > 0){
40166 label.cls += ' col-xs-' + this.labelxs;
40167 container.cls += ' col-xs-' + (12 - this.labelxs);
40177 var settings = this;
40179 ['xs','sm','md','lg'].map(function(size){
40180 if (settings[size]) {
40181 cfg.cls += ' col-' + size + '-' + settings[size];
40185 this.store = new Roo.data.Store({
40186 proxy : new Roo.data.MemoryProxy({}),
40187 reader : new Roo.data.JsonReader({
40198 'name' : 'dialCode',
40202 'name' : 'priority',
40206 'name' : 'areaCodes',
40213 if(!this.preferedCountries) {
40214 this.preferedCountries = [
40221 var p = this.preferedCountries.reverse();
40224 for (var i = 0; i < p.length; i++) {
40225 for (var j = 0; j < this.allCountries.length; j++) {
40226 if(this.allCountries[j].iso2 == p[i]) {
40227 var t = this.allCountries[j];
40228 this.allCountries.splice(j,1);
40229 this.allCountries.unshift(t);
40235 this.store.proxy.data = {
40237 data: this.allCountries
40243 initEvents : function()
40246 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40248 this.indicator = this.indicatorEl();
40249 this.flag = this.flagEl();
40250 this.dialCodeHolder = this.dialCodeHolderEl();
40252 this.trigger = this.el.select('div.flag-box',true).first();
40253 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40258 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40259 _this.list.setWidth(lw);
40262 this.list.on('mouseover', this.onViewOver, this);
40263 this.list.on('mousemove', this.onViewMove, this);
40264 this.inputEl().on("keyup", this.onKeyUp, this);
40265 this.inputEl().on("keypress", this.onKeyPress, this);
40267 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40269 this.view = new Roo.View(this.list, this.tpl, {
40270 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40273 this.view.on('click', this.onViewClick, this);
40274 this.setValue(this.defaultDialCode);
40277 onTriggerClick : function(e)
40279 Roo.log('trigger click');
40284 if(this.isExpanded()){
40286 this.hasFocus = false;
40288 this.store.load({});
40289 this.hasFocus = true;
40294 isExpanded : function()
40296 return this.list.isVisible();
40299 collapse : function()
40301 if(!this.isExpanded()){
40305 Roo.get(document).un('mousedown', this.collapseIf, this);
40306 Roo.get(document).un('mousewheel', this.collapseIf, this);
40307 this.fireEvent('collapse', this);
40311 expand : function()
40315 if(this.isExpanded() || !this.hasFocus){
40319 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40320 this.list.setWidth(lw);
40323 this.restrictHeight();
40325 Roo.get(document).on('mousedown', this.collapseIf, this);
40326 Roo.get(document).on('mousewheel', this.collapseIf, this);
40328 this.fireEvent('expand', this);
40331 restrictHeight : function()
40333 this.list.alignTo(this.inputEl(), this.listAlign);
40334 this.list.alignTo(this.inputEl(), this.listAlign);
40337 onViewOver : function(e, t)
40339 if(this.inKeyMode){
40342 var item = this.view.findItemFromChild(t);
40345 var index = this.view.indexOf(item);
40346 this.select(index, false);
40351 onViewClick : function(view, doFocus, el, e)
40353 var index = this.view.getSelectedIndexes()[0];
40355 var r = this.store.getAt(index);
40358 this.onSelect(r, index);
40360 if(doFocus !== false && !this.blockFocus){
40361 this.inputEl().focus();
40365 onViewMove : function(e, t)
40367 this.inKeyMode = false;
40370 select : function(index, scrollIntoView)
40372 this.selectedIndex = index;
40373 this.view.select(index);
40374 if(scrollIntoView !== false){
40375 var el = this.view.getNode(index);
40377 this.list.scrollChildIntoView(el, false);
40382 createList : function()
40384 this.list = Roo.get(document.body).createChild({
40386 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40387 style: 'display:none'
40390 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40393 collapseIf : function(e)
40395 var in_combo = e.within(this.el);
40396 var in_list = e.within(this.list);
40397 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40399 if (in_combo || in_list || is_list) {
40405 onSelect : function(record, index)
40407 if(this.fireEvent('beforeselect', this, record, index) !== false){
40409 this.setFlagClass(record.data.iso2);
40410 this.setDialCode(record.data.dialCode);
40411 this.hasFocus = false;
40413 this.fireEvent('select', this, record, index);
40417 flagEl : function()
40419 var flag = this.el.select('div.flag',true).first();
40426 dialCodeHolderEl : function()
40428 var d = this.el.select('input.dial-code-holder',true).first();
40435 setDialCode : function(v)
40437 this.dialCodeHolder.dom.value = '+'+v;
40440 setFlagClass : function(n)
40442 this.flag.dom.className = 'flag '+n;
40445 getValue : function()
40447 var v = this.inputEl().getValue();
40448 if(this.dialCodeHolder) {
40449 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40454 setValue : function(v)
40456 var d = this.getDialCode(v);
40458 //invalid dial code
40459 if(v.length == 0 || !d || d.length == 0) {
40461 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40462 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40468 this.setFlagClass(this.dialCodeMapping[d].iso2);
40469 this.setDialCode(d);
40470 this.inputEl().dom.value = v.replace('+'+d,'');
40471 this.hiddenEl().dom.value = this.getValue();
40476 getDialCode : function(v)
40480 if (v.length == 0) {
40481 return this.dialCodeHolder.dom.value;
40485 if (v.charAt(0) != "+") {
40488 var numericChars = "";
40489 for (var i = 1; i < v.length; i++) {
40490 var c = v.charAt(i);
40493 if (this.dialCodeMapping[numericChars]) {
40494 dialCode = v.substr(1, i);
40496 if (numericChars.length == 4) {
40506 this.setValue(this.defaultDialCode);
40510 hiddenEl : function()
40512 return this.el.select('input.hidden-tel-input',true).first();
40515 // after setting val
40516 onKeyUp : function(e){
40517 this.setValue(this.getValue());
40520 onKeyPress : function(e){
40521 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40528 * @class Roo.bootstrap.MoneyField
40529 * @extends Roo.bootstrap.ComboBox
40530 * Bootstrap MoneyField class
40533 * Create a new MoneyField.
40534 * @param {Object} config Configuration options
40537 Roo.bootstrap.MoneyField = function(config) {
40539 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40543 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40546 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40548 allowDecimals : true,
40550 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40552 decimalSeparator : ".",
40554 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40556 decimalPrecision : 0,
40558 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40560 allowNegative : true,
40562 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40566 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40568 minValue : Number.NEGATIVE_INFINITY,
40570 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40572 maxValue : Number.MAX_VALUE,
40574 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40576 minText : "The minimum value for this field is {0}",
40578 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40580 maxText : "The maximum value for this field is {0}",
40582 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40583 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40585 nanText : "{0} is not a valid number",
40587 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40591 * @cfg {String} defaults currency of the MoneyField
40592 * value should be in lkey
40594 defaultCurrency : false,
40596 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40598 thousandsDelimiter : false,
40600 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40611 getAutoCreate : function()
40613 var align = this.labelAlign || this.parentLabelAlign();
40625 cls : 'form-control roo-money-amount-input',
40626 autocomplete: 'new-password'
40629 var hiddenInput = {
40633 cls: 'hidden-number-input'
40636 if(this.max_length) {
40637 input.maxlength = this.max_length;
40641 hiddenInput.name = this.name;
40644 if (this.disabled) {
40645 input.disabled = true;
40648 var clg = 12 - this.inputlg;
40649 var cmd = 12 - this.inputmd;
40650 var csm = 12 - this.inputsm;
40651 var cxs = 12 - this.inputxs;
40655 cls : 'row roo-money-field',
40659 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40663 cls: 'roo-select2-container input-group',
40667 cls : 'form-control roo-money-currency-input',
40668 autocomplete: 'new-password',
40670 name : this.currencyName
40674 cls : 'input-group-addon',
40688 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40692 cls: this.hasFeedback ? 'has-feedback' : '',
40703 if (this.fieldLabel.length) {
40706 tooltip: 'This field is required'
40712 cls: 'control-label',
40718 html: this.fieldLabel
40721 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40727 if(this.indicatorpos == 'right') {
40728 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40735 if(align == 'left') {
40743 if(this.labelWidth > 12){
40744 label.style = "width: " + this.labelWidth + 'px';
40746 if(this.labelWidth < 13 && this.labelmd == 0){
40747 this.labelmd = this.labelWidth;
40749 if(this.labellg > 0){
40750 label.cls += ' col-lg-' + this.labellg;
40751 input.cls += ' col-lg-' + (12 - this.labellg);
40753 if(this.labelmd > 0){
40754 label.cls += ' col-md-' + this.labelmd;
40755 container.cls += ' col-md-' + (12 - this.labelmd);
40757 if(this.labelsm > 0){
40758 label.cls += ' col-sm-' + this.labelsm;
40759 container.cls += ' col-sm-' + (12 - this.labelsm);
40761 if(this.labelxs > 0){
40762 label.cls += ' col-xs-' + this.labelxs;
40763 container.cls += ' col-xs-' + (12 - this.labelxs);
40774 var settings = this;
40776 ['xs','sm','md','lg'].map(function(size){
40777 if (settings[size]) {
40778 cfg.cls += ' col-' + size + '-' + settings[size];
40785 initEvents : function()
40787 this.indicator = this.indicatorEl();
40789 this.initCurrencyEvent();
40791 this.initNumberEvent();
40794 initCurrencyEvent : function()
40797 throw "can not find store for combo";
40800 this.store = Roo.factory(this.store, Roo.data);
40801 this.store.parent = this;
40805 this.triggerEl = this.el.select('.input-group-addon', true).first();
40807 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40812 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40813 _this.list.setWidth(lw);
40816 this.list.on('mouseover', this.onViewOver, this);
40817 this.list.on('mousemove', this.onViewMove, this);
40818 this.list.on('scroll', this.onViewScroll, this);
40821 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40824 this.view = new Roo.View(this.list, this.tpl, {
40825 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40828 this.view.on('click', this.onViewClick, this);
40830 this.store.on('beforeload', this.onBeforeLoad, this);
40831 this.store.on('load', this.onLoad, this);
40832 this.store.on('loadexception', this.onLoadException, this);
40834 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40835 "up" : function(e){
40836 this.inKeyMode = true;
40840 "down" : function(e){
40841 if(!this.isExpanded()){
40842 this.onTriggerClick();
40844 this.inKeyMode = true;
40849 "enter" : function(e){
40852 if(this.fireEvent("specialkey", this, e)){
40853 this.onViewClick(false);
40859 "esc" : function(e){
40863 "tab" : function(e){
40866 if(this.fireEvent("specialkey", this, e)){
40867 this.onViewClick(false);
40875 doRelay : function(foo, bar, hname){
40876 if(hname == 'down' || this.scope.isExpanded()){
40877 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40885 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40889 initNumberEvent : function(e)
40891 this.inputEl().on("keydown" , this.fireKey, this);
40892 this.inputEl().on("focus", this.onFocus, this);
40893 this.inputEl().on("blur", this.onBlur, this);
40895 this.inputEl().relayEvent('keyup', this);
40897 if(this.indicator){
40898 this.indicator.addClass('invisible');
40901 this.originalValue = this.getValue();
40903 if(this.validationEvent == 'keyup'){
40904 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40905 this.inputEl().on('keyup', this.filterValidation, this);
40907 else if(this.validationEvent !== false){
40908 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40911 if(this.selectOnFocus){
40912 this.on("focus", this.preFocus, this);
40915 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40916 this.inputEl().on("keypress", this.filterKeys, this);
40918 this.inputEl().relayEvent('keypress', this);
40921 var allowed = "0123456789";
40923 if(this.allowDecimals){
40924 allowed += this.decimalSeparator;
40927 if(this.allowNegative){
40931 if(this.thousandsDelimiter) {
40935 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40937 var keyPress = function(e){
40939 var k = e.getKey();
40941 var c = e.getCharCode();
40944 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40945 allowed.indexOf(String.fromCharCode(c)) === -1
40951 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40955 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40960 this.inputEl().on("keypress", keyPress, this);
40964 onTriggerClick : function(e)
40971 this.loadNext = false;
40973 if(this.isExpanded()){
40978 this.hasFocus = true;
40980 if(this.triggerAction == 'all') {
40981 this.doQuery(this.allQuery, true);
40985 this.doQuery(this.getRawValue());
40988 getCurrency : function()
40990 var v = this.currencyEl().getValue();
40995 restrictHeight : function()
40997 this.list.alignTo(this.currencyEl(), this.listAlign);
40998 this.list.alignTo(this.currencyEl(), this.listAlign);
41001 onViewClick : function(view, doFocus, el, e)
41003 var index = this.view.getSelectedIndexes()[0];
41005 var r = this.store.getAt(index);
41008 this.onSelect(r, index);
41012 onSelect : function(record, index){
41014 if(this.fireEvent('beforeselect', this, record, index) !== false){
41016 this.setFromCurrencyData(index > -1 ? record.data : false);
41020 this.fireEvent('select', this, record, index);
41024 setFromCurrencyData : function(o)
41028 this.lastCurrency = o;
41030 if (this.currencyField) {
41031 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41033 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41036 this.lastSelectionText = currency;
41038 //setting default currency
41039 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41040 this.setCurrency(this.defaultCurrency);
41044 this.setCurrency(currency);
41047 setFromData : function(o)
41051 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41053 this.setFromCurrencyData(c);
41058 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41060 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41063 this.setValue(value);
41067 setCurrency : function(v)
41069 this.currencyValue = v;
41072 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41077 setValue : function(v)
41079 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41085 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41087 this.inputEl().dom.value = (v == '') ? '' :
41088 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41090 if(!this.allowZero && v === '0') {
41091 this.hiddenEl().dom.value = '';
41092 this.inputEl().dom.value = '';
41099 getRawValue : function()
41101 var v = this.inputEl().getValue();
41106 getValue : function()
41108 return this.fixPrecision(this.parseValue(this.getRawValue()));
41111 parseValue : function(value)
41113 if(this.thousandsDelimiter) {
41115 r = new RegExp(",", "g");
41116 value = value.replace(r, "");
41119 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41120 return isNaN(value) ? '' : value;
41124 fixPrecision : function(value)
41126 if(this.thousandsDelimiter) {
41128 r = new RegExp(",", "g");
41129 value = value.replace(r, "");
41132 var nan = isNaN(value);
41134 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41135 return nan ? '' : value;
41137 return parseFloat(value).toFixed(this.decimalPrecision);
41140 decimalPrecisionFcn : function(v)
41142 return Math.floor(v);
41145 validateValue : function(value)
41147 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41151 var num = this.parseValue(value);
41154 this.markInvalid(String.format(this.nanText, value));
41158 if(num < this.minValue){
41159 this.markInvalid(String.format(this.minText, this.minValue));
41163 if(num > this.maxValue){
41164 this.markInvalid(String.format(this.maxText, this.maxValue));
41171 validate : function()
41173 if(this.disabled || this.allowBlank){
41178 var currency = this.getCurrency();
41180 if(this.validateValue(this.getRawValue()) && currency.length){
41185 this.markInvalid();
41189 getName: function()
41194 beforeBlur : function()
41200 var v = this.parseValue(this.getRawValue());
41207 onBlur : function()
41211 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41212 //this.el.removeClass(this.focusClass);
41215 this.hasFocus = false;
41217 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41221 var v = this.getValue();
41223 if(String(v) !== String(this.startValue)){
41224 this.fireEvent('change', this, v, this.startValue);
41227 this.fireEvent("blur", this);
41230 inputEl : function()
41232 return this.el.select('.roo-money-amount-input', true).first();
41235 currencyEl : function()
41237 return this.el.select('.roo-money-currency-input', true).first();
41240 hiddenEl : function()
41242 return this.el.select('input.hidden-number-input',true).first();