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 ...
3873 ce.removeClass('collapsing');
3874 ce.addClass('collapsing');
3876 // now flag it as moving..
3879 ce.removeClass('collapsing');
3880 ce.addClass('show');
3881 ce.removeClass('collapse');
3883 ce.dom.style.height = '';
3888 ce.addClass('collapsing');
3889 ce.removeClass('show');
3891 ce.removeClass('collapsing');
3892 ce.addClass('collapse');
3906 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3908 var size = this.el.getSize();
3909 this.maskEl.setSize(size.width, size.height);
3910 this.maskEl.enableDisplayMode("block");
3919 getChildContainer : function()
3921 if (this.el.select('.collapse').getCount()) {
3922 return this.el.select('.collapse',true).first();
3955 * @class Roo.bootstrap.NavSimplebar
3956 * @extends Roo.bootstrap.Navbar
3957 * Bootstrap Sidebar class
3959 * @cfg {Boolean} inverse is inverted color
3961 * @cfg {String} type (nav | pills | tabs)
3962 * @cfg {Boolean} arrangement stacked | justified
3963 * @cfg {String} align (left | right) alignment
3965 * @cfg {Boolean} main (true|false) main nav bar? default false
3966 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3968 * @cfg {String} tag (header|footer|nav|div) default is nav
3970 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3974 * Create a new Sidebar
3975 * @param {Object} config The config object
3979 Roo.bootstrap.NavSimplebar = function(config){
3980 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3983 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3999 getAutoCreate : function(){
4003 tag : this.tag || 'div',
4004 cls : 'navbar navbar-expand-lg'
4006 if (['light','white'].indexOf(this.weight) > -1) {
4007 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4009 cfg.cls += ' bg-' + this.weight;
4021 this.type = this.type || 'nav';
4022 if (['tabs','pills'].indexOf(this.type)!==-1) {
4023 cfg.cn[0].cls += ' nav-' + this.type
4027 if (this.type!=='nav') {
4028 Roo.log('nav type must be nav/tabs/pills')
4030 cfg.cn[0].cls += ' navbar-nav'
4036 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4037 cfg.cn[0].cls += ' nav-' + this.arrangement;
4041 if (this.align === 'right') {
4042 cfg.cn[0].cls += ' navbar-right';
4046 cfg.cls += ' navbar-inverse';
4070 * navbar-expand-md fixed-top
4074 * @class Roo.bootstrap.NavHeaderbar
4075 * @extends Roo.bootstrap.NavSimplebar
4076 * Bootstrap Sidebar class
4078 * @cfg {String} brand what is brand
4079 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4080 * @cfg {String} brand_href href of the brand
4081 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4082 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4083 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4084 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4087 * Create a new Sidebar
4088 * @param {Object} config The config object
4092 Roo.bootstrap.NavHeaderbar = function(config){
4093 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4097 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4104 desktopCenter : false,
4107 getAutoCreate : function(){
4110 tag: this.nav || 'nav',
4111 cls: 'navbar navbar-expand-md',
4117 if (this.desktopCenter) {
4118 cn.push({cls : 'container', cn : []});
4126 cls: 'navbar-toggle navbar-toggler',
4127 'data-toggle': 'collapse',
4132 html: 'Toggle navigation'
4136 cls: 'icon-bar navbar-toggler-icon'
4149 cn.push( Roo.bootstrap.version == 4 ? btn : {
4151 cls: 'navbar-header',
4160 cls: 'collapse navbar-collapse',
4164 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4166 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4167 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4169 // tag can override this..
4171 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4174 if (this.brand !== '') {
4175 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4176 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4178 href: this.brand_href ? this.brand_href : '#',
4179 cls: 'navbar-brand',
4187 cfg.cls += ' main-nav';
4195 getHeaderChildContainer : function()
4197 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4198 return this.el.select('.navbar-header',true).first();
4201 return this.getChildContainer();
4205 initEvents : function()
4207 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4209 if (this.autohide) {
4214 Roo.get(document).on('scroll',function(e) {
4215 var ns = Roo.get(document).getScroll().top;
4216 var os = prevScroll;
4220 ft.removeClass('slideDown');
4221 ft.addClass('slideUp');
4224 ft.removeClass('slideUp');
4225 ft.addClass('slideDown');
4246 * @class Roo.bootstrap.NavSidebar
4247 * @extends Roo.bootstrap.Navbar
4248 * Bootstrap Sidebar class
4251 * Create a new Sidebar
4252 * @param {Object} config The config object
4256 Roo.bootstrap.NavSidebar = function(config){
4257 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4260 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4262 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4264 getAutoCreate : function(){
4269 cls: 'sidebar sidebar-nav'
4291 * @class Roo.bootstrap.NavGroup
4292 * @extends Roo.bootstrap.Component
4293 * Bootstrap NavGroup class
4294 * @cfg {String} align (left|right)
4295 * @cfg {Boolean} inverse
4296 * @cfg {String} type (nav|pills|tab) default nav
4297 * @cfg {String} navId - reference Id for navbar.
4301 * Create a new nav group
4302 * @param {Object} config The config object
4305 Roo.bootstrap.NavGroup = function(config){
4306 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4309 Roo.bootstrap.NavGroup.register(this);
4313 * Fires when the active item changes
4314 * @param {Roo.bootstrap.NavGroup} this
4315 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4316 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4323 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4334 getAutoCreate : function()
4336 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4343 if (['tabs','pills'].indexOf(this.type)!==-1) {
4344 cfg.cls += ' nav-' + this.type
4346 if (this.type!=='nav') {
4347 Roo.log('nav type must be nav/tabs/pills')
4349 cfg.cls += ' navbar-nav'
4352 if (this.parent() && this.parent().sidebar) {
4355 cls: 'dashboard-menu sidebar-menu'
4361 if (this.form === true) {
4367 if (this.align === 'right') {
4368 cfg.cls += ' navbar-right ml-md-auto';
4370 cfg.cls += ' navbar-left';
4374 if (this.align === 'right') {
4375 cfg.cls += ' navbar-right ml-md-auto';
4377 cfg.cls += ' mr-auto';
4381 cfg.cls += ' navbar-inverse';
4389 * sets the active Navigation item
4390 * @param {Roo.bootstrap.NavItem} the new current navitem
4392 setActiveItem : function(item)
4395 Roo.each(this.navItems, function(v){
4400 v.setActive(false, true);
4407 item.setActive(true, true);
4408 this.fireEvent('changed', this, item, prev);
4413 * gets the active Navigation item
4414 * @return {Roo.bootstrap.NavItem} the current navitem
4416 getActive : function()
4420 Roo.each(this.navItems, function(v){
4431 indexOfNav : function()
4435 Roo.each(this.navItems, function(v,i){
4446 * adds a Navigation item
4447 * @param {Roo.bootstrap.NavItem} the navitem to add
4449 addItem : function(cfg)
4451 var cn = new Roo.bootstrap.NavItem(cfg);
4453 cn.parentId = this.id;
4454 cn.onRender(this.el, null);
4458 * register a Navigation item
4459 * @param {Roo.bootstrap.NavItem} the navitem to add
4461 register : function(item)
4463 this.navItems.push( item);
4464 item.navId = this.navId;
4469 * clear all the Navigation item
4472 clearAll : function()
4475 this.el.dom.innerHTML = '';
4478 getNavItem: function(tabId)
4481 Roo.each(this.navItems, function(e) {
4482 if (e.tabId == tabId) {
4492 setActiveNext : function()
4494 var i = this.indexOfNav(this.getActive());
4495 if (i > this.navItems.length) {
4498 this.setActiveItem(this.navItems[i+1]);
4500 setActivePrev : function()
4502 var i = this.indexOfNav(this.getActive());
4506 this.setActiveItem(this.navItems[i-1]);
4508 clearWasActive : function(except) {
4509 Roo.each(this.navItems, function(e) {
4510 if (e.tabId != except.tabId && e.was_active) {
4511 e.was_active = false;
4518 getWasActive : function ()
4521 Roo.each(this.navItems, function(e) {
4536 Roo.apply(Roo.bootstrap.NavGroup, {
4540 * register a Navigation Group
4541 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4543 register : function(navgrp)
4545 this.groups[navgrp.navId] = navgrp;
4549 * fetch a Navigation Group based on the navigation ID
4550 * @param {string} the navgroup to add
4551 * @returns {Roo.bootstrap.NavGroup} the navgroup
4553 get: function(navId) {
4554 if (typeof(this.groups[navId]) == 'undefined') {
4556 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4558 return this.groups[navId] ;
4573 * @class Roo.bootstrap.NavItem
4574 * @extends Roo.bootstrap.Component
4575 * Bootstrap Navbar.NavItem class
4576 * @cfg {String} href link to
4577 * @cfg {String} html content of button
4578 * @cfg {String} badge text inside badge
4579 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4580 * @cfg {String} glyphicon DEPRICATED - use fa
4581 * @cfg {String} icon DEPRICATED - use fa
4582 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4583 * @cfg {Boolean} active Is item active
4584 * @cfg {Boolean} disabled Is item disabled
4586 * @cfg {Boolean} preventDefault (true | false) default false
4587 * @cfg {String} tabId the tab that this item activates.
4588 * @cfg {String} tagtype (a|span) render as a href or span?
4589 * @cfg {Boolean} animateRef (true|false) link to element default false
4592 * Create a new Navbar Item
4593 * @param {Object} config The config object
4595 Roo.bootstrap.NavItem = function(config){
4596 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4601 * The raw click event for the entire grid.
4602 * @param {Roo.EventObject} e
4607 * Fires when the active item active state changes
4608 * @param {Roo.bootstrap.NavItem} this
4609 * @param {boolean} state the new state
4615 * Fires when scroll to element
4616 * @param {Roo.bootstrap.NavItem} this
4617 * @param {Object} options
4618 * @param {Roo.EventObject} e
4626 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4635 preventDefault : false,
4642 getAutoCreate : function(){
4651 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4653 if (this.disabled) {
4654 cfg.cls += ' disabled';
4657 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4661 href : this.href || "#",
4662 html: this.html || ''
4665 if (this.tagtype == 'a') {
4666 cfg.cn[0].cls = 'nav-link';
4669 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4672 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4674 if(this.glyphicon) {
4675 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4680 cfg.cn[0].html += " <span class='caret'></span>";
4684 if (this.badge !== '') {
4686 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4694 initEvents: function()
4696 if (typeof (this.menu) != 'undefined') {
4697 this.menu.parentType = this.xtype;
4698 this.menu.triggerEl = this.el;
4699 this.menu = this.addxtype(Roo.apply({}, this.menu));
4702 this.el.select('a',true).on('click', this.onClick, this);
4704 if(this.tagtype == 'span'){
4705 this.el.select('span',true).on('click', this.onClick, this);
4708 // at this point parent should be available..
4709 this.parent().register(this);
4712 onClick : function(e)
4714 if (e.getTarget('.dropdown-menu-item')) {
4715 // did you click on a menu itemm.... - then don't trigger onclick..
4720 this.preventDefault ||
4723 Roo.log("NavItem - prevent Default?");
4727 if (this.disabled) {
4731 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4732 if (tg && tg.transition) {
4733 Roo.log("waiting for the transitionend");
4739 //Roo.log("fire event clicked");
4740 if(this.fireEvent('click', this, e) === false){
4744 if(this.tagtype == 'span'){
4748 //Roo.log(this.href);
4749 var ael = this.el.select('a',true).first();
4752 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4753 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4754 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4755 return; // ignore... - it's a 'hash' to another page.
4757 Roo.log("NavItem - prevent Default?");
4759 this.scrollToElement(e);
4763 var p = this.parent();
4765 if (['tabs','pills'].indexOf(p.type)!==-1) {
4766 if (typeof(p.setActiveItem) !== 'undefined') {
4767 p.setActiveItem(this);
4771 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4772 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4773 // remove the collapsed menu expand...
4774 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4778 isActive: function () {
4781 setActive : function(state, fire, is_was_active)
4783 if (this.active && !state && this.navId) {
4784 this.was_active = true;
4785 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4787 nv.clearWasActive(this);
4791 this.active = state;
4794 this.el.removeClass('active');
4795 } else if (!this.el.hasClass('active')) {
4796 this.el.addClass('active');
4799 this.fireEvent('changed', this, state);
4802 // show a panel if it's registered and related..
4804 if (!this.navId || !this.tabId || !state || is_was_active) {
4808 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812 var pan = tg.getPanelByName(this.tabId);
4816 // if we can not flip to new panel - go back to old nav highlight..
4817 if (false == tg.showPanel(pan)) {
4818 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4820 var onav = nv.getWasActive();
4822 onav.setActive(true, false, true);
4831 // this should not be here...
4832 setDisabled : function(state)
4834 this.disabled = state;
4836 this.el.removeClass('disabled');
4837 } else if (!this.el.hasClass('disabled')) {
4838 this.el.addClass('disabled');
4844 * Fetch the element to display the tooltip on.
4845 * @return {Roo.Element} defaults to this.el
4847 tooltipEl : function()
4849 return this.el.select('' + this.tagtype + '', true).first();
4852 scrollToElement : function(e)
4854 var c = document.body;
4857 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4859 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4860 c = document.documentElement;
4863 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4869 var o = target.calcOffsetsTo(c);
4876 this.fireEvent('scrollto', this, options, e);
4878 Roo.get(c).scrollTo('top', options.value, true);
4891 * <span> icon </span>
4892 * <span> text </span>
4893 * <span>badge </span>
4897 * @class Roo.bootstrap.NavSidebarItem
4898 * @extends Roo.bootstrap.NavItem
4899 * Bootstrap Navbar.NavSidebarItem class
4900 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4901 * {Boolean} open is the menu open
4902 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4903 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4904 * {String} buttonSize (sm|md|lg)the extra classes for the button
4905 * {Boolean} showArrow show arrow next to the text (default true)
4907 * Create a new Navbar Button
4908 * @param {Object} config The config object
4910 Roo.bootstrap.NavSidebarItem = function(config){
4911 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4916 * The raw click event for the entire grid.
4917 * @param {Roo.EventObject} e
4922 * Fires when the active item active state changes
4923 * @param {Roo.bootstrap.NavSidebarItem} this
4924 * @param {boolean} state the new state
4932 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4934 badgeWeight : 'default',
4940 buttonWeight : 'default',
4946 getAutoCreate : function(){
4951 href : this.href || '#',
4957 if(this.buttonView){
4960 href : this.href || '#',
4961 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4974 cfg.cls += ' active';
4977 if (this.disabled) {
4978 cfg.cls += ' disabled';
4981 cfg.cls += ' open x-open';
4984 if (this.glyphicon || this.icon) {
4985 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4986 a.cn.push({ tag : 'i', cls : c }) ;
4989 if(!this.buttonView){
4992 html : this.html || ''
4999 if (this.badge !== '') {
5000 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5006 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5009 a.cls += ' dropdown-toggle treeview' ;
5015 initEvents : function()
5017 if (typeof (this.menu) != 'undefined') {
5018 this.menu.parentType = this.xtype;
5019 this.menu.triggerEl = this.el;
5020 this.menu = this.addxtype(Roo.apply({}, this.menu));
5023 this.el.on('click', this.onClick, this);
5025 if(this.badge !== ''){
5026 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5031 onClick : function(e)
5038 if(this.preventDefault){
5042 this.fireEvent('click', this);
5045 disable : function()
5047 this.setDisabled(true);
5052 this.setDisabled(false);
5055 setDisabled : function(state)
5057 if(this.disabled == state){
5061 this.disabled = state;
5064 this.el.addClass('disabled');
5068 this.el.removeClass('disabled');
5073 setActive : function(state)
5075 if(this.active == state){
5079 this.active = state;
5082 this.el.addClass('active');
5086 this.el.removeClass('active');
5091 isActive: function ()
5096 setBadge : function(str)
5102 this.badgeEl.dom.innerHTML = str;
5119 * @class Roo.bootstrap.Row
5120 * @extends Roo.bootstrap.Component
5121 * Bootstrap Row class (contains columns...)
5125 * @param {Object} config The config object
5128 Roo.bootstrap.Row = function(config){
5129 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5132 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5134 getAutoCreate : function(){
5153 * @class Roo.bootstrap.Element
5154 * @extends Roo.bootstrap.Component
5155 * Bootstrap Element class
5156 * @cfg {String} html contents of the element
5157 * @cfg {String} tag tag of the element
5158 * @cfg {String} cls class of the element
5159 * @cfg {Boolean} preventDefault (true|false) default false
5160 * @cfg {Boolean} clickable (true|false) default false
5163 * Create a new Element
5164 * @param {Object} config The config object
5167 Roo.bootstrap.Element = function(config){
5168 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5174 * When a element is chick
5175 * @param {Roo.bootstrap.Element} this
5176 * @param {Roo.EventObject} e
5182 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5187 preventDefault: false,
5190 getAutoCreate : function(){
5194 // cls: this.cls, double assign in parent class Component.js :: onRender
5201 initEvents: function()
5203 Roo.bootstrap.Element.superclass.initEvents.call(this);
5206 this.el.on('click', this.onClick, this);
5211 onClick : function(e)
5213 if(this.preventDefault){
5217 this.fireEvent('click', this, e);
5220 getValue : function()
5222 return this.el.dom.innerHTML;
5225 setValue : function(value)
5227 this.el.dom.innerHTML = value;
5242 * @class Roo.bootstrap.Pagination
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Pagination class
5245 * @cfg {String} size xs | sm | md | lg
5246 * @cfg {Boolean} inverse false | true
5249 * Create a new Pagination
5250 * @param {Object} config The config object
5253 Roo.bootstrap.Pagination = function(config){
5254 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5257 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5263 getAutoCreate : function(){
5269 cfg.cls += ' inverse';
5275 cfg.cls += " " + this.cls;
5293 * @class Roo.bootstrap.PaginationItem
5294 * @extends Roo.bootstrap.Component
5295 * Bootstrap PaginationItem class
5296 * @cfg {String} html text
5297 * @cfg {String} href the link
5298 * @cfg {Boolean} preventDefault (true | false) default true
5299 * @cfg {Boolean} active (true | false) default false
5300 * @cfg {Boolean} disabled default false
5304 * Create a new PaginationItem
5305 * @param {Object} config The config object
5309 Roo.bootstrap.PaginationItem = function(config){
5310 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5315 * The raw click event for the entire grid.
5316 * @param {Roo.EventObject} e
5322 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5326 preventDefault: true,
5331 getAutoCreate : function(){
5337 href : this.href ? this.href : '#',
5338 html : this.html ? this.html : ''
5348 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5352 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5358 initEvents: function() {
5360 this.el.on('click', this.onClick, this);
5363 onClick : function(e)
5365 Roo.log('PaginationItem on click ');
5366 if(this.preventDefault){
5374 this.fireEvent('click', this, e);
5390 * @class Roo.bootstrap.Slider
5391 * @extends Roo.bootstrap.Component
5392 * Bootstrap Slider class
5395 * Create a new Slider
5396 * @param {Object} config The config object
5399 Roo.bootstrap.Slider = function(config){
5400 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5403 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5405 getAutoCreate : function(){
5409 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5413 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5425 * Ext JS Library 1.1.1
5426 * Copyright(c) 2006-2007, Ext JS, LLC.
5428 * Originally Released Under LGPL - original licence link has changed is not relivant.
5431 * <script type="text/javascript">
5436 * @class Roo.grid.ColumnModel
5437 * @extends Roo.util.Observable
5438 * This is the default implementation of a ColumnModel used by the Grid. It defines
5439 * the columns in the grid.
5442 var colModel = new Roo.grid.ColumnModel([
5443 {header: "Ticker", width: 60, sortable: true, locked: true},
5444 {header: "Company Name", width: 150, sortable: true},
5445 {header: "Market Cap.", width: 100, sortable: true},
5446 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5447 {header: "Employees", width: 100, sortable: true, resizable: false}
5452 * The config options listed for this class are options which may appear in each
5453 * individual column definition.
5454 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5456 * @param {Object} config An Array of column config objects. See this class's
5457 * config objects for details.
5459 Roo.grid.ColumnModel = function(config){
5461 * The config passed into the constructor
5463 this.config = config;
5466 // if no id, create one
5467 // if the column does not have a dataIndex mapping,
5468 // map it to the order it is in the config
5469 for(var i = 0, len = config.length; i < len; i++){
5471 if(typeof c.dataIndex == "undefined"){
5474 if(typeof c.renderer == "string"){
5475 c.renderer = Roo.util.Format[c.renderer];
5477 if(typeof c.id == "undefined"){
5480 if(c.editor && c.editor.xtype){
5481 c.editor = Roo.factory(c.editor, Roo.grid);
5483 if(c.editor && c.editor.isFormField){
5484 c.editor = new Roo.grid.GridEditor(c.editor);
5486 this.lookup[c.id] = c;
5490 * The width of columns which have no width specified (defaults to 100)
5493 this.defaultWidth = 100;
5496 * Default sortable of columns which have no sortable specified (defaults to false)
5499 this.defaultSortable = false;
5503 * @event widthchange
5504 * Fires when the width of a column changes.
5505 * @param {ColumnModel} this
5506 * @param {Number} columnIndex The column index
5507 * @param {Number} newWidth The new width
5509 "widthchange": true,
5511 * @event headerchange
5512 * Fires when the text of a header changes.
5513 * @param {ColumnModel} this
5514 * @param {Number} columnIndex The column index
5515 * @param {Number} newText The new header text
5517 "headerchange": true,
5519 * @event hiddenchange
5520 * Fires when a column is hidden or "unhidden".
5521 * @param {ColumnModel} this
5522 * @param {Number} columnIndex The column index
5523 * @param {Boolean} hidden true if hidden, false otherwise
5525 "hiddenchange": true,
5527 * @event columnmoved
5528 * Fires when a column is moved.
5529 * @param {ColumnModel} this
5530 * @param {Number} oldIndex
5531 * @param {Number} newIndex
5533 "columnmoved" : true,
5535 * @event columlockchange
5536 * Fires when a column's locked state is changed
5537 * @param {ColumnModel} this
5538 * @param {Number} colIndex
5539 * @param {Boolean} locked true if locked
5541 "columnlockchange" : true
5543 Roo.grid.ColumnModel.superclass.constructor.call(this);
5545 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5547 * @cfg {String} header The header text to display in the Grid view.
5550 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5551 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5552 * specified, the column's index is used as an index into the Record's data Array.
5555 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5556 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5559 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5560 * Defaults to the value of the {@link #defaultSortable} property.
5561 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5564 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5567 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5570 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5573 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5576 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5577 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5578 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5579 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5582 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5585 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5588 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5591 * @cfg {String} cursor (Optional)
5594 * @cfg {String} tooltip (Optional)
5597 * @cfg {Number} xs (Optional)
5600 * @cfg {Number} sm (Optional)
5603 * @cfg {Number} md (Optional)
5606 * @cfg {Number} lg (Optional)
5609 * Returns the id of the column at the specified index.
5610 * @param {Number} index The column index
5611 * @return {String} the id
5613 getColumnId : function(index){
5614 return this.config[index].id;
5618 * Returns the column for a specified id.
5619 * @param {String} id The column id
5620 * @return {Object} the column
5622 getColumnById : function(id){
5623 return this.lookup[id];
5628 * Returns the column for a specified dataIndex.
5629 * @param {String} dataIndex The column dataIndex
5630 * @return {Object|Boolean} the column or false if not found
5632 getColumnByDataIndex: function(dataIndex){
5633 var index = this.findColumnIndex(dataIndex);
5634 return index > -1 ? this.config[index] : false;
5638 * Returns the index for a specified column id.
5639 * @param {String} id The column id
5640 * @return {Number} the index, or -1 if not found
5642 getIndexById : function(id){
5643 for(var i = 0, len = this.config.length; i < len; i++){
5644 if(this.config[i].id == id){
5652 * Returns the index for a specified column dataIndex.
5653 * @param {String} dataIndex The column dataIndex
5654 * @return {Number} the index, or -1 if not found
5657 findColumnIndex : function(dataIndex){
5658 for(var i = 0, len = this.config.length; i < len; i++){
5659 if(this.config[i].dataIndex == dataIndex){
5667 moveColumn : function(oldIndex, newIndex){
5668 var c = this.config[oldIndex];
5669 this.config.splice(oldIndex, 1);
5670 this.config.splice(newIndex, 0, c);
5671 this.dataMap = null;
5672 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5675 isLocked : function(colIndex){
5676 return this.config[colIndex].locked === true;
5679 setLocked : function(colIndex, value, suppressEvent){
5680 if(this.isLocked(colIndex) == value){
5683 this.config[colIndex].locked = value;
5685 this.fireEvent("columnlockchange", this, colIndex, value);
5689 getTotalLockedWidth : function(){
5691 for(var i = 0; i < this.config.length; i++){
5692 if(this.isLocked(i) && !this.isHidden(i)){
5693 this.totalWidth += this.getColumnWidth(i);
5699 getLockedCount : function(){
5700 for(var i = 0, len = this.config.length; i < len; i++){
5701 if(!this.isLocked(i)){
5706 return this.config.length;
5710 * Returns the number of columns.
5713 getColumnCount : function(visibleOnly){
5714 if(visibleOnly === true){
5716 for(var i = 0, len = this.config.length; i < len; i++){
5717 if(!this.isHidden(i)){
5723 return this.config.length;
5727 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5728 * @param {Function} fn
5729 * @param {Object} scope (optional)
5730 * @return {Array} result
5732 getColumnsBy : function(fn, scope){
5734 for(var i = 0, len = this.config.length; i < len; i++){
5735 var c = this.config[i];
5736 if(fn.call(scope||this, c, i) === true){
5744 * Returns true if the specified column is sortable.
5745 * @param {Number} col The column index
5748 isSortable : function(col){
5749 if(typeof this.config[col].sortable == "undefined"){
5750 return this.defaultSortable;
5752 return this.config[col].sortable;
5756 * Returns the rendering (formatting) function defined for the column.
5757 * @param {Number} col The column index.
5758 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5760 getRenderer : function(col){
5761 if(!this.config[col].renderer){
5762 return Roo.grid.ColumnModel.defaultRenderer;
5764 return this.config[col].renderer;
5768 * Sets the rendering (formatting) function for a column.
5769 * @param {Number} col The column index
5770 * @param {Function} fn The function to use to process the cell's raw data
5771 * to return HTML markup for the grid view. The render function is called with
5772 * the following parameters:<ul>
5773 * <li>Data value.</li>
5774 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5775 * <li>css A CSS style string to apply to the table cell.</li>
5776 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5777 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5778 * <li>Row index</li>
5779 * <li>Column index</li>
5780 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5782 setRenderer : function(col, fn){
5783 this.config[col].renderer = fn;
5787 * Returns the width for the specified column.
5788 * @param {Number} col The column index
5791 getColumnWidth : function(col){
5792 return this.config[col].width * 1 || this.defaultWidth;
5796 * Sets the width for a column.
5797 * @param {Number} col The column index
5798 * @param {Number} width The new width
5800 setColumnWidth : function(col, width, suppressEvent){
5801 this.config[col].width = width;
5802 this.totalWidth = null;
5804 this.fireEvent("widthchange", this, col, width);
5809 * Returns the total width of all columns.
5810 * @param {Boolean} includeHidden True to include hidden column widths
5813 getTotalWidth : function(includeHidden){
5814 if(!this.totalWidth){
5815 this.totalWidth = 0;
5816 for(var i = 0, len = this.config.length; i < len; i++){
5817 if(includeHidden || !this.isHidden(i)){
5818 this.totalWidth += this.getColumnWidth(i);
5822 return this.totalWidth;
5826 * Returns the header for the specified column.
5827 * @param {Number} col The column index
5830 getColumnHeader : function(col){
5831 return this.config[col].header;
5835 * Sets the header for a column.
5836 * @param {Number} col The column index
5837 * @param {String} header The new header
5839 setColumnHeader : function(col, header){
5840 this.config[col].header = header;
5841 this.fireEvent("headerchange", this, col, header);
5845 * Returns the tooltip for the specified column.
5846 * @param {Number} col The column index
5849 getColumnTooltip : function(col){
5850 return this.config[col].tooltip;
5853 * Sets the tooltip for a column.
5854 * @param {Number} col The column index
5855 * @param {String} tooltip The new tooltip
5857 setColumnTooltip : function(col, tooltip){
5858 this.config[col].tooltip = tooltip;
5862 * Returns the dataIndex for the specified column.
5863 * @param {Number} col The column index
5866 getDataIndex : function(col){
5867 return this.config[col].dataIndex;
5871 * Sets the dataIndex for a column.
5872 * @param {Number} col The column index
5873 * @param {Number} dataIndex The new dataIndex
5875 setDataIndex : function(col, dataIndex){
5876 this.config[col].dataIndex = dataIndex;
5882 * Returns true if the cell is editable.
5883 * @param {Number} colIndex The column index
5884 * @param {Number} rowIndex The row index - this is nto actually used..?
5887 isCellEditable : function(colIndex, rowIndex){
5888 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5892 * Returns the editor defined for the cell/column.
5893 * return false or null to disable editing.
5894 * @param {Number} colIndex The column index
5895 * @param {Number} rowIndex The row index
5898 getCellEditor : function(colIndex, rowIndex){
5899 return this.config[colIndex].editor;
5903 * Sets if a column is editable.
5904 * @param {Number} col The column index
5905 * @param {Boolean} editable True if the column is editable
5907 setEditable : function(col, editable){
5908 this.config[col].editable = editable;
5913 * Returns true if the column is hidden.
5914 * @param {Number} colIndex The column index
5917 isHidden : function(colIndex){
5918 return this.config[colIndex].hidden;
5923 * Returns true if the column width cannot be changed
5925 isFixed : function(colIndex){
5926 return this.config[colIndex].fixed;
5930 * Returns true if the column can be resized
5933 isResizable : function(colIndex){
5934 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5937 * Sets if a column is hidden.
5938 * @param {Number} colIndex The column index
5939 * @param {Boolean} hidden True if the column is hidden
5941 setHidden : function(colIndex, hidden){
5942 this.config[colIndex].hidden = hidden;
5943 this.totalWidth = null;
5944 this.fireEvent("hiddenchange", this, colIndex, hidden);
5948 * Sets the editor for a column.
5949 * @param {Number} col The column index
5950 * @param {Object} editor The editor object
5952 setEditor : function(col, editor){
5953 this.config[col].editor = editor;
5957 Roo.grid.ColumnModel.defaultRenderer = function(value)
5959 if(typeof value == "object") {
5962 if(typeof value == "string" && value.length < 1){
5966 return String.format("{0}", value);
5969 // Alias for backwards compatibility
5970 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5973 * Ext JS Library 1.1.1
5974 * Copyright(c) 2006-2007, Ext JS, LLC.
5976 * Originally Released Under LGPL - original licence link has changed is not relivant.
5979 * <script type="text/javascript">
5983 * @class Roo.LoadMask
5984 * A simple utility class for generically masking elements while loading data. If the element being masked has
5985 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5986 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5987 * element's UpdateManager load indicator and will be destroyed after the initial load.
5989 * Create a new LoadMask
5990 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5991 * @param {Object} config The config object
5993 Roo.LoadMask = function(el, config){
5994 this.el = Roo.get(el);
5995 Roo.apply(this, config);
5997 this.store.on('beforeload', this.onBeforeLoad, this);
5998 this.store.on('load', this.onLoad, this);
5999 this.store.on('loadexception', this.onLoadException, this);
6000 this.removeMask = false;
6002 var um = this.el.getUpdateManager();
6003 um.showLoadIndicator = false; // disable the default indicator
6004 um.on('beforeupdate', this.onBeforeLoad, this);
6005 um.on('update', this.onLoad, this);
6006 um.on('failure', this.onLoad, this);
6007 this.removeMask = true;
6011 Roo.LoadMask.prototype = {
6013 * @cfg {Boolean} removeMask
6014 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6015 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6019 * The text to display in a centered loading message box (defaults to 'Loading...')
6023 * @cfg {String} msgCls
6024 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6026 msgCls : 'x-mask-loading',
6029 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6035 * Disables the mask to prevent it from being displayed
6037 disable : function(){
6038 this.disabled = true;
6042 * Enables the mask so that it can be displayed
6044 enable : function(){
6045 this.disabled = false;
6048 onLoadException : function()
6052 if (typeof(arguments[3]) != 'undefined') {
6053 Roo.MessageBox.alert("Error loading",arguments[3]);
6057 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6058 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6065 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6070 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6074 onBeforeLoad : function(){
6076 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6081 destroy : function(){
6083 this.store.un('beforeload', this.onBeforeLoad, this);
6084 this.store.un('load', this.onLoad, this);
6085 this.store.un('loadexception', this.onLoadException, this);
6087 var um = this.el.getUpdateManager();
6088 um.un('beforeupdate', this.onBeforeLoad, this);
6089 um.un('update', this.onLoad, this);
6090 um.un('failure', this.onLoad, this);
6101 * @class Roo.bootstrap.Table
6102 * @extends Roo.bootstrap.Component
6103 * Bootstrap Table class
6104 * @cfg {String} cls table class
6105 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6106 * @cfg {String} bgcolor Specifies the background color for a table
6107 * @cfg {Number} border Specifies whether the table cells should have borders or not
6108 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6109 * @cfg {Number} cellspacing Specifies the space between cells
6110 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6111 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6112 * @cfg {String} sortable Specifies that the table should be sortable
6113 * @cfg {String} summary Specifies a summary of the content of a table
6114 * @cfg {Number} width Specifies the width of a table
6115 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6117 * @cfg {boolean} striped Should the rows be alternative striped
6118 * @cfg {boolean} bordered Add borders to the table
6119 * @cfg {boolean} hover Add hover highlighting
6120 * @cfg {boolean} condensed Format condensed
6121 * @cfg {boolean} responsive Format condensed
6122 * @cfg {Boolean} loadMask (true|false) default false
6123 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6124 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6125 * @cfg {Boolean} rowSelection (true|false) default false
6126 * @cfg {Boolean} cellSelection (true|false) default false
6127 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6128 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6129 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6130 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6134 * Create a new Table
6135 * @param {Object} config The config object
6138 Roo.bootstrap.Table = function(config){
6139 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6144 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6145 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6146 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6147 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6149 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6151 this.sm.grid = this;
6152 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6153 this.sm = this.selModel;
6154 this.sm.xmodule = this.xmodule || false;
6157 if (this.cm && typeof(this.cm.config) == 'undefined') {
6158 this.colModel = new Roo.grid.ColumnModel(this.cm);
6159 this.cm = this.colModel;
6160 this.cm.xmodule = this.xmodule || false;
6163 this.store= Roo.factory(this.store, Roo.data);
6164 this.ds = this.store;
6165 this.ds.xmodule = this.xmodule || false;
6168 if (this.footer && this.store) {
6169 this.footer.dataSource = this.ds;
6170 this.footer = Roo.factory(this.footer);
6177 * Fires when a cell is clicked
6178 * @param {Roo.bootstrap.Table} this
6179 * @param {Roo.Element} el
6180 * @param {Number} rowIndex
6181 * @param {Number} columnIndex
6182 * @param {Roo.EventObject} e
6186 * @event celldblclick
6187 * Fires when a cell is double clicked
6188 * @param {Roo.bootstrap.Table} this
6189 * @param {Roo.Element} el
6190 * @param {Number} rowIndex
6191 * @param {Number} columnIndex
6192 * @param {Roo.EventObject} e
6194 "celldblclick" : true,
6197 * Fires when a row is clicked
6198 * @param {Roo.bootstrap.Table} this
6199 * @param {Roo.Element} el
6200 * @param {Number} rowIndex
6201 * @param {Roo.EventObject} e
6205 * @event rowdblclick
6206 * Fires when a row is double clicked
6207 * @param {Roo.bootstrap.Table} this
6208 * @param {Roo.Element} el
6209 * @param {Number} rowIndex
6210 * @param {Roo.EventObject} e
6212 "rowdblclick" : true,
6215 * Fires when a mouseover occur
6216 * @param {Roo.bootstrap.Table} this
6217 * @param {Roo.Element} el
6218 * @param {Number} rowIndex
6219 * @param {Number} columnIndex
6220 * @param {Roo.EventObject} e
6225 * Fires when a mouseout occur
6226 * @param {Roo.bootstrap.Table} this
6227 * @param {Roo.Element} el
6228 * @param {Number} rowIndex
6229 * @param {Number} columnIndex
6230 * @param {Roo.EventObject} e
6235 * Fires when a row is rendered, so you can change add a style to it.
6236 * @param {Roo.bootstrap.Table} this
6237 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6241 * @event rowsrendered
6242 * Fires when all the rows have been rendered
6243 * @param {Roo.bootstrap.Table} this
6245 'rowsrendered' : true,
6247 * @event contextmenu
6248 * The raw contextmenu event for the entire grid.
6249 * @param {Roo.EventObject} e
6251 "contextmenu" : true,
6253 * @event rowcontextmenu
6254 * Fires when a row is right clicked
6255 * @param {Roo.bootstrap.Table} this
6256 * @param {Number} rowIndex
6257 * @param {Roo.EventObject} e
6259 "rowcontextmenu" : true,
6261 * @event cellcontextmenu
6262 * Fires when a cell is right clicked
6263 * @param {Roo.bootstrap.Table} this
6264 * @param {Number} rowIndex
6265 * @param {Number} cellIndex
6266 * @param {Roo.EventObject} e
6268 "cellcontextmenu" : true,
6270 * @event headercontextmenu
6271 * Fires when a header is right clicked
6272 * @param {Roo.bootstrap.Table} this
6273 * @param {Number} columnIndex
6274 * @param {Roo.EventObject} e
6276 "headercontextmenu" : true
6280 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6306 rowSelection : false,
6307 cellSelection : false,
6310 // Roo.Element - the tbody
6312 // Roo.Element - thead element
6315 container: false, // used by gridpanel...
6321 auto_hide_footer : false,
6323 getAutoCreate : function()
6325 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6332 if (this.scrollBody) {
6333 cfg.cls += ' table-body-fixed';
6336 cfg.cls += ' table-striped';
6340 cfg.cls += ' table-hover';
6342 if (this.bordered) {
6343 cfg.cls += ' table-bordered';
6345 if (this.condensed) {
6346 cfg.cls += ' table-condensed';
6348 if (this.responsive) {
6349 cfg.cls += ' table-responsive';
6353 cfg.cls+= ' ' +this.cls;
6356 // this lot should be simplifed...
6369 ].forEach(function(k) {
6377 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6380 if(this.store || this.cm){
6381 if(this.headerShow){
6382 cfg.cn.push(this.renderHeader());
6385 cfg.cn.push(this.renderBody());
6387 if(this.footerShow){
6388 cfg.cn.push(this.renderFooter());
6390 // where does this come from?
6391 //cfg.cls+= ' TableGrid';
6394 return { cn : [ cfg ] };
6397 initEvents : function()
6399 if(!this.store || !this.cm){
6402 if (this.selModel) {
6403 this.selModel.initEvents();
6407 //Roo.log('initEvents with ds!!!!');
6409 this.mainBody = this.el.select('tbody', true).first();
6410 this.mainHead = this.el.select('thead', true).first();
6411 this.mainFoot = this.el.select('tfoot', true).first();
6417 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6418 e.on('click', _this.sort, _this);
6421 this.mainBody.on("click", this.onClick, this);
6422 this.mainBody.on("dblclick", this.onDblClick, this);
6424 // why is this done????? = it breaks dialogs??
6425 //this.parent().el.setStyle('position', 'relative');
6429 this.footer.parentId = this.id;
6430 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6433 this.el.select('tfoot tr td').first().addClass('hide');
6438 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6441 this.store.on('load', this.onLoad, this);
6442 this.store.on('beforeload', this.onBeforeLoad, this);
6443 this.store.on('update', this.onUpdate, this);
6444 this.store.on('add', this.onAdd, this);
6445 this.store.on("clear", this.clear, this);
6447 this.el.on("contextmenu", this.onContextMenu, this);
6449 this.mainBody.on('scroll', this.onBodyScroll, this);
6451 this.cm.on("headerchange", this.onHeaderChange, this);
6453 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6457 onContextMenu : function(e, t)
6459 this.processEvent("contextmenu", e);
6462 processEvent : function(name, e)
6464 if (name != 'touchstart' ) {
6465 this.fireEvent(name, e);
6468 var t = e.getTarget();
6470 var cell = Roo.get(t);
6476 if(cell.findParent('tfoot', false, true)){
6480 if(cell.findParent('thead', false, true)){
6482 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6483 cell = Roo.get(t).findParent('th', false, true);
6485 Roo.log("failed to find th in thead?");
6486 Roo.log(e.getTarget());
6491 var cellIndex = cell.dom.cellIndex;
6493 var ename = name == 'touchstart' ? 'click' : name;
6494 this.fireEvent("header" + ename, this, cellIndex, e);
6499 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6500 cell = Roo.get(t).findParent('td', false, true);
6502 Roo.log("failed to find th in tbody?");
6503 Roo.log(e.getTarget());
6508 var row = cell.findParent('tr', false, true);
6509 var cellIndex = cell.dom.cellIndex;
6510 var rowIndex = row.dom.rowIndex - 1;
6514 this.fireEvent("row" + name, this, rowIndex, e);
6518 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6524 onMouseover : function(e, el)
6526 var cell = Roo.get(el);
6532 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6533 cell = cell.findParent('td', false, true);
6536 var row = cell.findParent('tr', false, true);
6537 var cellIndex = cell.dom.cellIndex;
6538 var rowIndex = row.dom.rowIndex - 1; // start from 0
6540 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6544 onMouseout : function(e, el)
6546 var cell = Roo.get(el);
6552 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6553 cell = cell.findParent('td', false, true);
6556 var row = cell.findParent('tr', false, true);
6557 var cellIndex = cell.dom.cellIndex;
6558 var rowIndex = row.dom.rowIndex - 1; // start from 0
6560 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6564 onClick : function(e, el)
6566 var cell = Roo.get(el);
6568 if(!cell || (!this.cellSelection && !this.rowSelection)){
6572 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6573 cell = cell.findParent('td', false, true);
6576 if(!cell || typeof(cell) == 'undefined'){
6580 var row = cell.findParent('tr', false, true);
6582 if(!row || typeof(row) == 'undefined'){
6586 var cellIndex = cell.dom.cellIndex;
6587 var rowIndex = this.getRowIndex(row);
6589 // why??? - should these not be based on SelectionModel?
6590 if(this.cellSelection){
6591 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6594 if(this.rowSelection){
6595 this.fireEvent('rowclick', this, row, rowIndex, e);
6601 onDblClick : function(e,el)
6603 var cell = Roo.get(el);
6605 if(!cell || (!this.cellSelection && !this.rowSelection)){
6609 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6610 cell = cell.findParent('td', false, true);
6613 if(!cell || typeof(cell) == 'undefined'){
6617 var row = cell.findParent('tr', false, true);
6619 if(!row || typeof(row) == 'undefined'){
6623 var cellIndex = cell.dom.cellIndex;
6624 var rowIndex = this.getRowIndex(row);
6626 if(this.cellSelection){
6627 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6630 if(this.rowSelection){
6631 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6635 sort : function(e,el)
6637 var col = Roo.get(el);
6639 if(!col.hasClass('sortable')){
6643 var sort = col.attr('sort');
6646 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6650 this.store.sortInfo = {field : sort, direction : dir};
6653 Roo.log("calling footer first");
6654 this.footer.onClick('first');
6657 this.store.load({ params : { start : 0 } });
6661 renderHeader : function()
6669 this.totalWidth = 0;
6671 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6673 var config = cm.config[i];
6677 cls : 'x-hcol-' + i,
6679 html: cm.getColumnHeader(i)
6684 if(typeof(config.sortable) != 'undefined' && config.sortable){
6686 c.html = '<i class="glyphicon"></i>' + c.html;
6689 if(typeof(config.lgHeader) != 'undefined'){
6690 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6693 if(typeof(config.mdHeader) != 'undefined'){
6694 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6697 if(typeof(config.smHeader) != 'undefined'){
6698 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6701 if(typeof(config.xsHeader) != 'undefined'){
6702 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6709 if(typeof(config.tooltip) != 'undefined'){
6710 c.tooltip = config.tooltip;
6713 if(typeof(config.colspan) != 'undefined'){
6714 c.colspan = config.colspan;
6717 if(typeof(config.hidden) != 'undefined' && config.hidden){
6718 c.style += ' display:none;';
6721 if(typeof(config.dataIndex) != 'undefined'){
6722 c.sort = config.dataIndex;
6727 if(typeof(config.align) != 'undefined' && config.align.length){
6728 c.style += ' text-align:' + config.align + ';';
6731 if(typeof(config.width) != 'undefined'){
6732 c.style += ' width:' + config.width + 'px;';
6733 this.totalWidth += config.width;
6735 this.totalWidth += 100; // assume minimum of 100 per column?
6738 if(typeof(config.cls) != 'undefined'){
6739 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6742 ['xs','sm','md','lg'].map(function(size){
6744 if(typeof(config[size]) == 'undefined'){
6748 if (!config[size]) { // 0 = hidden
6749 c.cls += ' hidden-' + size;
6753 c.cls += ' col-' + size + '-' + config[size];
6763 renderBody : function()
6773 colspan : this.cm.getColumnCount()
6783 renderFooter : function()
6793 colspan : this.cm.getColumnCount()
6807 // Roo.log('ds onload');
6812 var ds = this.store;
6814 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6815 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6816 if (_this.store.sortInfo) {
6818 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6819 e.select('i', true).addClass(['glyphicon-arrow-up']);
6822 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6823 e.select('i', true).addClass(['glyphicon-arrow-down']);
6828 var tbody = this.mainBody;
6830 if(ds.getCount() > 0){
6831 ds.data.each(function(d,rowIndex){
6832 var row = this.renderRow(cm, ds, rowIndex);
6834 tbody.createChild(row);
6838 if(row.cellObjects.length){
6839 Roo.each(row.cellObjects, function(r){
6840 _this.renderCellObject(r);
6847 var tfoot = this.el.select('tfoot', true).first();
6849 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6851 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6853 var total = this.ds.getTotalCount();
6855 if(this.footer.pageSize < total){
6856 this.mainFoot.show();
6860 Roo.each(this.el.select('tbody td', true).elements, function(e){
6861 e.on('mouseover', _this.onMouseover, _this);
6864 Roo.each(this.el.select('tbody td', true).elements, function(e){
6865 e.on('mouseout', _this.onMouseout, _this);
6867 this.fireEvent('rowsrendered', this);
6873 onUpdate : function(ds,record)
6875 this.refreshRow(record);
6879 onRemove : function(ds, record, index, isUpdate){
6880 if(isUpdate !== true){
6881 this.fireEvent("beforerowremoved", this, index, record);
6883 var bt = this.mainBody.dom;
6885 var rows = this.el.select('tbody > tr', true).elements;
6887 if(typeof(rows[index]) != 'undefined'){
6888 bt.removeChild(rows[index].dom);
6891 // if(bt.rows[index]){
6892 // bt.removeChild(bt.rows[index]);
6895 if(isUpdate !== true){
6896 //this.stripeRows(index);
6897 //this.syncRowHeights(index, index);
6899 this.fireEvent("rowremoved", this, index, record);
6903 onAdd : function(ds, records, rowIndex)
6905 //Roo.log('on Add called');
6906 // - note this does not handle multiple adding very well..
6907 var bt = this.mainBody.dom;
6908 for (var i =0 ; i < records.length;i++) {
6909 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6910 //Roo.log(records[i]);
6911 //Roo.log(this.store.getAt(rowIndex+i));
6912 this.insertRow(this.store, rowIndex + i, false);
6919 refreshRow : function(record){
6920 var ds = this.store, index;
6921 if(typeof record == 'number'){
6923 record = ds.getAt(index);
6925 index = ds.indexOf(record);
6927 this.insertRow(ds, index, true);
6929 this.onRemove(ds, record, index+1, true);
6931 //this.syncRowHeights(index, index);
6933 this.fireEvent("rowupdated", this, index, record);
6936 insertRow : function(dm, rowIndex, isUpdate){
6939 this.fireEvent("beforerowsinserted", this, rowIndex);
6941 //var s = this.getScrollState();
6942 var row = this.renderRow(this.cm, this.store, rowIndex);
6943 // insert before rowIndex..
6944 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6948 if(row.cellObjects.length){
6949 Roo.each(row.cellObjects, function(r){
6950 _this.renderCellObject(r);
6955 this.fireEvent("rowsinserted", this, rowIndex);
6956 //this.syncRowHeights(firstRow, lastRow);
6957 //this.stripeRows(firstRow);
6964 getRowDom : function(rowIndex)
6966 var rows = this.el.select('tbody > tr', true).elements;
6968 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6971 // returns the object tree for a tr..
6974 renderRow : function(cm, ds, rowIndex)
6976 var d = ds.getAt(rowIndex);
6980 cls : 'x-row-' + rowIndex,
6984 var cellObjects = [];
6986 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6987 var config = cm.config[i];
6989 var renderer = cm.getRenderer(i);
6993 if(typeof(renderer) !== 'undefined'){
6994 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6996 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6997 // and are rendered into the cells after the row is rendered - using the id for the element.
6999 if(typeof(value) === 'object'){
7009 rowIndex : rowIndex,
7014 this.fireEvent('rowclass', this, rowcfg);
7018 cls : rowcfg.rowClass + ' x-col-' + i,
7020 html: (typeof(value) === 'object') ? '' : value
7027 if(typeof(config.colspan) != 'undefined'){
7028 td.colspan = config.colspan;
7031 if(typeof(config.hidden) != 'undefined' && config.hidden){
7032 td.style += ' display:none;';
7035 if(typeof(config.align) != 'undefined' && config.align.length){
7036 td.style += ' text-align:' + config.align + ';';
7038 if(typeof(config.valign) != 'undefined' && config.valign.length){
7039 td.style += ' vertical-align:' + config.valign + ';';
7042 if(typeof(config.width) != 'undefined'){
7043 td.style += ' width:' + config.width + 'px;';
7046 if(typeof(config.cursor) != 'undefined'){
7047 td.style += ' cursor:' + config.cursor + ';';
7050 if(typeof(config.cls) != 'undefined'){
7051 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7054 ['xs','sm','md','lg'].map(function(size){
7056 if(typeof(config[size]) == 'undefined'){
7060 if (!config[size]) { // 0 = hidden
7061 td.cls += ' hidden-' + size;
7065 td.cls += ' col-' + size + '-' + config[size];
7073 row.cellObjects = cellObjects;
7081 onBeforeLoad : function()
7090 this.el.select('tbody', true).first().dom.innerHTML = '';
7093 * Show or hide a row.
7094 * @param {Number} rowIndex to show or hide
7095 * @param {Boolean} state hide
7097 setRowVisibility : function(rowIndex, state)
7099 var bt = this.mainBody.dom;
7101 var rows = this.el.select('tbody > tr', true).elements;
7103 if(typeof(rows[rowIndex]) == 'undefined'){
7106 rows[rowIndex].dom.style.display = state ? '' : 'none';
7110 getSelectionModel : function(){
7112 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7114 return this.selModel;
7117 * Render the Roo.bootstrap object from renderder
7119 renderCellObject : function(r)
7123 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7125 var t = r.cfg.render(r.container);
7128 Roo.each(r.cfg.cn, function(c){
7130 container: t.getChildContainer(),
7133 _this.renderCellObject(child);
7138 getRowIndex : function(row)
7142 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7153 * Returns the grid's underlying element = used by panel.Grid
7154 * @return {Element} The element
7156 getGridEl : function(){
7160 * Forces a resize - used by panel.Grid
7161 * @return {Element} The element
7163 autoSize : function()
7165 //var ctr = Roo.get(this.container.dom.parentElement);
7166 var ctr = Roo.get(this.el.dom);
7168 var thd = this.getGridEl().select('thead',true).first();
7169 var tbd = this.getGridEl().select('tbody', true).first();
7170 var tfd = this.getGridEl().select('tfoot', true).first();
7172 var cw = ctr.getWidth();
7176 tbd.setSize(ctr.getWidth(),
7177 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7179 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7182 cw = Math.max(cw, this.totalWidth);
7183 this.getGridEl().select('tr',true).setWidth(cw);
7184 // resize 'expandable coloumn?
7186 return; // we doe not have a view in this design..
7189 onBodyScroll: function()
7191 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7193 this.mainHead.setStyle({
7194 'position' : 'relative',
7195 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7201 var scrollHeight = this.mainBody.dom.scrollHeight;
7203 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7205 var height = this.mainBody.getHeight();
7207 if(scrollHeight - height == scrollTop) {
7209 var total = this.ds.getTotalCount();
7211 if(this.footer.cursor + this.footer.pageSize < total){
7213 this.footer.ds.load({
7215 start : this.footer.cursor + this.footer.pageSize,
7216 limit : this.footer.pageSize
7226 onHeaderChange : function()
7228 var header = this.renderHeader();
7229 var table = this.el.select('table', true).first();
7231 this.mainHead.remove();
7232 this.mainHead = table.createChild(header, this.mainBody, false);
7235 onHiddenChange : function(colModel, colIndex, hidden)
7237 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7238 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7240 this.CSS.updateRule(thSelector, "display", "");
7241 this.CSS.updateRule(tdSelector, "display", "");
7244 this.CSS.updateRule(thSelector, "display", "none");
7245 this.CSS.updateRule(tdSelector, "display", "none");
7248 this.onHeaderChange();
7252 setColumnWidth: function(col_index, width)
7254 // width = "md-2 xs-2..."
7255 if(!this.colModel.config[col_index]) {
7259 var w = width.split(" ");
7261 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7263 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7266 for(var j = 0; j < w.length; j++) {
7272 var size_cls = w[j].split("-");
7274 if(!Number.isInteger(size_cls[1] * 1)) {
7278 if(!this.colModel.config[col_index][size_cls[0]]) {
7282 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7286 h_row[0].classList.replace(
7287 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7288 "col-"+size_cls[0]+"-"+size_cls[1]
7291 for(var i = 0; i < rows.length; i++) {
7293 var size_cls = w[j].split("-");
7295 if(!Number.isInteger(size_cls[1] * 1)) {
7299 if(!this.colModel.config[col_index][size_cls[0]]) {
7303 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7307 rows[i].classList.replace(
7308 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7309 "col-"+size_cls[0]+"-"+size_cls[1]
7313 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7328 * @class Roo.bootstrap.TableCell
7329 * @extends Roo.bootstrap.Component
7330 * Bootstrap TableCell class
7331 * @cfg {String} html cell contain text
7332 * @cfg {String} cls cell class
7333 * @cfg {String} tag cell tag (td|th) default td
7334 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7335 * @cfg {String} align Aligns the content in a cell
7336 * @cfg {String} axis Categorizes cells
7337 * @cfg {String} bgcolor Specifies the background color of a cell
7338 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7339 * @cfg {Number} colspan Specifies the number of columns a cell should span
7340 * @cfg {String} headers Specifies one or more header cells a cell is related to
7341 * @cfg {Number} height Sets the height of a cell
7342 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7343 * @cfg {Number} rowspan Sets the number of rows a cell should span
7344 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7345 * @cfg {String} valign Vertical aligns the content in a cell
7346 * @cfg {Number} width Specifies the width of a cell
7349 * Create a new TableCell
7350 * @param {Object} config The config object
7353 Roo.bootstrap.TableCell = function(config){
7354 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7357 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7377 getAutoCreate : function(){
7378 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7398 cfg.align=this.align
7404 cfg.bgcolor=this.bgcolor
7407 cfg.charoff=this.charoff
7410 cfg.colspan=this.colspan
7413 cfg.headers=this.headers
7416 cfg.height=this.height
7419 cfg.nowrap=this.nowrap
7422 cfg.rowspan=this.rowspan
7425 cfg.scope=this.scope
7428 cfg.valign=this.valign
7431 cfg.width=this.width
7450 * @class Roo.bootstrap.TableRow
7451 * @extends Roo.bootstrap.Component
7452 * Bootstrap TableRow class
7453 * @cfg {String} cls row class
7454 * @cfg {String} align Aligns the content in a table row
7455 * @cfg {String} bgcolor Specifies a background color for a table row
7456 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7457 * @cfg {String} valign Vertical aligns the content in a table row
7460 * Create a new TableRow
7461 * @param {Object} config The config object
7464 Roo.bootstrap.TableRow = function(config){
7465 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7468 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7476 getAutoCreate : function(){
7477 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7487 cfg.align = this.align;
7490 cfg.bgcolor = this.bgcolor;
7493 cfg.charoff = this.charoff;
7496 cfg.valign = this.valign;
7514 * @class Roo.bootstrap.TableBody
7515 * @extends Roo.bootstrap.Component
7516 * Bootstrap TableBody class
7517 * @cfg {String} cls element class
7518 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7519 * @cfg {String} align Aligns the content inside the element
7520 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7521 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7524 * Create a new TableBody
7525 * @param {Object} config The config object
7528 Roo.bootstrap.TableBody = function(config){
7529 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7532 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7540 getAutoCreate : function(){
7541 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7555 cfg.align = this.align;
7558 cfg.charoff = this.charoff;
7561 cfg.valign = this.valign;
7568 // initEvents : function()
7575 // this.store = Roo.factory(this.store, Roo.data);
7576 // this.store.on('load', this.onLoad, this);
7578 // this.store.load();
7582 // onLoad: function ()
7584 // this.fireEvent('load', this);
7594 * Ext JS Library 1.1.1
7595 * Copyright(c) 2006-2007, Ext JS, LLC.
7597 * Originally Released Under LGPL - original licence link has changed is not relivant.
7600 * <script type="text/javascript">
7603 // as we use this in bootstrap.
7604 Roo.namespace('Roo.form');
7606 * @class Roo.form.Action
7607 * Internal Class used to handle form actions
7609 * @param {Roo.form.BasicForm} el The form element or its id
7610 * @param {Object} config Configuration options
7615 // define the action interface
7616 Roo.form.Action = function(form, options){
7618 this.options = options || {};
7621 * Client Validation Failed
7624 Roo.form.Action.CLIENT_INVALID = 'client';
7626 * Server Validation Failed
7629 Roo.form.Action.SERVER_INVALID = 'server';
7631 * Connect to Server Failed
7634 Roo.form.Action.CONNECT_FAILURE = 'connect';
7636 * Reading Data from Server Failed
7639 Roo.form.Action.LOAD_FAILURE = 'load';
7641 Roo.form.Action.prototype = {
7643 failureType : undefined,
7644 response : undefined,
7648 run : function(options){
7653 success : function(response){
7658 handleResponse : function(response){
7662 // default connection failure
7663 failure : function(response){
7665 this.response = response;
7666 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7667 this.form.afterAction(this, false);
7670 processResponse : function(response){
7671 this.response = response;
7672 if(!response.responseText){
7675 this.result = this.handleResponse(response);
7679 // utility functions used internally
7680 getUrl : function(appendParams){
7681 var url = this.options.url || this.form.url || this.form.el.dom.action;
7683 var p = this.getParams();
7685 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7691 getMethod : function(){
7692 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7695 getParams : function(){
7696 var bp = this.form.baseParams;
7697 var p = this.options.params;
7699 if(typeof p == "object"){
7700 p = Roo.urlEncode(Roo.applyIf(p, bp));
7701 }else if(typeof p == 'string' && bp){
7702 p += '&' + Roo.urlEncode(bp);
7705 p = Roo.urlEncode(bp);
7710 createCallback : function(){
7712 success: this.success,
7713 failure: this.failure,
7715 timeout: (this.form.timeout*1000),
7716 upload: this.form.fileUpload ? this.success : undefined
7721 Roo.form.Action.Submit = function(form, options){
7722 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7725 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7728 haveProgress : false,
7729 uploadComplete : false,
7731 // uploadProgress indicator.
7732 uploadProgress : function()
7734 if (!this.form.progressUrl) {
7738 if (!this.haveProgress) {
7739 Roo.MessageBox.progress("Uploading", "Uploading");
7741 if (this.uploadComplete) {
7742 Roo.MessageBox.hide();
7746 this.haveProgress = true;
7748 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7750 var c = new Roo.data.Connection();
7752 url : this.form.progressUrl,
7757 success : function(req){
7758 //console.log(data);
7762 rdata = Roo.decode(req.responseText)
7764 Roo.log("Invalid data from server..");
7768 if (!rdata || !rdata.success) {
7770 Roo.MessageBox.alert(Roo.encode(rdata));
7773 var data = rdata.data;
7775 if (this.uploadComplete) {
7776 Roo.MessageBox.hide();
7781 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7782 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7785 this.uploadProgress.defer(2000,this);
7788 failure: function(data) {
7789 Roo.log('progress url failed ');
7800 // run get Values on the form, so it syncs any secondary forms.
7801 this.form.getValues();
7803 var o = this.options;
7804 var method = this.getMethod();
7805 var isPost = method == 'POST';
7806 if(o.clientValidation === false || this.form.isValid()){
7808 if (this.form.progressUrl) {
7809 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7810 (new Date() * 1) + '' + Math.random());
7815 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7816 form:this.form.el.dom,
7817 url:this.getUrl(!isPost),
7819 params:isPost ? this.getParams() : null,
7820 isUpload: this.form.fileUpload
7823 this.uploadProgress();
7825 }else if (o.clientValidation !== false){ // client validation failed
7826 this.failureType = Roo.form.Action.CLIENT_INVALID;
7827 this.form.afterAction(this, false);
7831 success : function(response)
7833 this.uploadComplete= true;
7834 if (this.haveProgress) {
7835 Roo.MessageBox.hide();
7839 var result = this.processResponse(response);
7840 if(result === true || result.success){
7841 this.form.afterAction(this, true);
7845 this.form.markInvalid(result.errors);
7846 this.failureType = Roo.form.Action.SERVER_INVALID;
7848 this.form.afterAction(this, false);
7850 failure : function(response)
7852 this.uploadComplete= true;
7853 if (this.haveProgress) {
7854 Roo.MessageBox.hide();
7857 this.response = response;
7858 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7859 this.form.afterAction(this, false);
7862 handleResponse : function(response){
7863 if(this.form.errorReader){
7864 var rs = this.form.errorReader.read(response);
7867 for(var i = 0, len = rs.records.length; i < len; i++) {
7868 var r = rs.records[i];
7872 if(errors.length < 1){
7876 success : rs.success,
7882 ret = Roo.decode(response.responseText);
7886 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7896 Roo.form.Action.Load = function(form, options){
7897 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7898 this.reader = this.form.reader;
7901 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7906 Roo.Ajax.request(Roo.apply(
7907 this.createCallback(), {
7908 method:this.getMethod(),
7909 url:this.getUrl(false),
7910 params:this.getParams()
7914 success : function(response){
7916 var result = this.processResponse(response);
7917 if(result === true || !result.success || !result.data){
7918 this.failureType = Roo.form.Action.LOAD_FAILURE;
7919 this.form.afterAction(this, false);
7922 this.form.clearInvalid();
7923 this.form.setValues(result.data);
7924 this.form.afterAction(this, true);
7927 handleResponse : function(response){
7928 if(this.form.reader){
7929 var rs = this.form.reader.read(response);
7930 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7932 success : rs.success,
7936 return Roo.decode(response.responseText);
7940 Roo.form.Action.ACTION_TYPES = {
7941 'load' : Roo.form.Action.Load,
7942 'submit' : Roo.form.Action.Submit
7951 * @class Roo.bootstrap.Form
7952 * @extends Roo.bootstrap.Component
7953 * Bootstrap Form class
7954 * @cfg {String} method GET | POST (default POST)
7955 * @cfg {String} labelAlign top | left (default top)
7956 * @cfg {String} align left | right - for navbars
7957 * @cfg {Boolean} loadMask load mask when submit (default true)
7962 * @param {Object} config The config object
7966 Roo.bootstrap.Form = function(config){
7968 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7970 Roo.bootstrap.Form.popover.apply();
7974 * @event clientvalidation
7975 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7976 * @param {Form} this
7977 * @param {Boolean} valid true if the form has passed client-side validation
7979 clientvalidation: true,
7981 * @event beforeaction
7982 * Fires before any action is performed. Return false to cancel the action.
7983 * @param {Form} this
7984 * @param {Action} action The action to be performed
7988 * @event actionfailed
7989 * Fires when an action fails.
7990 * @param {Form} this
7991 * @param {Action} action The action that failed
7993 actionfailed : true,
7995 * @event actioncomplete
7996 * Fires when an action is completed.
7997 * @param {Form} this
7998 * @param {Action} action The action that completed
8000 actioncomplete : true
8004 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8007 * @cfg {String} method
8008 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8013 * The URL to use for form actions if one isn't supplied in the action options.
8016 * @cfg {Boolean} fileUpload
8017 * Set to true if this form is a file upload.
8021 * @cfg {Object} baseParams
8022 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8026 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8030 * @cfg {Sting} align (left|right) for navbar forms
8035 activeAction : null,
8038 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8039 * element by passing it or its id or mask the form itself by passing in true.
8042 waitMsgTarget : false,
8047 * @cfg {Boolean} errorMask (true|false) default false
8052 * @cfg {Number} maskOffset Default 100
8057 * @cfg {Boolean} maskBody
8061 getAutoCreate : function(){
8065 method : this.method || 'POST',
8066 id : this.id || Roo.id(),
8069 if (this.parent().xtype.match(/^Nav/)) {
8070 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8074 if (this.labelAlign == 'left' ) {
8075 cfg.cls += ' form-horizontal';
8081 initEvents : function()
8083 this.el.on('submit', this.onSubmit, this);
8084 // this was added as random key presses on the form where triggering form submit.
8085 this.el.on('keypress', function(e) {
8086 if (e.getCharCode() != 13) {
8089 // we might need to allow it for textareas.. and some other items.
8090 // check e.getTarget().
8092 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8096 Roo.log("keypress blocked");
8104 onSubmit : function(e){
8109 * Returns true if client-side validation on the form is successful.
8112 isValid : function(){
8113 var items = this.getItems();
8117 items.each(function(f){
8123 Roo.log('invalid field: ' + f.name);
8127 if(!target && f.el.isVisible(true)){
8133 if(this.errorMask && !valid){
8134 Roo.bootstrap.Form.popover.mask(this, target);
8141 * Returns true if any fields in this form have changed since their original load.
8144 isDirty : function(){
8146 var items = this.getItems();
8147 items.each(function(f){
8157 * Performs a predefined action (submit or load) or custom actions you define on this form.
8158 * @param {String} actionName The name of the action type
8159 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8160 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8161 * accept other config options):
8163 Property Type Description
8164 ---------------- --------------- ----------------------------------------------------------------------------------
8165 url String The url for the action (defaults to the form's url)
8166 method String The form method to use (defaults to the form's method, or POST if not defined)
8167 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8168 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8169 validate the form on the client (defaults to false)
8171 * @return {BasicForm} this
8173 doAction : function(action, options){
8174 if(typeof action == 'string'){
8175 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8177 if(this.fireEvent('beforeaction', this, action) !== false){
8178 this.beforeAction(action);
8179 action.run.defer(100, action);
8185 beforeAction : function(action){
8186 var o = action.options;
8191 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8193 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8196 // not really supported yet.. ??
8198 //if(this.waitMsgTarget === true){
8199 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8200 //}else if(this.waitMsgTarget){
8201 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8202 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8204 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8210 afterAction : function(action, success){
8211 this.activeAction = null;
8212 var o = action.options;
8217 Roo.get(document.body).unmask();
8223 //if(this.waitMsgTarget === true){
8224 // this.el.unmask();
8225 //}else if(this.waitMsgTarget){
8226 // this.waitMsgTarget.unmask();
8228 // Roo.MessageBox.updateProgress(1);
8229 // Roo.MessageBox.hide();
8236 Roo.callback(o.success, o.scope, [this, action]);
8237 this.fireEvent('actioncomplete', this, action);
8241 // failure condition..
8242 // we have a scenario where updates need confirming.
8243 // eg. if a locking scenario exists..
8244 // we look for { errors : { needs_confirm : true }} in the response.
8246 (typeof(action.result) != 'undefined') &&
8247 (typeof(action.result.errors) != 'undefined') &&
8248 (typeof(action.result.errors.needs_confirm) != 'undefined')
8251 Roo.log("not supported yet");
8254 Roo.MessageBox.confirm(
8255 "Change requires confirmation",
8256 action.result.errorMsg,
8261 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8271 Roo.callback(o.failure, o.scope, [this, action]);
8272 // show an error message if no failed handler is set..
8273 if (!this.hasListener('actionfailed')) {
8274 Roo.log("need to add dialog support");
8276 Roo.MessageBox.alert("Error",
8277 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8278 action.result.errorMsg :
8279 "Saving Failed, please check your entries or try again"
8284 this.fireEvent('actionfailed', this, action);
8289 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8290 * @param {String} id The value to search for
8293 findField : function(id){
8294 var items = this.getItems();
8295 var field = items.get(id);
8297 items.each(function(f){
8298 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8305 return field || null;
8308 * Mark fields in this form invalid in bulk.
8309 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8310 * @return {BasicForm} this
8312 markInvalid : function(errors){
8313 if(errors instanceof Array){
8314 for(var i = 0, len = errors.length; i < len; i++){
8315 var fieldError = errors[i];
8316 var f = this.findField(fieldError.id);
8318 f.markInvalid(fieldError.msg);
8324 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8325 field.markInvalid(errors[id]);
8329 //Roo.each(this.childForms || [], function (f) {
8330 // f.markInvalid(errors);
8337 * Set values for fields in this form in bulk.
8338 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8339 * @return {BasicForm} this
8341 setValues : function(values){
8342 if(values instanceof Array){ // array of objects
8343 for(var i = 0, len = values.length; i < len; i++){
8345 var f = this.findField(v.id);
8347 f.setValue(v.value);
8348 if(this.trackResetOnLoad){
8349 f.originalValue = f.getValue();
8353 }else{ // object hash
8356 if(typeof values[id] != 'function' && (field = this.findField(id))){
8358 if (field.setFromData &&
8360 field.displayField &&
8361 // combos' with local stores can
8362 // be queried via setValue()
8363 // to set their value..
8364 (field.store && !field.store.isLocal)
8368 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8369 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8370 field.setFromData(sd);
8372 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8374 field.setFromData(values);
8377 field.setValue(values[id]);
8381 if(this.trackResetOnLoad){
8382 field.originalValue = field.getValue();
8388 //Roo.each(this.childForms || [], function (f) {
8389 // f.setValues(values);
8396 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8397 * they are returned as an array.
8398 * @param {Boolean} asString
8401 getValues : function(asString){
8402 //if (this.childForms) {
8403 // copy values from the child forms
8404 // Roo.each(this.childForms, function (f) {
8405 // this.setValues(f.getValues());
8411 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8412 if(asString === true){
8415 return Roo.urlDecode(fs);
8419 * Returns the fields in this form as an object with key/value pairs.
8420 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8423 getFieldValues : function(with_hidden)
8425 var items = this.getItems();
8427 items.each(function(f){
8433 var v = f.getValue();
8435 if (f.inputType =='radio') {
8436 if (typeof(ret[f.getName()]) == 'undefined') {
8437 ret[f.getName()] = ''; // empty..
8440 if (!f.el.dom.checked) {
8448 if(f.xtype == 'MoneyField'){
8449 ret[f.currencyName] = f.getCurrency();
8452 // not sure if this supported any more..
8453 if ((typeof(v) == 'object') && f.getRawValue) {
8454 v = f.getRawValue() ; // dates..
8456 // combo boxes where name != hiddenName...
8457 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8458 ret[f.name] = f.getRawValue();
8460 ret[f.getName()] = v;
8467 * Clears all invalid messages in this form.
8468 * @return {BasicForm} this
8470 clearInvalid : function(){
8471 var items = this.getItems();
8473 items.each(function(f){
8482 * @return {BasicForm} this
8485 var items = this.getItems();
8486 items.each(function(f){
8490 Roo.each(this.childForms || [], function (f) {
8498 getItems : function()
8500 var r=new Roo.util.MixedCollection(false, function(o){
8501 return o.id || (o.id = Roo.id());
8503 var iter = function(el) {
8510 Roo.each(el.items,function(e) {
8519 hideFields : function(items)
8521 Roo.each(items, function(i){
8523 var f = this.findField(i);
8534 showFields : function(items)
8536 Roo.each(items, function(i){
8538 var f = this.findField(i);
8551 Roo.apply(Roo.bootstrap.Form, {
8578 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8579 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8580 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8581 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8584 this.maskEl.top.enableDisplayMode("block");
8585 this.maskEl.left.enableDisplayMode("block");
8586 this.maskEl.bottom.enableDisplayMode("block");
8587 this.maskEl.right.enableDisplayMode("block");
8589 this.toolTip = new Roo.bootstrap.Tooltip({
8590 cls : 'roo-form-error-popover',
8592 'left' : ['r-l', [-2,0], 'right'],
8593 'right' : ['l-r', [2,0], 'left'],
8594 'bottom' : ['tl-bl', [0,2], 'top'],
8595 'top' : [ 'bl-tl', [0,-2], 'bottom']
8599 this.toolTip.render(Roo.get(document.body));
8601 this.toolTip.el.enableDisplayMode("block");
8603 Roo.get(document.body).on('click', function(){
8607 Roo.get(document.body).on('touchstart', function(){
8611 this.isApplied = true
8614 mask : function(form, target)
8618 this.target = target;
8620 if(!this.form.errorMask || !target.el){
8624 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8626 Roo.log(scrollable);
8628 var ot = this.target.el.calcOffsetsTo(scrollable);
8630 var scrollTo = ot[1] - this.form.maskOffset;
8632 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8634 scrollable.scrollTo('top', scrollTo);
8636 var box = this.target.el.getBox();
8638 var zIndex = Roo.bootstrap.Modal.zIndex++;
8641 this.maskEl.top.setStyle('position', 'absolute');
8642 this.maskEl.top.setStyle('z-index', zIndex);
8643 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8644 this.maskEl.top.setLeft(0);
8645 this.maskEl.top.setTop(0);
8646 this.maskEl.top.show();
8648 this.maskEl.left.setStyle('position', 'absolute');
8649 this.maskEl.left.setStyle('z-index', zIndex);
8650 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8651 this.maskEl.left.setLeft(0);
8652 this.maskEl.left.setTop(box.y - this.padding);
8653 this.maskEl.left.show();
8655 this.maskEl.bottom.setStyle('position', 'absolute');
8656 this.maskEl.bottom.setStyle('z-index', zIndex);
8657 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8658 this.maskEl.bottom.setLeft(0);
8659 this.maskEl.bottom.setTop(box.bottom + this.padding);
8660 this.maskEl.bottom.show();
8662 this.maskEl.right.setStyle('position', 'absolute');
8663 this.maskEl.right.setStyle('z-index', zIndex);
8664 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8665 this.maskEl.right.setLeft(box.right + this.padding);
8666 this.maskEl.right.setTop(box.y - this.padding);
8667 this.maskEl.right.show();
8669 this.toolTip.bindEl = this.target.el;
8671 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8673 var tip = this.target.blankText;
8675 if(this.target.getValue() !== '' ) {
8677 if (this.target.invalidText.length) {
8678 tip = this.target.invalidText;
8679 } else if (this.target.regexText.length){
8680 tip = this.target.regexText;
8684 this.toolTip.show(tip);
8686 this.intervalID = window.setInterval(function() {
8687 Roo.bootstrap.Form.popover.unmask();
8690 window.onwheel = function(){ return false;};
8692 (function(){ this.isMasked = true; }).defer(500, this);
8698 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8702 this.maskEl.top.setStyle('position', 'absolute');
8703 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8704 this.maskEl.top.hide();
8706 this.maskEl.left.setStyle('position', 'absolute');
8707 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8708 this.maskEl.left.hide();
8710 this.maskEl.bottom.setStyle('position', 'absolute');
8711 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8712 this.maskEl.bottom.hide();
8714 this.maskEl.right.setStyle('position', 'absolute');
8715 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8716 this.maskEl.right.hide();
8718 this.toolTip.hide();
8720 this.toolTip.el.hide();
8722 window.onwheel = function(){ return true;};
8724 if(this.intervalID){
8725 window.clearInterval(this.intervalID);
8726 this.intervalID = false;
8729 this.isMasked = false;
8739 * Ext JS Library 1.1.1
8740 * Copyright(c) 2006-2007, Ext JS, LLC.
8742 * Originally Released Under LGPL - original licence link has changed is not relivant.
8745 * <script type="text/javascript">
8748 * @class Roo.form.VTypes
8749 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8752 Roo.form.VTypes = function(){
8753 // closure these in so they are only created once.
8754 var alpha = /^[a-zA-Z_]+$/;
8755 var alphanum = /^[a-zA-Z0-9_]+$/;
8756 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8757 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8759 // All these messages and functions are configurable
8762 * The function used to validate email addresses
8763 * @param {String} value The email address
8765 'email' : function(v){
8766 return email.test(v);
8769 * The error text to display when the email validation function returns false
8772 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8774 * The keystroke filter mask to be applied on email input
8777 'emailMask' : /[a-z0-9_\.\-@]/i,
8780 * The function used to validate URLs
8781 * @param {String} value The URL
8783 'url' : function(v){
8787 * The error text to display when the url validation function returns false
8790 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8793 * The function used to validate alpha values
8794 * @param {String} value The value
8796 'alpha' : function(v){
8797 return alpha.test(v);
8800 * The error text to display when the alpha validation function returns false
8803 'alphaText' : 'This field should only contain letters and _',
8805 * The keystroke filter mask to be applied on alpha input
8808 'alphaMask' : /[a-z_]/i,
8811 * The function used to validate alphanumeric values
8812 * @param {String} value The value
8814 'alphanum' : function(v){
8815 return alphanum.test(v);
8818 * The error text to display when the alphanumeric validation function returns false
8821 'alphanumText' : 'This field should only contain letters, numbers and _',
8823 * The keystroke filter mask to be applied on alphanumeric input
8826 'alphanumMask' : /[a-z0-9_]/i
8836 * @class Roo.bootstrap.Input
8837 * @extends Roo.bootstrap.Component
8838 * Bootstrap Input class
8839 * @cfg {Boolean} disabled is it disabled
8840 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8841 * @cfg {String} name name of the input
8842 * @cfg {string} fieldLabel - the label associated
8843 * @cfg {string} placeholder - placeholder to put in text.
8844 * @cfg {string} before - input group add on before
8845 * @cfg {string} after - input group add on after
8846 * @cfg {string} size - (lg|sm) or leave empty..
8847 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8848 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8849 * @cfg {Number} md colspan out of 12 for computer-sized screens
8850 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8851 * @cfg {string} value default value of the input
8852 * @cfg {Number} labelWidth set the width of label
8853 * @cfg {Number} labellg set the width of label (1-12)
8854 * @cfg {Number} labelmd set the width of label (1-12)
8855 * @cfg {Number} labelsm set the width of label (1-12)
8856 * @cfg {Number} labelxs set the width of label (1-12)
8857 * @cfg {String} labelAlign (top|left)
8858 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8859 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8860 * @cfg {String} indicatorpos (left|right) default left
8861 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8862 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8864 * @cfg {String} align (left|center|right) Default left
8865 * @cfg {Boolean} forceFeedback (true|false) Default false
8868 * Create a new Input
8869 * @param {Object} config The config object
8872 Roo.bootstrap.Input = function(config){
8874 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8879 * Fires when this field receives input focus.
8880 * @param {Roo.form.Field} this
8885 * Fires when this field loses input focus.
8886 * @param {Roo.form.Field} this
8891 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8892 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8893 * @param {Roo.form.Field} this
8894 * @param {Roo.EventObject} e The event object
8899 * Fires just before the field blurs if the field value has changed.
8900 * @param {Roo.form.Field} this
8901 * @param {Mixed} newValue The new value
8902 * @param {Mixed} oldValue The original value
8907 * Fires after the field has been marked as invalid.
8908 * @param {Roo.form.Field} this
8909 * @param {String} msg The validation message
8914 * Fires after the field has been validated with no errors.
8915 * @param {Roo.form.Field} this
8920 * Fires after the key up
8921 * @param {Roo.form.Field} this
8922 * @param {Roo.EventObject} e The event Object
8928 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8930 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8931 automatic validation (defaults to "keyup").
8933 validationEvent : "keyup",
8935 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8937 validateOnBlur : true,
8939 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8941 validationDelay : 250,
8943 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8945 focusClass : "x-form-focus", // not needed???
8949 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8951 invalidClass : "has-warning",
8954 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8956 validClass : "has-success",
8959 * @cfg {Boolean} hasFeedback (true|false) default true
8964 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8966 invalidFeedbackClass : "glyphicon-warning-sign",
8969 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8971 validFeedbackClass : "glyphicon-ok",
8974 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8976 selectOnFocus : false,
8979 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8983 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8988 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8990 disableKeyFilter : false,
8993 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8997 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9001 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9003 blankText : "Please complete this mandatory field",
9006 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9010 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9012 maxLength : Number.MAX_VALUE,
9014 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9016 minLengthText : "The minimum length for this field is {0}",
9018 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9020 maxLengthText : "The maximum length for this field is {0}",
9024 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9025 * If available, this function will be called only after the basic validators all return true, and will be passed the
9026 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9030 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9031 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9032 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9036 * @cfg {String} regexText -- Depricated - use Invalid Text
9041 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9047 autocomplete: false,
9066 formatedValue : false,
9067 forceFeedback : false,
9069 indicatorpos : 'left',
9079 parentLabelAlign : function()
9082 while (parent.parent()) {
9083 parent = parent.parent();
9084 if (typeof(parent.labelAlign) !='undefined') {
9085 return parent.labelAlign;
9092 getAutoCreate : function()
9094 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9100 if(this.inputType != 'hidden'){
9101 cfg.cls = 'form-group' //input-group
9107 type : this.inputType,
9109 cls : 'form-control',
9110 placeholder : this.placeholder || '',
9111 autocomplete : this.autocomplete || 'new-password'
9114 if(this.capture.length){
9115 input.capture = this.capture;
9118 if(this.accept.length){
9119 input.accept = this.accept + "/*";
9123 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9126 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9127 input.maxLength = this.maxLength;
9130 if (this.disabled) {
9131 input.disabled=true;
9134 if (this.readOnly) {
9135 input.readonly=true;
9139 input.name = this.name;
9143 input.cls += ' input-' + this.size;
9147 ['xs','sm','md','lg'].map(function(size){
9148 if (settings[size]) {
9149 cfg.cls += ' col-' + size + '-' + settings[size];
9153 var inputblock = input;
9157 cls: 'glyphicon form-control-feedback'
9160 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9163 cls : 'has-feedback',
9171 if (this.before || this.after) {
9174 cls : 'input-group',
9178 if (this.before && typeof(this.before) == 'string') {
9180 inputblock.cn.push({
9182 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9186 if (this.before && typeof(this.before) == 'object') {
9187 this.before = Roo.factory(this.before);
9189 inputblock.cn.push({
9191 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9192 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9196 inputblock.cn.push(input);
9198 if (this.after && typeof(this.after) == 'string') {
9199 inputblock.cn.push({
9201 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9205 if (this.after && typeof(this.after) == 'object') {
9206 this.after = Roo.factory(this.after);
9208 inputblock.cn.push({
9210 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9211 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9215 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9216 inputblock.cls += ' has-feedback';
9217 inputblock.cn.push(feedback);
9222 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9223 tooltip : 'This field is required'
9225 if (Roo.bootstrap.version == 4) {
9228 style : 'display-none'
9231 if (align ==='left' && this.fieldLabel.length) {
9233 cfg.cls += ' roo-form-group-label-left row';
9240 cls : 'control-label col-form-label',
9241 html : this.fieldLabel
9252 var labelCfg = cfg.cn[1];
9253 var contentCfg = cfg.cn[2];
9255 if(this.indicatorpos == 'right'){
9260 cls : 'control-label col-form-label',
9264 html : this.fieldLabel
9278 labelCfg = cfg.cn[0];
9279 contentCfg = cfg.cn[1];
9283 if(this.labelWidth > 12){
9284 labelCfg.style = "width: " + this.labelWidth + 'px';
9287 if(this.labelWidth < 13 && this.labelmd == 0){
9288 this.labelmd = this.labelWidth;
9291 if(this.labellg > 0){
9292 labelCfg.cls += ' col-lg-' + this.labellg;
9293 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9296 if(this.labelmd > 0){
9297 labelCfg.cls += ' col-md-' + this.labelmd;
9298 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9301 if(this.labelsm > 0){
9302 labelCfg.cls += ' col-sm-' + this.labelsm;
9303 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9306 if(this.labelxs > 0){
9307 labelCfg.cls += ' col-xs-' + this.labelxs;
9308 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9312 } else if ( this.fieldLabel.length) {
9317 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9318 tooltip : 'This field is required'
9322 //cls : 'input-group-addon',
9323 html : this.fieldLabel
9331 if(this.indicatorpos == 'right'){
9336 //cls : 'input-group-addon',
9337 html : this.fieldLabel
9342 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9343 tooltip : 'This field is required'
9363 if (this.parentType === 'Navbar' && this.parent().bar) {
9364 cfg.cls += ' navbar-form';
9367 if (this.parentType === 'NavGroup') {
9368 cfg.cls += ' navbar-form';
9376 * return the real input element.
9378 inputEl: function ()
9380 return this.el.select('input.form-control',true).first();
9383 tooltipEl : function()
9385 return this.inputEl();
9388 indicatorEl : function()
9390 if (Roo.bootstrap.version == 4) {
9391 return false; // not enabled in v4 yet.
9394 var indicator = this.el.select('i.roo-required-indicator',true).first();
9404 setDisabled : function(v)
9406 var i = this.inputEl().dom;
9408 i.removeAttribute('disabled');
9412 i.setAttribute('disabled','true');
9414 initEvents : function()
9417 this.inputEl().on("keydown" , this.fireKey, this);
9418 this.inputEl().on("focus", this.onFocus, this);
9419 this.inputEl().on("blur", this.onBlur, this);
9421 this.inputEl().relayEvent('keyup', this);
9423 this.indicator = this.indicatorEl();
9426 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9429 // reference to original value for reset
9430 this.originalValue = this.getValue();
9431 //Roo.form.TextField.superclass.initEvents.call(this);
9432 if(this.validationEvent == 'keyup'){
9433 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9434 this.inputEl().on('keyup', this.filterValidation, this);
9436 else if(this.validationEvent !== false){
9437 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9440 if(this.selectOnFocus){
9441 this.on("focus", this.preFocus, this);
9444 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9445 this.inputEl().on("keypress", this.filterKeys, this);
9447 this.inputEl().relayEvent('keypress', this);
9450 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9451 this.el.on("click", this.autoSize, this);
9454 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9455 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9458 if (typeof(this.before) == 'object') {
9459 this.before.render(this.el.select('.roo-input-before',true).first());
9461 if (typeof(this.after) == 'object') {
9462 this.after.render(this.el.select('.roo-input-after',true).first());
9465 this.inputEl().on('change', this.onChange, this);
9468 filterValidation : function(e){
9469 if(!e.isNavKeyPress()){
9470 this.validationTask.delay(this.validationDelay);
9474 * Validates the field value
9475 * @return {Boolean} True if the value is valid, else false
9477 validate : function(){
9478 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9479 if(this.disabled || this.validateValue(this.getRawValue())){
9490 * Validates a value according to the field's validation rules and marks the field as invalid
9491 * if the validation fails
9492 * @param {Mixed} value The value to validate
9493 * @return {Boolean} True if the value is valid, else false
9495 validateValue : function(value)
9497 if(this.getVisibilityEl().hasClass('hidden')){
9501 if(value.length < 1) { // if it's blank
9502 if(this.allowBlank){
9508 if(value.length < this.minLength){
9511 if(value.length > this.maxLength){
9515 var vt = Roo.form.VTypes;
9516 if(!vt[this.vtype](value, this)){
9520 if(typeof this.validator == "function"){
9521 var msg = this.validator(value);
9525 if (typeof(msg) == 'string') {
9526 this.invalidText = msg;
9530 if(this.regex && !this.regex.test(value)){
9538 fireKey : function(e){
9539 //Roo.log('field ' + e.getKey());
9540 if(e.isNavKeyPress()){
9541 this.fireEvent("specialkey", this, e);
9544 focus : function (selectText){
9546 this.inputEl().focus();
9547 if(selectText === true){
9548 this.inputEl().dom.select();
9554 onFocus : function(){
9555 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9556 // this.el.addClass(this.focusClass);
9559 this.hasFocus = true;
9560 this.startValue = this.getValue();
9561 this.fireEvent("focus", this);
9565 beforeBlur : Roo.emptyFn,
9569 onBlur : function(){
9571 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9572 //this.el.removeClass(this.focusClass);
9574 this.hasFocus = false;
9575 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9578 var v = this.getValue();
9579 if(String(v) !== String(this.startValue)){
9580 this.fireEvent('change', this, v, this.startValue);
9582 this.fireEvent("blur", this);
9585 onChange : function(e)
9587 var v = this.getValue();
9588 if(String(v) !== String(this.startValue)){
9589 this.fireEvent('change', this, v, this.startValue);
9595 * Resets the current field value to the originally loaded value and clears any validation messages
9598 this.setValue(this.originalValue);
9602 * Returns the name of the field
9603 * @return {Mixed} name The name field
9605 getName: function(){
9609 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9610 * @return {Mixed} value The field value
9612 getValue : function(){
9614 var v = this.inputEl().getValue();
9619 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9620 * @return {Mixed} value The field value
9622 getRawValue : function(){
9623 var v = this.inputEl().getValue();
9629 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9630 * @param {Mixed} value The value to set
9632 setRawValue : function(v){
9633 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9636 selectText : function(start, end){
9637 var v = this.getRawValue();
9639 start = start === undefined ? 0 : start;
9640 end = end === undefined ? v.length : end;
9641 var d = this.inputEl().dom;
9642 if(d.setSelectionRange){
9643 d.setSelectionRange(start, end);
9644 }else if(d.createTextRange){
9645 var range = d.createTextRange();
9646 range.moveStart("character", start);
9647 range.moveEnd("character", v.length-end);
9654 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9655 * @param {Mixed} value The value to set
9657 setValue : function(v){
9660 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9666 processValue : function(value){
9667 if(this.stripCharsRe){
9668 var newValue = value.replace(this.stripCharsRe, '');
9669 if(newValue !== value){
9670 this.setRawValue(newValue);
9677 preFocus : function(){
9679 if(this.selectOnFocus){
9680 this.inputEl().dom.select();
9683 filterKeys : function(e){
9685 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9688 var c = e.getCharCode(), cc = String.fromCharCode(c);
9689 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9692 if(!this.maskRe.test(cc)){
9697 * Clear any invalid styles/messages for this field
9699 clearInvalid : function(){
9701 if(!this.el || this.preventMark){ // not rendered
9706 this.el.removeClass(this.invalidClass);
9708 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9710 var feedback = this.el.select('.form-control-feedback', true).first();
9713 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9719 this.indicator.removeClass('visible');
9720 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9723 this.fireEvent('valid', this);
9727 * Mark this field as valid
9729 markValid : function()
9731 if(!this.el || this.preventMark){ // not rendered...
9735 this.el.removeClass([this.invalidClass, this.validClass]);
9737 var feedback = this.el.select('.form-control-feedback', true).first();
9740 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9744 this.indicator.removeClass('visible');
9745 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9752 if(this.allowBlank && !this.getRawValue().length){
9756 this.el.addClass(this.validClass);
9758 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9760 var feedback = this.el.select('.form-control-feedback', true).first();
9763 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9764 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9769 this.fireEvent('valid', this);
9773 * Mark this field as invalid
9774 * @param {String} msg The validation message
9776 markInvalid : function(msg)
9778 if(!this.el || this.preventMark){ // not rendered
9782 this.el.removeClass([this.invalidClass, this.validClass]);
9784 var feedback = this.el.select('.form-control-feedback', true).first();
9787 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9794 if(this.allowBlank && !this.getRawValue().length){
9799 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9800 this.indicator.addClass('visible');
9803 this.el.addClass(this.invalidClass);
9805 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9807 var feedback = this.el.select('.form-control-feedback', true).first();
9810 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9812 if(this.getValue().length || this.forceFeedback){
9813 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9820 this.fireEvent('invalid', this, msg);
9823 SafariOnKeyDown : function(event)
9825 // this is a workaround for a password hang bug on chrome/ webkit.
9826 if (this.inputEl().dom.type != 'password') {
9830 var isSelectAll = false;
9832 if(this.inputEl().dom.selectionEnd > 0){
9833 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9835 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9836 event.preventDefault();
9841 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9843 event.preventDefault();
9844 // this is very hacky as keydown always get's upper case.
9846 var cc = String.fromCharCode(event.getCharCode());
9847 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9851 adjustWidth : function(tag, w){
9852 tag = tag.toLowerCase();
9853 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9854 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9858 if(tag == 'textarea'){
9861 }else if(Roo.isOpera){
9865 if(tag == 'textarea'){
9873 setFieldLabel : function(v)
9879 if(this.indicatorEl()){
9880 var ar = this.el.select('label > span',true);
9882 if (ar.elements.length) {
9883 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9884 this.fieldLabel = v;
9888 var br = this.el.select('label',true);
9890 if(br.elements.length) {
9891 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9892 this.fieldLabel = v;
9896 Roo.log('Cannot Found any of label > span || label in input');
9900 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9901 this.fieldLabel = v;
9916 * @class Roo.bootstrap.TextArea
9917 * @extends Roo.bootstrap.Input
9918 * Bootstrap TextArea class
9919 * @cfg {Number} cols Specifies the visible width of a text area
9920 * @cfg {Number} rows Specifies the visible number of lines in a text area
9921 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9922 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9923 * @cfg {string} html text
9926 * Create a new TextArea
9927 * @param {Object} config The config object
9930 Roo.bootstrap.TextArea = function(config){
9931 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9935 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9945 getAutoCreate : function(){
9947 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9953 if(this.inputType != 'hidden'){
9954 cfg.cls = 'form-group' //input-group
9962 value : this.value || '',
9963 html: this.html || '',
9964 cls : 'form-control',
9965 placeholder : this.placeholder || ''
9969 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9970 input.maxLength = this.maxLength;
9974 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9978 input.cols = this.cols;
9981 if (this.readOnly) {
9982 input.readonly = true;
9986 input.name = this.name;
9990 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9994 ['xs','sm','md','lg'].map(function(size){
9995 if (settings[size]) {
9996 cfg.cls += ' col-' + size + '-' + settings[size];
10000 var inputblock = input;
10002 if(this.hasFeedback && !this.allowBlank){
10006 cls: 'glyphicon form-control-feedback'
10010 cls : 'has-feedback',
10019 if (this.before || this.after) {
10022 cls : 'input-group',
10026 inputblock.cn.push({
10028 cls : 'input-group-addon',
10033 inputblock.cn.push(input);
10035 if(this.hasFeedback && !this.allowBlank){
10036 inputblock.cls += ' has-feedback';
10037 inputblock.cn.push(feedback);
10041 inputblock.cn.push({
10043 cls : 'input-group-addon',
10050 if (align ==='left' && this.fieldLabel.length) {
10055 cls : 'control-label',
10056 html : this.fieldLabel
10067 if(this.labelWidth > 12){
10068 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10071 if(this.labelWidth < 13 && this.labelmd == 0){
10072 this.labelmd = this.labelWidth;
10075 if(this.labellg > 0){
10076 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10077 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10080 if(this.labelmd > 0){
10081 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10082 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10085 if(this.labelsm > 0){
10086 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10087 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10090 if(this.labelxs > 0){
10091 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10092 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10095 } else if ( this.fieldLabel.length) {
10100 //cls : 'input-group-addon',
10101 html : this.fieldLabel
10119 if (this.disabled) {
10120 input.disabled=true;
10127 * return the real textarea element.
10129 inputEl: function ()
10131 return this.el.select('textarea.form-control',true).first();
10135 * Clear any invalid styles/messages for this field
10137 clearInvalid : function()
10140 if(!this.el || this.preventMark){ // not rendered
10144 var label = this.el.select('label', true).first();
10145 var icon = this.el.select('i.fa-star', true).first();
10151 this.el.removeClass(this.invalidClass);
10153 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10155 var feedback = this.el.select('.form-control-feedback', true).first();
10158 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10163 this.fireEvent('valid', this);
10167 * Mark this field as valid
10169 markValid : function()
10171 if(!this.el || this.preventMark){ // not rendered
10175 this.el.removeClass([this.invalidClass, this.validClass]);
10177 var feedback = this.el.select('.form-control-feedback', true).first();
10180 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10183 if(this.disabled || this.allowBlank){
10187 var label = this.el.select('label', true).first();
10188 var icon = this.el.select('i.fa-star', true).first();
10194 this.el.addClass(this.validClass);
10196 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10198 var feedback = this.el.select('.form-control-feedback', true).first();
10201 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10202 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10207 this.fireEvent('valid', this);
10211 * Mark this field as invalid
10212 * @param {String} msg The validation message
10214 markInvalid : function(msg)
10216 if(!this.el || this.preventMark){ // not rendered
10220 this.el.removeClass([this.invalidClass, this.validClass]);
10222 var feedback = this.el.select('.form-control-feedback', true).first();
10225 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10228 if(this.disabled || this.allowBlank){
10232 var label = this.el.select('label', true).first();
10233 var icon = this.el.select('i.fa-star', true).first();
10235 if(!this.getValue().length && label && !icon){
10236 this.el.createChild({
10238 cls : 'text-danger fa fa-lg fa-star',
10239 tooltip : 'This field is required',
10240 style : 'margin-right:5px;'
10244 this.el.addClass(this.invalidClass);
10246 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10248 var feedback = this.el.select('.form-control-feedback', true).first();
10251 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10253 if(this.getValue().length || this.forceFeedback){
10254 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10261 this.fireEvent('invalid', this, msg);
10269 * trigger field - base class for combo..
10274 * @class Roo.bootstrap.TriggerField
10275 * @extends Roo.bootstrap.Input
10276 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10277 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10278 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10279 * for which you can provide a custom implementation. For example:
10281 var trigger = new Roo.bootstrap.TriggerField();
10282 trigger.onTriggerClick = myTriggerFn;
10283 trigger.applyTo('my-field');
10286 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10287 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10288 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10289 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10290 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10293 * Create a new TriggerField.
10294 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10295 * to the base TextField)
10297 Roo.bootstrap.TriggerField = function(config){
10298 this.mimicing = false;
10299 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10302 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10304 * @cfg {String} triggerClass A CSS class to apply to the trigger
10307 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10312 * @cfg {Boolean} removable (true|false) special filter default false
10316 /** @cfg {Boolean} grow @hide */
10317 /** @cfg {Number} growMin @hide */
10318 /** @cfg {Number} growMax @hide */
10324 autoSize: Roo.emptyFn,
10328 deferHeight : true,
10331 actionMode : 'wrap',
10336 getAutoCreate : function(){
10338 var align = this.labelAlign || this.parentLabelAlign();
10343 cls: 'form-group' //input-group
10350 type : this.inputType,
10351 cls : 'form-control',
10352 autocomplete: 'new-password',
10353 placeholder : this.placeholder || ''
10357 input.name = this.name;
10360 input.cls += ' input-' + this.size;
10363 if (this.disabled) {
10364 input.disabled=true;
10367 var inputblock = input;
10369 if(this.hasFeedback && !this.allowBlank){
10373 cls: 'glyphicon form-control-feedback'
10376 if(this.removable && !this.editable && !this.tickable){
10378 cls : 'has-feedback',
10384 cls : 'roo-combo-removable-btn close'
10391 cls : 'has-feedback',
10400 if(this.removable && !this.editable && !this.tickable){
10402 cls : 'roo-removable',
10408 cls : 'roo-combo-removable-btn close'
10415 if (this.before || this.after) {
10418 cls : 'input-group',
10422 inputblock.cn.push({
10424 cls : 'input-group-addon input-group-prepend input-group-text',
10429 inputblock.cn.push(input);
10431 if(this.hasFeedback && !this.allowBlank){
10432 inputblock.cls += ' has-feedback';
10433 inputblock.cn.push(feedback);
10437 inputblock.cn.push({
10439 cls : 'input-group-addon input-group-append input-group-text',
10448 var ibwrap = inputblock;
10453 cls: 'roo-select2-choices',
10457 cls: 'roo-select2-search-field',
10469 cls: 'roo-select2-container input-group',
10474 cls: 'form-hidden-field'
10480 if(!this.multiple && this.showToggleBtn){
10486 if (this.caret != false) {
10489 cls: 'fa fa-' + this.caret
10496 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10501 cls: 'combobox-clear',
10515 combobox.cls += ' roo-select2-container-multi';
10519 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10520 tooltip : 'This field is required'
10522 if (Roo.bootstrap.version == 4) {
10525 style : 'display:none'
10530 if (align ==='left' && this.fieldLabel.length) {
10532 cfg.cls += ' roo-form-group-label-left row';
10539 cls : 'control-label',
10540 html : this.fieldLabel
10552 var labelCfg = cfg.cn[1];
10553 var contentCfg = cfg.cn[2];
10555 if(this.indicatorpos == 'right'){
10560 cls : 'control-label',
10564 html : this.fieldLabel
10578 labelCfg = cfg.cn[0];
10579 contentCfg = cfg.cn[1];
10582 if(this.labelWidth > 12){
10583 labelCfg.style = "width: " + this.labelWidth + 'px';
10586 if(this.labelWidth < 13 && this.labelmd == 0){
10587 this.labelmd = this.labelWidth;
10590 if(this.labellg > 0){
10591 labelCfg.cls += ' col-lg-' + this.labellg;
10592 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10595 if(this.labelmd > 0){
10596 labelCfg.cls += ' col-md-' + this.labelmd;
10597 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10600 if(this.labelsm > 0){
10601 labelCfg.cls += ' col-sm-' + this.labelsm;
10602 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10605 if(this.labelxs > 0){
10606 labelCfg.cls += ' col-xs-' + this.labelxs;
10607 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10610 } else if ( this.fieldLabel.length) {
10611 // Roo.log(" label");
10616 //cls : 'input-group-addon',
10617 html : this.fieldLabel
10625 if(this.indicatorpos == 'right'){
10633 html : this.fieldLabel
10647 // Roo.log(" no label && no align");
10654 ['xs','sm','md','lg'].map(function(size){
10655 if (settings[size]) {
10656 cfg.cls += ' col-' + size + '-' + settings[size];
10667 onResize : function(w, h){
10668 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10669 // if(typeof w == 'number'){
10670 // var x = w - this.trigger.getWidth();
10671 // this.inputEl().setWidth(this.adjustWidth('input', x));
10672 // this.trigger.setStyle('left', x+'px');
10677 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10680 getResizeEl : function(){
10681 return this.inputEl();
10685 getPositionEl : function(){
10686 return this.inputEl();
10690 alignErrorIcon : function(){
10691 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10695 initEvents : function(){
10699 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10700 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10701 if(!this.multiple && this.showToggleBtn){
10702 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10703 if(this.hideTrigger){
10704 this.trigger.setDisplayed(false);
10706 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10710 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10713 if(this.removable && !this.editable && !this.tickable){
10714 var close = this.closeTriggerEl();
10717 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10718 close.on('click', this.removeBtnClick, this, close);
10722 //this.trigger.addClassOnOver('x-form-trigger-over');
10723 //this.trigger.addClassOnClick('x-form-trigger-click');
10726 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10730 closeTriggerEl : function()
10732 var close = this.el.select('.roo-combo-removable-btn', true).first();
10733 return close ? close : false;
10736 removeBtnClick : function(e, h, el)
10738 e.preventDefault();
10740 if(this.fireEvent("remove", this) !== false){
10742 this.fireEvent("afterremove", this)
10746 createList : function()
10748 this.list = Roo.get(document.body).createChild({
10749 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10750 cls: 'typeahead typeahead-long dropdown-menu',
10751 style: 'display:none'
10754 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10759 initTrigger : function(){
10764 onDestroy : function(){
10766 this.trigger.removeAllListeners();
10767 // this.trigger.remove();
10770 // this.wrap.remove();
10772 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10776 onFocus : function(){
10777 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10779 if(!this.mimicing){
10780 this.wrap.addClass('x-trigger-wrap-focus');
10781 this.mimicing = true;
10782 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10783 if(this.monitorTab){
10784 this.el.on("keydown", this.checkTab, this);
10791 checkTab : function(e){
10792 if(e.getKey() == e.TAB){
10793 this.triggerBlur();
10798 onBlur : function(){
10803 mimicBlur : function(e, t){
10805 if(!this.wrap.contains(t) && this.validateBlur()){
10806 this.triggerBlur();
10812 triggerBlur : function(){
10813 this.mimicing = false;
10814 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10815 if(this.monitorTab){
10816 this.el.un("keydown", this.checkTab, this);
10818 //this.wrap.removeClass('x-trigger-wrap-focus');
10819 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10823 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10824 validateBlur : function(e, t){
10829 onDisable : function(){
10830 this.inputEl().dom.disabled = true;
10831 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10833 // this.wrap.addClass('x-item-disabled');
10838 onEnable : function(){
10839 this.inputEl().dom.disabled = false;
10840 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10842 // this.el.removeClass('x-item-disabled');
10847 onShow : function(){
10848 var ae = this.getActionEl();
10851 ae.dom.style.display = '';
10852 ae.dom.style.visibility = 'visible';
10858 onHide : function(){
10859 var ae = this.getActionEl();
10860 ae.dom.style.display = 'none';
10864 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10865 * by an implementing function.
10867 * @param {EventObject} e
10869 onTriggerClick : Roo.emptyFn
10873 * Ext JS Library 1.1.1
10874 * Copyright(c) 2006-2007, Ext JS, LLC.
10876 * Originally Released Under LGPL - original licence link has changed is not relivant.
10879 * <script type="text/javascript">
10884 * @class Roo.data.SortTypes
10886 * Defines the default sorting (casting?) comparison functions used when sorting data.
10888 Roo.data.SortTypes = {
10890 * Default sort that does nothing
10891 * @param {Mixed} s The value being converted
10892 * @return {Mixed} The comparison value
10894 none : function(s){
10899 * The regular expression used to strip tags
10903 stripTagsRE : /<\/?[^>]+>/gi,
10906 * Strips all HTML tags to sort on text only
10907 * @param {Mixed} s The value being converted
10908 * @return {String} The comparison value
10910 asText : function(s){
10911 return String(s).replace(this.stripTagsRE, "");
10915 * Strips all HTML tags to sort on text only - Case insensitive
10916 * @param {Mixed} s The value being converted
10917 * @return {String} The comparison value
10919 asUCText : function(s){
10920 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10924 * Case insensitive string
10925 * @param {Mixed} s The value being converted
10926 * @return {String} The comparison value
10928 asUCString : function(s) {
10929 return String(s).toUpperCase();
10934 * @param {Mixed} s The value being converted
10935 * @return {Number} The comparison value
10937 asDate : function(s) {
10941 if(s instanceof Date){
10942 return s.getTime();
10944 return Date.parse(String(s));
10949 * @param {Mixed} s The value being converted
10950 * @return {Float} The comparison value
10952 asFloat : function(s) {
10953 var val = parseFloat(String(s).replace(/,/g, ""));
10962 * @param {Mixed} s The value being converted
10963 * @return {Number} The comparison value
10965 asInt : function(s) {
10966 var val = parseInt(String(s).replace(/,/g, ""));
10974 * Ext JS Library 1.1.1
10975 * Copyright(c) 2006-2007, Ext JS, LLC.
10977 * Originally Released Under LGPL - original licence link has changed is not relivant.
10980 * <script type="text/javascript">
10984 * @class Roo.data.Record
10985 * Instances of this class encapsulate both record <em>definition</em> information, and record
10986 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10987 * to access Records cached in an {@link Roo.data.Store} object.<br>
10989 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10990 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10993 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10995 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10996 * {@link #create}. The parameters are the same.
10997 * @param {Array} data An associative Array of data values keyed by the field name.
10998 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10999 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11000 * not specified an integer id is generated.
11002 Roo.data.Record = function(data, id){
11003 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11008 * Generate a constructor for a specific record layout.
11009 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11010 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11011 * Each field definition object may contain the following properties: <ul>
11012 * <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,
11013 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11014 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11015 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11016 * is being used, then this is a string containing the javascript expression to reference the data relative to
11017 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11018 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11019 * this may be omitted.</p></li>
11020 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11021 * <ul><li>auto (Default, implies no conversion)</li>
11026 * <li>date</li></ul></p></li>
11027 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11028 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11029 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11030 * by the Reader into an object that will be stored in the Record. It is passed the
11031 * following parameters:<ul>
11032 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11034 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11036 * <br>usage:<br><pre><code>
11037 var TopicRecord = Roo.data.Record.create(
11038 {name: 'title', mapping: 'topic_title'},
11039 {name: 'author', mapping: 'username'},
11040 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11041 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11042 {name: 'lastPoster', mapping: 'user2'},
11043 {name: 'excerpt', mapping: 'post_text'}
11046 var myNewRecord = new TopicRecord({
11047 title: 'Do my job please',
11050 lastPost: new Date(),
11051 lastPoster: 'Animal',
11052 excerpt: 'No way dude!'
11054 myStore.add(myNewRecord);
11059 Roo.data.Record.create = function(o){
11060 var f = function(){
11061 f.superclass.constructor.apply(this, arguments);
11063 Roo.extend(f, Roo.data.Record);
11064 var p = f.prototype;
11065 p.fields = new Roo.util.MixedCollection(false, function(field){
11068 for(var i = 0, len = o.length; i < len; i++){
11069 p.fields.add(new Roo.data.Field(o[i]));
11071 f.getField = function(name){
11072 return p.fields.get(name);
11077 Roo.data.Record.AUTO_ID = 1000;
11078 Roo.data.Record.EDIT = 'edit';
11079 Roo.data.Record.REJECT = 'reject';
11080 Roo.data.Record.COMMIT = 'commit';
11082 Roo.data.Record.prototype = {
11084 * Readonly flag - true if this record has been modified.
11093 join : function(store){
11094 this.store = store;
11098 * Set the named field to the specified value.
11099 * @param {String} name The name of the field to set.
11100 * @param {Object} value The value to set the field to.
11102 set : function(name, value){
11103 if(this.data[name] == value){
11107 if(!this.modified){
11108 this.modified = {};
11110 if(typeof this.modified[name] == 'undefined'){
11111 this.modified[name] = this.data[name];
11113 this.data[name] = value;
11114 if(!this.editing && this.store){
11115 this.store.afterEdit(this);
11120 * Get the value of the named field.
11121 * @param {String} name The name of the field to get the value of.
11122 * @return {Object} The value of the field.
11124 get : function(name){
11125 return this.data[name];
11129 beginEdit : function(){
11130 this.editing = true;
11131 this.modified = {};
11135 cancelEdit : function(){
11136 this.editing = false;
11137 delete this.modified;
11141 endEdit : function(){
11142 this.editing = false;
11143 if(this.dirty && this.store){
11144 this.store.afterEdit(this);
11149 * Usually called by the {@link Roo.data.Store} which owns the Record.
11150 * Rejects all changes made to the Record since either creation, or the last commit operation.
11151 * Modified fields are reverted to their original values.
11153 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11154 * of reject operations.
11156 reject : function(){
11157 var m = this.modified;
11159 if(typeof m[n] != "function"){
11160 this.data[n] = m[n];
11163 this.dirty = false;
11164 delete this.modified;
11165 this.editing = false;
11167 this.store.afterReject(this);
11172 * Usually called by the {@link Roo.data.Store} which owns the Record.
11173 * Commits all changes made to the Record since either creation, or the last commit operation.
11175 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11176 * of commit operations.
11178 commit : function(){
11179 this.dirty = false;
11180 delete this.modified;
11181 this.editing = false;
11183 this.store.afterCommit(this);
11188 hasError : function(){
11189 return this.error != null;
11193 clearError : function(){
11198 * Creates a copy of this record.
11199 * @param {String} id (optional) A new record id if you don't want to use this record's id
11202 copy : function(newId) {
11203 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11207 * Ext JS Library 1.1.1
11208 * Copyright(c) 2006-2007, Ext JS, LLC.
11210 * Originally Released Under LGPL - original licence link has changed is not relivant.
11213 * <script type="text/javascript">
11219 * @class Roo.data.Store
11220 * @extends Roo.util.Observable
11221 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11222 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11224 * 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
11225 * has no knowledge of the format of the data returned by the Proxy.<br>
11227 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11228 * instances from the data object. These records are cached and made available through accessor functions.
11230 * Creates a new Store.
11231 * @param {Object} config A config object containing the objects needed for the Store to access data,
11232 * and read the data into Records.
11234 Roo.data.Store = function(config){
11235 this.data = new Roo.util.MixedCollection(false);
11236 this.data.getKey = function(o){
11239 this.baseParams = {};
11241 this.paramNames = {
11246 "multisort" : "_multisort"
11249 if(config && config.data){
11250 this.inlineData = config.data;
11251 delete config.data;
11254 Roo.apply(this, config);
11256 if(this.reader){ // reader passed
11257 this.reader = Roo.factory(this.reader, Roo.data);
11258 this.reader.xmodule = this.xmodule || false;
11259 if(!this.recordType){
11260 this.recordType = this.reader.recordType;
11262 if(this.reader.onMetaChange){
11263 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11267 if(this.recordType){
11268 this.fields = this.recordType.prototype.fields;
11270 this.modified = [];
11274 * @event datachanged
11275 * Fires when the data cache has changed, and a widget which is using this Store
11276 * as a Record cache should refresh its view.
11277 * @param {Store} this
11279 datachanged : true,
11281 * @event metachange
11282 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11283 * @param {Store} this
11284 * @param {Object} meta The JSON metadata
11289 * Fires when Records have been added to the Store
11290 * @param {Store} this
11291 * @param {Roo.data.Record[]} records The array of Records added
11292 * @param {Number} index The index at which the record(s) were added
11297 * Fires when a Record has been removed from the Store
11298 * @param {Store} this
11299 * @param {Roo.data.Record} record The Record that was removed
11300 * @param {Number} index The index at which the record was removed
11305 * Fires when a Record has been updated
11306 * @param {Store} this
11307 * @param {Roo.data.Record} record The Record that was updated
11308 * @param {String} operation The update operation being performed. Value may be one of:
11310 Roo.data.Record.EDIT
11311 Roo.data.Record.REJECT
11312 Roo.data.Record.COMMIT
11318 * Fires when the data cache has been cleared.
11319 * @param {Store} this
11323 * @event beforeload
11324 * Fires before a request is made for a new data object. If the beforeload handler returns false
11325 * the load action will be canceled.
11326 * @param {Store} this
11327 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11331 * @event beforeloadadd
11332 * Fires after a new set of Records has been loaded.
11333 * @param {Store} this
11334 * @param {Roo.data.Record[]} records The Records that were loaded
11335 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11337 beforeloadadd : true,
11340 * Fires after a new set of Records has been loaded, before they are added to the store.
11341 * @param {Store} this
11342 * @param {Roo.data.Record[]} records The Records that were loaded
11343 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11344 * @params {Object} return from reader
11348 * @event loadexception
11349 * Fires if an exception occurs in the Proxy during loading.
11350 * Called with the signature of the Proxy's "loadexception" event.
11351 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11354 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11355 * @param {Object} load options
11356 * @param {Object} jsonData from your request (normally this contains the Exception)
11358 loadexception : true
11362 this.proxy = Roo.factory(this.proxy, Roo.data);
11363 this.proxy.xmodule = this.xmodule || false;
11364 this.relayEvents(this.proxy, ["loadexception"]);
11366 this.sortToggle = {};
11367 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11369 Roo.data.Store.superclass.constructor.call(this);
11371 if(this.inlineData){
11372 this.loadData(this.inlineData);
11373 delete this.inlineData;
11377 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11379 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11380 * without a remote query - used by combo/forms at present.
11384 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11387 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11390 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11391 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11394 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11395 * on any HTTP request
11398 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11401 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11405 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11406 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11408 remoteSort : false,
11411 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11412 * loaded or when a record is removed. (defaults to false).
11414 pruneModifiedRecords : false,
11417 lastOptions : null,
11420 * Add Records to the Store and fires the add event.
11421 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11423 add : function(records){
11424 records = [].concat(records);
11425 for(var i = 0, len = records.length; i < len; i++){
11426 records[i].join(this);
11428 var index = this.data.length;
11429 this.data.addAll(records);
11430 this.fireEvent("add", this, records, index);
11434 * Remove a Record from the Store and fires the remove event.
11435 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11437 remove : function(record){
11438 var index = this.data.indexOf(record);
11439 this.data.removeAt(index);
11441 if(this.pruneModifiedRecords){
11442 this.modified.remove(record);
11444 this.fireEvent("remove", this, record, index);
11448 * Remove all Records from the Store and fires the clear event.
11450 removeAll : function(){
11452 if(this.pruneModifiedRecords){
11453 this.modified = [];
11455 this.fireEvent("clear", this);
11459 * Inserts Records to the Store at the given index and fires the add event.
11460 * @param {Number} index The start index at which to insert the passed Records.
11461 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11463 insert : function(index, records){
11464 records = [].concat(records);
11465 for(var i = 0, len = records.length; i < len; i++){
11466 this.data.insert(index, records[i]);
11467 records[i].join(this);
11469 this.fireEvent("add", this, records, index);
11473 * Get the index within the cache of the passed Record.
11474 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11475 * @return {Number} The index of the passed Record. Returns -1 if not found.
11477 indexOf : function(record){
11478 return this.data.indexOf(record);
11482 * Get the index within the cache of the Record with the passed id.
11483 * @param {String} id The id of the Record to find.
11484 * @return {Number} The index of the Record. Returns -1 if not found.
11486 indexOfId : function(id){
11487 return this.data.indexOfKey(id);
11491 * Get the Record with the specified id.
11492 * @param {String} id The id of the Record to find.
11493 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11495 getById : function(id){
11496 return this.data.key(id);
11500 * Get the Record at the specified index.
11501 * @param {Number} index The index of the Record to find.
11502 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11504 getAt : function(index){
11505 return this.data.itemAt(index);
11509 * Returns a range of Records between specified indices.
11510 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11511 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11512 * @return {Roo.data.Record[]} An array of Records
11514 getRange : function(start, end){
11515 return this.data.getRange(start, end);
11519 storeOptions : function(o){
11520 o = Roo.apply({}, o);
11523 this.lastOptions = o;
11527 * Loads the Record cache from the configured Proxy using the configured Reader.
11529 * If using remote paging, then the first load call must specify the <em>start</em>
11530 * and <em>limit</em> properties in the options.params property to establish the initial
11531 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11533 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11534 * and this call will return before the new data has been loaded. Perform any post-processing
11535 * in a callback function, or in a "load" event handler.</strong>
11537 * @param {Object} options An object containing properties which control loading options:<ul>
11538 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11539 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11540 * passed the following arguments:<ul>
11541 * <li>r : Roo.data.Record[]</li>
11542 * <li>options: Options object from the load call</li>
11543 * <li>success: Boolean success indicator</li></ul></li>
11544 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11545 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11548 load : function(options){
11549 options = options || {};
11550 if(this.fireEvent("beforeload", this, options) !== false){
11551 this.storeOptions(options);
11552 var p = Roo.apply(options.params || {}, this.baseParams);
11553 // if meta was not loaded from remote source.. try requesting it.
11554 if (!this.reader.metaFromRemote) {
11555 p._requestMeta = 1;
11557 if(this.sortInfo && this.remoteSort){
11558 var pn = this.paramNames;
11559 p[pn["sort"]] = this.sortInfo.field;
11560 p[pn["dir"]] = this.sortInfo.direction;
11562 if (this.multiSort) {
11563 var pn = this.paramNames;
11564 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11567 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11572 * Reloads the Record cache from the configured Proxy using the configured Reader and
11573 * the options from the last load operation performed.
11574 * @param {Object} options (optional) An object containing properties which may override the options
11575 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11576 * the most recently used options are reused).
11578 reload : function(options){
11579 this.load(Roo.applyIf(options||{}, this.lastOptions));
11583 // Called as a callback by the Reader during a load operation.
11584 loadRecords : function(o, options, success){
11585 if(!o || success === false){
11586 if(success !== false){
11587 this.fireEvent("load", this, [], options, o);
11589 if(options.callback){
11590 options.callback.call(options.scope || this, [], options, false);
11594 // if data returned failure - throw an exception.
11595 if (o.success === false) {
11596 // show a message if no listener is registered.
11597 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11598 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11600 // loadmask wil be hooked into this..
11601 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11604 var r = o.records, t = o.totalRecords || r.length;
11606 this.fireEvent("beforeloadadd", this, r, options, o);
11608 if(!options || options.add !== true){
11609 if(this.pruneModifiedRecords){
11610 this.modified = [];
11612 for(var i = 0, len = r.length; i < len; i++){
11616 this.data = this.snapshot;
11617 delete this.snapshot;
11620 this.data.addAll(r);
11621 this.totalLength = t;
11623 this.fireEvent("datachanged", this);
11625 this.totalLength = Math.max(t, this.data.length+r.length);
11629 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11631 var e = new Roo.data.Record({});
11633 e.set(this.parent.displayField, this.parent.emptyTitle);
11634 e.set(this.parent.valueField, '');
11639 this.fireEvent("load", this, r, options, o);
11640 if(options.callback){
11641 options.callback.call(options.scope || this, r, options, true);
11647 * Loads data from a passed data block. A Reader which understands the format of the data
11648 * must have been configured in the constructor.
11649 * @param {Object} data The data block from which to read the Records. The format of the data expected
11650 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11651 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11653 loadData : function(o, append){
11654 var r = this.reader.readRecords(o);
11655 this.loadRecords(r, {add: append}, true);
11659 * Gets the number of cached records.
11661 * <em>If using paging, this may not be the total size of the dataset. If the data object
11662 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11663 * the data set size</em>
11665 getCount : function(){
11666 return this.data.length || 0;
11670 * Gets the total number of records in the dataset as returned by the server.
11672 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11673 * the dataset size</em>
11675 getTotalCount : function(){
11676 return this.totalLength || 0;
11680 * Returns the sort state of the Store as an object with two properties:
11682 field {String} The name of the field by which the Records are sorted
11683 direction {String} The sort order, "ASC" or "DESC"
11686 getSortState : function(){
11687 return this.sortInfo;
11691 applySort : function(){
11692 if(this.sortInfo && !this.remoteSort){
11693 var s = this.sortInfo, f = s.field;
11694 var st = this.fields.get(f).sortType;
11695 var fn = function(r1, r2){
11696 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11697 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11699 this.data.sort(s.direction, fn);
11700 if(this.snapshot && this.snapshot != this.data){
11701 this.snapshot.sort(s.direction, fn);
11707 * Sets the default sort column and order to be used by the next load operation.
11708 * @param {String} fieldName The name of the field to sort by.
11709 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11711 setDefaultSort : function(field, dir){
11712 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11716 * Sort the Records.
11717 * If remote sorting is used, the sort is performed on the server, and the cache is
11718 * reloaded. If local sorting is used, the cache is sorted internally.
11719 * @param {String} fieldName The name of the field to sort by.
11720 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11722 sort : function(fieldName, dir){
11723 var f = this.fields.get(fieldName);
11725 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11727 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11728 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11733 this.sortToggle[f.name] = dir;
11734 this.sortInfo = {field: f.name, direction: dir};
11735 if(!this.remoteSort){
11737 this.fireEvent("datachanged", this);
11739 this.load(this.lastOptions);
11744 * Calls the specified function for each of the Records in the cache.
11745 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11746 * Returning <em>false</em> aborts and exits the iteration.
11747 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11749 each : function(fn, scope){
11750 this.data.each(fn, scope);
11754 * Gets all records modified since the last commit. Modified records are persisted across load operations
11755 * (e.g., during paging).
11756 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11758 getModifiedRecords : function(){
11759 return this.modified;
11763 createFilterFn : function(property, value, anyMatch){
11764 if(!value.exec){ // not a regex
11765 value = String(value);
11766 if(value.length == 0){
11769 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11771 return function(r){
11772 return value.test(r.data[property]);
11777 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11778 * @param {String} property A field on your records
11779 * @param {Number} start The record index to start at (defaults to 0)
11780 * @param {Number} end The last record index to include (defaults to length - 1)
11781 * @return {Number} The sum
11783 sum : function(property, start, end){
11784 var rs = this.data.items, v = 0;
11785 start = start || 0;
11786 end = (end || end === 0) ? end : rs.length-1;
11788 for(var i = start; i <= end; i++){
11789 v += (rs[i].data[property] || 0);
11795 * Filter the records by a specified property.
11796 * @param {String} field A field on your records
11797 * @param {String/RegExp} value Either a string that the field
11798 * should start with or a RegExp to test against the field
11799 * @param {Boolean} anyMatch True to match any part not just the beginning
11801 filter : function(property, value, anyMatch){
11802 var fn = this.createFilterFn(property, value, anyMatch);
11803 return fn ? this.filterBy(fn) : this.clearFilter();
11807 * Filter by a function. The specified function will be called with each
11808 * record in this data source. If the function returns true the record is included,
11809 * otherwise it is filtered.
11810 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11811 * @param {Object} scope (optional) The scope of the function (defaults to this)
11813 filterBy : function(fn, scope){
11814 this.snapshot = this.snapshot || this.data;
11815 this.data = this.queryBy(fn, scope||this);
11816 this.fireEvent("datachanged", this);
11820 * Query the records by a specified property.
11821 * @param {String} field A field on your records
11822 * @param {String/RegExp} value Either a string that the field
11823 * should start with or a RegExp to test against the field
11824 * @param {Boolean} anyMatch True to match any part not just the beginning
11825 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11827 query : function(property, value, anyMatch){
11828 var fn = this.createFilterFn(property, value, anyMatch);
11829 return fn ? this.queryBy(fn) : this.data.clone();
11833 * Query by a function. The specified function will be called with each
11834 * record in this data source. If the function returns true the record is included
11836 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11837 * @param {Object} scope (optional) The scope of the function (defaults to this)
11838 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11840 queryBy : function(fn, scope){
11841 var data = this.snapshot || this.data;
11842 return data.filterBy(fn, scope||this);
11846 * Collects unique values for a particular dataIndex from this store.
11847 * @param {String} dataIndex The property to collect
11848 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11849 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11850 * @return {Array} An array of the unique values
11852 collect : function(dataIndex, allowNull, bypassFilter){
11853 var d = (bypassFilter === true && this.snapshot) ?
11854 this.snapshot.items : this.data.items;
11855 var v, sv, r = [], l = {};
11856 for(var i = 0, len = d.length; i < len; i++){
11857 v = d[i].data[dataIndex];
11859 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11868 * Revert to a view of the Record cache with no filtering applied.
11869 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11871 clearFilter : function(suppressEvent){
11872 if(this.snapshot && this.snapshot != this.data){
11873 this.data = this.snapshot;
11874 delete this.snapshot;
11875 if(suppressEvent !== true){
11876 this.fireEvent("datachanged", this);
11882 afterEdit : function(record){
11883 if(this.modified.indexOf(record) == -1){
11884 this.modified.push(record);
11886 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11890 afterReject : function(record){
11891 this.modified.remove(record);
11892 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11896 afterCommit : function(record){
11897 this.modified.remove(record);
11898 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11902 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11903 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11905 commitChanges : function(){
11906 var m = this.modified.slice(0);
11907 this.modified = [];
11908 for(var i = 0, len = m.length; i < len; i++){
11914 * Cancel outstanding changes on all changed records.
11916 rejectChanges : function(){
11917 var m = this.modified.slice(0);
11918 this.modified = [];
11919 for(var i = 0, len = m.length; i < len; i++){
11924 onMetaChange : function(meta, rtype, o){
11925 this.recordType = rtype;
11926 this.fields = rtype.prototype.fields;
11927 delete this.snapshot;
11928 this.sortInfo = meta.sortInfo || this.sortInfo;
11929 this.modified = [];
11930 this.fireEvent('metachange', this, this.reader.meta);
11933 moveIndex : function(data, type)
11935 var index = this.indexOf(data);
11937 var newIndex = index + type;
11941 this.insert(newIndex, data);
11946 * Ext JS Library 1.1.1
11947 * Copyright(c) 2006-2007, Ext JS, LLC.
11949 * Originally Released Under LGPL - original licence link has changed is not relivant.
11952 * <script type="text/javascript">
11956 * @class Roo.data.SimpleStore
11957 * @extends Roo.data.Store
11958 * Small helper class to make creating Stores from Array data easier.
11959 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11960 * @cfg {Array} fields An array of field definition objects, or field name strings.
11961 * @cfg {Array} data The multi-dimensional array of data
11963 * @param {Object} config
11965 Roo.data.SimpleStore = function(config){
11966 Roo.data.SimpleStore.superclass.constructor.call(this, {
11968 reader: new Roo.data.ArrayReader({
11971 Roo.data.Record.create(config.fields)
11973 proxy : new Roo.data.MemoryProxy(config.data)
11977 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11979 * Ext JS Library 1.1.1
11980 * Copyright(c) 2006-2007, Ext JS, LLC.
11982 * Originally Released Under LGPL - original licence link has changed is not relivant.
11985 * <script type="text/javascript">
11990 * @extends Roo.data.Store
11991 * @class Roo.data.JsonStore
11992 * Small helper class to make creating Stores for JSON data easier. <br/>
11994 var store = new Roo.data.JsonStore({
11995 url: 'get-images.php',
11997 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12000 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12001 * JsonReader and HttpProxy (unless inline data is provided).</b>
12002 * @cfg {Array} fields An array of field definition objects, or field name strings.
12004 * @param {Object} config
12006 Roo.data.JsonStore = function(c){
12007 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12008 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12009 reader: new Roo.data.JsonReader(c, c.fields)
12012 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12014 * Ext JS Library 1.1.1
12015 * Copyright(c) 2006-2007, Ext JS, LLC.
12017 * Originally Released Under LGPL - original licence link has changed is not relivant.
12020 * <script type="text/javascript">
12024 Roo.data.Field = function(config){
12025 if(typeof config == "string"){
12026 config = {name: config};
12028 Roo.apply(this, config);
12031 this.type = "auto";
12034 var st = Roo.data.SortTypes;
12035 // named sortTypes are supported, here we look them up
12036 if(typeof this.sortType == "string"){
12037 this.sortType = st[this.sortType];
12040 // set default sortType for strings and dates
12041 if(!this.sortType){
12044 this.sortType = st.asUCString;
12047 this.sortType = st.asDate;
12050 this.sortType = st.none;
12055 var stripRe = /[\$,%]/g;
12057 // prebuilt conversion function for this field, instead of
12058 // switching every time we're reading a value
12060 var cv, dateFormat = this.dateFormat;
12065 cv = function(v){ return v; };
12068 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12072 return v !== undefined && v !== null && v !== '' ?
12073 parseInt(String(v).replace(stripRe, ""), 10) : '';
12078 return v !== undefined && v !== null && v !== '' ?
12079 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12084 cv = function(v){ return v === true || v === "true" || v == 1; };
12091 if(v instanceof Date){
12095 if(dateFormat == "timestamp"){
12096 return new Date(v*1000);
12098 return Date.parseDate(v, dateFormat);
12100 var parsed = Date.parse(v);
12101 return parsed ? new Date(parsed) : null;
12110 Roo.data.Field.prototype = {
12118 * Ext JS Library 1.1.1
12119 * Copyright(c) 2006-2007, Ext JS, LLC.
12121 * Originally Released Under LGPL - original licence link has changed is not relivant.
12124 * <script type="text/javascript">
12127 // Base class for reading structured data from a data source. This class is intended to be
12128 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12131 * @class Roo.data.DataReader
12132 * Base class for reading structured data from a data source. This class is intended to be
12133 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12136 Roo.data.DataReader = function(meta, recordType){
12140 this.recordType = recordType instanceof Array ?
12141 Roo.data.Record.create(recordType) : recordType;
12144 Roo.data.DataReader.prototype = {
12146 * Create an empty record
12147 * @param {Object} data (optional) - overlay some values
12148 * @return {Roo.data.Record} record created.
12150 newRow : function(d) {
12152 this.recordType.prototype.fields.each(function(c) {
12154 case 'int' : da[c.name] = 0; break;
12155 case 'date' : da[c.name] = new Date(); break;
12156 case 'float' : da[c.name] = 0.0; break;
12157 case 'boolean' : da[c.name] = false; break;
12158 default : da[c.name] = ""; break;
12162 return new this.recordType(Roo.apply(da, d));
12167 * Ext JS Library 1.1.1
12168 * Copyright(c) 2006-2007, Ext JS, LLC.
12170 * Originally Released Under LGPL - original licence link has changed is not relivant.
12173 * <script type="text/javascript">
12177 * @class Roo.data.DataProxy
12178 * @extends Roo.data.Observable
12179 * This class is an abstract base class for implementations which provide retrieval of
12180 * unformatted data objects.<br>
12182 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12183 * (of the appropriate type which knows how to parse the data object) to provide a block of
12184 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12186 * Custom implementations must implement the load method as described in
12187 * {@link Roo.data.HttpProxy#load}.
12189 Roo.data.DataProxy = function(){
12192 * @event beforeload
12193 * Fires before a network request is made to retrieve a data object.
12194 * @param {Object} This DataProxy object.
12195 * @param {Object} params The params parameter to the load function.
12200 * Fires before the load method's callback is called.
12201 * @param {Object} This DataProxy object.
12202 * @param {Object} o The data object.
12203 * @param {Object} arg The callback argument object passed to the load function.
12207 * @event loadexception
12208 * Fires if an Exception occurs during data retrieval.
12209 * @param {Object} This DataProxy object.
12210 * @param {Object} o The data object.
12211 * @param {Object} arg The callback argument object passed to the load function.
12212 * @param {Object} e The Exception.
12214 loadexception : true
12216 Roo.data.DataProxy.superclass.constructor.call(this);
12219 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12222 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12226 * Ext JS Library 1.1.1
12227 * Copyright(c) 2006-2007, Ext JS, LLC.
12229 * Originally Released Under LGPL - original licence link has changed is not relivant.
12232 * <script type="text/javascript">
12235 * @class Roo.data.MemoryProxy
12236 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12237 * to the Reader when its load method is called.
12239 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12241 Roo.data.MemoryProxy = function(data){
12245 Roo.data.MemoryProxy.superclass.constructor.call(this);
12249 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12252 * Load data from the requested source (in this case an in-memory
12253 * data object passed to the constructor), read the data object into
12254 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12255 * process that block using the passed callback.
12256 * @param {Object} params This parameter is not used by the MemoryProxy class.
12257 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12258 * object into a block of Roo.data.Records.
12259 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12260 * The function must be passed <ul>
12261 * <li>The Record block object</li>
12262 * <li>The "arg" argument from the load function</li>
12263 * <li>A boolean success indicator</li>
12265 * @param {Object} scope The scope in which to call the callback
12266 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12268 load : function(params, reader, callback, scope, arg){
12269 params = params || {};
12272 result = reader.readRecords(this.data);
12274 this.fireEvent("loadexception", this, arg, null, e);
12275 callback.call(scope, null, arg, false);
12278 callback.call(scope, result, arg, true);
12282 update : function(params, records){
12287 * Ext JS Library 1.1.1
12288 * Copyright(c) 2006-2007, Ext JS, LLC.
12290 * Originally Released Under LGPL - original licence link has changed is not relivant.
12293 * <script type="text/javascript">
12296 * @class Roo.data.HttpProxy
12297 * @extends Roo.data.DataProxy
12298 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12299 * configured to reference a certain URL.<br><br>
12301 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12302 * from which the running page was served.<br><br>
12304 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12306 * Be aware that to enable the browser to parse an XML document, the server must set
12307 * the Content-Type header in the HTTP response to "text/xml".
12309 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12310 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12311 * will be used to make the request.
12313 Roo.data.HttpProxy = function(conn){
12314 Roo.data.HttpProxy.superclass.constructor.call(this);
12315 // is conn a conn config or a real conn?
12317 this.useAjax = !conn || !conn.events;
12321 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12322 // thse are take from connection...
12325 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12328 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12329 * extra parameters to each request made by this object. (defaults to undefined)
12332 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12333 * to each request made by this object. (defaults to undefined)
12336 * @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)
12339 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12342 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12348 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12352 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12353 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12354 * a finer-grained basis than the DataProxy events.
12356 getConnection : function(){
12357 return this.useAjax ? Roo.Ajax : this.conn;
12361 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12362 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12363 * process that block using the passed callback.
12364 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12365 * for the request to the remote server.
12366 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12367 * object into a block of Roo.data.Records.
12368 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12369 * The function must be passed <ul>
12370 * <li>The Record block object</li>
12371 * <li>The "arg" argument from the load function</li>
12372 * <li>A boolean success indicator</li>
12374 * @param {Object} scope The scope in which to call the callback
12375 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12377 load : function(params, reader, callback, scope, arg){
12378 if(this.fireEvent("beforeload", this, params) !== false){
12380 params : params || {},
12382 callback : callback,
12387 callback : this.loadResponse,
12391 Roo.applyIf(o, this.conn);
12392 if(this.activeRequest){
12393 Roo.Ajax.abort(this.activeRequest);
12395 this.activeRequest = Roo.Ajax.request(o);
12397 this.conn.request(o);
12400 callback.call(scope||this, null, arg, false);
12405 loadResponse : function(o, success, response){
12406 delete this.activeRequest;
12408 this.fireEvent("loadexception", this, o, response);
12409 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12414 result = o.reader.read(response);
12416 this.fireEvent("loadexception", this, o, response, e);
12417 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12421 this.fireEvent("load", this, o, o.request.arg);
12422 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12426 update : function(dataSet){
12431 updateResponse : function(dataSet){
12436 * Ext JS Library 1.1.1
12437 * Copyright(c) 2006-2007, Ext JS, LLC.
12439 * Originally Released Under LGPL - original licence link has changed is not relivant.
12442 * <script type="text/javascript">
12446 * @class Roo.data.ScriptTagProxy
12447 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12448 * other than the originating domain of the running page.<br><br>
12450 * <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
12451 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12453 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12454 * source code that is used as the source inside a <script> tag.<br><br>
12456 * In order for the browser to process the returned data, the server must wrap the data object
12457 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12458 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12459 * depending on whether the callback name was passed:
12462 boolean scriptTag = false;
12463 String cb = request.getParameter("callback");
12466 response.setContentType("text/javascript");
12468 response.setContentType("application/x-json");
12470 Writer out = response.getWriter();
12472 out.write(cb + "(");
12474 out.print(dataBlock.toJsonString());
12481 * @param {Object} config A configuration object.
12483 Roo.data.ScriptTagProxy = function(config){
12484 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12485 Roo.apply(this, config);
12486 this.head = document.getElementsByTagName("head")[0];
12489 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12491 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12493 * @cfg {String} url The URL from which to request the data object.
12496 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12500 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12501 * the server the name of the callback function set up by the load call to process the returned data object.
12502 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12503 * javascript output which calls this named function passing the data object as its only parameter.
12505 callbackParam : "callback",
12507 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12508 * name to the request.
12513 * Load data from the configured URL, read the data object into
12514 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12515 * process that block using the passed callback.
12516 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12517 * for the request to the remote server.
12518 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12519 * object into a block of Roo.data.Records.
12520 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12521 * The function must be passed <ul>
12522 * <li>The Record block object</li>
12523 * <li>The "arg" argument from the load function</li>
12524 * <li>A boolean success indicator</li>
12526 * @param {Object} scope The scope in which to call the callback
12527 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12529 load : function(params, reader, callback, scope, arg){
12530 if(this.fireEvent("beforeload", this, params) !== false){
12532 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12534 var url = this.url;
12535 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12537 url += "&_dc=" + (new Date().getTime());
12539 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12542 cb : "stcCallback"+transId,
12543 scriptId : "stcScript"+transId,
12547 callback : callback,
12553 window[trans.cb] = function(o){
12554 conn.handleResponse(o, trans);
12557 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12559 if(this.autoAbort !== false){
12563 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12565 var script = document.createElement("script");
12566 script.setAttribute("src", url);
12567 script.setAttribute("type", "text/javascript");
12568 script.setAttribute("id", trans.scriptId);
12569 this.head.appendChild(script);
12571 this.trans = trans;
12573 callback.call(scope||this, null, arg, false);
12578 isLoading : function(){
12579 return this.trans ? true : false;
12583 * Abort the current server request.
12585 abort : function(){
12586 if(this.isLoading()){
12587 this.destroyTrans(this.trans);
12592 destroyTrans : function(trans, isLoaded){
12593 this.head.removeChild(document.getElementById(trans.scriptId));
12594 clearTimeout(trans.timeoutId);
12596 window[trans.cb] = undefined;
12598 delete window[trans.cb];
12601 // if hasn't been loaded, wait for load to remove it to prevent script error
12602 window[trans.cb] = function(){
12603 window[trans.cb] = undefined;
12605 delete window[trans.cb];
12612 handleResponse : function(o, trans){
12613 this.trans = false;
12614 this.destroyTrans(trans, true);
12617 result = trans.reader.readRecords(o);
12619 this.fireEvent("loadexception", this, o, trans.arg, e);
12620 trans.callback.call(trans.scope||window, null, trans.arg, false);
12623 this.fireEvent("load", this, o, trans.arg);
12624 trans.callback.call(trans.scope||window, result, trans.arg, true);
12628 handleFailure : function(trans){
12629 this.trans = false;
12630 this.destroyTrans(trans, false);
12631 this.fireEvent("loadexception", this, null, trans.arg);
12632 trans.callback.call(trans.scope||window, null, trans.arg, false);
12636 * Ext JS Library 1.1.1
12637 * Copyright(c) 2006-2007, Ext JS, LLC.
12639 * Originally Released Under LGPL - original licence link has changed is not relivant.
12642 * <script type="text/javascript">
12646 * @class Roo.data.JsonReader
12647 * @extends Roo.data.DataReader
12648 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12649 * based on mappings in a provided Roo.data.Record constructor.
12651 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12652 * in the reply previously.
12657 var RecordDef = Roo.data.Record.create([
12658 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12659 {name: 'occupation'} // This field will use "occupation" as the mapping.
12661 var myReader = new Roo.data.JsonReader({
12662 totalProperty: "results", // The property which contains the total dataset size (optional)
12663 root: "rows", // The property which contains an Array of row objects
12664 id: "id" // The property within each row object that provides an ID for the record (optional)
12668 * This would consume a JSON file like this:
12670 { 'results': 2, 'rows': [
12671 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12672 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12675 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12676 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12677 * paged from the remote server.
12678 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12679 * @cfg {String} root name of the property which contains the Array of row objects.
12680 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12681 * @cfg {Array} fields Array of field definition objects
12683 * Create a new JsonReader
12684 * @param {Object} meta Metadata configuration options
12685 * @param {Object} recordType Either an Array of field definition objects,
12686 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12688 Roo.data.JsonReader = function(meta, recordType){
12691 // set some defaults:
12692 Roo.applyIf(meta, {
12693 totalProperty: 'total',
12694 successProperty : 'success',
12699 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12701 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12704 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12705 * Used by Store query builder to append _requestMeta to params.
12708 metaFromRemote : false,
12710 * This method is only used by a DataProxy which has retrieved data from a remote server.
12711 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12712 * @return {Object} data A data block which is used by an Roo.data.Store object as
12713 * a cache of Roo.data.Records.
12715 read : function(response){
12716 var json = response.responseText;
12718 var o = /* eval:var:o */ eval("("+json+")");
12720 throw {message: "JsonReader.read: Json object not found"};
12726 this.metaFromRemote = true;
12727 this.meta = o.metaData;
12728 this.recordType = Roo.data.Record.create(o.metaData.fields);
12729 this.onMetaChange(this.meta, this.recordType, o);
12731 return this.readRecords(o);
12734 // private function a store will implement
12735 onMetaChange : function(meta, recordType, o){
12742 simpleAccess: function(obj, subsc) {
12749 getJsonAccessor: function(){
12751 return function(expr) {
12753 return(re.test(expr))
12754 ? new Function("obj", "return obj." + expr)
12759 return Roo.emptyFn;
12764 * Create a data block containing Roo.data.Records from an XML document.
12765 * @param {Object} o An object which contains an Array of row objects in the property specified
12766 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12767 * which contains the total size of the dataset.
12768 * @return {Object} data A data block which is used by an Roo.data.Store object as
12769 * a cache of Roo.data.Records.
12771 readRecords : function(o){
12773 * After any data loads, the raw JSON data is available for further custom processing.
12777 var s = this.meta, Record = this.recordType,
12778 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12780 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12782 if(s.totalProperty) {
12783 this.getTotal = this.getJsonAccessor(s.totalProperty);
12785 if(s.successProperty) {
12786 this.getSuccess = this.getJsonAccessor(s.successProperty);
12788 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12790 var g = this.getJsonAccessor(s.id);
12791 this.getId = function(rec) {
12793 return (r === undefined || r === "") ? null : r;
12796 this.getId = function(){return null;};
12799 for(var jj = 0; jj < fl; jj++){
12801 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12802 this.ef[jj] = this.getJsonAccessor(map);
12806 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12807 if(s.totalProperty){
12808 var vt = parseInt(this.getTotal(o), 10);
12813 if(s.successProperty){
12814 var vs = this.getSuccess(o);
12815 if(vs === false || vs === 'false'){
12820 for(var i = 0; i < c; i++){
12823 var id = this.getId(n);
12824 for(var j = 0; j < fl; j++){
12826 var v = this.ef[j](n);
12828 Roo.log('missing convert for ' + f.name);
12832 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12834 var record = new Record(values, id);
12836 records[i] = record;
12842 totalRecords : totalRecords
12847 * Ext JS Library 1.1.1
12848 * Copyright(c) 2006-2007, Ext JS, LLC.
12850 * Originally Released Under LGPL - original licence link has changed is not relivant.
12853 * <script type="text/javascript">
12857 * @class Roo.data.ArrayReader
12858 * @extends Roo.data.DataReader
12859 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12860 * Each element of that Array represents a row of data fields. The
12861 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12862 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12866 var RecordDef = Roo.data.Record.create([
12867 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12868 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12870 var myReader = new Roo.data.ArrayReader({
12871 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12875 * This would consume an Array like this:
12877 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12879 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12881 * Create a new JsonReader
12882 * @param {Object} meta Metadata configuration options.
12883 * @param {Object} recordType Either an Array of field definition objects
12884 * as specified to {@link Roo.data.Record#create},
12885 * or an {@link Roo.data.Record} object
12886 * created using {@link Roo.data.Record#create}.
12888 Roo.data.ArrayReader = function(meta, recordType){
12889 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12892 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12894 * Create a data block containing Roo.data.Records from an XML document.
12895 * @param {Object} o An Array of row objects which represents the dataset.
12896 * @return {Object} data A data block which is used by an Roo.data.Store object as
12897 * a cache of Roo.data.Records.
12899 readRecords : function(o){
12900 var sid = this.meta ? this.meta.id : null;
12901 var recordType = this.recordType, fields = recordType.prototype.fields;
12904 for(var i = 0; i < root.length; i++){
12907 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12908 for(var j = 0, jlen = fields.length; j < jlen; j++){
12909 var f = fields.items[j];
12910 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12911 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12913 values[f.name] = v;
12915 var record = new recordType(values, id);
12917 records[records.length] = record;
12921 totalRecords : records.length
12930 * @class Roo.bootstrap.ComboBox
12931 * @extends Roo.bootstrap.TriggerField
12932 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12933 * @cfg {Boolean} append (true|false) default false
12934 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12935 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12936 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12937 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12938 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12939 * @cfg {Boolean} animate default true
12940 * @cfg {Boolean} emptyResultText only for touch device
12941 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12942 * @cfg {String} emptyTitle default ''
12944 * Create a new ComboBox.
12945 * @param {Object} config Configuration options
12947 Roo.bootstrap.ComboBox = function(config){
12948 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12952 * Fires when the dropdown list is expanded
12953 * @param {Roo.bootstrap.ComboBox} combo This combo box
12958 * Fires when the dropdown list is collapsed
12959 * @param {Roo.bootstrap.ComboBox} combo This combo box
12963 * @event beforeselect
12964 * Fires before a list item is selected. Return false to cancel the selection.
12965 * @param {Roo.bootstrap.ComboBox} combo This combo box
12966 * @param {Roo.data.Record} record The data record returned from the underlying store
12967 * @param {Number} index The index of the selected item in the dropdown list
12969 'beforeselect' : true,
12972 * Fires when a list item is selected
12973 * @param {Roo.bootstrap.ComboBox} combo This combo box
12974 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12975 * @param {Number} index The index of the selected item in the dropdown list
12979 * @event beforequery
12980 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12981 * The event object passed has these properties:
12982 * @param {Roo.bootstrap.ComboBox} combo This combo box
12983 * @param {String} query The query
12984 * @param {Boolean} forceAll true to force "all" query
12985 * @param {Boolean} cancel true to cancel the query
12986 * @param {Object} e The query event object
12988 'beforequery': true,
12991 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12992 * @param {Roo.bootstrap.ComboBox} combo This combo box
12997 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12998 * @param {Roo.bootstrap.ComboBox} combo This combo box
12999 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13004 * Fires when the remove value from the combobox array
13005 * @param {Roo.bootstrap.ComboBox} combo This combo box
13009 * @event afterremove
13010 * Fires when the remove value from the combobox array
13011 * @param {Roo.bootstrap.ComboBox} combo This combo box
13013 'afterremove' : true,
13015 * @event specialfilter
13016 * Fires when specialfilter
13017 * @param {Roo.bootstrap.ComboBox} combo This combo box
13019 'specialfilter' : true,
13022 * Fires when tick the element
13023 * @param {Roo.bootstrap.ComboBox} combo This combo box
13027 * @event touchviewdisplay
13028 * Fires when touch view require special display (default is using displayField)
13029 * @param {Roo.bootstrap.ComboBox} combo This combo box
13030 * @param {Object} cfg set html .
13032 'touchviewdisplay' : true
13037 this.tickItems = [];
13039 this.selectedIndex = -1;
13040 if(this.mode == 'local'){
13041 if(config.queryDelay === undefined){
13042 this.queryDelay = 10;
13044 if(config.minChars === undefined){
13050 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13053 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13054 * rendering into an Roo.Editor, defaults to false)
13057 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13058 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13061 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13064 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13065 * the dropdown list (defaults to undefined, with no header element)
13069 * @cfg {String/Roo.Template} tpl The template to use to render the output
13073 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13075 listWidth: undefined,
13077 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13078 * mode = 'remote' or 'text' if mode = 'local')
13080 displayField: undefined,
13083 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13084 * mode = 'remote' or 'value' if mode = 'local').
13085 * Note: use of a valueField requires the user make a selection
13086 * in order for a value to be mapped.
13088 valueField: undefined,
13090 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13095 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13096 * field's data value (defaults to the underlying DOM element's name)
13098 hiddenName: undefined,
13100 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13104 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13106 selectedClass: 'active',
13109 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13113 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13114 * anchor positions (defaults to 'tl-bl')
13116 listAlign: 'tl-bl?',
13118 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13122 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13123 * query specified by the allQuery config option (defaults to 'query')
13125 triggerAction: 'query',
13127 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13128 * (defaults to 4, does not apply if editable = false)
13132 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13133 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13137 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13138 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13142 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13143 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13147 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13148 * when editable = true (defaults to false)
13150 selectOnFocus:false,
13152 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13154 queryParam: 'query',
13156 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13157 * when mode = 'remote' (defaults to 'Loading...')
13159 loadingText: 'Loading...',
13161 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13165 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13169 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13170 * traditional select (defaults to true)
13174 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13178 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13182 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13183 * listWidth has a higher value)
13187 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13188 * allow the user to set arbitrary text into the field (defaults to false)
13190 forceSelection:false,
13192 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13193 * if typeAhead = true (defaults to 250)
13195 typeAheadDelay : 250,
13197 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13198 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13200 valueNotFoundText : undefined,
13202 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13204 blockFocus : false,
13207 * @cfg {Boolean} disableClear Disable showing of clear button.
13209 disableClear : false,
13211 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13213 alwaysQuery : false,
13216 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13221 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13223 invalidClass : "has-warning",
13226 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13228 validClass : "has-success",
13231 * @cfg {Boolean} specialFilter (true|false) special filter default false
13233 specialFilter : false,
13236 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13238 mobileTouchView : true,
13241 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13243 useNativeIOS : false,
13246 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13248 mobile_restrict_height : false,
13250 ios_options : false,
13262 btnPosition : 'right',
13263 triggerList : true,
13264 showToggleBtn : true,
13266 emptyResultText: 'Empty',
13267 triggerText : 'Select',
13270 // element that contains real text value.. (when hidden is used..)
13272 getAutoCreate : function()
13277 * Render classic select for iso
13280 if(Roo.isIOS && this.useNativeIOS){
13281 cfg = this.getAutoCreateNativeIOS();
13289 if(Roo.isTouch && this.mobileTouchView){
13290 cfg = this.getAutoCreateTouchView();
13297 if(!this.tickable){
13298 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13303 * ComboBox with tickable selections
13306 var align = this.labelAlign || this.parentLabelAlign();
13309 cls : 'form-group roo-combobox-tickable' //input-group
13312 var btn_text_select = '';
13313 var btn_text_done = '';
13314 var btn_text_cancel = '';
13316 if (this.btn_text_show) {
13317 btn_text_select = 'Select';
13318 btn_text_done = 'Done';
13319 btn_text_cancel = 'Cancel';
13324 cls : 'tickable-buttons',
13329 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13330 //html : this.triggerText
13331 html: btn_text_select
13337 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13339 html: btn_text_done
13345 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13347 html: btn_text_cancel
13353 buttons.cn.unshift({
13355 cls: 'roo-select2-search-field-input'
13361 Roo.each(buttons.cn, function(c){
13363 c.cls += ' btn-' + _this.size;
13366 if (_this.disabled) {
13377 cls: 'form-hidden-field'
13381 cls: 'roo-select2-choices',
13385 cls: 'roo-select2-search-field',
13396 cls: 'roo-select2-container input-group roo-select2-container-multi',
13402 // cls: 'typeahead typeahead-long dropdown-menu',
13403 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13408 if(this.hasFeedback && !this.allowBlank){
13412 cls: 'glyphicon form-control-feedback'
13415 combobox.cn.push(feedback);
13420 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13421 tooltip : 'This field is required'
13423 if (Roo.bootstrap.version == 4) {
13426 style : 'display:none'
13429 if (align ==='left' && this.fieldLabel.length) {
13431 cfg.cls += ' roo-form-group-label-left row';
13438 cls : 'control-label col-form-label',
13439 html : this.fieldLabel
13451 var labelCfg = cfg.cn[1];
13452 var contentCfg = cfg.cn[2];
13455 if(this.indicatorpos == 'right'){
13461 cls : 'control-label col-form-label',
13465 html : this.fieldLabel
13481 labelCfg = cfg.cn[0];
13482 contentCfg = cfg.cn[1];
13486 if(this.labelWidth > 12){
13487 labelCfg.style = "width: " + this.labelWidth + 'px';
13490 if(this.labelWidth < 13 && this.labelmd == 0){
13491 this.labelmd = this.labelWidth;
13494 if(this.labellg > 0){
13495 labelCfg.cls += ' col-lg-' + this.labellg;
13496 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13499 if(this.labelmd > 0){
13500 labelCfg.cls += ' col-md-' + this.labelmd;
13501 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13504 if(this.labelsm > 0){
13505 labelCfg.cls += ' col-sm-' + this.labelsm;
13506 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13509 if(this.labelxs > 0){
13510 labelCfg.cls += ' col-xs-' + this.labelxs;
13511 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13515 } else if ( this.fieldLabel.length) {
13516 // Roo.log(" label");
13521 //cls : 'input-group-addon',
13522 html : this.fieldLabel
13527 if(this.indicatorpos == 'right'){
13531 //cls : 'input-group-addon',
13532 html : this.fieldLabel
13542 // Roo.log(" no label && no align");
13549 ['xs','sm','md','lg'].map(function(size){
13550 if (settings[size]) {
13551 cfg.cls += ' col-' + size + '-' + settings[size];
13559 _initEventsCalled : false,
13562 initEvents: function()
13564 if (this._initEventsCalled) { // as we call render... prevent looping...
13567 this._initEventsCalled = true;
13570 throw "can not find store for combo";
13573 this.indicator = this.indicatorEl();
13575 this.store = Roo.factory(this.store, Roo.data);
13576 this.store.parent = this;
13578 // if we are building from html. then this element is so complex, that we can not really
13579 // use the rendered HTML.
13580 // so we have to trash and replace the previous code.
13581 if (Roo.XComponent.build_from_html) {
13582 // remove this element....
13583 var e = this.el.dom, k=0;
13584 while (e ) { e = e.previousSibling; ++k;}
13589 this.rendered = false;
13591 this.render(this.parent().getChildContainer(true), k);
13594 if(Roo.isIOS && this.useNativeIOS){
13595 this.initIOSView();
13603 if(Roo.isTouch && this.mobileTouchView){
13604 this.initTouchView();
13609 this.initTickableEvents();
13613 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13615 if(this.hiddenName){
13617 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13619 this.hiddenField.dom.value =
13620 this.hiddenValue !== undefined ? this.hiddenValue :
13621 this.value !== undefined ? this.value : '';
13623 // prevent input submission
13624 this.el.dom.removeAttribute('name');
13625 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13630 // this.el.dom.setAttribute('autocomplete', 'off');
13633 var cls = 'x-combo-list';
13635 //this.list = new Roo.Layer({
13636 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13642 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13643 _this.list.setWidth(lw);
13646 this.list.on('mouseover', this.onViewOver, this);
13647 this.list.on('mousemove', this.onViewMove, this);
13648 this.list.on('scroll', this.onViewScroll, this);
13651 this.list.swallowEvent('mousewheel');
13652 this.assetHeight = 0;
13655 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13656 this.assetHeight += this.header.getHeight();
13659 this.innerList = this.list.createChild({cls:cls+'-inner'});
13660 this.innerList.on('mouseover', this.onViewOver, this);
13661 this.innerList.on('mousemove', this.onViewMove, this);
13662 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13664 if(this.allowBlank && !this.pageSize && !this.disableClear){
13665 this.footer = this.list.createChild({cls:cls+'-ft'});
13666 this.pageTb = new Roo.Toolbar(this.footer);
13670 this.footer = this.list.createChild({cls:cls+'-ft'});
13671 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13672 {pageSize: this.pageSize});
13676 if (this.pageTb && this.allowBlank && !this.disableClear) {
13678 this.pageTb.add(new Roo.Toolbar.Fill(), {
13679 cls: 'x-btn-icon x-btn-clear',
13681 handler: function()
13684 _this.clearValue();
13685 _this.onSelect(false, -1);
13690 this.assetHeight += this.footer.getHeight();
13695 this.tpl = Roo.bootstrap.version == 4 ?
13696 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13697 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13700 this.view = new Roo.View(this.list, this.tpl, {
13701 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13703 //this.view.wrapEl.setDisplayed(false);
13704 this.view.on('click', this.onViewClick, this);
13707 this.store.on('beforeload', this.onBeforeLoad, this);
13708 this.store.on('load', this.onLoad, this);
13709 this.store.on('loadexception', this.onLoadException, this);
13711 if(this.resizable){
13712 this.resizer = new Roo.Resizable(this.list, {
13713 pinned:true, handles:'se'
13715 this.resizer.on('resize', function(r, w, h){
13716 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13717 this.listWidth = w;
13718 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13719 this.restrictHeight();
13721 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13724 if(!this.editable){
13725 this.editable = true;
13726 this.setEditable(false);
13731 if (typeof(this.events.add.listeners) != 'undefined') {
13733 this.addicon = this.wrap.createChild(
13734 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13736 this.addicon.on('click', function(e) {
13737 this.fireEvent('add', this);
13740 if (typeof(this.events.edit.listeners) != 'undefined') {
13742 this.editicon = this.wrap.createChild(
13743 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13744 if (this.addicon) {
13745 this.editicon.setStyle('margin-left', '40px');
13747 this.editicon.on('click', function(e) {
13749 // we fire even if inothing is selected..
13750 this.fireEvent('edit', this, this.lastData );
13756 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13757 "up" : function(e){
13758 this.inKeyMode = true;
13762 "down" : function(e){
13763 if(!this.isExpanded()){
13764 this.onTriggerClick();
13766 this.inKeyMode = true;
13771 "enter" : function(e){
13772 // this.onViewClick();
13776 if(this.fireEvent("specialkey", this, e)){
13777 this.onViewClick(false);
13783 "esc" : function(e){
13787 "tab" : function(e){
13790 if(this.fireEvent("specialkey", this, e)){
13791 this.onViewClick(false);
13799 doRelay : function(foo, bar, hname){
13800 if(hname == 'down' || this.scope.isExpanded()){
13801 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13810 this.queryDelay = Math.max(this.queryDelay || 10,
13811 this.mode == 'local' ? 10 : 250);
13814 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13816 if(this.typeAhead){
13817 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13819 if(this.editable !== false){
13820 this.inputEl().on("keyup", this.onKeyUp, this);
13822 if(this.forceSelection){
13823 this.inputEl().on('blur', this.doForce, this);
13827 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13828 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13832 initTickableEvents: function()
13836 if(this.hiddenName){
13838 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13840 this.hiddenField.dom.value =
13841 this.hiddenValue !== undefined ? this.hiddenValue :
13842 this.value !== undefined ? this.value : '';
13844 // prevent input submission
13845 this.el.dom.removeAttribute('name');
13846 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13851 // this.list = this.el.select('ul.dropdown-menu',true).first();
13853 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13854 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13855 if(this.triggerList){
13856 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13859 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13860 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13862 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13863 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13865 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13866 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13868 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13869 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13870 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13873 this.cancelBtn.hide();
13878 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13879 _this.list.setWidth(lw);
13882 this.list.on('mouseover', this.onViewOver, this);
13883 this.list.on('mousemove', this.onViewMove, this);
13885 this.list.on('scroll', this.onViewScroll, this);
13888 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13889 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13892 this.view = new Roo.View(this.list, this.tpl, {
13897 selectedClass: this.selectedClass
13900 //this.view.wrapEl.setDisplayed(false);
13901 this.view.on('click', this.onViewClick, this);
13905 this.store.on('beforeload', this.onBeforeLoad, this);
13906 this.store.on('load', this.onLoad, this);
13907 this.store.on('loadexception', this.onLoadException, this);
13910 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13911 "up" : function(e){
13912 this.inKeyMode = true;
13916 "down" : function(e){
13917 this.inKeyMode = true;
13921 "enter" : function(e){
13922 if(this.fireEvent("specialkey", this, e)){
13923 this.onViewClick(false);
13929 "esc" : function(e){
13930 this.onTickableFooterButtonClick(e, false, false);
13933 "tab" : function(e){
13934 this.fireEvent("specialkey", this, e);
13936 this.onTickableFooterButtonClick(e, false, false);
13943 doRelay : function(e, fn, key){
13944 if(this.scope.isExpanded()){
13945 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13954 this.queryDelay = Math.max(this.queryDelay || 10,
13955 this.mode == 'local' ? 10 : 250);
13958 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13960 if(this.typeAhead){
13961 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13964 if(this.editable !== false){
13965 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13968 this.indicator = this.indicatorEl();
13970 if(this.indicator){
13971 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13972 this.indicator.hide();
13977 onDestroy : function(){
13979 this.view.setStore(null);
13980 this.view.el.removeAllListeners();
13981 this.view.el.remove();
13982 this.view.purgeListeners();
13985 this.list.dom.innerHTML = '';
13989 this.store.un('beforeload', this.onBeforeLoad, this);
13990 this.store.un('load', this.onLoad, this);
13991 this.store.un('loadexception', this.onLoadException, this);
13993 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13997 fireKey : function(e){
13998 if(e.isNavKeyPress() && !this.list.isVisible()){
13999 this.fireEvent("specialkey", this, e);
14004 onResize: function(w, h){
14005 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14007 // if(typeof w != 'number'){
14008 // // we do not handle it!?!?
14011 // var tw = this.trigger.getWidth();
14012 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14013 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14015 // this.inputEl().setWidth( this.adjustWidth('input', x));
14017 // //this.trigger.setStyle('left', x+'px');
14019 // if(this.list && this.listWidth === undefined){
14020 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14021 // this.list.setWidth(lw);
14022 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14030 * Allow or prevent the user from directly editing the field text. If false is passed,
14031 * the user will only be able to select from the items defined in the dropdown list. This method
14032 * is the runtime equivalent of setting the 'editable' config option at config time.
14033 * @param {Boolean} value True to allow the user to directly edit the field text
14035 setEditable : function(value){
14036 if(value == this.editable){
14039 this.editable = value;
14041 this.inputEl().dom.setAttribute('readOnly', true);
14042 this.inputEl().on('mousedown', this.onTriggerClick, this);
14043 this.inputEl().addClass('x-combo-noedit');
14045 this.inputEl().dom.setAttribute('readOnly', false);
14046 this.inputEl().un('mousedown', this.onTriggerClick, this);
14047 this.inputEl().removeClass('x-combo-noedit');
14053 onBeforeLoad : function(combo,opts){
14054 if(!this.hasFocus){
14058 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14060 this.restrictHeight();
14061 this.selectedIndex = -1;
14065 onLoad : function(){
14067 this.hasQuery = false;
14069 if(!this.hasFocus){
14073 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14074 this.loading.hide();
14077 if(this.store.getCount() > 0){
14080 this.restrictHeight();
14081 if(this.lastQuery == this.allQuery){
14082 if(this.editable && !this.tickable){
14083 this.inputEl().dom.select();
14087 !this.selectByValue(this.value, true) &&
14090 !this.store.lastOptions ||
14091 typeof(this.store.lastOptions.add) == 'undefined' ||
14092 this.store.lastOptions.add != true
14095 this.select(0, true);
14098 if(this.autoFocus){
14101 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14102 this.taTask.delay(this.typeAheadDelay);
14106 this.onEmptyResults();
14112 onLoadException : function()
14114 this.hasQuery = false;
14116 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14117 this.loading.hide();
14120 if(this.tickable && this.editable){
14125 // only causes errors at present
14126 //Roo.log(this.store.reader.jsonData);
14127 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14129 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14135 onTypeAhead : function(){
14136 if(this.store.getCount() > 0){
14137 var r = this.store.getAt(0);
14138 var newValue = r.data[this.displayField];
14139 var len = newValue.length;
14140 var selStart = this.getRawValue().length;
14142 if(selStart != len){
14143 this.setRawValue(newValue);
14144 this.selectText(selStart, newValue.length);
14150 onSelect : function(record, index){
14152 if(this.fireEvent('beforeselect', this, record, index) !== false){
14154 this.setFromData(index > -1 ? record.data : false);
14157 this.fireEvent('select', this, record, index);
14162 * Returns the currently selected field value or empty string if no value is set.
14163 * @return {String} value The selected value
14165 getValue : function()
14167 if(Roo.isIOS && this.useNativeIOS){
14168 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14172 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14175 if(this.valueField){
14176 return typeof this.value != 'undefined' ? this.value : '';
14178 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14182 getRawValue : function()
14184 if(Roo.isIOS && this.useNativeIOS){
14185 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14188 var v = this.inputEl().getValue();
14194 * Clears any text/value currently set in the field
14196 clearValue : function(){
14198 if(this.hiddenField){
14199 this.hiddenField.dom.value = '';
14202 this.setRawValue('');
14203 this.lastSelectionText = '';
14204 this.lastData = false;
14206 var close = this.closeTriggerEl();
14217 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14218 * will be displayed in the field. If the value does not match the data value of an existing item,
14219 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14220 * Otherwise the field will be blank (although the value will still be set).
14221 * @param {String} value The value to match
14223 setValue : function(v)
14225 if(Roo.isIOS && this.useNativeIOS){
14226 this.setIOSValue(v);
14236 if(this.valueField){
14237 var r = this.findRecord(this.valueField, v);
14239 text = r.data[this.displayField];
14240 }else if(this.valueNotFoundText !== undefined){
14241 text = this.valueNotFoundText;
14244 this.lastSelectionText = text;
14245 if(this.hiddenField){
14246 this.hiddenField.dom.value = v;
14248 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14251 var close = this.closeTriggerEl();
14254 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14260 * @property {Object} the last set data for the element
14265 * Sets the value of the field based on a object which is related to the record format for the store.
14266 * @param {Object} value the value to set as. or false on reset?
14268 setFromData : function(o){
14275 var dv = ''; // display value
14276 var vv = ''; // value value..
14278 if (this.displayField) {
14279 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14281 // this is an error condition!!!
14282 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14285 if(this.valueField){
14286 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14289 var close = this.closeTriggerEl();
14292 if(dv.length || vv * 1 > 0){
14294 this.blockFocus=true;
14300 if(this.hiddenField){
14301 this.hiddenField.dom.value = vv;
14303 this.lastSelectionText = dv;
14304 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14308 // no hidden field.. - we store the value in 'value', but still display
14309 // display field!!!!
14310 this.lastSelectionText = dv;
14311 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14318 reset : function(){
14319 // overridden so that last data is reset..
14326 this.setValue(this.originalValue);
14327 //this.clearInvalid();
14328 this.lastData = false;
14330 this.view.clearSelections();
14336 findRecord : function(prop, value){
14338 if(this.store.getCount() > 0){
14339 this.store.each(function(r){
14340 if(r.data[prop] == value){
14350 getName: function()
14352 // returns hidden if it's set..
14353 if (!this.rendered) {return ''};
14354 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14358 onViewMove : function(e, t){
14359 this.inKeyMode = false;
14363 onViewOver : function(e, t){
14364 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14367 var item = this.view.findItemFromChild(t);
14370 var index = this.view.indexOf(item);
14371 this.select(index, false);
14376 onViewClick : function(view, doFocus, el, e)
14378 var index = this.view.getSelectedIndexes()[0];
14380 var r = this.store.getAt(index);
14384 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14391 Roo.each(this.tickItems, function(v,k){
14393 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14395 _this.tickItems.splice(k, 1);
14397 if(typeof(e) == 'undefined' && view == false){
14398 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14410 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14411 this.tickItems.push(r.data);
14414 if(typeof(e) == 'undefined' && view == false){
14415 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14422 this.onSelect(r, index);
14424 if(doFocus !== false && !this.blockFocus){
14425 this.inputEl().focus();
14430 restrictHeight : function(){
14431 //this.innerList.dom.style.height = '';
14432 //var inner = this.innerList.dom;
14433 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14434 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14435 //this.list.beginUpdate();
14436 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14437 this.list.alignTo(this.inputEl(), this.listAlign);
14438 this.list.alignTo(this.inputEl(), this.listAlign);
14439 //this.list.endUpdate();
14443 onEmptyResults : function(){
14445 if(this.tickable && this.editable){
14446 this.hasFocus = false;
14447 this.restrictHeight();
14455 * Returns true if the dropdown list is expanded, else false.
14457 isExpanded : function(){
14458 return this.list.isVisible();
14462 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14463 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14464 * @param {String} value The data value of the item to select
14465 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14466 * selected item if it is not currently in view (defaults to true)
14467 * @return {Boolean} True if the value matched an item in the list, else false
14469 selectByValue : function(v, scrollIntoView){
14470 if(v !== undefined && v !== null){
14471 var r = this.findRecord(this.valueField || this.displayField, v);
14473 this.select(this.store.indexOf(r), scrollIntoView);
14481 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14482 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14483 * @param {Number} index The zero-based index of the list item to select
14484 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14485 * selected item if it is not currently in view (defaults to true)
14487 select : function(index, scrollIntoView){
14488 this.selectedIndex = index;
14489 this.view.select(index);
14490 if(scrollIntoView !== false){
14491 var el = this.view.getNode(index);
14493 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14496 this.list.scrollChildIntoView(el, false);
14502 selectNext : function(){
14503 var ct = this.store.getCount();
14505 if(this.selectedIndex == -1){
14507 }else if(this.selectedIndex < ct-1){
14508 this.select(this.selectedIndex+1);
14514 selectPrev : function(){
14515 var ct = this.store.getCount();
14517 if(this.selectedIndex == -1){
14519 }else if(this.selectedIndex != 0){
14520 this.select(this.selectedIndex-1);
14526 onKeyUp : function(e){
14527 if(this.editable !== false && !e.isSpecialKey()){
14528 this.lastKey = e.getKey();
14529 this.dqTask.delay(this.queryDelay);
14534 validateBlur : function(){
14535 return !this.list || !this.list.isVisible();
14539 initQuery : function(){
14541 var v = this.getRawValue();
14543 if(this.tickable && this.editable){
14544 v = this.tickableInputEl().getValue();
14551 doForce : function(){
14552 if(this.inputEl().dom.value.length > 0){
14553 this.inputEl().dom.value =
14554 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14560 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14561 * query allowing the query action to be canceled if needed.
14562 * @param {String} query The SQL query to execute
14563 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14564 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14565 * saved in the current store (defaults to false)
14567 doQuery : function(q, forceAll){
14569 if(q === undefined || q === null){
14574 forceAll: forceAll,
14578 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14583 forceAll = qe.forceAll;
14584 if(forceAll === true || (q.length >= this.minChars)){
14586 this.hasQuery = true;
14588 if(this.lastQuery != q || this.alwaysQuery){
14589 this.lastQuery = q;
14590 if(this.mode == 'local'){
14591 this.selectedIndex = -1;
14593 this.store.clearFilter();
14596 if(this.specialFilter){
14597 this.fireEvent('specialfilter', this);
14602 this.store.filter(this.displayField, q);
14605 this.store.fireEvent("datachanged", this.store);
14612 this.store.baseParams[this.queryParam] = q;
14614 var options = {params : this.getParams(q)};
14617 options.add = true;
14618 options.params.start = this.page * this.pageSize;
14621 this.store.load(options);
14624 * this code will make the page width larger, at the beginning, the list not align correctly,
14625 * we should expand the list on onLoad
14626 * so command out it
14631 this.selectedIndex = -1;
14636 this.loadNext = false;
14640 getParams : function(q){
14642 //p[this.queryParam] = q;
14646 p.limit = this.pageSize;
14652 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14654 collapse : function(){
14655 if(!this.isExpanded()){
14661 this.hasFocus = false;
14665 this.cancelBtn.hide();
14666 this.trigger.show();
14669 this.tickableInputEl().dom.value = '';
14670 this.tickableInputEl().blur();
14675 Roo.get(document).un('mousedown', this.collapseIf, this);
14676 Roo.get(document).un('mousewheel', this.collapseIf, this);
14677 if (!this.editable) {
14678 Roo.get(document).un('keydown', this.listKeyPress, this);
14680 this.fireEvent('collapse', this);
14686 collapseIf : function(e){
14687 var in_combo = e.within(this.el);
14688 var in_list = e.within(this.list);
14689 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14691 if (in_combo || in_list || is_list) {
14692 //e.stopPropagation();
14697 this.onTickableFooterButtonClick(e, false, false);
14705 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14707 expand : function(){
14709 if(this.isExpanded() || !this.hasFocus){
14713 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14714 this.list.setWidth(lw);
14720 this.restrictHeight();
14724 this.tickItems = Roo.apply([], this.item);
14727 this.cancelBtn.show();
14728 this.trigger.hide();
14731 this.tickableInputEl().focus();
14736 Roo.get(document).on('mousedown', this.collapseIf, this);
14737 Roo.get(document).on('mousewheel', this.collapseIf, this);
14738 if (!this.editable) {
14739 Roo.get(document).on('keydown', this.listKeyPress, this);
14742 this.fireEvent('expand', this);
14746 // Implements the default empty TriggerField.onTriggerClick function
14747 onTriggerClick : function(e)
14749 Roo.log('trigger click');
14751 if(this.disabled || !this.triggerList){
14756 this.loadNext = false;
14758 if(this.isExpanded()){
14760 if (!this.blockFocus) {
14761 this.inputEl().focus();
14765 this.hasFocus = true;
14766 if(this.triggerAction == 'all') {
14767 this.doQuery(this.allQuery, true);
14769 this.doQuery(this.getRawValue());
14771 if (!this.blockFocus) {
14772 this.inputEl().focus();
14777 onTickableTriggerClick : function(e)
14784 this.loadNext = false;
14785 this.hasFocus = true;
14787 if(this.triggerAction == 'all') {
14788 this.doQuery(this.allQuery, true);
14790 this.doQuery(this.getRawValue());
14794 onSearchFieldClick : function(e)
14796 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14797 this.onTickableFooterButtonClick(e, false, false);
14801 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14806 this.loadNext = false;
14807 this.hasFocus = true;
14809 if(this.triggerAction == 'all') {
14810 this.doQuery(this.allQuery, true);
14812 this.doQuery(this.getRawValue());
14816 listKeyPress : function(e)
14818 //Roo.log('listkeypress');
14819 // scroll to first matching element based on key pres..
14820 if (e.isSpecialKey()) {
14823 var k = String.fromCharCode(e.getKey()).toUpperCase();
14826 var csel = this.view.getSelectedNodes();
14827 var cselitem = false;
14829 var ix = this.view.indexOf(csel[0]);
14830 cselitem = this.store.getAt(ix);
14831 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14837 this.store.each(function(v) {
14839 // start at existing selection.
14840 if (cselitem.id == v.id) {
14846 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14847 match = this.store.indexOf(v);
14853 if (match === false) {
14854 return true; // no more action?
14857 this.view.select(match);
14858 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14859 sn.scrollIntoView(sn.dom.parentNode, false);
14862 onViewScroll : function(e, t){
14864 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){
14868 this.hasQuery = true;
14870 this.loading = this.list.select('.loading', true).first();
14872 if(this.loading === null){
14873 this.list.createChild({
14875 cls: 'loading roo-select2-more-results roo-select2-active',
14876 html: 'Loading more results...'
14879 this.loading = this.list.select('.loading', true).first();
14881 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14883 this.loading.hide();
14886 this.loading.show();
14891 this.loadNext = true;
14893 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14898 addItem : function(o)
14900 var dv = ''; // display value
14902 if (this.displayField) {
14903 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14905 // this is an error condition!!!
14906 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14913 var choice = this.choices.createChild({
14915 cls: 'roo-select2-search-choice',
14924 cls: 'roo-select2-search-choice-close fa fa-times',
14929 }, this.searchField);
14931 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14933 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14941 this.inputEl().dom.value = '';
14946 onRemoveItem : function(e, _self, o)
14948 e.preventDefault();
14950 this.lastItem = Roo.apply([], this.item);
14952 var index = this.item.indexOf(o.data) * 1;
14955 Roo.log('not this item?!');
14959 this.item.splice(index, 1);
14964 this.fireEvent('remove', this, e);
14970 syncValue : function()
14972 if(!this.item.length){
14979 Roo.each(this.item, function(i){
14980 if(_this.valueField){
14981 value.push(i[_this.valueField]);
14988 this.value = value.join(',');
14990 if(this.hiddenField){
14991 this.hiddenField.dom.value = this.value;
14994 this.store.fireEvent("datachanged", this.store);
14999 clearItem : function()
15001 if(!this.multiple){
15007 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15015 if(this.tickable && !Roo.isTouch){
15016 this.view.refresh();
15020 inputEl: function ()
15022 if(Roo.isIOS && this.useNativeIOS){
15023 return this.el.select('select.roo-ios-select', true).first();
15026 if(Roo.isTouch && this.mobileTouchView){
15027 return this.el.select('input.form-control',true).first();
15031 return this.searchField;
15034 return this.el.select('input.form-control',true).first();
15037 onTickableFooterButtonClick : function(e, btn, el)
15039 e.preventDefault();
15041 this.lastItem = Roo.apply([], this.item);
15043 if(btn && btn.name == 'cancel'){
15044 this.tickItems = Roo.apply([], this.item);
15053 Roo.each(this.tickItems, function(o){
15061 validate : function()
15063 if(this.getVisibilityEl().hasClass('hidden')){
15067 var v = this.getRawValue();
15070 v = this.getValue();
15073 if(this.disabled || this.allowBlank || v.length){
15078 this.markInvalid();
15082 tickableInputEl : function()
15084 if(!this.tickable || !this.editable){
15085 return this.inputEl();
15088 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15092 getAutoCreateTouchView : function()
15097 cls: 'form-group' //input-group
15103 type : this.inputType,
15104 cls : 'form-control x-combo-noedit',
15105 autocomplete: 'new-password',
15106 placeholder : this.placeholder || '',
15111 input.name = this.name;
15115 input.cls += ' input-' + this.size;
15118 if (this.disabled) {
15119 input.disabled = true;
15130 inputblock.cls += ' input-group';
15132 inputblock.cn.unshift({
15134 cls : 'input-group-addon input-group-prepend input-group-text',
15139 if(this.removable && !this.multiple){
15140 inputblock.cls += ' roo-removable';
15142 inputblock.cn.push({
15145 cls : 'roo-combo-removable-btn close'
15149 if(this.hasFeedback && !this.allowBlank){
15151 inputblock.cls += ' has-feedback';
15153 inputblock.cn.push({
15155 cls: 'glyphicon form-control-feedback'
15162 inputblock.cls += (this.before) ? '' : ' input-group';
15164 inputblock.cn.push({
15166 cls : 'input-group-addon input-group-append input-group-text',
15172 var ibwrap = inputblock;
15177 cls: 'roo-select2-choices',
15181 cls: 'roo-select2-search-field',
15194 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15199 cls: 'form-hidden-field'
15205 if(!this.multiple && this.showToggleBtn){
15212 if (this.caret != false) {
15215 cls: 'fa fa-' + this.caret
15222 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15227 cls: 'combobox-clear',
15241 combobox.cls += ' roo-select2-container-multi';
15244 var align = this.labelAlign || this.parentLabelAlign();
15246 if (align ==='left' && this.fieldLabel.length) {
15251 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15252 tooltip : 'This field is required'
15256 cls : 'control-label col-form-label',
15257 html : this.fieldLabel
15268 var labelCfg = cfg.cn[1];
15269 var contentCfg = cfg.cn[2];
15272 if(this.indicatorpos == 'right'){
15277 cls : 'control-label col-form-label',
15281 html : this.fieldLabel
15285 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15286 tooltip : 'This field is required'
15299 labelCfg = cfg.cn[0];
15300 contentCfg = cfg.cn[1];
15305 if(this.labelWidth > 12){
15306 labelCfg.style = "width: " + this.labelWidth + 'px';
15309 if(this.labelWidth < 13 && this.labelmd == 0){
15310 this.labelmd = this.labelWidth;
15313 if(this.labellg > 0){
15314 labelCfg.cls += ' col-lg-' + this.labellg;
15315 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15318 if(this.labelmd > 0){
15319 labelCfg.cls += ' col-md-' + this.labelmd;
15320 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15323 if(this.labelsm > 0){
15324 labelCfg.cls += ' col-sm-' + this.labelsm;
15325 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15328 if(this.labelxs > 0){
15329 labelCfg.cls += ' col-xs-' + this.labelxs;
15330 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15334 } else if ( this.fieldLabel.length) {
15338 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15339 tooltip : 'This field is required'
15343 cls : 'control-label',
15344 html : this.fieldLabel
15355 if(this.indicatorpos == 'right'){
15359 cls : 'control-label',
15360 html : this.fieldLabel,
15364 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15365 tooltip : 'This field is required'
15382 var settings = this;
15384 ['xs','sm','md','lg'].map(function(size){
15385 if (settings[size]) {
15386 cfg.cls += ' col-' + size + '-' + settings[size];
15393 initTouchView : function()
15395 this.renderTouchView();
15397 this.touchViewEl.on('scroll', function(){
15398 this.el.dom.scrollTop = 0;
15401 this.originalValue = this.getValue();
15403 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15405 this.inputEl().on("click", this.showTouchView, this);
15406 if (this.triggerEl) {
15407 this.triggerEl.on("click", this.showTouchView, this);
15411 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15412 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15414 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15416 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15417 this.store.on('load', this.onTouchViewLoad, this);
15418 this.store.on('loadexception', this.onTouchViewLoadException, this);
15420 if(this.hiddenName){
15422 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15424 this.hiddenField.dom.value =
15425 this.hiddenValue !== undefined ? this.hiddenValue :
15426 this.value !== undefined ? this.value : '';
15428 this.el.dom.removeAttribute('name');
15429 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15433 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15434 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15437 if(this.removable && !this.multiple){
15438 var close = this.closeTriggerEl();
15440 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15441 close.on('click', this.removeBtnClick, this, close);
15445 * fix the bug in Safari iOS8
15447 this.inputEl().on("focus", function(e){
15448 document.activeElement.blur();
15451 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15458 renderTouchView : function()
15460 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15461 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15463 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15464 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15466 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15467 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468 this.touchViewBodyEl.setStyle('overflow', 'auto');
15470 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15471 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15473 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15474 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15478 showTouchView : function()
15484 this.touchViewHeaderEl.hide();
15486 if(this.modalTitle.length){
15487 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15488 this.touchViewHeaderEl.show();
15491 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15492 this.touchViewEl.show();
15494 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15496 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15497 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15499 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15501 if(this.modalTitle.length){
15502 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15505 this.touchViewBodyEl.setHeight(bodyHeight);
15509 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15511 this.touchViewEl.addClass('in');
15514 if(this._touchViewMask){
15515 Roo.get(document.body).addClass("x-body-masked");
15516 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15517 this._touchViewMask.setStyle('z-index', 10000);
15518 this._touchViewMask.addClass('show');
15521 this.doTouchViewQuery();
15525 hideTouchView : function()
15527 this.touchViewEl.removeClass('in');
15531 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15533 this.touchViewEl.setStyle('display', 'none');
15536 if(this._touchViewMask){
15537 this._touchViewMask.removeClass('show');
15538 Roo.get(document.body).removeClass("x-body-masked");
15542 setTouchViewValue : function()
15549 Roo.each(this.tickItems, function(o){
15554 this.hideTouchView();
15557 doTouchViewQuery : function()
15566 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15570 if(!this.alwaysQuery || this.mode == 'local'){
15571 this.onTouchViewLoad();
15578 onTouchViewBeforeLoad : function(combo,opts)
15584 onTouchViewLoad : function()
15586 if(this.store.getCount() < 1){
15587 this.onTouchViewEmptyResults();
15591 this.clearTouchView();
15593 var rawValue = this.getRawValue();
15595 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15597 this.tickItems = [];
15599 this.store.data.each(function(d, rowIndex){
15600 var row = this.touchViewListGroup.createChild(template);
15602 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15603 row.addClass(d.data.cls);
15606 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15609 html : d.data[this.displayField]
15612 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15613 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15616 row.removeClass('selected');
15617 if(!this.multiple && this.valueField &&
15618 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15621 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15622 row.addClass('selected');
15625 if(this.multiple && this.valueField &&
15626 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15630 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15631 this.tickItems.push(d.data);
15634 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15638 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15640 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15642 if(this.modalTitle.length){
15643 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15646 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15648 if(this.mobile_restrict_height && listHeight < bodyHeight){
15649 this.touchViewBodyEl.setHeight(listHeight);
15654 if(firstChecked && listHeight > bodyHeight){
15655 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15660 onTouchViewLoadException : function()
15662 this.hideTouchView();
15665 onTouchViewEmptyResults : function()
15667 this.clearTouchView();
15669 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15671 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15675 clearTouchView : function()
15677 this.touchViewListGroup.dom.innerHTML = '';
15680 onTouchViewClick : function(e, el, o)
15682 e.preventDefault();
15685 var rowIndex = o.rowIndex;
15687 var r = this.store.getAt(rowIndex);
15689 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15691 if(!this.multiple){
15692 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15693 c.dom.removeAttribute('checked');
15696 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15698 this.setFromData(r.data);
15700 var close = this.closeTriggerEl();
15706 this.hideTouchView();
15708 this.fireEvent('select', this, r, rowIndex);
15713 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15714 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15715 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15719 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15720 this.addItem(r.data);
15721 this.tickItems.push(r.data);
15725 getAutoCreateNativeIOS : function()
15728 cls: 'form-group' //input-group,
15733 cls : 'roo-ios-select'
15737 combobox.name = this.name;
15740 if (this.disabled) {
15741 combobox.disabled = true;
15744 var settings = this;
15746 ['xs','sm','md','lg'].map(function(size){
15747 if (settings[size]) {
15748 cfg.cls += ' col-' + size + '-' + settings[size];
15758 initIOSView : function()
15760 this.store.on('load', this.onIOSViewLoad, this);
15765 onIOSViewLoad : function()
15767 if(this.store.getCount() < 1){
15771 this.clearIOSView();
15773 if(this.allowBlank) {
15775 var default_text = '-- SELECT --';
15777 if(this.placeholder.length){
15778 default_text = this.placeholder;
15781 if(this.emptyTitle.length){
15782 default_text += ' - ' + this.emptyTitle + ' -';
15785 var opt = this.inputEl().createChild({
15788 html : default_text
15792 o[this.valueField] = 0;
15793 o[this.displayField] = default_text;
15795 this.ios_options.push({
15802 this.store.data.each(function(d, rowIndex){
15806 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15807 html = d.data[this.displayField];
15812 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15813 value = d.data[this.valueField];
15822 if(this.value == d.data[this.valueField]){
15823 option['selected'] = true;
15826 var opt = this.inputEl().createChild(option);
15828 this.ios_options.push({
15835 this.inputEl().on('change', function(){
15836 this.fireEvent('select', this);
15841 clearIOSView: function()
15843 this.inputEl().dom.innerHTML = '';
15845 this.ios_options = [];
15848 setIOSValue: function(v)
15852 if(!this.ios_options){
15856 Roo.each(this.ios_options, function(opts){
15858 opts.el.dom.removeAttribute('selected');
15860 if(opts.data[this.valueField] != v){
15864 opts.el.dom.setAttribute('selected', true);
15870 * @cfg {Boolean} grow
15874 * @cfg {Number} growMin
15878 * @cfg {Number} growMax
15887 Roo.apply(Roo.bootstrap.ComboBox, {
15891 cls: 'modal-header',
15913 cls: 'list-group-item',
15917 cls: 'roo-combobox-list-group-item-value'
15921 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15935 listItemCheckbox : {
15937 cls: 'list-group-item',
15941 cls: 'roo-combobox-list-group-item-value'
15945 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15961 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15966 cls: 'modal-footer',
15974 cls: 'col-xs-6 text-left',
15977 cls: 'btn btn-danger roo-touch-view-cancel',
15983 cls: 'col-xs-6 text-right',
15986 cls: 'btn btn-success roo-touch-view-ok',
15997 Roo.apply(Roo.bootstrap.ComboBox, {
15999 touchViewTemplate : {
16001 cls: 'modal fade roo-combobox-touch-view',
16005 cls: 'modal-dialog',
16006 style : 'position:fixed', // we have to fix position....
16010 cls: 'modal-content',
16012 Roo.bootstrap.ComboBox.header,
16013 Roo.bootstrap.ComboBox.body,
16014 Roo.bootstrap.ComboBox.footer
16023 * Ext JS Library 1.1.1
16024 * Copyright(c) 2006-2007, Ext JS, LLC.
16026 * Originally Released Under LGPL - original licence link has changed is not relivant.
16029 * <script type="text/javascript">
16034 * @extends Roo.util.Observable
16035 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16036 * This class also supports single and multi selection modes. <br>
16037 * Create a data model bound view:
16039 var store = new Roo.data.Store(...);
16041 var view = new Roo.View({
16043 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16045 singleSelect: true,
16046 selectedClass: "ydataview-selected",
16050 // listen for node click?
16051 view.on("click", function(vw, index, node, e){
16052 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16056 dataModel.load("foobar.xml");
16058 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16060 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16061 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16063 * Note: old style constructor is still suported (container, template, config)
16066 * Create a new View
16067 * @param {Object} config The config object
16070 Roo.View = function(config, depreciated_tpl, depreciated_config){
16072 this.parent = false;
16074 if (typeof(depreciated_tpl) == 'undefined') {
16075 // new way.. - universal constructor.
16076 Roo.apply(this, config);
16077 this.el = Roo.get(this.el);
16080 this.el = Roo.get(config);
16081 this.tpl = depreciated_tpl;
16082 Roo.apply(this, depreciated_config);
16084 this.wrapEl = this.el.wrap().wrap();
16085 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16088 if(typeof(this.tpl) == "string"){
16089 this.tpl = new Roo.Template(this.tpl);
16091 // support xtype ctors..
16092 this.tpl = new Roo.factory(this.tpl, Roo);
16096 this.tpl.compile();
16101 * @event beforeclick
16102 * Fires before a click is processed. Returns false to cancel the default action.
16103 * @param {Roo.View} this
16104 * @param {Number} index The index of the target node
16105 * @param {HTMLElement} node The target node
16106 * @param {Roo.EventObject} e The raw event object
16108 "beforeclick" : true,
16111 * Fires when a template node is clicked.
16112 * @param {Roo.View} this
16113 * @param {Number} index The index of the target node
16114 * @param {HTMLElement} node The target node
16115 * @param {Roo.EventObject} e The raw event object
16120 * Fires when a template node is double clicked.
16121 * @param {Roo.View} this
16122 * @param {Number} index The index of the target node
16123 * @param {HTMLElement} node The target node
16124 * @param {Roo.EventObject} e The raw event object
16128 * @event contextmenu
16129 * Fires when a template node is right clicked.
16130 * @param {Roo.View} this
16131 * @param {Number} index The index of the target node
16132 * @param {HTMLElement} node The target node
16133 * @param {Roo.EventObject} e The raw event object
16135 "contextmenu" : true,
16137 * @event selectionchange
16138 * Fires when the selected nodes change.
16139 * @param {Roo.View} this
16140 * @param {Array} selections Array of the selected nodes
16142 "selectionchange" : true,
16145 * @event beforeselect
16146 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16147 * @param {Roo.View} this
16148 * @param {HTMLElement} node The node to be selected
16149 * @param {Array} selections Array of currently selected nodes
16151 "beforeselect" : true,
16153 * @event preparedata
16154 * Fires on every row to render, to allow you to change the data.
16155 * @param {Roo.View} this
16156 * @param {Object} data to be rendered (change this)
16158 "preparedata" : true
16166 "click": this.onClick,
16167 "dblclick": this.onDblClick,
16168 "contextmenu": this.onContextMenu,
16172 this.selections = [];
16174 this.cmp = new Roo.CompositeElementLite([]);
16176 this.store = Roo.factory(this.store, Roo.data);
16177 this.setStore(this.store, true);
16180 if ( this.footer && this.footer.xtype) {
16182 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16184 this.footer.dataSource = this.store;
16185 this.footer.container = fctr;
16186 this.footer = Roo.factory(this.footer, Roo);
16187 fctr.insertFirst(this.el);
16189 // this is a bit insane - as the paging toolbar seems to detach the el..
16190 // dom.parentNode.parentNode.parentNode
16191 // they get detached?
16195 Roo.View.superclass.constructor.call(this);
16200 Roo.extend(Roo.View, Roo.util.Observable, {
16203 * @cfg {Roo.data.Store} store Data store to load data from.
16208 * @cfg {String|Roo.Element} el The container element.
16213 * @cfg {String|Roo.Template} tpl The template used by this View
16217 * @cfg {String} dataName the named area of the template to use as the data area
16218 * Works with domtemplates roo-name="name"
16222 * @cfg {String} selectedClass The css class to add to selected nodes
16224 selectedClass : "x-view-selected",
16226 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16231 * @cfg {String} text to display on mask (default Loading)
16235 * @cfg {Boolean} multiSelect Allow multiple selection
16237 multiSelect : false,
16239 * @cfg {Boolean} singleSelect Allow single selection
16241 singleSelect: false,
16244 * @cfg {Boolean} toggleSelect - selecting
16246 toggleSelect : false,
16249 * @cfg {Boolean} tickable - selecting
16254 * Returns the element this view is bound to.
16255 * @return {Roo.Element}
16257 getEl : function(){
16258 return this.wrapEl;
16264 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16266 refresh : function(){
16267 //Roo.log('refresh');
16270 // if we are using something like 'domtemplate', then
16271 // the what gets used is:
16272 // t.applySubtemplate(NAME, data, wrapping data..)
16273 // the outer template then get' applied with
16274 // the store 'extra data'
16275 // and the body get's added to the
16276 // roo-name="data" node?
16277 // <span class='roo-tpl-{name}'></span> ?????
16281 this.clearSelections();
16282 this.el.update("");
16284 var records = this.store.getRange();
16285 if(records.length < 1) {
16287 // is this valid?? = should it render a template??
16289 this.el.update(this.emptyText);
16293 if (this.dataName) {
16294 this.el.update(t.apply(this.store.meta)); //????
16295 el = this.el.child('.roo-tpl-' + this.dataName);
16298 for(var i = 0, len = records.length; i < len; i++){
16299 var data = this.prepareData(records[i].data, i, records[i]);
16300 this.fireEvent("preparedata", this, data, i, records[i]);
16302 var d = Roo.apply({}, data);
16305 Roo.apply(d, {'roo-id' : Roo.id()});
16309 Roo.each(this.parent.item, function(item){
16310 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16313 Roo.apply(d, {'roo-data-checked' : 'checked'});
16317 html[html.length] = Roo.util.Format.trim(
16319 t.applySubtemplate(this.dataName, d, this.store.meta) :
16326 el.update(html.join(""));
16327 this.nodes = el.dom.childNodes;
16328 this.updateIndexes(0);
16333 * Function to override to reformat the data that is sent to
16334 * the template for each node.
16335 * DEPRICATED - use the preparedata event handler.
16336 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16337 * a JSON object for an UpdateManager bound view).
16339 prepareData : function(data, index, record)
16341 this.fireEvent("preparedata", this, data, index, record);
16345 onUpdate : function(ds, record){
16346 // Roo.log('on update');
16347 this.clearSelections();
16348 var index = this.store.indexOf(record);
16349 var n = this.nodes[index];
16350 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16351 n.parentNode.removeChild(n);
16352 this.updateIndexes(index, index);
16358 onAdd : function(ds, records, index)
16360 //Roo.log(['on Add', ds, records, index] );
16361 this.clearSelections();
16362 if(this.nodes.length == 0){
16366 var n = this.nodes[index];
16367 for(var i = 0, len = records.length; i < len; i++){
16368 var d = this.prepareData(records[i].data, i, records[i]);
16370 this.tpl.insertBefore(n, d);
16373 this.tpl.append(this.el, d);
16376 this.updateIndexes(index);
16379 onRemove : function(ds, record, index){
16380 // Roo.log('onRemove');
16381 this.clearSelections();
16382 var el = this.dataName ?
16383 this.el.child('.roo-tpl-' + this.dataName) :
16386 el.dom.removeChild(this.nodes[index]);
16387 this.updateIndexes(index);
16391 * Refresh an individual node.
16392 * @param {Number} index
16394 refreshNode : function(index){
16395 this.onUpdate(this.store, this.store.getAt(index));
16398 updateIndexes : function(startIndex, endIndex){
16399 var ns = this.nodes;
16400 startIndex = startIndex || 0;
16401 endIndex = endIndex || ns.length - 1;
16402 for(var i = startIndex; i <= endIndex; i++){
16403 ns[i].nodeIndex = i;
16408 * Changes the data store this view uses and refresh the view.
16409 * @param {Store} store
16411 setStore : function(store, initial){
16412 if(!initial && this.store){
16413 this.store.un("datachanged", this.refresh);
16414 this.store.un("add", this.onAdd);
16415 this.store.un("remove", this.onRemove);
16416 this.store.un("update", this.onUpdate);
16417 this.store.un("clear", this.refresh);
16418 this.store.un("beforeload", this.onBeforeLoad);
16419 this.store.un("load", this.onLoad);
16420 this.store.un("loadexception", this.onLoad);
16424 store.on("datachanged", this.refresh, this);
16425 store.on("add", this.onAdd, this);
16426 store.on("remove", this.onRemove, this);
16427 store.on("update", this.onUpdate, this);
16428 store.on("clear", this.refresh, this);
16429 store.on("beforeload", this.onBeforeLoad, this);
16430 store.on("load", this.onLoad, this);
16431 store.on("loadexception", this.onLoad, this);
16439 * onbeforeLoad - masks the loading area.
16442 onBeforeLoad : function(store,opts)
16444 //Roo.log('onBeforeLoad');
16446 this.el.update("");
16448 this.el.mask(this.mask ? this.mask : "Loading" );
16450 onLoad : function ()
16457 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16458 * @param {HTMLElement} node
16459 * @return {HTMLElement} The template node
16461 findItemFromChild : function(node){
16462 var el = this.dataName ?
16463 this.el.child('.roo-tpl-' + this.dataName,true) :
16466 if(!node || node.parentNode == el){
16469 var p = node.parentNode;
16470 while(p && p != el){
16471 if(p.parentNode == el){
16480 onClick : function(e){
16481 var item = this.findItemFromChild(e.getTarget());
16483 var index = this.indexOf(item);
16484 if(this.onItemClick(item, index, e) !== false){
16485 this.fireEvent("click", this, index, item, e);
16488 this.clearSelections();
16493 onContextMenu : function(e){
16494 var item = this.findItemFromChild(e.getTarget());
16496 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16501 onDblClick : function(e){
16502 var item = this.findItemFromChild(e.getTarget());
16504 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16508 onItemClick : function(item, index, e)
16510 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16513 if (this.toggleSelect) {
16514 var m = this.isSelected(item) ? 'unselect' : 'select';
16517 _t[m](item, true, false);
16520 if(this.multiSelect || this.singleSelect){
16521 if(this.multiSelect && e.shiftKey && this.lastSelection){
16522 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16524 this.select(item, this.multiSelect && e.ctrlKey);
16525 this.lastSelection = item;
16528 if(!this.tickable){
16529 e.preventDefault();
16537 * Get the number of selected nodes.
16540 getSelectionCount : function(){
16541 return this.selections.length;
16545 * Get the currently selected nodes.
16546 * @return {Array} An array of HTMLElements
16548 getSelectedNodes : function(){
16549 return this.selections;
16553 * Get the indexes of the selected nodes.
16556 getSelectedIndexes : function(){
16557 var indexes = [], s = this.selections;
16558 for(var i = 0, len = s.length; i < len; i++){
16559 indexes.push(s[i].nodeIndex);
16565 * Clear all selections
16566 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16568 clearSelections : function(suppressEvent){
16569 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16570 this.cmp.elements = this.selections;
16571 this.cmp.removeClass(this.selectedClass);
16572 this.selections = [];
16573 if(!suppressEvent){
16574 this.fireEvent("selectionchange", this, this.selections);
16580 * Returns true if the passed node is selected
16581 * @param {HTMLElement/Number} node The node or node index
16582 * @return {Boolean}
16584 isSelected : function(node){
16585 var s = this.selections;
16589 node = this.getNode(node);
16590 return s.indexOf(node) !== -1;
16595 * @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
16596 * @param {Boolean} keepExisting (optional) true to keep existing selections
16597 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16599 select : function(nodeInfo, keepExisting, suppressEvent){
16600 if(nodeInfo instanceof Array){
16602 this.clearSelections(true);
16604 for(var i = 0, len = nodeInfo.length; i < len; i++){
16605 this.select(nodeInfo[i], true, true);
16609 var node = this.getNode(nodeInfo);
16610 if(!node || this.isSelected(node)){
16611 return; // already selected.
16614 this.clearSelections(true);
16617 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16618 Roo.fly(node).addClass(this.selectedClass);
16619 this.selections.push(node);
16620 if(!suppressEvent){
16621 this.fireEvent("selectionchange", this, this.selections);
16629 * @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
16630 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16631 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16633 unselect : function(nodeInfo, keepExisting, suppressEvent)
16635 if(nodeInfo instanceof Array){
16636 Roo.each(this.selections, function(s) {
16637 this.unselect(s, nodeInfo);
16641 var node = this.getNode(nodeInfo);
16642 if(!node || !this.isSelected(node)){
16643 //Roo.log("not selected");
16644 return; // not selected.
16648 Roo.each(this.selections, function(s) {
16650 Roo.fly(node).removeClass(this.selectedClass);
16657 this.selections= ns;
16658 this.fireEvent("selectionchange", this, this.selections);
16662 * Gets a template node.
16663 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16664 * @return {HTMLElement} The node or null if it wasn't found
16666 getNode : function(nodeInfo){
16667 if(typeof nodeInfo == "string"){
16668 return document.getElementById(nodeInfo);
16669 }else if(typeof nodeInfo == "number"){
16670 return this.nodes[nodeInfo];
16676 * Gets a range template nodes.
16677 * @param {Number} startIndex
16678 * @param {Number} endIndex
16679 * @return {Array} An array of nodes
16681 getNodes : function(start, end){
16682 var ns = this.nodes;
16683 start = start || 0;
16684 end = typeof end == "undefined" ? ns.length - 1 : end;
16687 for(var i = start; i <= end; i++){
16691 for(var i = start; i >= end; i--){
16699 * Finds the index of the passed node
16700 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16701 * @return {Number} The index of the node or -1
16703 indexOf : function(node){
16704 node = this.getNode(node);
16705 if(typeof node.nodeIndex == "number"){
16706 return node.nodeIndex;
16708 var ns = this.nodes;
16709 for(var i = 0, len = ns.length; i < len; i++){
16720 * based on jquery fullcalendar
16724 Roo.bootstrap = Roo.bootstrap || {};
16726 * @class Roo.bootstrap.Calendar
16727 * @extends Roo.bootstrap.Component
16728 * Bootstrap Calendar class
16729 * @cfg {Boolean} loadMask (true|false) default false
16730 * @cfg {Object} header generate the user specific header of the calendar, default false
16733 * Create a new Container
16734 * @param {Object} config The config object
16739 Roo.bootstrap.Calendar = function(config){
16740 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16744 * Fires when a date is selected
16745 * @param {DatePicker} this
16746 * @param {Date} date The selected date
16750 * @event monthchange
16751 * Fires when the displayed month changes
16752 * @param {DatePicker} this
16753 * @param {Date} date The selected month
16755 'monthchange': true,
16757 * @event evententer
16758 * Fires when mouse over an event
16759 * @param {Calendar} this
16760 * @param {event} Event
16762 'evententer': true,
16764 * @event eventleave
16765 * Fires when the mouse leaves an
16766 * @param {Calendar} this
16769 'eventleave': true,
16771 * @event eventclick
16772 * Fires when the mouse click an
16773 * @param {Calendar} this
16782 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16785 * @cfg {Number} startDay
16786 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16794 getAutoCreate : function(){
16797 var fc_button = function(name, corner, style, content ) {
16798 return Roo.apply({},{
16800 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16802 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16805 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16816 style : 'width:100%',
16823 cls : 'fc-header-left',
16825 fc_button('prev', 'left', 'arrow', '‹' ),
16826 fc_button('next', 'right', 'arrow', '›' ),
16827 { tag: 'span', cls: 'fc-header-space' },
16828 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16836 cls : 'fc-header-center',
16840 cls: 'fc-header-title',
16843 html : 'month / year'
16851 cls : 'fc-header-right',
16853 /* fc_button('month', 'left', '', 'month' ),
16854 fc_button('week', '', '', 'week' ),
16855 fc_button('day', 'right', '', 'day' )
16867 header = this.header;
16870 var cal_heads = function() {
16872 // fixme - handle this.
16874 for (var i =0; i < Date.dayNames.length; i++) {
16875 var d = Date.dayNames[i];
16878 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16879 html : d.substring(0,3)
16883 ret[0].cls += ' fc-first';
16884 ret[6].cls += ' fc-last';
16887 var cal_cell = function(n) {
16890 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16895 cls: 'fc-day-number',
16899 cls: 'fc-day-content',
16903 style: 'position: relative;' // height: 17px;
16915 var cal_rows = function() {
16918 for (var r = 0; r < 6; r++) {
16925 for (var i =0; i < Date.dayNames.length; i++) {
16926 var d = Date.dayNames[i];
16927 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16930 row.cn[0].cls+=' fc-first';
16931 row.cn[0].cn[0].style = 'min-height:90px';
16932 row.cn[6].cls+=' fc-last';
16936 ret[0].cls += ' fc-first';
16937 ret[4].cls += ' fc-prev-last';
16938 ret[5].cls += ' fc-last';
16945 cls: 'fc-border-separate',
16946 style : 'width:100%',
16954 cls : 'fc-first fc-last',
16972 cls : 'fc-content',
16973 style : "position: relative;",
16976 cls : 'fc-view fc-view-month fc-grid',
16977 style : 'position: relative',
16978 unselectable : 'on',
16981 cls : 'fc-event-container',
16982 style : 'position:absolute;z-index:8;top:0;left:0;'
17000 initEvents : function()
17003 throw "can not find store for calendar";
17009 style: "text-align:center",
17013 style: "background-color:white;width:50%;margin:250 auto",
17017 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17028 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17030 var size = this.el.select('.fc-content', true).first().getSize();
17031 this.maskEl.setSize(size.width, size.height);
17032 this.maskEl.enableDisplayMode("block");
17033 if(!this.loadMask){
17034 this.maskEl.hide();
17037 this.store = Roo.factory(this.store, Roo.data);
17038 this.store.on('load', this.onLoad, this);
17039 this.store.on('beforeload', this.onBeforeLoad, this);
17043 this.cells = this.el.select('.fc-day',true);
17044 //Roo.log(this.cells);
17045 this.textNodes = this.el.query('.fc-day-number');
17046 this.cells.addClassOnOver('fc-state-hover');
17048 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17049 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17050 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17051 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17053 this.on('monthchange', this.onMonthChange, this);
17055 this.update(new Date().clearTime());
17058 resize : function() {
17059 var sz = this.el.getSize();
17061 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17062 this.el.select('.fc-day-content div',true).setHeight(34);
17067 showPrevMonth : function(e){
17068 this.update(this.activeDate.add("mo", -1));
17070 showToday : function(e){
17071 this.update(new Date().clearTime());
17074 showNextMonth : function(e){
17075 this.update(this.activeDate.add("mo", 1));
17079 showPrevYear : function(){
17080 this.update(this.activeDate.add("y", -1));
17084 showNextYear : function(){
17085 this.update(this.activeDate.add("y", 1));
17090 update : function(date)
17092 var vd = this.activeDate;
17093 this.activeDate = date;
17094 // if(vd && this.el){
17095 // var t = date.getTime();
17096 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17097 // Roo.log('using add remove');
17099 // this.fireEvent('monthchange', this, date);
17101 // this.cells.removeClass("fc-state-highlight");
17102 // this.cells.each(function(c){
17103 // if(c.dateValue == t){
17104 // c.addClass("fc-state-highlight");
17105 // setTimeout(function(){
17106 // try{c.dom.firstChild.focus();}catch(e){}
17116 var days = date.getDaysInMonth();
17118 var firstOfMonth = date.getFirstDateOfMonth();
17119 var startingPos = firstOfMonth.getDay()-this.startDay;
17121 if(startingPos < this.startDay){
17125 var pm = date.add(Date.MONTH, -1);
17126 var prevStart = pm.getDaysInMonth()-startingPos;
17128 this.cells = this.el.select('.fc-day',true);
17129 this.textNodes = this.el.query('.fc-day-number');
17130 this.cells.addClassOnOver('fc-state-hover');
17132 var cells = this.cells.elements;
17133 var textEls = this.textNodes;
17135 Roo.each(cells, function(cell){
17136 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17139 days += startingPos;
17141 // convert everything to numbers so it's fast
17142 var day = 86400000;
17143 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17146 //Roo.log(prevStart);
17148 var today = new Date().clearTime().getTime();
17149 var sel = date.clearTime().getTime();
17150 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17151 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17152 var ddMatch = this.disabledDatesRE;
17153 var ddText = this.disabledDatesText;
17154 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17155 var ddaysText = this.disabledDaysText;
17156 var format = this.format;
17158 var setCellClass = function(cal, cell){
17162 //Roo.log('set Cell Class');
17164 var t = d.getTime();
17168 cell.dateValue = t;
17170 cell.className += " fc-today";
17171 cell.className += " fc-state-highlight";
17172 cell.title = cal.todayText;
17175 // disable highlight in other month..
17176 //cell.className += " fc-state-highlight";
17181 cell.className = " fc-state-disabled";
17182 cell.title = cal.minText;
17186 cell.className = " fc-state-disabled";
17187 cell.title = cal.maxText;
17191 if(ddays.indexOf(d.getDay()) != -1){
17192 cell.title = ddaysText;
17193 cell.className = " fc-state-disabled";
17196 if(ddMatch && format){
17197 var fvalue = d.dateFormat(format);
17198 if(ddMatch.test(fvalue)){
17199 cell.title = ddText.replace("%0", fvalue);
17200 cell.className = " fc-state-disabled";
17204 if (!cell.initialClassName) {
17205 cell.initialClassName = cell.dom.className;
17208 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17213 for(; i < startingPos; i++) {
17214 textEls[i].innerHTML = (++prevStart);
17215 d.setDate(d.getDate()+1);
17217 cells[i].className = "fc-past fc-other-month";
17218 setCellClass(this, cells[i]);
17223 for(; i < days; i++){
17224 intDay = i - startingPos + 1;
17225 textEls[i].innerHTML = (intDay);
17226 d.setDate(d.getDate()+1);
17228 cells[i].className = ''; // "x-date-active";
17229 setCellClass(this, cells[i]);
17233 for(; i < 42; i++) {
17234 textEls[i].innerHTML = (++extraDays);
17235 d.setDate(d.getDate()+1);
17237 cells[i].className = "fc-future fc-other-month";
17238 setCellClass(this, cells[i]);
17241 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17243 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17245 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17246 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17248 if(totalRows != 6){
17249 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17250 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17253 this.fireEvent('monthchange', this, date);
17257 if(!this.internalRender){
17258 var main = this.el.dom.firstChild;
17259 var w = main.offsetWidth;
17260 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17261 Roo.fly(main).setWidth(w);
17262 this.internalRender = true;
17263 // opera does not respect the auto grow header center column
17264 // then, after it gets a width opera refuses to recalculate
17265 // without a second pass
17266 if(Roo.isOpera && !this.secondPass){
17267 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17268 this.secondPass = true;
17269 this.update.defer(10, this, [date]);
17276 findCell : function(dt) {
17277 dt = dt.clearTime().getTime();
17279 this.cells.each(function(c){
17280 //Roo.log("check " +c.dateValue + '?=' + dt);
17281 if(c.dateValue == dt){
17291 findCells : function(ev) {
17292 var s = ev.start.clone().clearTime().getTime();
17294 var e= ev.end.clone().clearTime().getTime();
17297 this.cells.each(function(c){
17298 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17300 if(c.dateValue > e){
17303 if(c.dateValue < s){
17312 // findBestRow: function(cells)
17316 // for (var i =0 ; i < cells.length;i++) {
17317 // ret = Math.max(cells[i].rows || 0,ret);
17324 addItem : function(ev)
17326 // look for vertical location slot in
17327 var cells = this.findCells(ev);
17329 // ev.row = this.findBestRow(cells);
17331 // work out the location.
17335 for(var i =0; i < cells.length; i++) {
17337 cells[i].row = cells[0].row;
17340 cells[i].row = cells[i].row + 1;
17350 if (crow.start.getY() == cells[i].getY()) {
17352 crow.end = cells[i];
17369 cells[0].events.push(ev);
17371 this.calevents.push(ev);
17374 clearEvents: function() {
17376 if(!this.calevents){
17380 Roo.each(this.cells.elements, function(c){
17386 Roo.each(this.calevents, function(e) {
17387 Roo.each(e.els, function(el) {
17388 el.un('mouseenter' ,this.onEventEnter, this);
17389 el.un('mouseleave' ,this.onEventLeave, this);
17394 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17400 renderEvents: function()
17404 this.cells.each(function(c) {
17413 if(c.row != c.events.length){
17414 r = 4 - (4 - (c.row - c.events.length));
17417 c.events = ev.slice(0, r);
17418 c.more = ev.slice(r);
17420 if(c.more.length && c.more.length == 1){
17421 c.events.push(c.more.pop());
17424 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17428 this.cells.each(function(c) {
17430 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17433 for (var e = 0; e < c.events.length; e++){
17434 var ev = c.events[e];
17435 var rows = ev.rows;
17437 for(var i = 0; i < rows.length; i++) {
17439 // how many rows should it span..
17442 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17443 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17445 unselectable : "on",
17448 cls: 'fc-event-inner',
17452 // cls: 'fc-event-time',
17453 // html : cells.length > 1 ? '' : ev.time
17457 cls: 'fc-event-title',
17458 html : String.format('{0}', ev.title)
17465 cls: 'ui-resizable-handle ui-resizable-e',
17466 html : '  '
17473 cfg.cls += ' fc-event-start';
17475 if ((i+1) == rows.length) {
17476 cfg.cls += ' fc-event-end';
17479 var ctr = _this.el.select('.fc-event-container',true).first();
17480 var cg = ctr.createChild(cfg);
17482 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17483 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17485 var r = (c.more.length) ? 1 : 0;
17486 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17487 cg.setWidth(ebox.right - sbox.x -2);
17489 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17490 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17491 cg.on('click', _this.onEventClick, _this, ev);
17502 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17503 style : 'position: absolute',
17504 unselectable : "on",
17507 cls: 'fc-event-inner',
17511 cls: 'fc-event-title',
17519 cls: 'ui-resizable-handle ui-resizable-e',
17520 html : '  '
17526 var ctr = _this.el.select('.fc-event-container',true).first();
17527 var cg = ctr.createChild(cfg);
17529 var sbox = c.select('.fc-day-content',true).first().getBox();
17530 var ebox = c.select('.fc-day-content',true).first().getBox();
17532 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17533 cg.setWidth(ebox.right - sbox.x -2);
17535 cg.on('click', _this.onMoreEventClick, _this, c.more);
17545 onEventEnter: function (e, el,event,d) {
17546 this.fireEvent('evententer', this, el, event);
17549 onEventLeave: function (e, el,event,d) {
17550 this.fireEvent('eventleave', this, el, event);
17553 onEventClick: function (e, el,event,d) {
17554 this.fireEvent('eventclick', this, el, event);
17557 onMonthChange: function () {
17561 onMoreEventClick: function(e, el, more)
17565 this.calpopover.placement = 'right';
17566 this.calpopover.setTitle('More');
17568 this.calpopover.setContent('');
17570 var ctr = this.calpopover.el.select('.popover-content', true).first();
17572 Roo.each(more, function(m){
17574 cls : 'fc-event-hori fc-event-draggable',
17577 var cg = ctr.createChild(cfg);
17579 cg.on('click', _this.onEventClick, _this, m);
17582 this.calpopover.show(el);
17587 onLoad: function ()
17589 this.calevents = [];
17592 if(this.store.getCount() > 0){
17593 this.store.data.each(function(d){
17596 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17597 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17598 time : d.data.start_time,
17599 title : d.data.title,
17600 description : d.data.description,
17601 venue : d.data.venue
17606 this.renderEvents();
17608 if(this.calevents.length && this.loadMask){
17609 this.maskEl.hide();
17613 onBeforeLoad: function()
17615 this.clearEvents();
17617 this.maskEl.show();
17631 * @class Roo.bootstrap.Popover
17632 * @extends Roo.bootstrap.Component
17633 * Bootstrap Popover class
17634 * @cfg {String} html contents of the popover (or false to use children..)
17635 * @cfg {String} title of popover (or false to hide)
17636 * @cfg {String} placement how it is placed
17637 * @cfg {String} trigger click || hover (or false to trigger manually)
17638 * @cfg {String} over what (parent or false to trigger manually.)
17639 * @cfg {Number} delay - delay before showing
17642 * Create a new Popover
17643 * @param {Object} config The config object
17646 Roo.bootstrap.Popover = function(config){
17647 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17653 * After the popover show
17655 * @param {Roo.bootstrap.Popover} this
17660 * After the popover hide
17662 * @param {Roo.bootstrap.Popover} this
17668 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17670 title: 'Fill in a title',
17673 placement : 'right',
17674 trigger : 'hover', // hover
17680 can_build_overlaid : false,
17682 getChildContainer : function()
17684 return this.el.select('.popover-content',true).first();
17687 getAutoCreate : function(){
17690 cls : 'popover roo-dynamic',
17691 style: 'display:block',
17697 cls : 'popover-inner',
17701 cls: 'popover-title popover-header',
17705 cls : 'popover-content popover-body',
17716 setTitle: function(str)
17719 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17721 setContent: function(str)
17724 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17726 // as it get's added to the bottom of the page.
17727 onRender : function(ct, position)
17729 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17731 var cfg = Roo.apply({}, this.getAutoCreate());
17735 cfg.cls += ' ' + this.cls;
17738 cfg.style = this.style;
17740 //Roo.log("adding to ");
17741 this.el = Roo.get(document.body).createChild(cfg, position);
17742 // Roo.log(this.el);
17747 initEvents : function()
17749 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17750 this.el.enableDisplayMode('block');
17752 if (this.over === false) {
17755 if (this.triggers === false) {
17758 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17759 var triggers = this.trigger ? this.trigger.split(' ') : [];
17760 Roo.each(triggers, function(trigger) {
17762 if (trigger == 'click') {
17763 on_el.on('click', this.toggle, this);
17764 } else if (trigger != 'manual') {
17765 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17766 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17768 on_el.on(eventIn ,this.enter, this);
17769 on_el.on(eventOut, this.leave, this);
17780 toggle : function () {
17781 this.hoverState == 'in' ? this.leave() : this.enter();
17784 enter : function () {
17786 clearTimeout(this.timeout);
17788 this.hoverState = 'in';
17790 if (!this.delay || !this.delay.show) {
17795 this.timeout = setTimeout(function () {
17796 if (_t.hoverState == 'in') {
17799 }, this.delay.show)
17802 leave : function() {
17803 clearTimeout(this.timeout);
17805 this.hoverState = 'out';
17807 if (!this.delay || !this.delay.hide) {
17812 this.timeout = setTimeout(function () {
17813 if (_t.hoverState == 'out') {
17816 }, this.delay.hide)
17819 show : function (on_el)
17822 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17826 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17827 if (this.html !== false) {
17828 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17830 this.el.removeClass([
17831 'fade','top','bottom', 'left', 'right','in',
17832 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17834 if (!this.title.length) {
17835 this.el.select('.popover-title',true).hide();
17838 var placement = typeof this.placement == 'function' ?
17839 this.placement.call(this, this.el, on_el) :
17842 var autoToken = /\s?auto?\s?/i;
17843 var autoPlace = autoToken.test(placement);
17845 placement = placement.replace(autoToken, '') || 'top';
17849 //this.el.setXY([0,0]);
17851 this.el.dom.style.display='block';
17852 this.el.addClass(placement);
17854 //this.el.appendTo(on_el);
17856 var p = this.getPosition();
17857 var box = this.el.getBox();
17862 var align = Roo.bootstrap.Popover.alignment[placement];
17865 this.el.alignTo(on_el, align[0],align[1]);
17866 //var arrow = this.el.select('.arrow',true).first();
17867 //arrow.set(align[2],
17869 this.el.addClass('in');
17872 if (this.el.hasClass('fade')) {
17876 this.hoverState = 'in';
17878 this.fireEvent('show', this);
17883 this.el.setXY([0,0]);
17884 this.el.removeClass('in');
17886 this.hoverState = null;
17888 this.fireEvent('hide', this);
17893 Roo.bootstrap.Popover.alignment = {
17894 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17895 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17896 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17897 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17908 * @class Roo.bootstrap.Progress
17909 * @extends Roo.bootstrap.Component
17910 * Bootstrap Progress class
17911 * @cfg {Boolean} striped striped of the progress bar
17912 * @cfg {Boolean} active animated of the progress bar
17916 * Create a new Progress
17917 * @param {Object} config The config object
17920 Roo.bootstrap.Progress = function(config){
17921 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17924 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17929 getAutoCreate : function(){
17937 cfg.cls += ' progress-striped';
17941 cfg.cls += ' active';
17960 * @class Roo.bootstrap.ProgressBar
17961 * @extends Roo.bootstrap.Component
17962 * Bootstrap ProgressBar class
17963 * @cfg {Number} aria_valuenow aria-value now
17964 * @cfg {Number} aria_valuemin aria-value min
17965 * @cfg {Number} aria_valuemax aria-value max
17966 * @cfg {String} label label for the progress bar
17967 * @cfg {String} panel (success | info | warning | danger )
17968 * @cfg {String} role role of the progress bar
17969 * @cfg {String} sr_only text
17973 * Create a new ProgressBar
17974 * @param {Object} config The config object
17977 Roo.bootstrap.ProgressBar = function(config){
17978 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17981 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17985 aria_valuemax : 100,
17991 getAutoCreate : function()
17996 cls: 'progress-bar',
17997 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18009 cfg.role = this.role;
18012 if(this.aria_valuenow){
18013 cfg['aria-valuenow'] = this.aria_valuenow;
18016 if(this.aria_valuemin){
18017 cfg['aria-valuemin'] = this.aria_valuemin;
18020 if(this.aria_valuemax){
18021 cfg['aria-valuemax'] = this.aria_valuemax;
18024 if(this.label && !this.sr_only){
18025 cfg.html = this.label;
18029 cfg.cls += ' progress-bar-' + this.panel;
18035 update : function(aria_valuenow)
18037 this.aria_valuenow = aria_valuenow;
18039 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18054 * @class Roo.bootstrap.TabGroup
18055 * @extends Roo.bootstrap.Column
18056 * Bootstrap Column class
18057 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18058 * @cfg {Boolean} carousel true to make the group behave like a carousel
18059 * @cfg {Boolean} bullets show bullets for the panels
18060 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18061 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18062 * @cfg {Boolean} showarrow (true|false) show arrow default true
18065 * Create a new TabGroup
18066 * @param {Object} config The config object
18069 Roo.bootstrap.TabGroup = function(config){
18070 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18072 this.navId = Roo.id();
18075 Roo.bootstrap.TabGroup.register(this);
18079 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18082 transition : false,
18087 slideOnTouch : false,
18090 getAutoCreate : function()
18092 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18094 cfg.cls += ' tab-content';
18096 if (this.carousel) {
18097 cfg.cls += ' carousel slide';
18100 cls : 'carousel-inner',
18104 if(this.bullets && !Roo.isTouch){
18107 cls : 'carousel-bullets',
18111 if(this.bullets_cls){
18112 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18119 cfg.cn[0].cn.push(bullets);
18122 if(this.showarrow){
18123 cfg.cn[0].cn.push({
18125 class : 'carousel-arrow',
18129 class : 'carousel-prev',
18133 class : 'fa fa-chevron-left'
18139 class : 'carousel-next',
18143 class : 'fa fa-chevron-right'
18156 initEvents: function()
18158 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18159 // this.el.on("touchstart", this.onTouchStart, this);
18162 if(this.autoslide){
18165 this.slideFn = window.setInterval(function() {
18166 _this.showPanelNext();
18170 if(this.showarrow){
18171 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18172 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18178 // onTouchStart : function(e, el, o)
18180 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18184 // this.showPanelNext();
18188 getChildContainer : function()
18190 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18194 * register a Navigation item
18195 * @param {Roo.bootstrap.NavItem} the navitem to add
18197 register : function(item)
18199 this.tabs.push( item);
18200 item.navId = this.navId; // not really needed..
18205 getActivePanel : function()
18208 Roo.each(this.tabs, function(t) {
18218 getPanelByName : function(n)
18221 Roo.each(this.tabs, function(t) {
18222 if (t.tabId == n) {
18230 indexOfPanel : function(p)
18233 Roo.each(this.tabs, function(t,i) {
18234 if (t.tabId == p.tabId) {
18243 * show a specific panel
18244 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18245 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18247 showPanel : function (pan)
18249 if(this.transition || typeof(pan) == 'undefined'){
18250 Roo.log("waiting for the transitionend");
18254 if (typeof(pan) == 'number') {
18255 pan = this.tabs[pan];
18258 if (typeof(pan) == 'string') {
18259 pan = this.getPanelByName(pan);
18262 var cur = this.getActivePanel();
18265 Roo.log('pan or acitve pan is undefined');
18269 if (pan.tabId == this.getActivePanel().tabId) {
18273 if (false === cur.fireEvent('beforedeactivate')) {
18277 if(this.bullets > 0 && !Roo.isTouch){
18278 this.setActiveBullet(this.indexOfPanel(pan));
18281 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18283 this.transition = true;
18284 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18285 var lr = dir == 'next' ? 'left' : 'right';
18286 pan.el.addClass(dir); // or prev
18287 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18288 cur.el.addClass(lr); // or right
18289 pan.el.addClass(lr);
18292 cur.el.on('transitionend', function() {
18293 Roo.log("trans end?");
18295 pan.el.removeClass([lr,dir]);
18296 pan.setActive(true);
18298 cur.el.removeClass([lr]);
18299 cur.setActive(false);
18301 _this.transition = false;
18303 }, this, { single: true } );
18308 cur.setActive(false);
18309 pan.setActive(true);
18314 showPanelNext : function()
18316 var i = this.indexOfPanel(this.getActivePanel());
18318 if (i >= this.tabs.length - 1 && !this.autoslide) {
18322 if (i >= this.tabs.length - 1 && this.autoslide) {
18326 this.showPanel(this.tabs[i+1]);
18329 showPanelPrev : function()
18331 var i = this.indexOfPanel(this.getActivePanel());
18333 if (i < 1 && !this.autoslide) {
18337 if (i < 1 && this.autoslide) {
18338 i = this.tabs.length;
18341 this.showPanel(this.tabs[i-1]);
18345 addBullet: function()
18347 if(!this.bullets || Roo.isTouch){
18350 var ctr = this.el.select('.carousel-bullets',true).first();
18351 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18352 var bullet = ctr.createChild({
18353 cls : 'bullet bullet-' + i
18354 },ctr.dom.lastChild);
18359 bullet.on('click', (function(e, el, o, ii, t){
18361 e.preventDefault();
18363 this.showPanel(ii);
18365 if(this.autoslide && this.slideFn){
18366 clearInterval(this.slideFn);
18367 this.slideFn = window.setInterval(function() {
18368 _this.showPanelNext();
18372 }).createDelegate(this, [i, bullet], true));
18377 setActiveBullet : function(i)
18383 Roo.each(this.el.select('.bullet', true).elements, function(el){
18384 el.removeClass('selected');
18387 var bullet = this.el.select('.bullet-' + i, true).first();
18393 bullet.addClass('selected');
18404 Roo.apply(Roo.bootstrap.TabGroup, {
18408 * register a Navigation Group
18409 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18411 register : function(navgrp)
18413 this.groups[navgrp.navId] = navgrp;
18417 * fetch a Navigation Group based on the navigation ID
18418 * if one does not exist , it will get created.
18419 * @param {string} the navgroup to add
18420 * @returns {Roo.bootstrap.NavGroup} the navgroup
18422 get: function(navId) {
18423 if (typeof(this.groups[navId]) == 'undefined') {
18424 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18426 return this.groups[navId] ;
18441 * @class Roo.bootstrap.TabPanel
18442 * @extends Roo.bootstrap.Component
18443 * Bootstrap TabPanel class
18444 * @cfg {Boolean} active panel active
18445 * @cfg {String} html panel content
18446 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18447 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18448 * @cfg {String} href click to link..
18452 * Create a new TabPanel
18453 * @param {Object} config The config object
18456 Roo.bootstrap.TabPanel = function(config){
18457 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18461 * Fires when the active status changes
18462 * @param {Roo.bootstrap.TabPanel} this
18463 * @param {Boolean} state the new state
18468 * @event beforedeactivate
18469 * Fires before a tab is de-activated - can be used to do validation on a form.
18470 * @param {Roo.bootstrap.TabPanel} this
18471 * @return {Boolean} false if there is an error
18474 'beforedeactivate': true
18477 this.tabId = this.tabId || Roo.id();
18481 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18489 getAutoCreate : function(){
18492 // item is needed for carousel - not sure if it has any effect otherwise
18493 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18494 html: this.html || ''
18498 cfg.cls += ' active';
18502 cfg.tabId = this.tabId;
18509 initEvents: function()
18511 var p = this.parent();
18513 this.navId = this.navId || p.navId;
18515 if (typeof(this.navId) != 'undefined') {
18516 // not really needed.. but just in case.. parent should be a NavGroup.
18517 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18521 var i = tg.tabs.length - 1;
18523 if(this.active && tg.bullets > 0 && i < tg.bullets){
18524 tg.setActiveBullet(i);
18528 this.el.on('click', this.onClick, this);
18531 this.el.on("touchstart", this.onTouchStart, this);
18532 this.el.on("touchmove", this.onTouchMove, this);
18533 this.el.on("touchend", this.onTouchEnd, this);
18538 onRender : function(ct, position)
18540 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18543 setActive : function(state)
18545 Roo.log("panel - set active " + this.tabId + "=" + state);
18547 this.active = state;
18549 this.el.removeClass('active');
18551 } else if (!this.el.hasClass('active')) {
18552 this.el.addClass('active');
18555 this.fireEvent('changed', this, state);
18558 onClick : function(e)
18560 e.preventDefault();
18562 if(!this.href.length){
18566 window.location.href = this.href;
18575 onTouchStart : function(e)
18577 this.swiping = false;
18579 this.startX = e.browserEvent.touches[0].clientX;
18580 this.startY = e.browserEvent.touches[0].clientY;
18583 onTouchMove : function(e)
18585 this.swiping = true;
18587 this.endX = e.browserEvent.touches[0].clientX;
18588 this.endY = e.browserEvent.touches[0].clientY;
18591 onTouchEnd : function(e)
18598 var tabGroup = this.parent();
18600 if(this.endX > this.startX){ // swiping right
18601 tabGroup.showPanelPrev();
18605 if(this.startX > this.endX){ // swiping left
18606 tabGroup.showPanelNext();
18625 * @class Roo.bootstrap.DateField
18626 * @extends Roo.bootstrap.Input
18627 * Bootstrap DateField class
18628 * @cfg {Number} weekStart default 0
18629 * @cfg {String} viewMode default empty, (months|years)
18630 * @cfg {String} minViewMode default empty, (months|years)
18631 * @cfg {Number} startDate default -Infinity
18632 * @cfg {Number} endDate default Infinity
18633 * @cfg {Boolean} todayHighlight default false
18634 * @cfg {Boolean} todayBtn default false
18635 * @cfg {Boolean} calendarWeeks default false
18636 * @cfg {Object} daysOfWeekDisabled default empty
18637 * @cfg {Boolean} singleMode default false (true | false)
18639 * @cfg {Boolean} keyboardNavigation default true
18640 * @cfg {String} language default en
18643 * Create a new DateField
18644 * @param {Object} config The config object
18647 Roo.bootstrap.DateField = function(config){
18648 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18652 * Fires when this field show.
18653 * @param {Roo.bootstrap.DateField} this
18654 * @param {Mixed} date The date value
18659 * Fires when this field hide.
18660 * @param {Roo.bootstrap.DateField} this
18661 * @param {Mixed} date The date value
18666 * Fires when select a date.
18667 * @param {Roo.bootstrap.DateField} this
18668 * @param {Mixed} date The date value
18672 * @event beforeselect
18673 * Fires when before select a date.
18674 * @param {Roo.bootstrap.DateField} this
18675 * @param {Mixed} date The date value
18677 beforeselect : true
18681 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18684 * @cfg {String} format
18685 * The default date format string which can be overriden for localization support. The format must be
18686 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18690 * @cfg {String} altFormats
18691 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18692 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18694 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18702 todayHighlight : false,
18708 keyboardNavigation: true,
18710 calendarWeeks: false,
18712 startDate: -Infinity,
18716 daysOfWeekDisabled: [],
18720 singleMode : false,
18722 UTCDate: function()
18724 return new Date(Date.UTC.apply(Date, arguments));
18727 UTCToday: function()
18729 var today = new Date();
18730 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18733 getDate: function() {
18734 var d = this.getUTCDate();
18735 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18738 getUTCDate: function() {
18742 setDate: function(d) {
18743 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18746 setUTCDate: function(d) {
18748 this.setValue(this.formatDate(this.date));
18751 onRender: function(ct, position)
18754 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18756 this.language = this.language || 'en';
18757 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18758 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18760 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18761 this.format = this.format || 'm/d/y';
18762 this.isInline = false;
18763 this.isInput = true;
18764 this.component = this.el.select('.add-on', true).first() || false;
18765 this.component = (this.component && this.component.length === 0) ? false : this.component;
18766 this.hasInput = this.component && this.inputEl().length;
18768 if (typeof(this.minViewMode === 'string')) {
18769 switch (this.minViewMode) {
18771 this.minViewMode = 1;
18774 this.minViewMode = 2;
18777 this.minViewMode = 0;
18782 if (typeof(this.viewMode === 'string')) {
18783 switch (this.viewMode) {
18796 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18798 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18800 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18802 this.picker().on('mousedown', this.onMousedown, this);
18803 this.picker().on('click', this.onClick, this);
18805 this.picker().addClass('datepicker-dropdown');
18807 this.startViewMode = this.viewMode;
18809 if(this.singleMode){
18810 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18811 v.setVisibilityMode(Roo.Element.DISPLAY);
18815 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18816 v.setStyle('width', '189px');
18820 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18821 if(!this.calendarWeeks){
18826 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18827 v.attr('colspan', function(i, val){
18828 return parseInt(val) + 1;
18833 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18835 this.setStartDate(this.startDate);
18836 this.setEndDate(this.endDate);
18838 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18845 if(this.isInline) {
18850 picker : function()
18852 return this.pickerEl;
18853 // return this.el.select('.datepicker', true).first();
18856 fillDow: function()
18858 var dowCnt = this.weekStart;
18867 if(this.calendarWeeks){
18875 while (dowCnt < this.weekStart + 7) {
18879 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18883 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18886 fillMonths: function()
18889 var months = this.picker().select('>.datepicker-months td', true).first();
18891 months.dom.innerHTML = '';
18897 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18900 months.createChild(month);
18907 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;
18909 if (this.date < this.startDate) {
18910 this.viewDate = new Date(this.startDate);
18911 } else if (this.date > this.endDate) {
18912 this.viewDate = new Date(this.endDate);
18914 this.viewDate = new Date(this.date);
18922 var d = new Date(this.viewDate),
18923 year = d.getUTCFullYear(),
18924 month = d.getUTCMonth(),
18925 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18926 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18927 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18928 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18929 currentDate = this.date && this.date.valueOf(),
18930 today = this.UTCToday();
18932 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18934 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18936 // this.picker.select('>tfoot th.today').
18937 // .text(dates[this.language].today)
18938 // .toggle(this.todayBtn !== false);
18940 this.updateNavArrows();
18943 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18945 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18947 prevMonth.setUTCDate(day);
18949 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18951 var nextMonth = new Date(prevMonth);
18953 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18955 nextMonth = nextMonth.valueOf();
18957 var fillMonths = false;
18959 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18961 while(prevMonth.valueOf() <= nextMonth) {
18964 if (prevMonth.getUTCDay() === this.weekStart) {
18966 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18974 if(this.calendarWeeks){
18975 // ISO 8601: First week contains first thursday.
18976 // ISO also states week starts on Monday, but we can be more abstract here.
18978 // Start of current week: based on weekstart/current date
18979 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18980 // Thursday of this week
18981 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18982 // First Thursday of year, year from thursday
18983 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18984 // Calendar week: ms between thursdays, div ms per day, div 7 days
18985 calWeek = (th - yth) / 864e5 / 7 + 1;
18987 fillMonths.cn.push({
18995 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18997 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19000 if (this.todayHighlight &&
19001 prevMonth.getUTCFullYear() == today.getFullYear() &&
19002 prevMonth.getUTCMonth() == today.getMonth() &&
19003 prevMonth.getUTCDate() == today.getDate()) {
19004 clsName += ' today';
19007 if (currentDate && prevMonth.valueOf() === currentDate) {
19008 clsName += ' active';
19011 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19012 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19013 clsName += ' disabled';
19016 fillMonths.cn.push({
19018 cls: 'day ' + clsName,
19019 html: prevMonth.getDate()
19022 prevMonth.setDate(prevMonth.getDate()+1);
19025 var currentYear = this.date && this.date.getUTCFullYear();
19026 var currentMonth = this.date && this.date.getUTCMonth();
19028 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19030 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19031 v.removeClass('active');
19033 if(currentYear === year && k === currentMonth){
19034 v.addClass('active');
19037 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19038 v.addClass('disabled');
19044 year = parseInt(year/10, 10) * 10;
19046 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19048 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19051 for (var i = -1; i < 11; i++) {
19052 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19054 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19062 showMode: function(dir)
19065 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19068 Roo.each(this.picker().select('>div',true).elements, function(v){
19069 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19072 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19077 if(this.isInline) {
19081 this.picker().removeClass(['bottom', 'top']);
19083 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19085 * place to the top of element!
19089 this.picker().addClass('top');
19090 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19095 this.picker().addClass('bottom');
19097 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19100 parseDate : function(value)
19102 if(!value || value instanceof Date){
19105 var v = Date.parseDate(value, this.format);
19106 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19107 v = Date.parseDate(value, 'Y-m-d');
19109 if(!v && this.altFormats){
19110 if(!this.altFormatsArray){
19111 this.altFormatsArray = this.altFormats.split("|");
19113 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19114 v = Date.parseDate(value, this.altFormatsArray[i]);
19120 formatDate : function(date, fmt)
19122 return (!date || !(date instanceof Date)) ?
19123 date : date.dateFormat(fmt || this.format);
19126 onFocus : function()
19128 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19132 onBlur : function()
19134 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19136 var d = this.inputEl().getValue();
19143 showPopup : function()
19145 this.picker().show();
19149 this.fireEvent('showpopup', this, this.date);
19152 hidePopup : function()
19154 if(this.isInline) {
19157 this.picker().hide();
19158 this.viewMode = this.startViewMode;
19161 this.fireEvent('hidepopup', this, this.date);
19165 onMousedown: function(e)
19167 e.stopPropagation();
19168 e.preventDefault();
19173 Roo.bootstrap.DateField.superclass.keyup.call(this);
19177 setValue: function(v)
19179 if(this.fireEvent('beforeselect', this, v) !== false){
19180 var d = new Date(this.parseDate(v) ).clearTime();
19182 if(isNaN(d.getTime())){
19183 this.date = this.viewDate = '';
19184 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19188 v = this.formatDate(d);
19190 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19192 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19196 this.fireEvent('select', this, this.date);
19200 getValue: function()
19202 return this.formatDate(this.date);
19205 fireKey: function(e)
19207 if (!this.picker().isVisible()){
19208 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19214 var dateChanged = false,
19216 newDate, newViewDate;
19221 e.preventDefault();
19225 if (!this.keyboardNavigation) {
19228 dir = e.keyCode == 37 ? -1 : 1;
19231 newDate = this.moveYear(this.date, dir);
19232 newViewDate = this.moveYear(this.viewDate, dir);
19233 } else if (e.shiftKey){
19234 newDate = this.moveMonth(this.date, dir);
19235 newViewDate = this.moveMonth(this.viewDate, dir);
19237 newDate = new Date(this.date);
19238 newDate.setUTCDate(this.date.getUTCDate() + dir);
19239 newViewDate = new Date(this.viewDate);
19240 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19242 if (this.dateWithinRange(newDate)){
19243 this.date = newDate;
19244 this.viewDate = newViewDate;
19245 this.setValue(this.formatDate(this.date));
19247 e.preventDefault();
19248 dateChanged = true;
19253 if (!this.keyboardNavigation) {
19256 dir = e.keyCode == 38 ? -1 : 1;
19258 newDate = this.moveYear(this.date, dir);
19259 newViewDate = this.moveYear(this.viewDate, dir);
19260 } else if (e.shiftKey){
19261 newDate = this.moveMonth(this.date, dir);
19262 newViewDate = this.moveMonth(this.viewDate, dir);
19264 newDate = new Date(this.date);
19265 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19266 newViewDate = new Date(this.viewDate);
19267 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19269 if (this.dateWithinRange(newDate)){
19270 this.date = newDate;
19271 this.viewDate = newViewDate;
19272 this.setValue(this.formatDate(this.date));
19274 e.preventDefault();
19275 dateChanged = true;
19279 this.setValue(this.formatDate(this.date));
19281 e.preventDefault();
19284 this.setValue(this.formatDate(this.date));
19298 onClick: function(e)
19300 e.stopPropagation();
19301 e.preventDefault();
19303 var target = e.getTarget();
19305 if(target.nodeName.toLowerCase() === 'i'){
19306 target = Roo.get(target).dom.parentNode;
19309 var nodeName = target.nodeName;
19310 var className = target.className;
19311 var html = target.innerHTML;
19312 //Roo.log(nodeName);
19314 switch(nodeName.toLowerCase()) {
19316 switch(className) {
19322 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19323 switch(this.viewMode){
19325 this.viewDate = this.moveMonth(this.viewDate, dir);
19329 this.viewDate = this.moveYear(this.viewDate, dir);
19335 var date = new Date();
19336 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19338 this.setValue(this.formatDate(this.date));
19345 if (className.indexOf('disabled') < 0) {
19346 this.viewDate.setUTCDate(1);
19347 if (className.indexOf('month') > -1) {
19348 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19350 var year = parseInt(html, 10) || 0;
19351 this.viewDate.setUTCFullYear(year);
19355 if(this.singleMode){
19356 this.setValue(this.formatDate(this.viewDate));
19367 //Roo.log(className);
19368 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19369 var day = parseInt(html, 10) || 1;
19370 var year = this.viewDate.getUTCFullYear(),
19371 month = this.viewDate.getUTCMonth();
19373 if (className.indexOf('old') > -1) {
19380 } else if (className.indexOf('new') > -1) {
19388 //Roo.log([year,month,day]);
19389 this.date = this.UTCDate(year, month, day,0,0,0,0);
19390 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19392 //Roo.log(this.formatDate(this.date));
19393 this.setValue(this.formatDate(this.date));
19400 setStartDate: function(startDate)
19402 this.startDate = startDate || -Infinity;
19403 if (this.startDate !== -Infinity) {
19404 this.startDate = this.parseDate(this.startDate);
19407 this.updateNavArrows();
19410 setEndDate: function(endDate)
19412 this.endDate = endDate || Infinity;
19413 if (this.endDate !== Infinity) {
19414 this.endDate = this.parseDate(this.endDate);
19417 this.updateNavArrows();
19420 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19422 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19423 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19424 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19426 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19427 return parseInt(d, 10);
19430 this.updateNavArrows();
19433 updateNavArrows: function()
19435 if(this.singleMode){
19439 var d = new Date(this.viewDate),
19440 year = d.getUTCFullYear(),
19441 month = d.getUTCMonth();
19443 Roo.each(this.picker().select('.prev', true).elements, function(v){
19445 switch (this.viewMode) {
19448 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19454 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19461 Roo.each(this.picker().select('.next', true).elements, function(v){
19463 switch (this.viewMode) {
19466 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19472 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19480 moveMonth: function(date, dir)
19485 var new_date = new Date(date.valueOf()),
19486 day = new_date.getUTCDate(),
19487 month = new_date.getUTCMonth(),
19488 mag = Math.abs(dir),
19490 dir = dir > 0 ? 1 : -1;
19493 // If going back one month, make sure month is not current month
19494 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19496 return new_date.getUTCMonth() == month;
19498 // If going forward one month, make sure month is as expected
19499 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19501 return new_date.getUTCMonth() != new_month;
19503 new_month = month + dir;
19504 new_date.setUTCMonth(new_month);
19505 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19506 if (new_month < 0 || new_month > 11) {
19507 new_month = (new_month + 12) % 12;
19510 // For magnitudes >1, move one month at a time...
19511 for (var i=0; i<mag; i++) {
19512 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19513 new_date = this.moveMonth(new_date, dir);
19515 // ...then reset the day, keeping it in the new month
19516 new_month = new_date.getUTCMonth();
19517 new_date.setUTCDate(day);
19519 return new_month != new_date.getUTCMonth();
19522 // Common date-resetting loop -- if date is beyond end of month, make it
19525 new_date.setUTCDate(--day);
19526 new_date.setUTCMonth(new_month);
19531 moveYear: function(date, dir)
19533 return this.moveMonth(date, dir*12);
19536 dateWithinRange: function(date)
19538 return date >= this.startDate && date <= this.endDate;
19544 this.picker().remove();
19547 validateValue : function(value)
19549 if(this.getVisibilityEl().hasClass('hidden')){
19553 if(value.length < 1) {
19554 if(this.allowBlank){
19560 if(value.length < this.minLength){
19563 if(value.length > this.maxLength){
19567 var vt = Roo.form.VTypes;
19568 if(!vt[this.vtype](value, this)){
19572 if(typeof this.validator == "function"){
19573 var msg = this.validator(value);
19579 if(this.regex && !this.regex.test(value)){
19583 if(typeof(this.parseDate(value)) == 'undefined'){
19587 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19591 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19601 this.date = this.viewDate = '';
19603 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19608 Roo.apply(Roo.bootstrap.DateField, {
19619 html: '<i class="fa fa-arrow-left"/>'
19629 html: '<i class="fa fa-arrow-right"/>'
19671 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19672 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19673 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19674 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19675 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19688 navFnc: 'FullYear',
19693 navFnc: 'FullYear',
19698 Roo.apply(Roo.bootstrap.DateField, {
19702 cls: 'datepicker dropdown-menu roo-dynamic',
19706 cls: 'datepicker-days',
19710 cls: 'table-condensed',
19712 Roo.bootstrap.DateField.head,
19716 Roo.bootstrap.DateField.footer
19723 cls: 'datepicker-months',
19727 cls: 'table-condensed',
19729 Roo.bootstrap.DateField.head,
19730 Roo.bootstrap.DateField.content,
19731 Roo.bootstrap.DateField.footer
19738 cls: 'datepicker-years',
19742 cls: 'table-condensed',
19744 Roo.bootstrap.DateField.head,
19745 Roo.bootstrap.DateField.content,
19746 Roo.bootstrap.DateField.footer
19765 * @class Roo.bootstrap.TimeField
19766 * @extends Roo.bootstrap.Input
19767 * Bootstrap DateField class
19771 * Create a new TimeField
19772 * @param {Object} config The config object
19775 Roo.bootstrap.TimeField = function(config){
19776 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19780 * Fires when this field show.
19781 * @param {Roo.bootstrap.DateField} thisthis
19782 * @param {Mixed} date The date value
19787 * Fires when this field hide.
19788 * @param {Roo.bootstrap.DateField} this
19789 * @param {Mixed} date The date value
19794 * Fires when select a date.
19795 * @param {Roo.bootstrap.DateField} this
19796 * @param {Mixed} date The date value
19802 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19805 * @cfg {String} format
19806 * The default time format string which can be overriden for localization support. The format must be
19807 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19811 onRender: function(ct, position)
19814 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19816 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19818 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19820 this.pop = this.picker().select('>.datepicker-time',true).first();
19821 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19823 this.picker().on('mousedown', this.onMousedown, this);
19824 this.picker().on('click', this.onClick, this);
19826 this.picker().addClass('datepicker-dropdown');
19831 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19832 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19833 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19834 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19835 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19836 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19840 fireKey: function(e){
19841 if (!this.picker().isVisible()){
19842 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19848 e.preventDefault();
19856 this.onTogglePeriod();
19859 this.onIncrementMinutes();
19862 this.onDecrementMinutes();
19871 onClick: function(e) {
19872 e.stopPropagation();
19873 e.preventDefault();
19876 picker : function()
19878 return this.el.select('.datepicker', true).first();
19881 fillTime: function()
19883 var time = this.pop.select('tbody', true).first();
19885 time.dom.innerHTML = '';
19900 cls: 'hours-up glyphicon glyphicon-chevron-up'
19920 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19941 cls: 'timepicker-hour',
19956 cls: 'timepicker-minute',
19971 cls: 'btn btn-primary period',
19993 cls: 'hours-down glyphicon glyphicon-chevron-down'
20013 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20031 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20038 var hours = this.time.getHours();
20039 var minutes = this.time.getMinutes();
20052 hours = hours - 12;
20056 hours = '0' + hours;
20060 minutes = '0' + minutes;
20063 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20064 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20065 this.pop.select('button', true).first().dom.innerHTML = period;
20071 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20073 var cls = ['bottom'];
20075 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20082 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20087 this.picker().addClass(cls.join('-'));
20091 Roo.each(cls, function(c){
20093 _this.picker().setTop(_this.inputEl().getHeight());
20097 _this.picker().setTop(0 - _this.picker().getHeight());
20102 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20106 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20113 onFocus : function()
20115 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20119 onBlur : function()
20121 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20127 this.picker().show();
20132 this.fireEvent('show', this, this.date);
20137 this.picker().hide();
20140 this.fireEvent('hide', this, this.date);
20143 setTime : function()
20146 this.setValue(this.time.format(this.format));
20148 this.fireEvent('select', this, this.date);
20153 onMousedown: function(e){
20154 e.stopPropagation();
20155 e.preventDefault();
20158 onIncrementHours: function()
20160 Roo.log('onIncrementHours');
20161 this.time = this.time.add(Date.HOUR, 1);
20166 onDecrementHours: function()
20168 Roo.log('onDecrementHours');
20169 this.time = this.time.add(Date.HOUR, -1);
20173 onIncrementMinutes: function()
20175 Roo.log('onIncrementMinutes');
20176 this.time = this.time.add(Date.MINUTE, 1);
20180 onDecrementMinutes: function()
20182 Roo.log('onDecrementMinutes');
20183 this.time = this.time.add(Date.MINUTE, -1);
20187 onTogglePeriod: function()
20189 Roo.log('onTogglePeriod');
20190 this.time = this.time.add(Date.HOUR, 12);
20197 Roo.apply(Roo.bootstrap.TimeField, {
20227 cls: 'btn btn-info ok',
20239 Roo.apply(Roo.bootstrap.TimeField, {
20243 cls: 'datepicker dropdown-menu',
20247 cls: 'datepicker-time',
20251 cls: 'table-condensed',
20253 Roo.bootstrap.TimeField.content,
20254 Roo.bootstrap.TimeField.footer
20273 * @class Roo.bootstrap.MonthField
20274 * @extends Roo.bootstrap.Input
20275 * Bootstrap MonthField class
20277 * @cfg {String} language default en
20280 * Create a new MonthField
20281 * @param {Object} config The config object
20284 Roo.bootstrap.MonthField = function(config){
20285 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20290 * Fires when this field show.
20291 * @param {Roo.bootstrap.MonthField} this
20292 * @param {Mixed} date The date value
20297 * Fires when this field hide.
20298 * @param {Roo.bootstrap.MonthField} this
20299 * @param {Mixed} date The date value
20304 * Fires when select a date.
20305 * @param {Roo.bootstrap.MonthField} this
20306 * @param {String} oldvalue The old value
20307 * @param {String} newvalue The new value
20313 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20315 onRender: function(ct, position)
20318 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20320 this.language = this.language || 'en';
20321 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20322 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20324 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20325 this.isInline = false;
20326 this.isInput = true;
20327 this.component = this.el.select('.add-on', true).first() || false;
20328 this.component = (this.component && this.component.length === 0) ? false : this.component;
20329 this.hasInput = this.component && this.inputEL().length;
20331 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20333 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20335 this.picker().on('mousedown', this.onMousedown, this);
20336 this.picker().on('click', this.onClick, this);
20338 this.picker().addClass('datepicker-dropdown');
20340 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20341 v.setStyle('width', '189px');
20348 if(this.isInline) {
20354 setValue: function(v, suppressEvent)
20356 var o = this.getValue();
20358 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20362 if(suppressEvent !== true){
20363 this.fireEvent('select', this, o, v);
20368 getValue: function()
20373 onClick: function(e)
20375 e.stopPropagation();
20376 e.preventDefault();
20378 var target = e.getTarget();
20380 if(target.nodeName.toLowerCase() === 'i'){
20381 target = Roo.get(target).dom.parentNode;
20384 var nodeName = target.nodeName;
20385 var className = target.className;
20386 var html = target.innerHTML;
20388 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20392 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20394 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20400 picker : function()
20402 return this.pickerEl;
20405 fillMonths: function()
20408 var months = this.picker().select('>.datepicker-months td', true).first();
20410 months.dom.innerHTML = '';
20416 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20419 months.createChild(month);
20428 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20429 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20432 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20433 e.removeClass('active');
20435 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20436 e.addClass('active');
20443 if(this.isInline) {
20447 this.picker().removeClass(['bottom', 'top']);
20449 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20451 * place to the top of element!
20455 this.picker().addClass('top');
20456 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20461 this.picker().addClass('bottom');
20463 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20466 onFocus : function()
20468 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20472 onBlur : function()
20474 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20476 var d = this.inputEl().getValue();
20485 this.picker().show();
20486 this.picker().select('>.datepicker-months', true).first().show();
20490 this.fireEvent('show', this, this.date);
20495 if(this.isInline) {
20498 this.picker().hide();
20499 this.fireEvent('hide', this, this.date);
20503 onMousedown: function(e)
20505 e.stopPropagation();
20506 e.preventDefault();
20511 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20515 fireKey: function(e)
20517 if (!this.picker().isVisible()){
20518 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20529 e.preventDefault();
20533 dir = e.keyCode == 37 ? -1 : 1;
20535 this.vIndex = this.vIndex + dir;
20537 if(this.vIndex < 0){
20541 if(this.vIndex > 11){
20545 if(isNaN(this.vIndex)){
20549 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20555 dir = e.keyCode == 38 ? -1 : 1;
20557 this.vIndex = this.vIndex + dir * 4;
20559 if(this.vIndex < 0){
20563 if(this.vIndex > 11){
20567 if(isNaN(this.vIndex)){
20571 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20576 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20577 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20581 e.preventDefault();
20584 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20585 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20601 this.picker().remove();
20606 Roo.apply(Roo.bootstrap.MonthField, {
20625 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20626 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20631 Roo.apply(Roo.bootstrap.MonthField, {
20635 cls: 'datepicker dropdown-menu roo-dynamic',
20639 cls: 'datepicker-months',
20643 cls: 'table-condensed',
20645 Roo.bootstrap.DateField.content
20665 * @class Roo.bootstrap.CheckBox
20666 * @extends Roo.bootstrap.Input
20667 * Bootstrap CheckBox class
20669 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20670 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20671 * @cfg {String} boxLabel The text that appears beside the checkbox
20672 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20673 * @cfg {Boolean} checked initnal the element
20674 * @cfg {Boolean} inline inline the element (default false)
20675 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20676 * @cfg {String} tooltip label tooltip
20679 * Create a new CheckBox
20680 * @param {Object} config The config object
20683 Roo.bootstrap.CheckBox = function(config){
20684 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20689 * Fires when the element is checked or unchecked.
20690 * @param {Roo.bootstrap.CheckBox} this This input
20691 * @param {Boolean} checked The new checked value
20696 * Fires when the element is click.
20697 * @param {Roo.bootstrap.CheckBox} this This input
20704 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20706 inputType: 'checkbox',
20715 getAutoCreate : function()
20717 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20723 cfg.cls = 'form-group ' + this.inputType; //input-group
20726 cfg.cls += ' ' + this.inputType + '-inline';
20732 type : this.inputType,
20733 value : this.inputValue,
20734 cls : 'roo-' + this.inputType, //'form-box',
20735 placeholder : this.placeholder || ''
20739 if(this.inputType != 'radio'){
20743 cls : 'roo-hidden-value',
20744 value : this.checked ? this.inputValue : this.valueOff
20749 if (this.weight) { // Validity check?
20750 cfg.cls += " " + this.inputType + "-" + this.weight;
20753 if (this.disabled) {
20754 input.disabled=true;
20758 input.checked = this.checked;
20763 input.name = this.name;
20765 if(this.inputType != 'radio'){
20766 hidden.name = this.name;
20767 input.name = '_hidden_' + this.name;
20772 input.cls += ' input-' + this.size;
20777 ['xs','sm','md','lg'].map(function(size){
20778 if (settings[size]) {
20779 cfg.cls += ' col-' + size + '-' + settings[size];
20783 var inputblock = input;
20785 if (this.before || this.after) {
20788 cls : 'input-group',
20793 inputblock.cn.push({
20795 cls : 'input-group-addon',
20800 inputblock.cn.push(input);
20802 if(this.inputType != 'radio'){
20803 inputblock.cn.push(hidden);
20807 inputblock.cn.push({
20809 cls : 'input-group-addon',
20816 if (align ==='left' && this.fieldLabel.length) {
20817 // Roo.log("left and has label");
20822 cls : 'control-label',
20823 html : this.fieldLabel
20833 if(this.labelWidth > 12){
20834 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20837 if(this.labelWidth < 13 && this.labelmd == 0){
20838 this.labelmd = this.labelWidth;
20841 if(this.labellg > 0){
20842 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20843 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20846 if(this.labelmd > 0){
20847 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20848 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20851 if(this.labelsm > 0){
20852 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20853 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20856 if(this.labelxs > 0){
20857 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20858 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20861 } else if ( this.fieldLabel.length) {
20862 // Roo.log(" label");
20866 tag: this.boxLabel ? 'span' : 'label',
20868 cls: 'control-label box-input-label',
20869 //cls : 'input-group-addon',
20870 html : this.fieldLabel
20879 // Roo.log(" no label && no align");
20880 cfg.cn = [ inputblock ] ;
20886 var boxLabelCfg = {
20888 //'for': id, // box label is handled by onclick - so no for...
20890 html: this.boxLabel
20894 boxLabelCfg.tooltip = this.tooltip;
20897 cfg.cn.push(boxLabelCfg);
20900 if(this.inputType != 'radio'){
20901 cfg.cn.push(hidden);
20909 * return the real input element.
20911 inputEl: function ()
20913 return this.el.select('input.roo-' + this.inputType,true).first();
20915 hiddenEl: function ()
20917 return this.el.select('input.roo-hidden-value',true).first();
20920 labelEl: function()
20922 return this.el.select('label.control-label',true).first();
20924 /* depricated... */
20928 return this.labelEl();
20931 boxLabelEl: function()
20933 return this.el.select('label.box-label',true).first();
20936 initEvents : function()
20938 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20940 this.inputEl().on('click', this.onClick, this);
20942 if (this.boxLabel) {
20943 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20946 this.startValue = this.getValue();
20949 Roo.bootstrap.CheckBox.register(this);
20953 onClick : function(e)
20955 if(this.fireEvent('click', this, e) !== false){
20956 this.setChecked(!this.checked);
20961 setChecked : function(state,suppressEvent)
20963 this.startValue = this.getValue();
20965 if(this.inputType == 'radio'){
20967 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20968 e.dom.checked = false;
20971 this.inputEl().dom.checked = true;
20973 this.inputEl().dom.value = this.inputValue;
20975 if(suppressEvent !== true){
20976 this.fireEvent('check', this, true);
20984 this.checked = state;
20986 this.inputEl().dom.checked = state;
20989 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20991 if(suppressEvent !== true){
20992 this.fireEvent('check', this, state);
20998 getValue : function()
21000 if(this.inputType == 'radio'){
21001 return this.getGroupValue();
21004 return this.hiddenEl().dom.value;
21008 getGroupValue : function()
21010 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21014 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21017 setValue : function(v,suppressEvent)
21019 if(this.inputType == 'radio'){
21020 this.setGroupValue(v, suppressEvent);
21024 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21029 setGroupValue : function(v, suppressEvent)
21031 this.startValue = this.getValue();
21033 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21034 e.dom.checked = false;
21036 if(e.dom.value == v){
21037 e.dom.checked = true;
21041 if(suppressEvent !== true){
21042 this.fireEvent('check', this, true);
21050 validate : function()
21052 if(this.getVisibilityEl().hasClass('hidden')){
21058 (this.inputType == 'radio' && this.validateRadio()) ||
21059 (this.inputType == 'checkbox' && this.validateCheckbox())
21065 this.markInvalid();
21069 validateRadio : function()
21071 if(this.getVisibilityEl().hasClass('hidden')){
21075 if(this.allowBlank){
21081 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21082 if(!e.dom.checked){
21094 validateCheckbox : function()
21097 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21098 //return (this.getValue() == this.inputValue) ? true : false;
21101 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21109 for(var i in group){
21110 if(group[i].el.isVisible(true)){
21118 for(var i in group){
21123 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21130 * Mark this field as valid
21132 markValid : function()
21136 this.fireEvent('valid', this);
21138 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21141 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21148 if(this.inputType == 'radio'){
21149 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21151 e.findParent('.form-group', false, true).addClass(_this.validClass);
21158 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21159 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21163 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21169 for(var i in group){
21170 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21171 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21176 * Mark this field as invalid
21177 * @param {String} msg The validation message
21179 markInvalid : function(msg)
21181 if(this.allowBlank){
21187 this.fireEvent('invalid', this, msg);
21189 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21192 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21196 label.markInvalid();
21199 if(this.inputType == 'radio'){
21200 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21201 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21202 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21209 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21210 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21214 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21220 for(var i in group){
21221 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21222 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21227 clearInvalid : function()
21229 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21231 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21233 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21235 if (label && label.iconEl) {
21236 label.iconEl.removeClass(label.validClass);
21237 label.iconEl.removeClass(label.invalidClass);
21241 disable : function()
21243 if(this.inputType != 'radio'){
21244 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21251 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21252 _this.getActionEl().addClass(this.disabledClass);
21253 e.dom.disabled = true;
21257 this.disabled = true;
21258 this.fireEvent("disable", this);
21262 enable : function()
21264 if(this.inputType != 'radio'){
21265 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21272 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21273 _this.getActionEl().removeClass(this.disabledClass);
21274 e.dom.disabled = false;
21278 this.disabled = false;
21279 this.fireEvent("enable", this);
21283 setBoxLabel : function(v)
21288 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21294 Roo.apply(Roo.bootstrap.CheckBox, {
21299 * register a CheckBox Group
21300 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21302 register : function(checkbox)
21304 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21305 this.groups[checkbox.groupId] = {};
21308 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21312 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21316 * fetch a CheckBox Group based on the group ID
21317 * @param {string} the group ID
21318 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21320 get: function(groupId) {
21321 if (typeof(this.groups[groupId]) == 'undefined') {
21325 return this.groups[groupId] ;
21338 * @class Roo.bootstrap.Radio
21339 * @extends Roo.bootstrap.Component
21340 * Bootstrap Radio class
21341 * @cfg {String} boxLabel - the label associated
21342 * @cfg {String} value - the value of radio
21345 * Create a new Radio
21346 * @param {Object} config The config object
21348 Roo.bootstrap.Radio = function(config){
21349 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21353 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21359 getAutoCreate : function()
21363 cls : 'form-group radio',
21368 html : this.boxLabel
21376 initEvents : function()
21378 this.parent().register(this);
21380 this.el.on('click', this.onClick, this);
21384 onClick : function(e)
21386 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21387 this.setChecked(true);
21391 setChecked : function(state, suppressEvent)
21393 this.parent().setValue(this.value, suppressEvent);
21397 setBoxLabel : function(v)
21402 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21417 * @class Roo.bootstrap.SecurePass
21418 * @extends Roo.bootstrap.Input
21419 * Bootstrap SecurePass class
21423 * Create a new SecurePass
21424 * @param {Object} config The config object
21427 Roo.bootstrap.SecurePass = function (config) {
21428 // these go here, so the translation tool can replace them..
21430 PwdEmpty: "Please type a password, and then retype it to confirm.",
21431 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21432 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21433 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21434 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21435 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21436 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21437 TooWeak: "Your password is Too Weak."
21439 this.meterLabel = "Password strength:";
21440 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21441 this.meterClass = [
21442 "roo-password-meter-tooweak",
21443 "roo-password-meter-weak",
21444 "roo-password-meter-medium",
21445 "roo-password-meter-strong",
21446 "roo-password-meter-grey"
21451 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21454 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21456 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21458 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21459 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21460 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21461 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21462 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21463 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21464 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21474 * @cfg {String/Object} Label for the strength meter (defaults to
21475 * 'Password strength:')
21480 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21481 * ['Weak', 'Medium', 'Strong'])
21484 pwdStrengths: false,
21497 initEvents: function ()
21499 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21501 if (this.el.is('input[type=password]') && Roo.isSafari) {
21502 this.el.on('keydown', this.SafariOnKeyDown, this);
21505 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21508 onRender: function (ct, position)
21510 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21511 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21512 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21514 this.trigger.createChild({
21519 cls: 'roo-password-meter-grey col-xs-12',
21522 //width: this.meterWidth + 'px'
21526 cls: 'roo-password-meter-text'
21532 if (this.hideTrigger) {
21533 this.trigger.setDisplayed(false);
21535 this.setSize(this.width || '', this.height || '');
21538 onDestroy: function ()
21540 if (this.trigger) {
21541 this.trigger.removeAllListeners();
21542 this.trigger.remove();
21545 this.wrap.remove();
21547 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21550 checkStrength: function ()
21552 var pwd = this.inputEl().getValue();
21553 if (pwd == this._lastPwd) {
21558 if (this.ClientSideStrongPassword(pwd)) {
21560 } else if (this.ClientSideMediumPassword(pwd)) {
21562 } else if (this.ClientSideWeakPassword(pwd)) {
21568 Roo.log('strength1: ' + strength);
21570 //var pm = this.trigger.child('div/div/div').dom;
21571 var pm = this.trigger.child('div/div');
21572 pm.removeClass(this.meterClass);
21573 pm.addClass(this.meterClass[strength]);
21576 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21578 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21580 this._lastPwd = pwd;
21584 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21586 this._lastPwd = '';
21588 var pm = this.trigger.child('div/div');
21589 pm.removeClass(this.meterClass);
21590 pm.addClass('roo-password-meter-grey');
21593 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21596 this.inputEl().dom.type='password';
21599 validateValue: function (value)
21602 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21605 if (value.length == 0) {
21606 if (this.allowBlank) {
21607 this.clearInvalid();
21611 this.markInvalid(this.errors.PwdEmpty);
21612 this.errorMsg = this.errors.PwdEmpty;
21620 if ('[\x21-\x7e]*'.match(value)) {
21621 this.markInvalid(this.errors.PwdBadChar);
21622 this.errorMsg = this.errors.PwdBadChar;
21625 if (value.length < 6) {
21626 this.markInvalid(this.errors.PwdShort);
21627 this.errorMsg = this.errors.PwdShort;
21630 if (value.length > 16) {
21631 this.markInvalid(this.errors.PwdLong);
21632 this.errorMsg = this.errors.PwdLong;
21636 if (this.ClientSideStrongPassword(value)) {
21638 } else if (this.ClientSideMediumPassword(value)) {
21640 } else if (this.ClientSideWeakPassword(value)) {
21647 if (strength < 2) {
21648 //this.markInvalid(this.errors.TooWeak);
21649 this.errorMsg = this.errors.TooWeak;
21654 console.log('strength2: ' + strength);
21656 //var pm = this.trigger.child('div/div/div').dom;
21658 var pm = this.trigger.child('div/div');
21659 pm.removeClass(this.meterClass);
21660 pm.addClass(this.meterClass[strength]);
21662 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21664 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21666 this.errorMsg = '';
21670 CharacterSetChecks: function (type)
21673 this.fResult = false;
21676 isctype: function (character, type)
21679 case this.kCapitalLetter:
21680 if (character >= 'A' && character <= 'Z') {
21685 case this.kSmallLetter:
21686 if (character >= 'a' && character <= 'z') {
21692 if (character >= '0' && character <= '9') {
21697 case this.kPunctuation:
21698 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21709 IsLongEnough: function (pwd, size)
21711 return !(pwd == null || isNaN(size) || pwd.length < size);
21714 SpansEnoughCharacterSets: function (word, nb)
21716 if (!this.IsLongEnough(word, nb))
21721 var characterSetChecks = new Array(
21722 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21723 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21726 for (var index = 0; index < word.length; ++index) {
21727 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21728 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21729 characterSetChecks[nCharSet].fResult = true;
21736 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21737 if (characterSetChecks[nCharSet].fResult) {
21742 if (nCharSets < nb) {
21748 ClientSideStrongPassword: function (pwd)
21750 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21753 ClientSideMediumPassword: function (pwd)
21755 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21758 ClientSideWeakPassword: function (pwd)
21760 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21763 })//<script type="text/javascript">
21766 * Based Ext JS Library 1.1.1
21767 * Copyright(c) 2006-2007, Ext JS, LLC.
21773 * @class Roo.HtmlEditorCore
21774 * @extends Roo.Component
21775 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21777 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21780 Roo.HtmlEditorCore = function(config){
21783 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21788 * @event initialize
21789 * Fires when the editor is fully initialized (including the iframe)
21790 * @param {Roo.HtmlEditorCore} this
21795 * Fires when the editor is first receives the focus. Any insertion must wait
21796 * until after this event.
21797 * @param {Roo.HtmlEditorCore} this
21801 * @event beforesync
21802 * Fires before the textarea is updated with content from the editor iframe. Return false
21803 * to cancel the sync.
21804 * @param {Roo.HtmlEditorCore} this
21805 * @param {String} html
21809 * @event beforepush
21810 * Fires before the iframe editor is updated with content from the textarea. Return false
21811 * to cancel the push.
21812 * @param {Roo.HtmlEditorCore} this
21813 * @param {String} html
21818 * Fires when the textarea is updated with content from the editor iframe.
21819 * @param {Roo.HtmlEditorCore} this
21820 * @param {String} html
21825 * Fires when the iframe editor is updated with content from the textarea.
21826 * @param {Roo.HtmlEditorCore} this
21827 * @param {String} html
21832 * @event editorevent
21833 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21834 * @param {Roo.HtmlEditorCore} this
21840 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21842 // defaults : white / black...
21843 this.applyBlacklists();
21850 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21854 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21860 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21865 * @cfg {Number} height (in pixels)
21869 * @cfg {Number} width (in pixels)
21874 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21877 stylesheets: false,
21882 // private properties
21883 validationEvent : false,
21885 initialized : false,
21887 sourceEditMode : false,
21888 onFocus : Roo.emptyFn,
21890 hideMode:'offsets',
21894 // blacklist + whitelisted elements..
21901 * Protected method that will not generally be called directly. It
21902 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21903 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21905 getDocMarkup : function(){
21909 // inherit styels from page...??
21910 if (this.stylesheets === false) {
21912 Roo.get(document.head).select('style').each(function(node) {
21913 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21916 Roo.get(document.head).select('link').each(function(node) {
21917 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21920 } else if (!this.stylesheets.length) {
21922 st = '<style type="text/css">' +
21923 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21926 st = '<style type="text/css">' +
21931 st += '<style type="text/css">' +
21932 'IMG { cursor: pointer } ' +
21935 var cls = 'roo-htmleditor-body';
21937 if(this.bodyCls.length){
21938 cls += ' ' + this.bodyCls;
21941 return '<html><head>' + st +
21942 //<style type="text/css">' +
21943 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21945 ' </head><body class="' + cls + '"></body></html>';
21949 onRender : function(ct, position)
21952 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21953 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21956 this.el.dom.style.border = '0 none';
21957 this.el.dom.setAttribute('tabIndex', -1);
21958 this.el.addClass('x-hidden hide');
21962 if(Roo.isIE){ // fix IE 1px bogus margin
21963 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21967 this.frameId = Roo.id();
21971 var iframe = this.owner.wrap.createChild({
21973 cls: 'form-control', // bootstrap..
21975 name: this.frameId,
21976 frameBorder : 'no',
21977 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21982 this.iframe = iframe.dom;
21984 this.assignDocWin();
21986 this.doc.designMode = 'on';
21989 this.doc.write(this.getDocMarkup());
21993 var task = { // must defer to wait for browser to be ready
21995 //console.log("run task?" + this.doc.readyState);
21996 this.assignDocWin();
21997 if(this.doc.body || this.doc.readyState == 'complete'){
21999 this.doc.designMode="on";
22003 Roo.TaskMgr.stop(task);
22004 this.initEditor.defer(10, this);
22011 Roo.TaskMgr.start(task);
22016 onResize : function(w, h)
22018 Roo.log('resize: ' +w + ',' + h );
22019 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22023 if(typeof w == 'number'){
22025 this.iframe.style.width = w + 'px';
22027 if(typeof h == 'number'){
22029 this.iframe.style.height = h + 'px';
22031 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22038 * Toggles the editor between standard and source edit mode.
22039 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22041 toggleSourceEdit : function(sourceEditMode){
22043 this.sourceEditMode = sourceEditMode === true;
22045 if(this.sourceEditMode){
22047 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22050 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22051 //this.iframe.className = '';
22054 //this.setSize(this.owner.wrap.getSize());
22055 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22062 * Protected method that will not generally be called directly. If you need/want
22063 * custom HTML cleanup, this is the method you should override.
22064 * @param {String} html The HTML to be cleaned
22065 * return {String} The cleaned HTML
22067 cleanHtml : function(html){
22068 html = String(html);
22069 if(html.length > 5){
22070 if(Roo.isSafari){ // strip safari nonsense
22071 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22074 if(html == ' '){
22081 * HTML Editor -> Textarea
22082 * Protected method that will not generally be called directly. Syncs the contents
22083 * of the editor iframe with the textarea.
22085 syncValue : function(){
22086 if(this.initialized){
22087 var bd = (this.doc.body || this.doc.documentElement);
22088 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22089 var html = bd.innerHTML;
22091 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22092 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22094 html = '<div style="'+m[0]+'">' + html + '</div>';
22097 html = this.cleanHtml(html);
22098 // fix up the special chars.. normaly like back quotes in word...
22099 // however we do not want to do this with chinese..
22100 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22101 var cc = b.charCodeAt();
22103 (cc >= 0x4E00 && cc < 0xA000 ) ||
22104 (cc >= 0x3400 && cc < 0x4E00 ) ||
22105 (cc >= 0xf900 && cc < 0xfb00 )
22111 if(this.owner.fireEvent('beforesync', this, html) !== false){
22112 this.el.dom.value = html;
22113 this.owner.fireEvent('sync', this, html);
22119 * Protected method that will not generally be called directly. Pushes the value of the textarea
22120 * into the iframe editor.
22122 pushValue : function(){
22123 if(this.initialized){
22124 var v = this.el.dom.value.trim();
22126 // if(v.length < 1){
22130 if(this.owner.fireEvent('beforepush', this, v) !== false){
22131 var d = (this.doc.body || this.doc.documentElement);
22133 this.cleanUpPaste();
22134 this.el.dom.value = d.innerHTML;
22135 this.owner.fireEvent('push', this, v);
22141 deferFocus : function(){
22142 this.focus.defer(10, this);
22146 focus : function(){
22147 if(this.win && !this.sourceEditMode){
22154 assignDocWin: function()
22156 var iframe = this.iframe;
22159 this.doc = iframe.contentWindow.document;
22160 this.win = iframe.contentWindow;
22162 // if (!Roo.get(this.frameId)) {
22165 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22166 // this.win = Roo.get(this.frameId).dom.contentWindow;
22168 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22172 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22173 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22178 initEditor : function(){
22179 //console.log("INIT EDITOR");
22180 this.assignDocWin();
22184 this.doc.designMode="on";
22186 this.doc.write(this.getDocMarkup());
22189 var dbody = (this.doc.body || this.doc.documentElement);
22190 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22191 // this copies styles from the containing element into thsi one..
22192 // not sure why we need all of this..
22193 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22195 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22196 //ss['background-attachment'] = 'fixed'; // w3c
22197 dbody.bgProperties = 'fixed'; // ie
22198 //Roo.DomHelper.applyStyles(dbody, ss);
22199 Roo.EventManager.on(this.doc, {
22200 //'mousedown': this.onEditorEvent,
22201 'mouseup': this.onEditorEvent,
22202 'dblclick': this.onEditorEvent,
22203 'click': this.onEditorEvent,
22204 'keyup': this.onEditorEvent,
22209 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22211 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22212 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22214 this.initialized = true;
22216 this.owner.fireEvent('initialize', this);
22221 onDestroy : function(){
22227 //for (var i =0; i < this.toolbars.length;i++) {
22228 // // fixme - ask toolbars for heights?
22229 // this.toolbars[i].onDestroy();
22232 //this.wrap.dom.innerHTML = '';
22233 //this.wrap.remove();
22238 onFirstFocus : function(){
22240 this.assignDocWin();
22243 this.activated = true;
22246 if(Roo.isGecko){ // prevent silly gecko errors
22248 var s = this.win.getSelection();
22249 if(!s.focusNode || s.focusNode.nodeType != 3){
22250 var r = s.getRangeAt(0);
22251 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22256 this.execCmd('useCSS', true);
22257 this.execCmd('styleWithCSS', false);
22260 this.owner.fireEvent('activate', this);
22264 adjustFont: function(btn){
22265 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22266 //if(Roo.isSafari){ // safari
22269 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22270 if(Roo.isSafari){ // safari
22271 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22272 v = (v < 10) ? 10 : v;
22273 v = (v > 48) ? 48 : v;
22274 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22279 v = Math.max(1, v+adjust);
22281 this.execCmd('FontSize', v );
22284 onEditorEvent : function(e)
22286 this.owner.fireEvent('editorevent', this, e);
22287 // this.updateToolbar();
22288 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22291 insertTag : function(tg)
22293 // could be a bit smarter... -> wrap the current selected tRoo..
22294 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22296 range = this.createRange(this.getSelection());
22297 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22298 wrappingNode.appendChild(range.extractContents());
22299 range.insertNode(wrappingNode);
22306 this.execCmd("formatblock", tg);
22310 insertText : function(txt)
22314 var range = this.createRange();
22315 range.deleteContents();
22316 //alert(Sender.getAttribute('label'));
22318 range.insertNode(this.doc.createTextNode(txt));
22324 * Executes a Midas editor command on the editor document and performs necessary focus and
22325 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22326 * @param {String} cmd The Midas command
22327 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22329 relayCmd : function(cmd, value){
22331 this.execCmd(cmd, value);
22332 this.owner.fireEvent('editorevent', this);
22333 //this.updateToolbar();
22334 this.owner.deferFocus();
22338 * Executes a Midas editor command directly on the editor document.
22339 * For visual commands, you should use {@link #relayCmd} instead.
22340 * <b>This should only be called after the editor is initialized.</b>
22341 * @param {String} cmd The Midas command
22342 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22344 execCmd : function(cmd, value){
22345 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22352 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22354 * @param {String} text | dom node..
22356 insertAtCursor : function(text)
22359 if(!this.activated){
22365 var r = this.doc.selection.createRange();
22376 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22380 // from jquery ui (MIT licenced)
22382 var win = this.win;
22384 if (win.getSelection && win.getSelection().getRangeAt) {
22385 range = win.getSelection().getRangeAt(0);
22386 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22387 range.insertNode(node);
22388 } else if (win.document.selection && win.document.selection.createRange) {
22389 // no firefox support
22390 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22391 win.document.selection.createRange().pasteHTML(txt);
22393 // no firefox support
22394 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22395 this.execCmd('InsertHTML', txt);
22404 mozKeyPress : function(e){
22406 var c = e.getCharCode(), cmd;
22409 c = String.fromCharCode(c).toLowerCase();
22423 this.cleanUpPaste.defer(100, this);
22431 e.preventDefault();
22439 fixKeys : function(){ // load time branching for fastest keydown performance
22441 return function(e){
22442 var k = e.getKey(), r;
22445 r = this.doc.selection.createRange();
22448 r.pasteHTML('    ');
22455 r = this.doc.selection.createRange();
22457 var target = r.parentElement();
22458 if(!target || target.tagName.toLowerCase() != 'li'){
22460 r.pasteHTML('<br />');
22466 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22467 this.cleanUpPaste.defer(100, this);
22473 }else if(Roo.isOpera){
22474 return function(e){
22475 var k = e.getKey();
22479 this.execCmd('InsertHTML','    ');
22482 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22483 this.cleanUpPaste.defer(100, this);
22488 }else if(Roo.isSafari){
22489 return function(e){
22490 var k = e.getKey();
22494 this.execCmd('InsertText','\t');
22498 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22499 this.cleanUpPaste.defer(100, this);
22507 getAllAncestors: function()
22509 var p = this.getSelectedNode();
22512 a.push(p); // push blank onto stack..
22513 p = this.getParentElement();
22517 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22521 a.push(this.doc.body);
22525 lastSelNode : false,
22528 getSelection : function()
22530 this.assignDocWin();
22531 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22534 getSelectedNode: function()
22536 // this may only work on Gecko!!!
22538 // should we cache this!!!!
22543 var range = this.createRange(this.getSelection()).cloneRange();
22546 var parent = range.parentElement();
22548 var testRange = range.duplicate();
22549 testRange.moveToElementText(parent);
22550 if (testRange.inRange(range)) {
22553 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22556 parent = parent.parentElement;
22561 // is ancestor a text element.
22562 var ac = range.commonAncestorContainer;
22563 if (ac.nodeType == 3) {
22564 ac = ac.parentNode;
22567 var ar = ac.childNodes;
22570 var other_nodes = [];
22571 var has_other_nodes = false;
22572 for (var i=0;i<ar.length;i++) {
22573 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22576 // fullly contained node.
22578 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22583 // probably selected..
22584 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22585 other_nodes.push(ar[i]);
22589 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22594 has_other_nodes = true;
22596 if (!nodes.length && other_nodes.length) {
22597 nodes= other_nodes;
22599 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22605 createRange: function(sel)
22607 // this has strange effects when using with
22608 // top toolbar - not sure if it's a great idea.
22609 //this.editor.contentWindow.focus();
22610 if (typeof sel != "undefined") {
22612 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22614 return this.doc.createRange();
22617 return this.doc.createRange();
22620 getParentElement: function()
22623 this.assignDocWin();
22624 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22626 var range = this.createRange(sel);
22629 var p = range.commonAncestorContainer;
22630 while (p.nodeType == 3) { // text node
22641 * Range intersection.. the hard stuff...
22645 * [ -- selected range --- ]
22649 * if end is before start or hits it. fail.
22650 * if start is after end or hits it fail.
22652 * if either hits (but other is outside. - then it's not
22658 // @see http://www.thismuchiknow.co.uk/?p=64.
22659 rangeIntersectsNode : function(range, node)
22661 var nodeRange = node.ownerDocument.createRange();
22663 nodeRange.selectNode(node);
22665 nodeRange.selectNodeContents(node);
22668 var rangeStartRange = range.cloneRange();
22669 rangeStartRange.collapse(true);
22671 var rangeEndRange = range.cloneRange();
22672 rangeEndRange.collapse(false);
22674 var nodeStartRange = nodeRange.cloneRange();
22675 nodeStartRange.collapse(true);
22677 var nodeEndRange = nodeRange.cloneRange();
22678 nodeEndRange.collapse(false);
22680 return rangeStartRange.compareBoundaryPoints(
22681 Range.START_TO_START, nodeEndRange) == -1 &&
22682 rangeEndRange.compareBoundaryPoints(
22683 Range.START_TO_START, nodeStartRange) == 1;
22687 rangeCompareNode : function(range, node)
22689 var nodeRange = node.ownerDocument.createRange();
22691 nodeRange.selectNode(node);
22693 nodeRange.selectNodeContents(node);
22697 range.collapse(true);
22699 nodeRange.collapse(true);
22701 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22702 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22704 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22706 var nodeIsBefore = ss == 1;
22707 var nodeIsAfter = ee == -1;
22709 if (nodeIsBefore && nodeIsAfter) {
22712 if (!nodeIsBefore && nodeIsAfter) {
22713 return 1; //right trailed.
22716 if (nodeIsBefore && !nodeIsAfter) {
22717 return 2; // left trailed.
22723 // private? - in a new class?
22724 cleanUpPaste : function()
22726 // cleans up the whole document..
22727 Roo.log('cleanuppaste');
22729 this.cleanUpChildren(this.doc.body);
22730 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22731 if (clean != this.doc.body.innerHTML) {
22732 this.doc.body.innerHTML = clean;
22737 cleanWordChars : function(input) {// change the chars to hex code
22738 var he = Roo.HtmlEditorCore;
22740 var output = input;
22741 Roo.each(he.swapCodes, function(sw) {
22742 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22744 output = output.replace(swapper, sw[1]);
22751 cleanUpChildren : function (n)
22753 if (!n.childNodes.length) {
22756 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22757 this.cleanUpChild(n.childNodes[i]);
22764 cleanUpChild : function (node)
22767 //console.log(node);
22768 if (node.nodeName == "#text") {
22769 // clean up silly Windows -- stuff?
22772 if (node.nodeName == "#comment") {
22773 node.parentNode.removeChild(node);
22774 // clean up silly Windows -- stuff?
22777 var lcname = node.tagName.toLowerCase();
22778 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22779 // whitelist of tags..
22781 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22783 node.parentNode.removeChild(node);
22788 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22790 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22791 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22793 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22794 // remove_keep_children = true;
22797 if (remove_keep_children) {
22798 this.cleanUpChildren(node);
22799 // inserts everything just before this node...
22800 while (node.childNodes.length) {
22801 var cn = node.childNodes[0];
22802 node.removeChild(cn);
22803 node.parentNode.insertBefore(cn, node);
22805 node.parentNode.removeChild(node);
22809 if (!node.attributes || !node.attributes.length) {
22810 this.cleanUpChildren(node);
22814 function cleanAttr(n,v)
22817 if (v.match(/^\./) || v.match(/^\//)) {
22820 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22823 if (v.match(/^#/)) {
22826 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22827 node.removeAttribute(n);
22831 var cwhite = this.cwhite;
22832 var cblack = this.cblack;
22834 function cleanStyle(n,v)
22836 if (v.match(/expression/)) { //XSS?? should we even bother..
22837 node.removeAttribute(n);
22841 var parts = v.split(/;/);
22844 Roo.each(parts, function(p) {
22845 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22849 var l = p.split(':').shift().replace(/\s+/g,'');
22850 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22852 if ( cwhite.length && cblack.indexOf(l) > -1) {
22853 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22854 //node.removeAttribute(n);
22858 // only allow 'c whitelisted system attributes'
22859 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22860 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22861 //node.removeAttribute(n);
22871 if (clean.length) {
22872 node.setAttribute(n, clean.join(';'));
22874 node.removeAttribute(n);
22880 for (var i = node.attributes.length-1; i > -1 ; i--) {
22881 var a = node.attributes[i];
22884 if (a.name.toLowerCase().substr(0,2)=='on') {
22885 node.removeAttribute(a.name);
22888 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22889 node.removeAttribute(a.name);
22892 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22893 cleanAttr(a.name,a.value); // fixme..
22896 if (a.name == 'style') {
22897 cleanStyle(a.name,a.value);
22900 /// clean up MS crap..
22901 // tecnically this should be a list of valid class'es..
22904 if (a.name == 'class') {
22905 if (a.value.match(/^Mso/)) {
22906 node.className = '';
22909 if (a.value.match(/^body$/)) {
22910 node.className = '';
22921 this.cleanUpChildren(node);
22927 * Clean up MS wordisms...
22929 cleanWord : function(node)
22934 this.cleanWord(this.doc.body);
22937 if (node.nodeName == "#text") {
22938 // clean up silly Windows -- stuff?
22941 if (node.nodeName == "#comment") {
22942 node.parentNode.removeChild(node);
22943 // clean up silly Windows -- stuff?
22947 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22948 node.parentNode.removeChild(node);
22952 // remove - but keep children..
22953 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22954 while (node.childNodes.length) {
22955 var cn = node.childNodes[0];
22956 node.removeChild(cn);
22957 node.parentNode.insertBefore(cn, node);
22959 node.parentNode.removeChild(node);
22960 this.iterateChildren(node, this.cleanWord);
22964 if (node.className.length) {
22966 var cn = node.className.split(/\W+/);
22968 Roo.each(cn, function(cls) {
22969 if (cls.match(/Mso[a-zA-Z]+/)) {
22974 node.className = cna.length ? cna.join(' ') : '';
22976 node.removeAttribute("class");
22980 if (node.hasAttribute("lang")) {
22981 node.removeAttribute("lang");
22984 if (node.hasAttribute("style")) {
22986 var styles = node.getAttribute("style").split(";");
22988 Roo.each(styles, function(s) {
22989 if (!s.match(/:/)) {
22992 var kv = s.split(":");
22993 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22996 // what ever is left... we allow.
22999 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23000 if (!nstyle.length) {
23001 node.removeAttribute('style');
23004 this.iterateChildren(node, this.cleanWord);
23010 * iterateChildren of a Node, calling fn each time, using this as the scole..
23011 * @param {DomNode} node node to iterate children of.
23012 * @param {Function} fn method of this class to call on each item.
23014 iterateChildren : function(node, fn)
23016 if (!node.childNodes.length) {
23019 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23020 fn.call(this, node.childNodes[i])
23026 * cleanTableWidths.
23028 * Quite often pasting from word etc.. results in tables with column and widths.
23029 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23032 cleanTableWidths : function(node)
23037 this.cleanTableWidths(this.doc.body);
23042 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23045 Roo.log(node.tagName);
23046 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23047 this.iterateChildren(node, this.cleanTableWidths);
23050 if (node.hasAttribute('width')) {
23051 node.removeAttribute('width');
23055 if (node.hasAttribute("style")) {
23058 var styles = node.getAttribute("style").split(";");
23060 Roo.each(styles, function(s) {
23061 if (!s.match(/:/)) {
23064 var kv = s.split(":");
23065 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23068 // what ever is left... we allow.
23071 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23072 if (!nstyle.length) {
23073 node.removeAttribute('style');
23077 this.iterateChildren(node, this.cleanTableWidths);
23085 domToHTML : function(currentElement, depth, nopadtext) {
23087 depth = depth || 0;
23088 nopadtext = nopadtext || false;
23090 if (!currentElement) {
23091 return this.domToHTML(this.doc.body);
23094 //Roo.log(currentElement);
23096 var allText = false;
23097 var nodeName = currentElement.nodeName;
23098 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23100 if (nodeName == '#text') {
23102 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23107 if (nodeName != 'BODY') {
23110 // Prints the node tagName, such as <A>, <IMG>, etc
23113 for(i = 0; i < currentElement.attributes.length;i++) {
23115 var aname = currentElement.attributes.item(i).name;
23116 if (!currentElement.attributes.item(i).value.length) {
23119 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23122 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23131 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23134 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23139 // Traverse the tree
23141 var currentElementChild = currentElement.childNodes.item(i);
23142 var allText = true;
23143 var innerHTML = '';
23145 while (currentElementChild) {
23146 // Formatting code (indent the tree so it looks nice on the screen)
23147 var nopad = nopadtext;
23148 if (lastnode == 'SPAN') {
23152 if (currentElementChild.nodeName == '#text') {
23153 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23154 toadd = nopadtext ? toadd : toadd.trim();
23155 if (!nopad && toadd.length > 80) {
23156 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23158 innerHTML += toadd;
23161 currentElementChild = currentElement.childNodes.item(i);
23167 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23169 // Recursively traverse the tree structure of the child node
23170 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23171 lastnode = currentElementChild.nodeName;
23173 currentElementChild=currentElement.childNodes.item(i);
23179 // The remaining code is mostly for formatting the tree
23180 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23185 ret+= "</"+tagName+">";
23191 applyBlacklists : function()
23193 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23194 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23198 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23199 if (b.indexOf(tag) > -1) {
23202 this.white.push(tag);
23206 Roo.each(w, function(tag) {
23207 if (b.indexOf(tag) > -1) {
23210 if (this.white.indexOf(tag) > -1) {
23213 this.white.push(tag);
23218 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23219 if (w.indexOf(tag) > -1) {
23222 this.black.push(tag);
23226 Roo.each(b, function(tag) {
23227 if (w.indexOf(tag) > -1) {
23230 if (this.black.indexOf(tag) > -1) {
23233 this.black.push(tag);
23238 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23239 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23243 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23244 if (b.indexOf(tag) > -1) {
23247 this.cwhite.push(tag);
23251 Roo.each(w, function(tag) {
23252 if (b.indexOf(tag) > -1) {
23255 if (this.cwhite.indexOf(tag) > -1) {
23258 this.cwhite.push(tag);
23263 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23264 if (w.indexOf(tag) > -1) {
23267 this.cblack.push(tag);
23271 Roo.each(b, function(tag) {
23272 if (w.indexOf(tag) > -1) {
23275 if (this.cblack.indexOf(tag) > -1) {
23278 this.cblack.push(tag);
23283 setStylesheets : function(stylesheets)
23285 if(typeof(stylesheets) == 'string'){
23286 Roo.get(this.iframe.contentDocument.head).createChild({
23288 rel : 'stylesheet',
23297 Roo.each(stylesheets, function(s) {
23302 Roo.get(_this.iframe.contentDocument.head).createChild({
23304 rel : 'stylesheet',
23313 removeStylesheets : function()
23317 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23322 setStyle : function(style)
23324 Roo.get(this.iframe.contentDocument.head).createChild({
23333 // hide stuff that is not compatible
23347 * @event specialkey
23351 * @cfg {String} fieldClass @hide
23354 * @cfg {String} focusClass @hide
23357 * @cfg {String} autoCreate @hide
23360 * @cfg {String} inputType @hide
23363 * @cfg {String} invalidClass @hide
23366 * @cfg {String} invalidText @hide
23369 * @cfg {String} msgFx @hide
23372 * @cfg {String} validateOnBlur @hide
23376 Roo.HtmlEditorCore.white = [
23377 'area', 'br', 'img', 'input', 'hr', 'wbr',
23379 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23380 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23381 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23382 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23383 'table', 'ul', 'xmp',
23385 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23388 'dir', 'menu', 'ol', 'ul', 'dl',
23394 Roo.HtmlEditorCore.black = [
23395 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23397 'base', 'basefont', 'bgsound', 'blink', 'body',
23398 'frame', 'frameset', 'head', 'html', 'ilayer',
23399 'iframe', 'layer', 'link', 'meta', 'object',
23400 'script', 'style' ,'title', 'xml' // clean later..
23402 Roo.HtmlEditorCore.clean = [
23403 'script', 'style', 'title', 'xml'
23405 Roo.HtmlEditorCore.remove = [
23410 Roo.HtmlEditorCore.ablack = [
23414 Roo.HtmlEditorCore.aclean = [
23415 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23419 Roo.HtmlEditorCore.pwhite= [
23420 'http', 'https', 'mailto'
23423 // white listed style attributes.
23424 Roo.HtmlEditorCore.cwhite= [
23425 // 'text-align', /// default is to allow most things..
23431 // black listed style attributes.
23432 Roo.HtmlEditorCore.cblack= [
23433 // 'font-size' -- this can be set by the project
23437 Roo.HtmlEditorCore.swapCodes =[
23456 * @class Roo.bootstrap.HtmlEditor
23457 * @extends Roo.bootstrap.TextArea
23458 * Bootstrap HtmlEditor class
23461 * Create a new HtmlEditor
23462 * @param {Object} config The config object
23465 Roo.bootstrap.HtmlEditor = function(config){
23466 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23467 if (!this.toolbars) {
23468 this.toolbars = [];
23471 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23474 * @event initialize
23475 * Fires when the editor is fully initialized (including the iframe)
23476 * @param {HtmlEditor} this
23481 * Fires when the editor is first receives the focus. Any insertion must wait
23482 * until after this event.
23483 * @param {HtmlEditor} this
23487 * @event beforesync
23488 * Fires before the textarea is updated with content from the editor iframe. Return false
23489 * to cancel the sync.
23490 * @param {HtmlEditor} this
23491 * @param {String} html
23495 * @event beforepush
23496 * Fires before the iframe editor is updated with content from the textarea. Return false
23497 * to cancel the push.
23498 * @param {HtmlEditor} this
23499 * @param {String} html
23504 * Fires when the textarea is updated with content from the editor iframe.
23505 * @param {HtmlEditor} this
23506 * @param {String} html
23511 * Fires when the iframe editor is updated with content from the textarea.
23512 * @param {HtmlEditor} this
23513 * @param {String} html
23517 * @event editmodechange
23518 * Fires when the editor switches edit modes
23519 * @param {HtmlEditor} this
23520 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23522 editmodechange: true,
23524 * @event editorevent
23525 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23526 * @param {HtmlEditor} this
23530 * @event firstfocus
23531 * Fires when on first focus - needed by toolbars..
23532 * @param {HtmlEditor} this
23537 * Auto save the htmlEditor value as a file into Events
23538 * @param {HtmlEditor} this
23542 * @event savedpreview
23543 * preview the saved version of htmlEditor
23544 * @param {HtmlEditor} this
23551 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23555 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23560 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23565 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23570 * @cfg {Number} height (in pixels)
23574 * @cfg {Number} width (in pixels)
23579 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23582 stylesheets: false,
23587 // private properties
23588 validationEvent : false,
23590 initialized : false,
23593 onFocus : Roo.emptyFn,
23595 hideMode:'offsets',
23597 tbContainer : false,
23601 toolbarContainer :function() {
23602 return this.wrap.select('.x-html-editor-tb',true).first();
23606 * Protected method that will not generally be called directly. It
23607 * is called when the editor creates its toolbar. Override this method if you need to
23608 * add custom toolbar buttons.
23609 * @param {HtmlEditor} editor
23611 createToolbar : function(){
23612 Roo.log('renewing');
23613 Roo.log("create toolbars");
23615 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23616 this.toolbars[0].render(this.toolbarContainer());
23620 // if (!editor.toolbars || !editor.toolbars.length) {
23621 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23624 // for (var i =0 ; i < editor.toolbars.length;i++) {
23625 // editor.toolbars[i] = Roo.factory(
23626 // typeof(editor.toolbars[i]) == 'string' ?
23627 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23628 // Roo.bootstrap.HtmlEditor);
23629 // editor.toolbars[i].init(editor);
23635 onRender : function(ct, position)
23637 // Roo.log("Call onRender: " + this.xtype);
23639 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23641 this.wrap = this.inputEl().wrap({
23642 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23645 this.editorcore.onRender(ct, position);
23647 if (this.resizable) {
23648 this.resizeEl = new Roo.Resizable(this.wrap, {
23652 minHeight : this.height,
23653 height: this.height,
23654 handles : this.resizable,
23657 resize : function(r, w, h) {
23658 _t.onResize(w,h); // -something
23664 this.createToolbar(this);
23667 if(!this.width && this.resizable){
23668 this.setSize(this.wrap.getSize());
23670 if (this.resizeEl) {
23671 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23672 // should trigger onReize..
23678 onResize : function(w, h)
23680 Roo.log('resize: ' +w + ',' + h );
23681 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23685 if(this.inputEl() ){
23686 if(typeof w == 'number'){
23687 var aw = w - this.wrap.getFrameWidth('lr');
23688 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23691 if(typeof h == 'number'){
23692 var tbh = -11; // fixme it needs to tool bar size!
23693 for (var i =0; i < this.toolbars.length;i++) {
23694 // fixme - ask toolbars for heights?
23695 tbh += this.toolbars[i].el.getHeight();
23696 //if (this.toolbars[i].footer) {
23697 // tbh += this.toolbars[i].footer.el.getHeight();
23705 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23706 ah -= 5; // knock a few pixes off for look..
23707 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23711 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23712 this.editorcore.onResize(ew,eh);
23717 * Toggles the editor between standard and source edit mode.
23718 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23720 toggleSourceEdit : function(sourceEditMode)
23722 this.editorcore.toggleSourceEdit(sourceEditMode);
23724 if(this.editorcore.sourceEditMode){
23725 Roo.log('editor - showing textarea');
23728 // Roo.log(this.syncValue());
23730 this.inputEl().removeClass(['hide', 'x-hidden']);
23731 this.inputEl().dom.removeAttribute('tabIndex');
23732 this.inputEl().focus();
23734 Roo.log('editor - hiding textarea');
23736 // Roo.log(this.pushValue());
23739 this.inputEl().addClass(['hide', 'x-hidden']);
23740 this.inputEl().dom.setAttribute('tabIndex', -1);
23741 //this.deferFocus();
23744 if(this.resizable){
23745 this.setSize(this.wrap.getSize());
23748 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23751 // private (for BoxComponent)
23752 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23754 // private (for BoxComponent)
23755 getResizeEl : function(){
23759 // private (for BoxComponent)
23760 getPositionEl : function(){
23765 initEvents : function(){
23766 this.originalValue = this.getValue();
23770 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23773 // markInvalid : Roo.emptyFn,
23775 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23778 // clearInvalid : Roo.emptyFn,
23780 setValue : function(v){
23781 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23782 this.editorcore.pushValue();
23787 deferFocus : function(){
23788 this.focus.defer(10, this);
23792 focus : function(){
23793 this.editorcore.focus();
23799 onDestroy : function(){
23805 for (var i =0; i < this.toolbars.length;i++) {
23806 // fixme - ask toolbars for heights?
23807 this.toolbars[i].onDestroy();
23810 this.wrap.dom.innerHTML = '';
23811 this.wrap.remove();
23816 onFirstFocus : function(){
23817 //Roo.log("onFirstFocus");
23818 this.editorcore.onFirstFocus();
23819 for (var i =0; i < this.toolbars.length;i++) {
23820 this.toolbars[i].onFirstFocus();
23826 syncValue : function()
23828 this.editorcore.syncValue();
23831 pushValue : function()
23833 this.editorcore.pushValue();
23837 // hide stuff that is not compatible
23851 * @event specialkey
23855 * @cfg {String} fieldClass @hide
23858 * @cfg {String} focusClass @hide
23861 * @cfg {String} autoCreate @hide
23864 * @cfg {String} inputType @hide
23867 * @cfg {String} invalidClass @hide
23870 * @cfg {String} invalidText @hide
23873 * @cfg {String} msgFx @hide
23876 * @cfg {String} validateOnBlur @hide
23885 Roo.namespace('Roo.bootstrap.htmleditor');
23887 * @class Roo.bootstrap.HtmlEditorToolbar1
23892 new Roo.bootstrap.HtmlEditor({
23895 new Roo.bootstrap.HtmlEditorToolbar1({
23896 disable : { fonts: 1 , format: 1, ..., ... , ...],
23902 * @cfg {Object} disable List of elements to disable..
23903 * @cfg {Array} btns List of additional buttons.
23907 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23910 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23913 Roo.apply(this, config);
23915 // default disabled, based on 'good practice'..
23916 this.disable = this.disable || {};
23917 Roo.applyIf(this.disable, {
23920 specialElements : true
23922 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23924 this.editor = config.editor;
23925 this.editorcore = config.editor.editorcore;
23927 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23929 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23930 // dont call parent... till later.
23932 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23937 editorcore : false,
23942 "h1","h2","h3","h4","h5","h6",
23944 "abbr", "acronym", "address", "cite", "samp", "var",
23948 onRender : function(ct, position)
23950 // Roo.log("Call onRender: " + this.xtype);
23952 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23954 this.el.dom.style.marginBottom = '0';
23956 var editorcore = this.editorcore;
23957 var editor= this.editor;
23960 var btn = function(id,cmd , toggle, handler, html){
23962 var event = toggle ? 'toggle' : 'click';
23967 xns: Roo.bootstrap,
23970 enableToggle:toggle !== false,
23972 pressed : toggle ? false : null,
23975 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23976 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23982 // var cb_box = function...
23987 xns: Roo.bootstrap,
23988 glyphicon : 'font',
23992 xns: Roo.bootstrap,
23996 Roo.each(this.formats, function(f) {
23997 style.menu.items.push({
23999 xns: Roo.bootstrap,
24000 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24005 editorcore.insertTag(this.tagname);
24012 children.push(style);
24014 btn('bold',false,true);
24015 btn('italic',false,true);
24016 btn('align-left', 'justifyleft',true);
24017 btn('align-center', 'justifycenter',true);
24018 btn('align-right' , 'justifyright',true);
24019 btn('link', false, false, function(btn) {
24020 //Roo.log("create link?");
24021 var url = prompt(this.createLinkText, this.defaultLinkValue);
24022 if(url && url != 'http:/'+'/'){
24023 this.editorcore.relayCmd('createlink', url);
24026 btn('list','insertunorderedlist',true);
24027 btn('pencil', false,true, function(btn){
24029 this.toggleSourceEdit(btn.pressed);
24032 if (this.editor.btns.length > 0) {
24033 for (var i = 0; i<this.editor.btns.length; i++) {
24034 children.push(this.editor.btns[i]);
24042 xns: Roo.bootstrap,
24047 xns: Roo.bootstrap,
24052 cog.menu.items.push({
24054 xns: Roo.bootstrap,
24055 html : Clean styles,
24060 editorcore.insertTag(this.tagname);
24069 this.xtype = 'NavSimplebar';
24071 for(var i=0;i< children.length;i++) {
24073 this.buttons.add(this.addxtypeChild(children[i]));
24077 editor.on('editorevent', this.updateToolbar, this);
24079 onBtnClick : function(id)
24081 this.editorcore.relayCmd(id);
24082 this.editorcore.focus();
24086 * Protected method that will not generally be called directly. It triggers
24087 * a toolbar update by reading the markup state of the current selection in the editor.
24089 updateToolbar: function(){
24091 if(!this.editorcore.activated){
24092 this.editor.onFirstFocus(); // is this neeed?
24096 var btns = this.buttons;
24097 var doc = this.editorcore.doc;
24098 btns.get('bold').setActive(doc.queryCommandState('bold'));
24099 btns.get('italic').setActive(doc.queryCommandState('italic'));
24100 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24102 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24103 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24104 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24106 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24107 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24110 var ans = this.editorcore.getAllAncestors();
24111 if (this.formatCombo) {
24114 var store = this.formatCombo.store;
24115 this.formatCombo.setValue("");
24116 for (var i =0; i < ans.length;i++) {
24117 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24119 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24127 // hides menus... - so this cant be on a menu...
24128 Roo.bootstrap.MenuMgr.hideAll();
24130 Roo.bootstrap.MenuMgr.hideAll();
24131 //this.editorsyncValue();
24133 onFirstFocus: function() {
24134 this.buttons.each(function(item){
24138 toggleSourceEdit : function(sourceEditMode){
24141 if(sourceEditMode){
24142 Roo.log("disabling buttons");
24143 this.buttons.each( function(item){
24144 if(item.cmd != 'pencil'){
24150 Roo.log("enabling buttons");
24151 if(this.editorcore.initialized){
24152 this.buttons.each( function(item){
24158 Roo.log("calling toggole on editor");
24159 // tell the editor that it's been pressed..
24160 this.editor.toggleSourceEdit(sourceEditMode);
24170 * @class Roo.bootstrap.Table.AbstractSelectionModel
24171 * @extends Roo.util.Observable
24172 * Abstract base class for grid SelectionModels. It provides the interface that should be
24173 * implemented by descendant classes. This class should not be directly instantiated.
24176 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24177 this.locked = false;
24178 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24182 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24183 /** @ignore Called by the grid automatically. Do not call directly. */
24184 init : function(grid){
24190 * Locks the selections.
24193 this.locked = true;
24197 * Unlocks the selections.
24199 unlock : function(){
24200 this.locked = false;
24204 * Returns true if the selections are locked.
24205 * @return {Boolean}
24207 isLocked : function(){
24208 return this.locked;
24212 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24213 * @class Roo.bootstrap.Table.RowSelectionModel
24214 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24215 * It supports multiple selections and keyboard selection/navigation.
24217 * @param {Object} config
24220 Roo.bootstrap.Table.RowSelectionModel = function(config){
24221 Roo.apply(this, config);
24222 this.selections = new Roo.util.MixedCollection(false, function(o){
24227 this.lastActive = false;
24231 * @event selectionchange
24232 * Fires when the selection changes
24233 * @param {SelectionModel} this
24235 "selectionchange" : true,
24237 * @event afterselectionchange
24238 * Fires after the selection changes (eg. by key press or clicking)
24239 * @param {SelectionModel} this
24241 "afterselectionchange" : true,
24243 * @event beforerowselect
24244 * Fires when a row is selected being selected, return false to cancel.
24245 * @param {SelectionModel} this
24246 * @param {Number} rowIndex The selected index
24247 * @param {Boolean} keepExisting False if other selections will be cleared
24249 "beforerowselect" : true,
24252 * Fires when a row is selected.
24253 * @param {SelectionModel} this
24254 * @param {Number} rowIndex The selected index
24255 * @param {Roo.data.Record} r The record
24257 "rowselect" : true,
24259 * @event rowdeselect
24260 * Fires when a row is deselected.
24261 * @param {SelectionModel} this
24262 * @param {Number} rowIndex The selected index
24264 "rowdeselect" : true
24266 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24267 this.locked = false;
24270 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24272 * @cfg {Boolean} singleSelect
24273 * True to allow selection of only one row at a time (defaults to false)
24275 singleSelect : false,
24278 initEvents : function()
24281 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24282 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24283 //}else{ // allow click to work like normal
24284 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24286 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24287 this.grid.on("rowclick", this.handleMouseDown, this);
24289 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24290 "up" : function(e){
24292 this.selectPrevious(e.shiftKey);
24293 }else if(this.last !== false && this.lastActive !== false){
24294 var last = this.last;
24295 this.selectRange(this.last, this.lastActive-1);
24296 this.grid.getView().focusRow(this.lastActive);
24297 if(last !== false){
24301 this.selectFirstRow();
24303 this.fireEvent("afterselectionchange", this);
24305 "down" : function(e){
24307 this.selectNext(e.shiftKey);
24308 }else if(this.last !== false && this.lastActive !== false){
24309 var last = this.last;
24310 this.selectRange(this.last, this.lastActive+1);
24311 this.grid.getView().focusRow(this.lastActive);
24312 if(last !== false){
24316 this.selectFirstRow();
24318 this.fireEvent("afterselectionchange", this);
24322 this.grid.store.on('load', function(){
24323 this.selections.clear();
24326 var view = this.grid.view;
24327 view.on("refresh", this.onRefresh, this);
24328 view.on("rowupdated", this.onRowUpdated, this);
24329 view.on("rowremoved", this.onRemove, this);
24334 onRefresh : function()
24336 var ds = this.grid.store, i, v = this.grid.view;
24337 var s = this.selections;
24338 s.each(function(r){
24339 if((i = ds.indexOfId(r.id)) != -1){
24348 onRemove : function(v, index, r){
24349 this.selections.remove(r);
24353 onRowUpdated : function(v, index, r){
24354 if(this.isSelected(r)){
24355 v.onRowSelect(index);
24361 * @param {Array} records The records to select
24362 * @param {Boolean} keepExisting (optional) True to keep existing selections
24364 selectRecords : function(records, keepExisting)
24367 this.clearSelections();
24369 var ds = this.grid.store;
24370 for(var i = 0, len = records.length; i < len; i++){
24371 this.selectRow(ds.indexOf(records[i]), true);
24376 * Gets the number of selected rows.
24379 getCount : function(){
24380 return this.selections.length;
24384 * Selects the first row in the grid.
24386 selectFirstRow : function(){
24391 * Select the last row.
24392 * @param {Boolean} keepExisting (optional) True to keep existing selections
24394 selectLastRow : function(keepExisting){
24395 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24396 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24400 * Selects the row immediately following the last selected row.
24401 * @param {Boolean} keepExisting (optional) True to keep existing selections
24403 selectNext : function(keepExisting)
24405 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24406 this.selectRow(this.last+1, keepExisting);
24407 this.grid.getView().focusRow(this.last);
24412 * Selects the row that precedes the last selected row.
24413 * @param {Boolean} keepExisting (optional) True to keep existing selections
24415 selectPrevious : function(keepExisting){
24417 this.selectRow(this.last-1, keepExisting);
24418 this.grid.getView().focusRow(this.last);
24423 * Returns the selected records
24424 * @return {Array} Array of selected records
24426 getSelections : function(){
24427 return [].concat(this.selections.items);
24431 * Returns the first selected record.
24434 getSelected : function(){
24435 return this.selections.itemAt(0);
24440 * Clears all selections.
24442 clearSelections : function(fast)
24448 var ds = this.grid.store;
24449 var s = this.selections;
24450 s.each(function(r){
24451 this.deselectRow(ds.indexOfId(r.id));
24455 this.selections.clear();
24462 * Selects all rows.
24464 selectAll : function(){
24468 this.selections.clear();
24469 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24470 this.selectRow(i, true);
24475 * Returns True if there is a selection.
24476 * @return {Boolean}
24478 hasSelection : function(){
24479 return this.selections.length > 0;
24483 * Returns True if the specified row is selected.
24484 * @param {Number/Record} record The record or index of the record to check
24485 * @return {Boolean}
24487 isSelected : function(index){
24488 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24489 return (r && this.selections.key(r.id) ? true : false);
24493 * Returns True if the specified record id is selected.
24494 * @param {String} id The id of record to check
24495 * @return {Boolean}
24497 isIdSelected : function(id){
24498 return (this.selections.key(id) ? true : false);
24503 handleMouseDBClick : function(e, t){
24507 handleMouseDown : function(e, t)
24509 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24510 if(this.isLocked() || rowIndex < 0 ){
24513 if(e.shiftKey && this.last !== false){
24514 var last = this.last;
24515 this.selectRange(last, rowIndex, e.ctrlKey);
24516 this.last = last; // reset the last
24520 var isSelected = this.isSelected(rowIndex);
24521 //Roo.log("select row:" + rowIndex);
24523 this.deselectRow(rowIndex);
24525 this.selectRow(rowIndex, true);
24529 if(e.button !== 0 && isSelected){
24530 alert('rowIndex 2: ' + rowIndex);
24531 view.focusRow(rowIndex);
24532 }else if(e.ctrlKey && isSelected){
24533 this.deselectRow(rowIndex);
24534 }else if(!isSelected){
24535 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24536 view.focusRow(rowIndex);
24540 this.fireEvent("afterselectionchange", this);
24543 handleDragableRowClick : function(grid, rowIndex, e)
24545 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24546 this.selectRow(rowIndex, false);
24547 grid.view.focusRow(rowIndex);
24548 this.fireEvent("afterselectionchange", this);
24553 * Selects multiple rows.
24554 * @param {Array} rows Array of the indexes of the row to select
24555 * @param {Boolean} keepExisting (optional) True to keep existing selections
24557 selectRows : function(rows, keepExisting){
24559 this.clearSelections();
24561 for(var i = 0, len = rows.length; i < len; i++){
24562 this.selectRow(rows[i], true);
24567 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24568 * @param {Number} startRow The index of the first row in the range
24569 * @param {Number} endRow The index of the last row in the range
24570 * @param {Boolean} keepExisting (optional) True to retain existing selections
24572 selectRange : function(startRow, endRow, keepExisting){
24577 this.clearSelections();
24579 if(startRow <= endRow){
24580 for(var i = startRow; i <= endRow; i++){
24581 this.selectRow(i, true);
24584 for(var i = startRow; i >= endRow; i--){
24585 this.selectRow(i, true);
24591 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24592 * @param {Number} startRow The index of the first row in the range
24593 * @param {Number} endRow The index of the last row in the range
24595 deselectRange : function(startRow, endRow, preventViewNotify){
24599 for(var i = startRow; i <= endRow; i++){
24600 this.deselectRow(i, preventViewNotify);
24606 * @param {Number} row The index of the row to select
24607 * @param {Boolean} keepExisting (optional) True to keep existing selections
24609 selectRow : function(index, keepExisting, preventViewNotify)
24611 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24614 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24615 if(!keepExisting || this.singleSelect){
24616 this.clearSelections();
24619 var r = this.grid.store.getAt(index);
24620 //console.log('selectRow - record id :' + r.id);
24622 this.selections.add(r);
24623 this.last = this.lastActive = index;
24624 if(!preventViewNotify){
24625 var proxy = new Roo.Element(
24626 this.grid.getRowDom(index)
24628 proxy.addClass('bg-info info');
24630 this.fireEvent("rowselect", this, index, r);
24631 this.fireEvent("selectionchange", this);
24637 * @param {Number} row The index of the row to deselect
24639 deselectRow : function(index, preventViewNotify)
24644 if(this.last == index){
24647 if(this.lastActive == index){
24648 this.lastActive = false;
24651 var r = this.grid.store.getAt(index);
24656 this.selections.remove(r);
24657 //.console.log('deselectRow - record id :' + r.id);
24658 if(!preventViewNotify){
24660 var proxy = new Roo.Element(
24661 this.grid.getRowDom(index)
24663 proxy.removeClass('bg-info info');
24665 this.fireEvent("rowdeselect", this, index);
24666 this.fireEvent("selectionchange", this);
24670 restoreLast : function(){
24672 this.last = this._last;
24677 acceptsNav : function(row, col, cm){
24678 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24682 onEditorKey : function(field, e){
24683 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24688 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24690 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24692 }else if(k == e.ENTER && !e.ctrlKey){
24696 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24698 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24700 }else if(k == e.ESC){
24704 g.startEditing(newCell[0], newCell[1]);
24710 * Ext JS Library 1.1.1
24711 * Copyright(c) 2006-2007, Ext JS, LLC.
24713 * Originally Released Under LGPL - original licence link has changed is not relivant.
24716 * <script type="text/javascript">
24720 * @class Roo.bootstrap.PagingToolbar
24721 * @extends Roo.bootstrap.NavSimplebar
24722 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24724 * Create a new PagingToolbar
24725 * @param {Object} config The config object
24726 * @param {Roo.data.Store} store
24728 Roo.bootstrap.PagingToolbar = function(config)
24730 // old args format still supported... - xtype is prefered..
24731 // created from xtype...
24733 this.ds = config.dataSource;
24735 if (config.store && !this.ds) {
24736 this.store= Roo.factory(config.store, Roo.data);
24737 this.ds = this.store;
24738 this.ds.xmodule = this.xmodule || false;
24741 this.toolbarItems = [];
24742 if (config.items) {
24743 this.toolbarItems = config.items;
24746 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24751 this.bind(this.ds);
24754 if (Roo.bootstrap.version == 4) {
24755 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24757 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24762 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24764 * @cfg {Roo.data.Store} dataSource
24765 * The underlying data store providing the paged data
24768 * @cfg {String/HTMLElement/Element} container
24769 * container The id or element that will contain the toolbar
24772 * @cfg {Boolean} displayInfo
24773 * True to display the displayMsg (defaults to false)
24776 * @cfg {Number} pageSize
24777 * The number of records to display per page (defaults to 20)
24781 * @cfg {String} displayMsg
24782 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24784 displayMsg : 'Displaying {0} - {1} of {2}',
24786 * @cfg {String} emptyMsg
24787 * The message to display when no records are found (defaults to "No data to display")
24789 emptyMsg : 'No data to display',
24791 * Customizable piece of the default paging text (defaults to "Page")
24794 beforePageText : "Page",
24796 * Customizable piece of the default paging text (defaults to "of %0")
24799 afterPageText : "of {0}",
24801 * Customizable piece of the default paging text (defaults to "First Page")
24804 firstText : "First Page",
24806 * Customizable piece of the default paging text (defaults to "Previous Page")
24809 prevText : "Previous Page",
24811 * Customizable piece of the default paging text (defaults to "Next Page")
24814 nextText : "Next Page",
24816 * Customizable piece of the default paging text (defaults to "Last Page")
24819 lastText : "Last Page",
24821 * Customizable piece of the default paging text (defaults to "Refresh")
24824 refreshText : "Refresh",
24828 onRender : function(ct, position)
24830 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24831 this.navgroup.parentId = this.id;
24832 this.navgroup.onRender(this.el, null);
24833 // add the buttons to the navgroup
24835 if(this.displayInfo){
24836 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24837 this.displayEl = this.el.select('.x-paging-info', true).first();
24838 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24839 // this.displayEl = navel.el.select('span',true).first();
24845 Roo.each(_this.buttons, function(e){ // this might need to use render????
24846 Roo.factory(e).render(_this.el);
24850 Roo.each(_this.toolbarItems, function(e) {
24851 _this.navgroup.addItem(e);
24855 this.first = this.navgroup.addItem({
24856 tooltip: this.firstText,
24857 cls: "prev btn-outline-secondary",
24858 html : ' <i class="fa fa-step-backward"></i>',
24860 preventDefault: true,
24861 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24864 this.prev = this.navgroup.addItem({
24865 tooltip: this.prevText,
24866 cls: "prev btn-outline-secondary",
24867 html : ' <i class="fa fa-backward"></i>',
24869 preventDefault: true,
24870 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24872 //this.addSeparator();
24875 var field = this.navgroup.addItem( {
24877 cls : 'x-paging-position btn-outline-secondary',
24879 html : this.beforePageText +
24880 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24881 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24884 this.field = field.el.select('input', true).first();
24885 this.field.on("keydown", this.onPagingKeydown, this);
24886 this.field.on("focus", function(){this.dom.select();});
24889 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24890 //this.field.setHeight(18);
24891 //this.addSeparator();
24892 this.next = this.navgroup.addItem({
24893 tooltip: this.nextText,
24894 cls: "next btn-outline-secondary",
24895 html : ' <i class="fa fa-forward"></i>',
24897 preventDefault: true,
24898 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24900 this.last = this.navgroup.addItem({
24901 tooltip: this.lastText,
24902 html : ' <i class="fa fa-step-forward"></i>',
24903 cls: "next btn-outline-secondary",
24905 preventDefault: true,
24906 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24908 //this.addSeparator();
24909 this.loading = this.navgroup.addItem({
24910 tooltip: this.refreshText,
24911 cls: "btn-outline-secondary",
24912 html : ' <i class="fa fa-refresh"></i>',
24913 preventDefault: true,
24914 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24920 updateInfo : function(){
24921 if(this.displayEl){
24922 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24923 var msg = count == 0 ?
24927 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24929 this.displayEl.update(msg);
24934 onLoad : function(ds, r, o)
24936 this.cursor = o.params.start ? o.params.start : 0;
24938 var d = this.getPageData(),
24943 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24944 this.field.dom.value = ap;
24945 this.first.setDisabled(ap == 1);
24946 this.prev.setDisabled(ap == 1);
24947 this.next.setDisabled(ap == ps);
24948 this.last.setDisabled(ap == ps);
24949 this.loading.enable();
24954 getPageData : function(){
24955 var total = this.ds.getTotalCount();
24958 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24959 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24964 onLoadError : function(){
24965 this.loading.enable();
24969 onPagingKeydown : function(e){
24970 var k = e.getKey();
24971 var d = this.getPageData();
24973 var v = this.field.dom.value, pageNum;
24974 if(!v || isNaN(pageNum = parseInt(v, 10))){
24975 this.field.dom.value = d.activePage;
24978 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24979 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24982 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))
24984 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24985 this.field.dom.value = pageNum;
24986 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24989 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24991 var v = this.field.dom.value, pageNum;
24992 var increment = (e.shiftKey) ? 10 : 1;
24993 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24996 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24997 this.field.dom.value = d.activePage;
25000 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25002 this.field.dom.value = parseInt(v, 10) + increment;
25003 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25004 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25011 beforeLoad : function(){
25013 this.loading.disable();
25018 onClick : function(which){
25027 ds.load({params:{start: 0, limit: this.pageSize}});
25030 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25033 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25036 var total = ds.getTotalCount();
25037 var extra = total % this.pageSize;
25038 var lastStart = extra ? (total - extra) : total-this.pageSize;
25039 ds.load({params:{start: lastStart, limit: this.pageSize}});
25042 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25048 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25049 * @param {Roo.data.Store} store The data store to unbind
25051 unbind : function(ds){
25052 ds.un("beforeload", this.beforeLoad, this);
25053 ds.un("load", this.onLoad, this);
25054 ds.un("loadexception", this.onLoadError, this);
25055 ds.un("remove", this.updateInfo, this);
25056 ds.un("add", this.updateInfo, this);
25057 this.ds = undefined;
25061 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25062 * @param {Roo.data.Store} store The data store to bind
25064 bind : function(ds){
25065 ds.on("beforeload", this.beforeLoad, this);
25066 ds.on("load", this.onLoad, this);
25067 ds.on("loadexception", this.onLoadError, this);
25068 ds.on("remove", this.updateInfo, this);
25069 ds.on("add", this.updateInfo, this);
25080 * @class Roo.bootstrap.MessageBar
25081 * @extends Roo.bootstrap.Component
25082 * Bootstrap MessageBar class
25083 * @cfg {String} html contents of the MessageBar
25084 * @cfg {String} weight (info | success | warning | danger) default info
25085 * @cfg {String} beforeClass insert the bar before the given class
25086 * @cfg {Boolean} closable (true | false) default false
25087 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25090 * Create a new Element
25091 * @param {Object} config The config object
25094 Roo.bootstrap.MessageBar = function(config){
25095 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25098 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25104 beforeClass: 'bootstrap-sticky-wrap',
25106 getAutoCreate : function(){
25110 cls: 'alert alert-dismissable alert-' + this.weight,
25115 html: this.html || ''
25121 cfg.cls += ' alert-messages-fixed';
25135 onRender : function(ct, position)
25137 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25140 var cfg = Roo.apply({}, this.getAutoCreate());
25144 cfg.cls += ' ' + this.cls;
25147 cfg.style = this.style;
25149 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25151 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25154 this.el.select('>button.close').on('click', this.hide, this);
25160 if (!this.rendered) {
25166 this.fireEvent('show', this);
25172 if (!this.rendered) {
25178 this.fireEvent('hide', this);
25181 update : function()
25183 // var e = this.el.dom.firstChild;
25185 // if(this.closable){
25186 // e = e.nextSibling;
25189 // e.data = this.html || '';
25191 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25207 * @class Roo.bootstrap.Graph
25208 * @extends Roo.bootstrap.Component
25209 * Bootstrap Graph class
25213 @cfg {String} graphtype bar | vbar | pie
25214 @cfg {number} g_x coodinator | centre x (pie)
25215 @cfg {number} g_y coodinator | centre y (pie)
25216 @cfg {number} g_r radius (pie)
25217 @cfg {number} g_height height of the chart (respected by all elements in the set)
25218 @cfg {number} g_width width of the chart (respected by all elements in the set)
25219 @cfg {Object} title The title of the chart
25222 -opts (object) options for the chart
25224 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25225 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25227 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.
25228 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25230 o stretch (boolean)
25232 -opts (object) options for the pie
25235 o startAngle (number)
25236 o endAngle (number)
25240 * Create a new Input
25241 * @param {Object} config The config object
25244 Roo.bootstrap.Graph = function(config){
25245 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25251 * The img click event for the img.
25252 * @param {Roo.EventObject} e
25258 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25269 //g_colors: this.colors,
25276 getAutoCreate : function(){
25287 onRender : function(ct,position){
25290 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25292 if (typeof(Raphael) == 'undefined') {
25293 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25297 this.raphael = Raphael(this.el.dom);
25299 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25300 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25301 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25302 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25304 r.text(160, 10, "Single Series Chart").attr(txtattr);
25305 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25306 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25307 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25309 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25310 r.barchart(330, 10, 300, 220, data1);
25311 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25312 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25315 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25316 // r.barchart(30, 30, 560, 250, xdata, {
25317 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25318 // axis : "0 0 1 1",
25319 // axisxlabels : xdata
25320 // //yvalues : cols,
25323 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25325 // this.load(null,xdata,{
25326 // axis : "0 0 1 1",
25327 // axisxlabels : xdata
25332 load : function(graphtype,xdata,opts)
25334 this.raphael.clear();
25336 graphtype = this.graphtype;
25341 var r = this.raphael,
25342 fin = function () {
25343 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25345 fout = function () {
25346 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25348 pfin = function() {
25349 this.sector.stop();
25350 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25353 this.label[0].stop();
25354 this.label[0].attr({ r: 7.5 });
25355 this.label[1].attr({ "font-weight": 800 });
25358 pfout = function() {
25359 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25362 this.label[0].animate({ r: 5 }, 500, "bounce");
25363 this.label[1].attr({ "font-weight": 400 });
25369 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25372 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25375 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25376 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25378 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25385 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25390 setTitle: function(o)
25395 initEvents: function() {
25398 this.el.on('click', this.onClick, this);
25402 onClick : function(e)
25404 Roo.log('img onclick');
25405 this.fireEvent('click', this, e);
25417 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25420 * @class Roo.bootstrap.dash.NumberBox
25421 * @extends Roo.bootstrap.Component
25422 * Bootstrap NumberBox class
25423 * @cfg {String} headline Box headline
25424 * @cfg {String} content Box content
25425 * @cfg {String} icon Box icon
25426 * @cfg {String} footer Footer text
25427 * @cfg {String} fhref Footer href
25430 * Create a new NumberBox
25431 * @param {Object} config The config object
25435 Roo.bootstrap.dash.NumberBox = function(config){
25436 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25440 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25449 getAutoCreate : function(){
25453 cls : 'small-box ',
25461 cls : 'roo-headline',
25462 html : this.headline
25466 cls : 'roo-content',
25467 html : this.content
25481 cls : 'ion ' + this.icon
25490 cls : 'small-box-footer',
25491 href : this.fhref || '#',
25495 cfg.cn.push(footer);
25502 onRender : function(ct,position){
25503 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25510 setHeadline: function (value)
25512 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25515 setFooter: function (value, href)
25517 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25520 this.el.select('a.small-box-footer',true).first().attr('href', href);
25525 setContent: function (value)
25527 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25530 initEvents: function()
25544 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25547 * @class Roo.bootstrap.dash.TabBox
25548 * @extends Roo.bootstrap.Component
25549 * Bootstrap TabBox class
25550 * @cfg {String} title Title of the TabBox
25551 * @cfg {String} icon Icon of the TabBox
25552 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25553 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25556 * Create a new TabBox
25557 * @param {Object} config The config object
25561 Roo.bootstrap.dash.TabBox = function(config){
25562 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25567 * When a pane is added
25568 * @param {Roo.bootstrap.dash.TabPane} pane
25572 * @event activatepane
25573 * When a pane is activated
25574 * @param {Roo.bootstrap.dash.TabPane} pane
25576 "activatepane" : true
25584 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25589 tabScrollable : false,
25591 getChildContainer : function()
25593 return this.el.select('.tab-content', true).first();
25596 getAutoCreate : function(){
25600 cls: 'pull-left header',
25608 cls: 'fa ' + this.icon
25614 cls: 'nav nav-tabs pull-right',
25620 if(this.tabScrollable){
25627 cls: 'nav nav-tabs pull-right',
25638 cls: 'nav-tabs-custom',
25643 cls: 'tab-content no-padding',
25651 initEvents : function()
25653 //Roo.log('add add pane handler');
25654 this.on('addpane', this.onAddPane, this);
25657 * Updates the box title
25658 * @param {String} html to set the title to.
25660 setTitle : function(value)
25662 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25664 onAddPane : function(pane)
25666 this.panes.push(pane);
25667 //Roo.log('addpane');
25669 // tabs are rendere left to right..
25670 if(!this.showtabs){
25674 var ctr = this.el.select('.nav-tabs', true).first();
25677 var existing = ctr.select('.nav-tab',true);
25678 var qty = existing.getCount();;
25681 var tab = ctr.createChild({
25683 cls : 'nav-tab' + (qty ? '' : ' active'),
25691 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25694 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25696 pane.el.addClass('active');
25701 onTabClick : function(ev,un,ob,pane)
25703 //Roo.log('tab - prev default');
25704 ev.preventDefault();
25707 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25708 pane.tab.addClass('active');
25709 //Roo.log(pane.title);
25710 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25711 // technically we should have a deactivate event.. but maybe add later.
25712 // and it should not de-activate the selected tab...
25713 this.fireEvent('activatepane', pane);
25714 pane.el.addClass('active');
25715 pane.fireEvent('activate');
25720 getActivePane : function()
25723 Roo.each(this.panes, function(p) {
25724 if(p.el.hasClass('active')){
25745 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25747 * @class Roo.bootstrap.TabPane
25748 * @extends Roo.bootstrap.Component
25749 * Bootstrap TabPane class
25750 * @cfg {Boolean} active (false | true) Default false
25751 * @cfg {String} title title of panel
25755 * Create a new TabPane
25756 * @param {Object} config The config object
25759 Roo.bootstrap.dash.TabPane = function(config){
25760 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25766 * When a pane is activated
25767 * @param {Roo.bootstrap.dash.TabPane} pane
25774 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25779 // the tabBox that this is attached to.
25782 getAutoCreate : function()
25790 cfg.cls += ' active';
25795 initEvents : function()
25797 //Roo.log('trigger add pane handler');
25798 this.parent().fireEvent('addpane', this)
25802 * Updates the tab title
25803 * @param {String} html to set the title to.
25805 setTitle: function(str)
25811 this.tab.select('a', true).first().dom.innerHTML = str;
25828 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25831 * @class Roo.bootstrap.menu.Menu
25832 * @extends Roo.bootstrap.Component
25833 * Bootstrap Menu class - container for Menu
25834 * @cfg {String} html Text of the menu
25835 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25836 * @cfg {String} icon Font awesome icon
25837 * @cfg {String} pos Menu align to (top | bottom) default bottom
25841 * Create a new Menu
25842 * @param {Object} config The config object
25846 Roo.bootstrap.menu.Menu = function(config){
25847 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25851 * @event beforeshow
25852 * Fires before this menu is displayed
25853 * @param {Roo.bootstrap.menu.Menu} this
25857 * @event beforehide
25858 * Fires before this menu is hidden
25859 * @param {Roo.bootstrap.menu.Menu} this
25864 * Fires after this menu is displayed
25865 * @param {Roo.bootstrap.menu.Menu} this
25870 * Fires after this menu is hidden
25871 * @param {Roo.bootstrap.menu.Menu} this
25876 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25877 * @param {Roo.bootstrap.menu.Menu} this
25878 * @param {Roo.EventObject} e
25885 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25889 weight : 'default',
25894 getChildContainer : function() {
25895 if(this.isSubMenu){
25899 return this.el.select('ul.dropdown-menu', true).first();
25902 getAutoCreate : function()
25907 cls : 'roo-menu-text',
25915 cls : 'fa ' + this.icon
25926 cls : 'dropdown-button btn btn-' + this.weight,
25931 cls : 'dropdown-toggle btn btn-' + this.weight,
25941 cls : 'dropdown-menu'
25947 if(this.pos == 'top'){
25948 cfg.cls += ' dropup';
25951 if(this.isSubMenu){
25954 cls : 'dropdown-menu'
25961 onRender : function(ct, position)
25963 this.isSubMenu = ct.hasClass('dropdown-submenu');
25965 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25968 initEvents : function()
25970 if(this.isSubMenu){
25974 this.hidden = true;
25976 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25977 this.triggerEl.on('click', this.onTriggerPress, this);
25979 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25980 this.buttonEl.on('click', this.onClick, this);
25986 if(this.isSubMenu){
25990 return this.el.select('ul.dropdown-menu', true).first();
25993 onClick : function(e)
25995 this.fireEvent("click", this, e);
25998 onTriggerPress : function(e)
26000 if (this.isVisible()) {
26007 isVisible : function(){
26008 return !this.hidden;
26013 this.fireEvent("beforeshow", this);
26015 this.hidden = false;
26016 this.el.addClass('open');
26018 Roo.get(document).on("mouseup", this.onMouseUp, this);
26020 this.fireEvent("show", this);
26027 this.fireEvent("beforehide", this);
26029 this.hidden = true;
26030 this.el.removeClass('open');
26032 Roo.get(document).un("mouseup", this.onMouseUp);
26034 this.fireEvent("hide", this);
26037 onMouseUp : function()
26051 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26054 * @class Roo.bootstrap.menu.Item
26055 * @extends Roo.bootstrap.Component
26056 * Bootstrap MenuItem class
26057 * @cfg {Boolean} submenu (true | false) default false
26058 * @cfg {String} html text of the item
26059 * @cfg {String} href the link
26060 * @cfg {Boolean} disable (true | false) default false
26061 * @cfg {Boolean} preventDefault (true | false) default true
26062 * @cfg {String} icon Font awesome icon
26063 * @cfg {String} pos Submenu align to (left | right) default right
26067 * Create a new Item
26068 * @param {Object} config The config object
26072 Roo.bootstrap.menu.Item = function(config){
26073 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26077 * Fires when the mouse is hovering over this menu
26078 * @param {Roo.bootstrap.menu.Item} this
26079 * @param {Roo.EventObject} e
26084 * Fires when the mouse exits this menu
26085 * @param {Roo.bootstrap.menu.Item} this
26086 * @param {Roo.EventObject} e
26092 * The raw click event for the entire grid.
26093 * @param {Roo.EventObject} e
26099 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26104 preventDefault: true,
26109 getAutoCreate : function()
26114 cls : 'roo-menu-item-text',
26122 cls : 'fa ' + this.icon
26131 href : this.href || '#',
26138 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26142 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26144 if(this.pos == 'left'){
26145 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26152 initEvents : function()
26154 this.el.on('mouseover', this.onMouseOver, this);
26155 this.el.on('mouseout', this.onMouseOut, this);
26157 this.el.select('a', true).first().on('click', this.onClick, this);
26161 onClick : function(e)
26163 if(this.preventDefault){
26164 e.preventDefault();
26167 this.fireEvent("click", this, e);
26170 onMouseOver : function(e)
26172 if(this.submenu && this.pos == 'left'){
26173 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26176 this.fireEvent("mouseover", this, e);
26179 onMouseOut : function(e)
26181 this.fireEvent("mouseout", this, e);
26193 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26196 * @class Roo.bootstrap.menu.Separator
26197 * @extends Roo.bootstrap.Component
26198 * Bootstrap Separator class
26201 * Create a new Separator
26202 * @param {Object} config The config object
26206 Roo.bootstrap.menu.Separator = function(config){
26207 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26210 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26212 getAutoCreate : function(){
26233 * @class Roo.bootstrap.Tooltip
26234 * Bootstrap Tooltip class
26235 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26236 * to determine which dom element triggers the tooltip.
26238 * It needs to add support for additional attributes like tooltip-position
26241 * Create a new Toolti
26242 * @param {Object} config The config object
26245 Roo.bootstrap.Tooltip = function(config){
26246 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26248 this.alignment = Roo.bootstrap.Tooltip.alignment;
26250 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26251 this.alignment = config.alignment;
26256 Roo.apply(Roo.bootstrap.Tooltip, {
26258 * @function init initialize tooltip monitoring.
26262 currentTip : false,
26263 currentRegion : false,
26269 Roo.get(document).on('mouseover', this.enter ,this);
26270 Roo.get(document).on('mouseout', this.leave, this);
26273 this.currentTip = new Roo.bootstrap.Tooltip();
26276 enter : function(ev)
26278 var dom = ev.getTarget();
26280 //Roo.log(['enter',dom]);
26281 var el = Roo.fly(dom);
26282 if (this.currentEl) {
26284 //Roo.log(this.currentEl);
26285 //Roo.log(this.currentEl.contains(dom));
26286 if (this.currentEl == el) {
26289 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26295 if (this.currentTip.el) {
26296 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26300 if(!el || el.dom == document){
26306 // you can not look for children, as if el is the body.. then everythign is the child..
26307 if (!el.attr('tooltip')) { //
26308 if (!el.select("[tooltip]").elements.length) {
26311 // is the mouse over this child...?
26312 bindEl = el.select("[tooltip]").first();
26313 var xy = ev.getXY();
26314 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26315 //Roo.log("not in region.");
26318 //Roo.log("child element over..");
26321 this.currentEl = bindEl;
26322 this.currentTip.bind(bindEl);
26323 this.currentRegion = Roo.lib.Region.getRegion(dom);
26324 this.currentTip.enter();
26327 leave : function(ev)
26329 var dom = ev.getTarget();
26330 //Roo.log(['leave',dom]);
26331 if (!this.currentEl) {
26336 if (dom != this.currentEl.dom) {
26339 var xy = ev.getXY();
26340 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26343 // only activate leave if mouse cursor is outside... bounding box..
26348 if (this.currentTip) {
26349 this.currentTip.leave();
26351 //Roo.log('clear currentEl');
26352 this.currentEl = false;
26357 'left' : ['r-l', [-2,0], 'right'],
26358 'right' : ['l-r', [2,0], 'left'],
26359 'bottom' : ['t-b', [0,2], 'top'],
26360 'top' : [ 'b-t', [0,-2], 'bottom']
26366 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26371 delay : null, // can be { show : 300 , hide: 500}
26375 hoverState : null, //???
26377 placement : 'bottom',
26381 getAutoCreate : function(){
26388 cls : 'tooltip-arrow'
26391 cls : 'tooltip-inner'
26398 bind : function(el)
26404 enter : function () {
26406 if (this.timeout != null) {
26407 clearTimeout(this.timeout);
26410 this.hoverState = 'in';
26411 //Roo.log("enter - show");
26412 if (!this.delay || !this.delay.show) {
26417 this.timeout = setTimeout(function () {
26418 if (_t.hoverState == 'in') {
26421 }, this.delay.show);
26425 clearTimeout(this.timeout);
26427 this.hoverState = 'out';
26428 if (!this.delay || !this.delay.hide) {
26434 this.timeout = setTimeout(function () {
26435 //Roo.log("leave - timeout");
26437 if (_t.hoverState == 'out') {
26439 Roo.bootstrap.Tooltip.currentEl = false;
26444 show : function (msg)
26447 this.render(document.body);
26450 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26452 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26454 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26456 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26458 var placement = typeof this.placement == 'function' ?
26459 this.placement.call(this, this.el, on_el) :
26462 var autoToken = /\s?auto?\s?/i;
26463 var autoPlace = autoToken.test(placement);
26465 placement = placement.replace(autoToken, '') || 'top';
26469 //this.el.setXY([0,0]);
26471 //this.el.dom.style.display='block';
26473 //this.el.appendTo(on_el);
26475 var p = this.getPosition();
26476 var box = this.el.getBox();
26482 var align = this.alignment[placement];
26484 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26486 if(placement == 'top' || placement == 'bottom'){
26488 placement = 'right';
26491 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26492 placement = 'left';
26495 var scroll = Roo.select('body', true).first().getScroll();
26497 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26501 align = this.alignment[placement];
26504 this.el.alignTo(this.bindEl, align[0],align[1]);
26505 //var arrow = this.el.select('.arrow',true).first();
26506 //arrow.set(align[2],
26508 this.el.addClass(placement);
26510 this.el.addClass('in fade');
26512 this.hoverState = null;
26514 if (this.el.hasClass('fade')) {
26525 //this.el.setXY([0,0]);
26526 this.el.removeClass('in');
26542 * @class Roo.bootstrap.LocationPicker
26543 * @extends Roo.bootstrap.Component
26544 * Bootstrap LocationPicker class
26545 * @cfg {Number} latitude Position when init default 0
26546 * @cfg {Number} longitude Position when init default 0
26547 * @cfg {Number} zoom default 15
26548 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26549 * @cfg {Boolean} mapTypeControl default false
26550 * @cfg {Boolean} disableDoubleClickZoom default false
26551 * @cfg {Boolean} scrollwheel default true
26552 * @cfg {Boolean} streetViewControl default false
26553 * @cfg {Number} radius default 0
26554 * @cfg {String} locationName
26555 * @cfg {Boolean} draggable default true
26556 * @cfg {Boolean} enableAutocomplete default false
26557 * @cfg {Boolean} enableReverseGeocode default true
26558 * @cfg {String} markerTitle
26561 * Create a new LocationPicker
26562 * @param {Object} config The config object
26566 Roo.bootstrap.LocationPicker = function(config){
26568 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26573 * Fires when the picker initialized.
26574 * @param {Roo.bootstrap.LocationPicker} this
26575 * @param {Google Location} location
26579 * @event positionchanged
26580 * Fires when the picker position changed.
26581 * @param {Roo.bootstrap.LocationPicker} this
26582 * @param {Google Location} location
26584 positionchanged : true,
26587 * Fires when the map resize.
26588 * @param {Roo.bootstrap.LocationPicker} this
26593 * Fires when the map show.
26594 * @param {Roo.bootstrap.LocationPicker} this
26599 * Fires when the map hide.
26600 * @param {Roo.bootstrap.LocationPicker} this
26605 * Fires when click the map.
26606 * @param {Roo.bootstrap.LocationPicker} this
26607 * @param {Map event} e
26611 * @event mapRightClick
26612 * Fires when right click the map.
26613 * @param {Roo.bootstrap.LocationPicker} this
26614 * @param {Map event} e
26616 mapRightClick : true,
26618 * @event markerClick
26619 * Fires when click the marker.
26620 * @param {Roo.bootstrap.LocationPicker} this
26621 * @param {Map event} e
26623 markerClick : true,
26625 * @event markerRightClick
26626 * Fires when right click the marker.
26627 * @param {Roo.bootstrap.LocationPicker} this
26628 * @param {Map event} e
26630 markerRightClick : true,
26632 * @event OverlayViewDraw
26633 * Fires when OverlayView Draw
26634 * @param {Roo.bootstrap.LocationPicker} this
26636 OverlayViewDraw : true,
26638 * @event OverlayViewOnAdd
26639 * Fires when OverlayView Draw
26640 * @param {Roo.bootstrap.LocationPicker} this
26642 OverlayViewOnAdd : true,
26644 * @event OverlayViewOnRemove
26645 * Fires when OverlayView Draw
26646 * @param {Roo.bootstrap.LocationPicker} this
26648 OverlayViewOnRemove : true,
26650 * @event OverlayViewShow
26651 * Fires when OverlayView Draw
26652 * @param {Roo.bootstrap.LocationPicker} this
26653 * @param {Pixel} cpx
26655 OverlayViewShow : true,
26657 * @event OverlayViewHide
26658 * Fires when OverlayView Draw
26659 * @param {Roo.bootstrap.LocationPicker} this
26661 OverlayViewHide : true,
26663 * @event loadexception
26664 * Fires when load google lib failed.
26665 * @param {Roo.bootstrap.LocationPicker} this
26667 loadexception : true
26672 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26674 gMapContext: false,
26680 mapTypeControl: false,
26681 disableDoubleClickZoom: false,
26683 streetViewControl: false,
26687 enableAutocomplete: false,
26688 enableReverseGeocode: true,
26691 getAutoCreate: function()
26696 cls: 'roo-location-picker'
26702 initEvents: function(ct, position)
26704 if(!this.el.getWidth() || this.isApplied()){
26708 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26713 initial: function()
26715 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26716 this.fireEvent('loadexception', this);
26720 if(!this.mapTypeId){
26721 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26724 this.gMapContext = this.GMapContext();
26726 this.initOverlayView();
26728 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26732 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26733 _this.setPosition(_this.gMapContext.marker.position);
26736 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26737 _this.fireEvent('mapClick', this, event);
26741 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26742 _this.fireEvent('mapRightClick', this, event);
26746 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26747 _this.fireEvent('markerClick', this, event);
26751 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26752 _this.fireEvent('markerRightClick', this, event);
26756 this.setPosition(this.gMapContext.location);
26758 this.fireEvent('initial', this, this.gMapContext.location);
26761 initOverlayView: function()
26765 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26769 _this.fireEvent('OverlayViewDraw', _this);
26774 _this.fireEvent('OverlayViewOnAdd', _this);
26777 onRemove: function()
26779 _this.fireEvent('OverlayViewOnRemove', _this);
26782 show: function(cpx)
26784 _this.fireEvent('OverlayViewShow', _this, cpx);
26789 _this.fireEvent('OverlayViewHide', _this);
26795 fromLatLngToContainerPixel: function(event)
26797 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26800 isApplied: function()
26802 return this.getGmapContext() == false ? false : true;
26805 getGmapContext: function()
26807 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26810 GMapContext: function()
26812 var position = new google.maps.LatLng(this.latitude, this.longitude);
26814 var _map = new google.maps.Map(this.el.dom, {
26817 mapTypeId: this.mapTypeId,
26818 mapTypeControl: this.mapTypeControl,
26819 disableDoubleClickZoom: this.disableDoubleClickZoom,
26820 scrollwheel: this.scrollwheel,
26821 streetViewControl: this.streetViewControl,
26822 locationName: this.locationName,
26823 draggable: this.draggable,
26824 enableAutocomplete: this.enableAutocomplete,
26825 enableReverseGeocode: this.enableReverseGeocode
26828 var _marker = new google.maps.Marker({
26829 position: position,
26831 title: this.markerTitle,
26832 draggable: this.draggable
26839 location: position,
26840 radius: this.radius,
26841 locationName: this.locationName,
26842 addressComponents: {
26843 formatted_address: null,
26844 addressLine1: null,
26845 addressLine2: null,
26847 streetNumber: null,
26851 stateOrProvince: null
26854 domContainer: this.el.dom,
26855 geodecoder: new google.maps.Geocoder()
26859 drawCircle: function(center, radius, options)
26861 if (this.gMapContext.circle != null) {
26862 this.gMapContext.circle.setMap(null);
26866 options = Roo.apply({}, options, {
26867 strokeColor: "#0000FF",
26868 strokeOpacity: .35,
26870 fillColor: "#0000FF",
26874 options.map = this.gMapContext.map;
26875 options.radius = radius;
26876 options.center = center;
26877 this.gMapContext.circle = new google.maps.Circle(options);
26878 return this.gMapContext.circle;
26884 setPosition: function(location)
26886 this.gMapContext.location = location;
26887 this.gMapContext.marker.setPosition(location);
26888 this.gMapContext.map.panTo(location);
26889 this.drawCircle(location, this.gMapContext.radius, {});
26893 if (this.gMapContext.settings.enableReverseGeocode) {
26894 this.gMapContext.geodecoder.geocode({
26895 latLng: this.gMapContext.location
26896 }, function(results, status) {
26898 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26899 _this.gMapContext.locationName = results[0].formatted_address;
26900 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26902 _this.fireEvent('positionchanged', this, location);
26909 this.fireEvent('positionchanged', this, location);
26914 google.maps.event.trigger(this.gMapContext.map, "resize");
26916 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26918 this.fireEvent('resize', this);
26921 setPositionByLatLng: function(latitude, longitude)
26923 this.setPosition(new google.maps.LatLng(latitude, longitude));
26926 getCurrentPosition: function()
26929 latitude: this.gMapContext.location.lat(),
26930 longitude: this.gMapContext.location.lng()
26934 getAddressName: function()
26936 return this.gMapContext.locationName;
26939 getAddressComponents: function()
26941 return this.gMapContext.addressComponents;
26944 address_component_from_google_geocode: function(address_components)
26948 for (var i = 0; i < address_components.length; i++) {
26949 var component = address_components[i];
26950 if (component.types.indexOf("postal_code") >= 0) {
26951 result.postalCode = component.short_name;
26952 } else if (component.types.indexOf("street_number") >= 0) {
26953 result.streetNumber = component.short_name;
26954 } else if (component.types.indexOf("route") >= 0) {
26955 result.streetName = component.short_name;
26956 } else if (component.types.indexOf("neighborhood") >= 0) {
26957 result.city = component.short_name;
26958 } else if (component.types.indexOf("locality") >= 0) {
26959 result.city = component.short_name;
26960 } else if (component.types.indexOf("sublocality") >= 0) {
26961 result.district = component.short_name;
26962 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26963 result.stateOrProvince = component.short_name;
26964 } else if (component.types.indexOf("country") >= 0) {
26965 result.country = component.short_name;
26969 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26970 result.addressLine2 = "";
26974 setZoomLevel: function(zoom)
26976 this.gMapContext.map.setZoom(zoom);
26989 this.fireEvent('show', this);
27000 this.fireEvent('hide', this);
27005 Roo.apply(Roo.bootstrap.LocationPicker, {
27007 OverlayView : function(map, options)
27009 options = options || {};
27023 * @class Roo.bootstrap.Alert
27024 * @extends Roo.bootstrap.Component
27025 * Bootstrap Alert class
27026 * @cfg {String} title The title of alert
27027 * @cfg {String} html The content of alert
27028 * @cfg {String} weight ( success | info | warning | danger )
27029 * @cfg {String} faicon font-awesomeicon
27032 * Create a new alert
27033 * @param {Object} config The config object
27037 Roo.bootstrap.Alert = function(config){
27038 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27042 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27049 getAutoCreate : function()
27058 cls : 'roo-alert-icon'
27063 cls : 'roo-alert-title',
27068 cls : 'roo-alert-text',
27075 cfg.cn[0].cls += ' fa ' + this.faicon;
27079 cfg.cls += ' alert-' + this.weight;
27085 initEvents: function()
27087 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27090 setTitle : function(str)
27092 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27095 setText : function(str)
27097 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27100 setWeight : function(weight)
27103 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27106 this.weight = weight;
27108 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27111 setIcon : function(icon)
27114 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27117 this.faicon = icon;
27119 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27140 * @class Roo.bootstrap.UploadCropbox
27141 * @extends Roo.bootstrap.Component
27142 * Bootstrap UploadCropbox class
27143 * @cfg {String} emptyText show when image has been loaded
27144 * @cfg {String} rotateNotify show when image too small to rotate
27145 * @cfg {Number} errorTimeout default 3000
27146 * @cfg {Number} minWidth default 300
27147 * @cfg {Number} minHeight default 300
27148 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27149 * @cfg {Boolean} isDocument (true|false) default false
27150 * @cfg {String} url action url
27151 * @cfg {String} paramName default 'imageUpload'
27152 * @cfg {String} method default POST
27153 * @cfg {Boolean} loadMask (true|false) default true
27154 * @cfg {Boolean} loadingText default 'Loading...'
27157 * Create a new UploadCropbox
27158 * @param {Object} config The config object
27161 Roo.bootstrap.UploadCropbox = function(config){
27162 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27166 * @event beforeselectfile
27167 * Fire before select file
27168 * @param {Roo.bootstrap.UploadCropbox} this
27170 "beforeselectfile" : true,
27173 * Fire after initEvent
27174 * @param {Roo.bootstrap.UploadCropbox} this
27179 * Fire after initEvent
27180 * @param {Roo.bootstrap.UploadCropbox} this
27181 * @param {String} data
27186 * Fire when preparing the file data
27187 * @param {Roo.bootstrap.UploadCropbox} this
27188 * @param {Object} file
27193 * Fire when get exception
27194 * @param {Roo.bootstrap.UploadCropbox} this
27195 * @param {XMLHttpRequest} xhr
27197 "exception" : true,
27199 * @event beforeloadcanvas
27200 * Fire before load the canvas
27201 * @param {Roo.bootstrap.UploadCropbox} this
27202 * @param {String} src
27204 "beforeloadcanvas" : true,
27207 * Fire when trash image
27208 * @param {Roo.bootstrap.UploadCropbox} this
27213 * Fire when download the image
27214 * @param {Roo.bootstrap.UploadCropbox} this
27218 * @event footerbuttonclick
27219 * Fire when footerbuttonclick
27220 * @param {Roo.bootstrap.UploadCropbox} this
27221 * @param {String} type
27223 "footerbuttonclick" : true,
27227 * @param {Roo.bootstrap.UploadCropbox} this
27232 * Fire when rotate the image
27233 * @param {Roo.bootstrap.UploadCropbox} this
27234 * @param {String} pos
27239 * Fire when inspect the file
27240 * @param {Roo.bootstrap.UploadCropbox} this
27241 * @param {Object} file
27246 * Fire when xhr upload the file
27247 * @param {Roo.bootstrap.UploadCropbox} this
27248 * @param {Object} data
27253 * Fire when arrange the file data
27254 * @param {Roo.bootstrap.UploadCropbox} this
27255 * @param {Object} formData
27260 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27263 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27265 emptyText : 'Click to upload image',
27266 rotateNotify : 'Image is too small to rotate',
27267 errorTimeout : 3000,
27281 cropType : 'image/jpeg',
27283 canvasLoaded : false,
27284 isDocument : false,
27286 paramName : 'imageUpload',
27288 loadingText : 'Loading...',
27291 getAutoCreate : function()
27295 cls : 'roo-upload-cropbox',
27299 cls : 'roo-upload-cropbox-selector',
27304 cls : 'roo-upload-cropbox-body',
27305 style : 'cursor:pointer',
27309 cls : 'roo-upload-cropbox-preview'
27313 cls : 'roo-upload-cropbox-thumb'
27317 cls : 'roo-upload-cropbox-empty-notify',
27318 html : this.emptyText
27322 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27323 html : this.rotateNotify
27329 cls : 'roo-upload-cropbox-footer',
27332 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27342 onRender : function(ct, position)
27344 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27346 if (this.buttons.length) {
27348 Roo.each(this.buttons, function(bb) {
27350 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27352 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27358 this.maskEl = this.el;
27362 initEvents : function()
27364 this.urlAPI = (window.createObjectURL && window) ||
27365 (window.URL && URL.revokeObjectURL && URL) ||
27366 (window.webkitURL && webkitURL);
27368 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27369 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27371 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27372 this.selectorEl.hide();
27374 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27375 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27377 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27378 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27379 this.thumbEl.hide();
27381 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27382 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27384 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27385 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27386 this.errorEl.hide();
27388 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27389 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27390 this.footerEl.hide();
27392 this.setThumbBoxSize();
27398 this.fireEvent('initial', this);
27405 window.addEventListener("resize", function() { _this.resize(); } );
27407 this.bodyEl.on('click', this.beforeSelectFile, this);
27410 this.bodyEl.on('touchstart', this.onTouchStart, this);
27411 this.bodyEl.on('touchmove', this.onTouchMove, this);
27412 this.bodyEl.on('touchend', this.onTouchEnd, this);
27416 this.bodyEl.on('mousedown', this.onMouseDown, this);
27417 this.bodyEl.on('mousemove', this.onMouseMove, this);
27418 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27419 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27420 Roo.get(document).on('mouseup', this.onMouseUp, this);
27423 this.selectorEl.on('change', this.onFileSelected, this);
27429 this.baseScale = 1;
27431 this.baseRotate = 1;
27432 this.dragable = false;
27433 this.pinching = false;
27436 this.cropData = false;
27437 this.notifyEl.dom.innerHTML = this.emptyText;
27439 this.selectorEl.dom.value = '';
27443 resize : function()
27445 if(this.fireEvent('resize', this) != false){
27446 this.setThumbBoxPosition();
27447 this.setCanvasPosition();
27451 onFooterButtonClick : function(e, el, o, type)
27454 case 'rotate-left' :
27455 this.onRotateLeft(e);
27457 case 'rotate-right' :
27458 this.onRotateRight(e);
27461 this.beforeSelectFile(e);
27476 this.fireEvent('footerbuttonclick', this, type);
27479 beforeSelectFile : function(e)
27481 e.preventDefault();
27483 if(this.fireEvent('beforeselectfile', this) != false){
27484 this.selectorEl.dom.click();
27488 onFileSelected : function(e)
27490 e.preventDefault();
27492 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27496 var file = this.selectorEl.dom.files[0];
27498 if(this.fireEvent('inspect', this, file) != false){
27499 this.prepare(file);
27504 trash : function(e)
27506 this.fireEvent('trash', this);
27509 download : function(e)
27511 this.fireEvent('download', this);
27514 loadCanvas : function(src)
27516 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27520 this.imageEl = document.createElement('img');
27524 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27526 this.imageEl.src = src;
27530 onLoadCanvas : function()
27532 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27533 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27535 this.bodyEl.un('click', this.beforeSelectFile, this);
27537 this.notifyEl.hide();
27538 this.thumbEl.show();
27539 this.footerEl.show();
27541 this.baseRotateLevel();
27543 if(this.isDocument){
27544 this.setThumbBoxSize();
27547 this.setThumbBoxPosition();
27549 this.baseScaleLevel();
27555 this.canvasLoaded = true;
27558 this.maskEl.unmask();
27563 setCanvasPosition : function()
27565 if(!this.canvasEl){
27569 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27570 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27572 this.previewEl.setLeft(pw);
27573 this.previewEl.setTop(ph);
27577 onMouseDown : function(e)
27581 this.dragable = true;
27582 this.pinching = false;
27584 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27585 this.dragable = false;
27589 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27590 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27594 onMouseMove : function(e)
27598 if(!this.canvasLoaded){
27602 if (!this.dragable){
27606 var minX = Math.ceil(this.thumbEl.getLeft(true));
27607 var minY = Math.ceil(this.thumbEl.getTop(true));
27609 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27610 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27612 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27613 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27615 x = x - this.mouseX;
27616 y = y - this.mouseY;
27618 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27619 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27621 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27622 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27624 this.previewEl.setLeft(bgX);
27625 this.previewEl.setTop(bgY);
27627 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27628 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27631 onMouseUp : function(e)
27635 this.dragable = false;
27638 onMouseWheel : function(e)
27642 this.startScale = this.scale;
27644 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27646 if(!this.zoomable()){
27647 this.scale = this.startScale;
27656 zoomable : function()
27658 var minScale = this.thumbEl.getWidth() / this.minWidth;
27660 if(this.minWidth < this.minHeight){
27661 minScale = this.thumbEl.getHeight() / this.minHeight;
27664 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27665 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27669 (this.rotate == 0 || this.rotate == 180) &&
27671 width > this.imageEl.OriginWidth ||
27672 height > this.imageEl.OriginHeight ||
27673 (width < this.minWidth && height < this.minHeight)
27681 (this.rotate == 90 || this.rotate == 270) &&
27683 width > this.imageEl.OriginWidth ||
27684 height > this.imageEl.OriginHeight ||
27685 (width < this.minHeight && height < this.minWidth)
27692 !this.isDocument &&
27693 (this.rotate == 0 || this.rotate == 180) &&
27695 width < this.minWidth ||
27696 width > this.imageEl.OriginWidth ||
27697 height < this.minHeight ||
27698 height > this.imageEl.OriginHeight
27705 !this.isDocument &&
27706 (this.rotate == 90 || this.rotate == 270) &&
27708 width < this.minHeight ||
27709 width > this.imageEl.OriginWidth ||
27710 height < this.minWidth ||
27711 height > this.imageEl.OriginHeight
27721 onRotateLeft : function(e)
27723 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27725 var minScale = this.thumbEl.getWidth() / this.minWidth;
27727 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27728 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27730 this.startScale = this.scale;
27732 while (this.getScaleLevel() < minScale){
27734 this.scale = this.scale + 1;
27736 if(!this.zoomable()){
27741 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27742 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27747 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27754 this.scale = this.startScale;
27756 this.onRotateFail();
27761 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27763 if(this.isDocument){
27764 this.setThumbBoxSize();
27765 this.setThumbBoxPosition();
27766 this.setCanvasPosition();
27771 this.fireEvent('rotate', this, 'left');
27775 onRotateRight : function(e)
27777 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27779 var minScale = this.thumbEl.getWidth() / this.minWidth;
27781 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27782 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27784 this.startScale = this.scale;
27786 while (this.getScaleLevel() < minScale){
27788 this.scale = this.scale + 1;
27790 if(!this.zoomable()){
27795 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27796 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27801 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27808 this.scale = this.startScale;
27810 this.onRotateFail();
27815 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27817 if(this.isDocument){
27818 this.setThumbBoxSize();
27819 this.setThumbBoxPosition();
27820 this.setCanvasPosition();
27825 this.fireEvent('rotate', this, 'right');
27828 onRotateFail : function()
27830 this.errorEl.show(true);
27834 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27839 this.previewEl.dom.innerHTML = '';
27841 var canvasEl = document.createElement("canvas");
27843 var contextEl = canvasEl.getContext("2d");
27845 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27846 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27847 var center = this.imageEl.OriginWidth / 2;
27849 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27850 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27851 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27852 center = this.imageEl.OriginHeight / 2;
27855 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27857 contextEl.translate(center, center);
27858 contextEl.rotate(this.rotate * Math.PI / 180);
27860 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27862 this.canvasEl = document.createElement("canvas");
27864 this.contextEl = this.canvasEl.getContext("2d");
27866 switch (this.rotate) {
27869 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27870 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27872 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27877 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27878 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27880 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27881 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);
27885 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27890 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27891 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27893 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27894 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);
27898 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);
27903 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27904 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27906 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27907 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27911 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);
27918 this.previewEl.appendChild(this.canvasEl);
27920 this.setCanvasPosition();
27925 if(!this.canvasLoaded){
27929 var imageCanvas = document.createElement("canvas");
27931 var imageContext = imageCanvas.getContext("2d");
27933 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27934 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27936 var center = imageCanvas.width / 2;
27938 imageContext.translate(center, center);
27940 imageContext.rotate(this.rotate * Math.PI / 180);
27942 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27944 var canvas = document.createElement("canvas");
27946 var context = canvas.getContext("2d");
27948 canvas.width = this.minWidth;
27949 canvas.height = this.minHeight;
27951 switch (this.rotate) {
27954 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27955 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27957 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27958 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27960 var targetWidth = this.minWidth - 2 * x;
27961 var targetHeight = this.minHeight - 2 * y;
27965 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27966 scale = targetWidth / width;
27969 if(x > 0 && y == 0){
27970 scale = targetHeight / height;
27973 if(x > 0 && y > 0){
27974 scale = targetWidth / width;
27976 if(width < height){
27977 scale = targetHeight / height;
27981 context.scale(scale, scale);
27983 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27984 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27986 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27987 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27989 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27994 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27995 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27997 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27998 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28000 var targetWidth = this.minWidth - 2 * x;
28001 var targetHeight = this.minHeight - 2 * y;
28005 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28006 scale = targetWidth / width;
28009 if(x > 0 && y == 0){
28010 scale = targetHeight / height;
28013 if(x > 0 && y > 0){
28014 scale = targetWidth / width;
28016 if(width < height){
28017 scale = targetHeight / height;
28021 context.scale(scale, scale);
28023 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28024 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28026 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28027 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28029 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28031 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28036 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28037 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28039 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28040 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28042 var targetWidth = this.minWidth - 2 * x;
28043 var targetHeight = this.minHeight - 2 * y;
28047 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28048 scale = targetWidth / width;
28051 if(x > 0 && y == 0){
28052 scale = targetHeight / height;
28055 if(x > 0 && y > 0){
28056 scale = targetWidth / width;
28058 if(width < height){
28059 scale = targetHeight / height;
28063 context.scale(scale, scale);
28065 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28066 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28068 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28069 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28071 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28072 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28074 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28079 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28080 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28082 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28083 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28085 var targetWidth = this.minWidth - 2 * x;
28086 var targetHeight = this.minHeight - 2 * y;
28090 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28091 scale = targetWidth / width;
28094 if(x > 0 && y == 0){
28095 scale = targetHeight / height;
28098 if(x > 0 && y > 0){
28099 scale = targetWidth / width;
28101 if(width < height){
28102 scale = targetHeight / height;
28106 context.scale(scale, scale);
28108 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28109 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28111 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28112 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28114 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28116 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28123 this.cropData = canvas.toDataURL(this.cropType);
28125 if(this.fireEvent('crop', this, this.cropData) !== false){
28126 this.process(this.file, this.cropData);
28133 setThumbBoxSize : function()
28137 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28138 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28139 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28141 this.minWidth = width;
28142 this.minHeight = height;
28144 if(this.rotate == 90 || this.rotate == 270){
28145 this.minWidth = height;
28146 this.minHeight = width;
28151 width = Math.ceil(this.minWidth * height / this.minHeight);
28153 if(this.minWidth > this.minHeight){
28155 height = Math.ceil(this.minHeight * width / this.minWidth);
28158 this.thumbEl.setStyle({
28159 width : width + 'px',
28160 height : height + 'px'
28167 setThumbBoxPosition : function()
28169 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28170 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28172 this.thumbEl.setLeft(x);
28173 this.thumbEl.setTop(y);
28177 baseRotateLevel : function()
28179 this.baseRotate = 1;
28182 typeof(this.exif) != 'undefined' &&
28183 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28184 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28186 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28189 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28193 baseScaleLevel : function()
28197 if(this.isDocument){
28199 if(this.baseRotate == 6 || this.baseRotate == 8){
28201 height = this.thumbEl.getHeight();
28202 this.baseScale = height / this.imageEl.OriginWidth;
28204 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28205 width = this.thumbEl.getWidth();
28206 this.baseScale = width / this.imageEl.OriginHeight;
28212 height = this.thumbEl.getHeight();
28213 this.baseScale = height / this.imageEl.OriginHeight;
28215 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28216 width = this.thumbEl.getWidth();
28217 this.baseScale = width / this.imageEl.OriginWidth;
28223 if(this.baseRotate == 6 || this.baseRotate == 8){
28225 width = this.thumbEl.getHeight();
28226 this.baseScale = width / this.imageEl.OriginHeight;
28228 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28229 height = this.thumbEl.getWidth();
28230 this.baseScale = height / this.imageEl.OriginHeight;
28233 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28234 height = this.thumbEl.getWidth();
28235 this.baseScale = height / this.imageEl.OriginHeight;
28237 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28238 width = this.thumbEl.getHeight();
28239 this.baseScale = width / this.imageEl.OriginWidth;
28246 width = this.thumbEl.getWidth();
28247 this.baseScale = width / this.imageEl.OriginWidth;
28249 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28250 height = this.thumbEl.getHeight();
28251 this.baseScale = height / this.imageEl.OriginHeight;
28254 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28256 height = this.thumbEl.getHeight();
28257 this.baseScale = height / this.imageEl.OriginHeight;
28259 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28260 width = this.thumbEl.getWidth();
28261 this.baseScale = width / this.imageEl.OriginWidth;
28269 getScaleLevel : function()
28271 return this.baseScale * Math.pow(1.1, this.scale);
28274 onTouchStart : function(e)
28276 if(!this.canvasLoaded){
28277 this.beforeSelectFile(e);
28281 var touches = e.browserEvent.touches;
28287 if(touches.length == 1){
28288 this.onMouseDown(e);
28292 if(touches.length != 2){
28298 for(var i = 0, finger; finger = touches[i]; i++){
28299 coords.push(finger.pageX, finger.pageY);
28302 var x = Math.pow(coords[0] - coords[2], 2);
28303 var y = Math.pow(coords[1] - coords[3], 2);
28305 this.startDistance = Math.sqrt(x + y);
28307 this.startScale = this.scale;
28309 this.pinching = true;
28310 this.dragable = false;
28314 onTouchMove : function(e)
28316 if(!this.pinching && !this.dragable){
28320 var touches = e.browserEvent.touches;
28327 this.onMouseMove(e);
28333 for(var i = 0, finger; finger = touches[i]; i++){
28334 coords.push(finger.pageX, finger.pageY);
28337 var x = Math.pow(coords[0] - coords[2], 2);
28338 var y = Math.pow(coords[1] - coords[3], 2);
28340 this.endDistance = Math.sqrt(x + y);
28342 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28344 if(!this.zoomable()){
28345 this.scale = this.startScale;
28353 onTouchEnd : function(e)
28355 this.pinching = false;
28356 this.dragable = false;
28360 process : function(file, crop)
28363 this.maskEl.mask(this.loadingText);
28366 this.xhr = new XMLHttpRequest();
28368 file.xhr = this.xhr;
28370 this.xhr.open(this.method, this.url, true);
28373 "Accept": "application/json",
28374 "Cache-Control": "no-cache",
28375 "X-Requested-With": "XMLHttpRequest"
28378 for (var headerName in headers) {
28379 var headerValue = headers[headerName];
28381 this.xhr.setRequestHeader(headerName, headerValue);
28387 this.xhr.onload = function()
28389 _this.xhrOnLoad(_this.xhr);
28392 this.xhr.onerror = function()
28394 _this.xhrOnError(_this.xhr);
28397 var formData = new FormData();
28399 formData.append('returnHTML', 'NO');
28402 formData.append('crop', crop);
28405 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28406 formData.append(this.paramName, file, file.name);
28409 if(typeof(file.filename) != 'undefined'){
28410 formData.append('filename', file.filename);
28413 if(typeof(file.mimetype) != 'undefined'){
28414 formData.append('mimetype', file.mimetype);
28417 if(this.fireEvent('arrange', this, formData) != false){
28418 this.xhr.send(formData);
28422 xhrOnLoad : function(xhr)
28425 this.maskEl.unmask();
28428 if (xhr.readyState !== 4) {
28429 this.fireEvent('exception', this, xhr);
28433 var response = Roo.decode(xhr.responseText);
28435 if(!response.success){
28436 this.fireEvent('exception', this, xhr);
28440 var response = Roo.decode(xhr.responseText);
28442 this.fireEvent('upload', this, response);
28446 xhrOnError : function()
28449 this.maskEl.unmask();
28452 Roo.log('xhr on error');
28454 var response = Roo.decode(xhr.responseText);
28460 prepare : function(file)
28463 this.maskEl.mask(this.loadingText);
28469 if(typeof(file) === 'string'){
28470 this.loadCanvas(file);
28474 if(!file || !this.urlAPI){
28479 this.cropType = file.type;
28483 if(this.fireEvent('prepare', this, this.file) != false){
28485 var reader = new FileReader();
28487 reader.onload = function (e) {
28488 if (e.target.error) {
28489 Roo.log(e.target.error);
28493 var buffer = e.target.result,
28494 dataView = new DataView(buffer),
28496 maxOffset = dataView.byteLength - 4,
28500 if (dataView.getUint16(0) === 0xffd8) {
28501 while (offset < maxOffset) {
28502 markerBytes = dataView.getUint16(offset);
28504 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28505 markerLength = dataView.getUint16(offset + 2) + 2;
28506 if (offset + markerLength > dataView.byteLength) {
28507 Roo.log('Invalid meta data: Invalid segment size.');
28511 if(markerBytes == 0xffe1){
28512 _this.parseExifData(
28519 offset += markerLength;
28529 var url = _this.urlAPI.createObjectURL(_this.file);
28531 _this.loadCanvas(url);
28536 reader.readAsArrayBuffer(this.file);
28542 parseExifData : function(dataView, offset, length)
28544 var tiffOffset = offset + 10,
28548 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28549 // No Exif data, might be XMP data instead
28553 // Check for the ASCII code for "Exif" (0x45786966):
28554 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28555 // No Exif data, might be XMP data instead
28558 if (tiffOffset + 8 > dataView.byteLength) {
28559 Roo.log('Invalid Exif data: Invalid segment size.');
28562 // Check for the two null bytes:
28563 if (dataView.getUint16(offset + 8) !== 0x0000) {
28564 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28567 // Check the byte alignment:
28568 switch (dataView.getUint16(tiffOffset)) {
28570 littleEndian = true;
28573 littleEndian = false;
28576 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28579 // Check for the TIFF tag marker (0x002A):
28580 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28581 Roo.log('Invalid Exif data: Missing TIFF marker.');
28584 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28585 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28587 this.parseExifTags(
28590 tiffOffset + dirOffset,
28595 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28600 if (dirOffset + 6 > dataView.byteLength) {
28601 Roo.log('Invalid Exif data: Invalid directory offset.');
28604 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28605 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28606 if (dirEndOffset + 4 > dataView.byteLength) {
28607 Roo.log('Invalid Exif data: Invalid directory size.');
28610 for (i = 0; i < tagsNumber; i += 1) {
28614 dirOffset + 2 + 12 * i, // tag offset
28618 // Return the offset to the next directory:
28619 return dataView.getUint32(dirEndOffset, littleEndian);
28622 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28624 var tag = dataView.getUint16(offset, littleEndian);
28626 this.exif[tag] = this.getExifValue(
28630 dataView.getUint16(offset + 2, littleEndian), // tag type
28631 dataView.getUint32(offset + 4, littleEndian), // tag length
28636 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28638 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28647 Roo.log('Invalid Exif data: Invalid tag type.');
28651 tagSize = tagType.size * length;
28652 // Determine if the value is contained in the dataOffset bytes,
28653 // or if the value at the dataOffset is a pointer to the actual data:
28654 dataOffset = tagSize > 4 ?
28655 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28656 if (dataOffset + tagSize > dataView.byteLength) {
28657 Roo.log('Invalid Exif data: Invalid data offset.');
28660 if (length === 1) {
28661 return tagType.getValue(dataView, dataOffset, littleEndian);
28664 for (i = 0; i < length; i += 1) {
28665 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28668 if (tagType.ascii) {
28670 // Concatenate the chars:
28671 for (i = 0; i < values.length; i += 1) {
28673 // Ignore the terminating NULL byte(s):
28674 if (c === '\u0000') {
28686 Roo.apply(Roo.bootstrap.UploadCropbox, {
28688 'Orientation': 0x0112
28692 1: 0, //'top-left',
28694 3: 180, //'bottom-right',
28695 // 4: 'bottom-left',
28697 6: 90, //'right-top',
28698 // 7: 'right-bottom',
28699 8: 270 //'left-bottom'
28703 // byte, 8-bit unsigned int:
28705 getValue: function (dataView, dataOffset) {
28706 return dataView.getUint8(dataOffset);
28710 // ascii, 8-bit byte:
28712 getValue: function (dataView, dataOffset) {
28713 return String.fromCharCode(dataView.getUint8(dataOffset));
28718 // short, 16 bit int:
28720 getValue: function (dataView, dataOffset, littleEndian) {
28721 return dataView.getUint16(dataOffset, littleEndian);
28725 // long, 32 bit int:
28727 getValue: function (dataView, dataOffset, littleEndian) {
28728 return dataView.getUint32(dataOffset, littleEndian);
28732 // rational = two long values, first is numerator, second is denominator:
28734 getValue: function (dataView, dataOffset, littleEndian) {
28735 return dataView.getUint32(dataOffset, littleEndian) /
28736 dataView.getUint32(dataOffset + 4, littleEndian);
28740 // slong, 32 bit signed int:
28742 getValue: function (dataView, dataOffset, littleEndian) {
28743 return dataView.getInt32(dataOffset, littleEndian);
28747 // srational, two slongs, first is numerator, second is denominator:
28749 getValue: function (dataView, dataOffset, littleEndian) {
28750 return dataView.getInt32(dataOffset, littleEndian) /
28751 dataView.getInt32(dataOffset + 4, littleEndian);
28761 cls : 'btn-group roo-upload-cropbox-rotate-left',
28762 action : 'rotate-left',
28766 cls : 'btn btn-default',
28767 html : '<i class="fa fa-undo"></i>'
28773 cls : 'btn-group roo-upload-cropbox-picture',
28774 action : 'picture',
28778 cls : 'btn btn-default',
28779 html : '<i class="fa fa-picture-o"></i>'
28785 cls : 'btn-group roo-upload-cropbox-rotate-right',
28786 action : 'rotate-right',
28790 cls : 'btn btn-default',
28791 html : '<i class="fa fa-repeat"></i>'
28799 cls : 'btn-group roo-upload-cropbox-rotate-left',
28800 action : 'rotate-left',
28804 cls : 'btn btn-default',
28805 html : '<i class="fa fa-undo"></i>'
28811 cls : 'btn-group roo-upload-cropbox-download',
28812 action : 'download',
28816 cls : 'btn btn-default',
28817 html : '<i class="fa fa-download"></i>'
28823 cls : 'btn-group roo-upload-cropbox-crop',
28828 cls : 'btn btn-default',
28829 html : '<i class="fa fa-crop"></i>'
28835 cls : 'btn-group roo-upload-cropbox-trash',
28840 cls : 'btn btn-default',
28841 html : '<i class="fa fa-trash"></i>'
28847 cls : 'btn-group roo-upload-cropbox-rotate-right',
28848 action : 'rotate-right',
28852 cls : 'btn btn-default',
28853 html : '<i class="fa fa-repeat"></i>'
28861 cls : 'btn-group roo-upload-cropbox-rotate-left',
28862 action : 'rotate-left',
28866 cls : 'btn btn-default',
28867 html : '<i class="fa fa-undo"></i>'
28873 cls : 'btn-group roo-upload-cropbox-rotate-right',
28874 action : 'rotate-right',
28878 cls : 'btn btn-default',
28879 html : '<i class="fa fa-repeat"></i>'
28892 * @class Roo.bootstrap.DocumentManager
28893 * @extends Roo.bootstrap.Component
28894 * Bootstrap DocumentManager class
28895 * @cfg {String} paramName default 'imageUpload'
28896 * @cfg {String} toolTipName default 'filename'
28897 * @cfg {String} method default POST
28898 * @cfg {String} url action url
28899 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28900 * @cfg {Boolean} multiple multiple upload default true
28901 * @cfg {Number} thumbSize default 300
28902 * @cfg {String} fieldLabel
28903 * @cfg {Number} labelWidth default 4
28904 * @cfg {String} labelAlign (left|top) default left
28905 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28906 * @cfg {Number} labellg set the width of label (1-12)
28907 * @cfg {Number} labelmd set the width of label (1-12)
28908 * @cfg {Number} labelsm set the width of label (1-12)
28909 * @cfg {Number} labelxs set the width of label (1-12)
28912 * Create a new DocumentManager
28913 * @param {Object} config The config object
28916 Roo.bootstrap.DocumentManager = function(config){
28917 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28920 this.delegates = [];
28925 * Fire when initial the DocumentManager
28926 * @param {Roo.bootstrap.DocumentManager} this
28931 * inspect selected file
28932 * @param {Roo.bootstrap.DocumentManager} this
28933 * @param {File} file
28938 * Fire when xhr load exception
28939 * @param {Roo.bootstrap.DocumentManager} this
28940 * @param {XMLHttpRequest} xhr
28942 "exception" : true,
28944 * @event afterupload
28945 * Fire when xhr load exception
28946 * @param {Roo.bootstrap.DocumentManager} this
28947 * @param {XMLHttpRequest} xhr
28949 "afterupload" : true,
28952 * prepare the form data
28953 * @param {Roo.bootstrap.DocumentManager} this
28954 * @param {Object} formData
28959 * Fire when remove the file
28960 * @param {Roo.bootstrap.DocumentManager} this
28961 * @param {Object} file
28966 * Fire after refresh the file
28967 * @param {Roo.bootstrap.DocumentManager} this
28972 * Fire after click the image
28973 * @param {Roo.bootstrap.DocumentManager} this
28974 * @param {Object} file
28979 * Fire when upload a image and editable set to true
28980 * @param {Roo.bootstrap.DocumentManager} this
28981 * @param {Object} file
28985 * @event beforeselectfile
28986 * Fire before select file
28987 * @param {Roo.bootstrap.DocumentManager} this
28989 "beforeselectfile" : true,
28992 * Fire before process file
28993 * @param {Roo.bootstrap.DocumentManager} this
28994 * @param {Object} file
28998 * @event previewrendered
28999 * Fire when preview rendered
29000 * @param {Roo.bootstrap.DocumentManager} this
29001 * @param {Object} file
29003 "previewrendered" : true,
29006 "previewResize" : true
29011 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29020 paramName : 'imageUpload',
29021 toolTipName : 'filename',
29024 labelAlign : 'left',
29034 getAutoCreate : function()
29036 var managerWidget = {
29038 cls : 'roo-document-manager',
29042 cls : 'roo-document-manager-selector',
29047 cls : 'roo-document-manager-uploader',
29051 cls : 'roo-document-manager-upload-btn',
29052 html : '<i class="fa fa-plus"></i>'
29063 cls : 'column col-md-12',
29068 if(this.fieldLabel.length){
29073 cls : 'column col-md-12',
29074 html : this.fieldLabel
29078 cls : 'column col-md-12',
29083 if(this.labelAlign == 'left'){
29088 html : this.fieldLabel
29097 if(this.labelWidth > 12){
29098 content[0].style = "width: " + this.labelWidth + 'px';
29101 if(this.labelWidth < 13 && this.labelmd == 0){
29102 this.labelmd = this.labelWidth;
29105 if(this.labellg > 0){
29106 content[0].cls += ' col-lg-' + this.labellg;
29107 content[1].cls += ' col-lg-' + (12 - this.labellg);
29110 if(this.labelmd > 0){
29111 content[0].cls += ' col-md-' + this.labelmd;
29112 content[1].cls += ' col-md-' + (12 - this.labelmd);
29115 if(this.labelsm > 0){
29116 content[0].cls += ' col-sm-' + this.labelsm;
29117 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29120 if(this.labelxs > 0){
29121 content[0].cls += ' col-xs-' + this.labelxs;
29122 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29130 cls : 'row clearfix',
29138 initEvents : function()
29140 this.managerEl = this.el.select('.roo-document-manager', true).first();
29141 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29143 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29144 this.selectorEl.hide();
29147 this.selectorEl.attr('multiple', 'multiple');
29150 this.selectorEl.on('change', this.onFileSelected, this);
29152 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29153 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29155 this.uploader.on('click', this.onUploaderClick, this);
29157 this.renderProgressDialog();
29161 window.addEventListener("resize", function() { _this.refresh(); } );
29163 this.fireEvent('initial', this);
29166 renderProgressDialog : function()
29170 this.progressDialog = new Roo.bootstrap.Modal({
29171 cls : 'roo-document-manager-progress-dialog',
29172 allow_close : false,
29182 btnclick : function() {
29183 _this.uploadCancel();
29189 this.progressDialog.render(Roo.get(document.body));
29191 this.progress = new Roo.bootstrap.Progress({
29192 cls : 'roo-document-manager-progress',
29197 this.progress.render(this.progressDialog.getChildContainer());
29199 this.progressBar = new Roo.bootstrap.ProgressBar({
29200 cls : 'roo-document-manager-progress-bar',
29203 aria_valuemax : 12,
29207 this.progressBar.render(this.progress.getChildContainer());
29210 onUploaderClick : function(e)
29212 e.preventDefault();
29214 if(this.fireEvent('beforeselectfile', this) != false){
29215 this.selectorEl.dom.click();
29220 onFileSelected : function(e)
29222 e.preventDefault();
29224 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29228 Roo.each(this.selectorEl.dom.files, function(file){
29229 if(this.fireEvent('inspect', this, file) != false){
29230 this.files.push(file);
29240 this.selectorEl.dom.value = '';
29242 if(!this.files || !this.files.length){
29246 if(this.boxes > 0 && this.files.length > this.boxes){
29247 this.files = this.files.slice(0, this.boxes);
29250 this.uploader.show();
29252 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29253 this.uploader.hide();
29262 Roo.each(this.files, function(file){
29264 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29265 var f = this.renderPreview(file);
29270 if(file.type.indexOf('image') != -1){
29271 this.delegates.push(
29273 _this.process(file);
29274 }).createDelegate(this)
29282 _this.process(file);
29283 }).createDelegate(this)
29288 this.files = files;
29290 this.delegates = this.delegates.concat(docs);
29292 if(!this.delegates.length){
29297 this.progressBar.aria_valuemax = this.delegates.length;
29304 arrange : function()
29306 if(!this.delegates.length){
29307 this.progressDialog.hide();
29312 var delegate = this.delegates.shift();
29314 this.progressDialog.show();
29316 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29318 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29323 refresh : function()
29325 this.uploader.show();
29327 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29328 this.uploader.hide();
29331 Roo.isTouch ? this.closable(false) : this.closable(true);
29333 this.fireEvent('refresh', this);
29336 onRemove : function(e, el, o)
29338 e.preventDefault();
29340 this.fireEvent('remove', this, o);
29344 remove : function(o)
29348 Roo.each(this.files, function(file){
29349 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29358 this.files = files;
29365 Roo.each(this.files, function(file){
29370 file.target.remove();
29379 onClick : function(e, el, o)
29381 e.preventDefault();
29383 this.fireEvent('click', this, o);
29387 closable : function(closable)
29389 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29391 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29403 xhrOnLoad : function(xhr)
29405 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29409 if (xhr.readyState !== 4) {
29411 this.fireEvent('exception', this, xhr);
29415 var response = Roo.decode(xhr.responseText);
29417 if(!response.success){
29419 this.fireEvent('exception', this, xhr);
29423 var file = this.renderPreview(response.data);
29425 this.files.push(file);
29429 this.fireEvent('afterupload', this, xhr);
29433 xhrOnError : function(xhr)
29435 Roo.log('xhr on error');
29437 var response = Roo.decode(xhr.responseText);
29444 process : function(file)
29446 if(this.fireEvent('process', this, file) !== false){
29447 if(this.editable && file.type.indexOf('image') != -1){
29448 this.fireEvent('edit', this, file);
29452 this.uploadStart(file, false);
29459 uploadStart : function(file, crop)
29461 this.xhr = new XMLHttpRequest();
29463 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29468 file.xhr = this.xhr;
29470 this.managerEl.createChild({
29472 cls : 'roo-document-manager-loading',
29476 tooltip : file.name,
29477 cls : 'roo-document-manager-thumb',
29478 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29484 this.xhr.open(this.method, this.url, true);
29487 "Accept": "application/json",
29488 "Cache-Control": "no-cache",
29489 "X-Requested-With": "XMLHttpRequest"
29492 for (var headerName in headers) {
29493 var headerValue = headers[headerName];
29495 this.xhr.setRequestHeader(headerName, headerValue);
29501 this.xhr.onload = function()
29503 _this.xhrOnLoad(_this.xhr);
29506 this.xhr.onerror = function()
29508 _this.xhrOnError(_this.xhr);
29511 var formData = new FormData();
29513 formData.append('returnHTML', 'NO');
29516 formData.append('crop', crop);
29519 formData.append(this.paramName, file, file.name);
29526 if(this.fireEvent('prepare', this, formData, options) != false){
29528 if(options.manually){
29532 this.xhr.send(formData);
29536 this.uploadCancel();
29539 uploadCancel : function()
29545 this.delegates = [];
29547 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29554 renderPreview : function(file)
29556 if(typeof(file.target) != 'undefined' && file.target){
29560 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29562 var previewEl = this.managerEl.createChild({
29564 cls : 'roo-document-manager-preview',
29568 tooltip : file[this.toolTipName],
29569 cls : 'roo-document-manager-thumb',
29570 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29575 html : '<i class="fa fa-times-circle"></i>'
29580 var close = previewEl.select('button.close', true).first();
29582 close.on('click', this.onRemove, this, file);
29584 file.target = previewEl;
29586 var image = previewEl.select('img', true).first();
29590 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29592 image.on('click', this.onClick, this, file);
29594 this.fireEvent('previewrendered', this, file);
29600 onPreviewLoad : function(file, image)
29602 if(typeof(file.target) == 'undefined' || !file.target){
29606 var width = image.dom.naturalWidth || image.dom.width;
29607 var height = image.dom.naturalHeight || image.dom.height;
29609 if(!this.previewResize) {
29613 if(width > height){
29614 file.target.addClass('wide');
29618 file.target.addClass('tall');
29623 uploadFromSource : function(file, crop)
29625 this.xhr = new XMLHttpRequest();
29627 this.managerEl.createChild({
29629 cls : 'roo-document-manager-loading',
29633 tooltip : file.name,
29634 cls : 'roo-document-manager-thumb',
29635 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29641 this.xhr.open(this.method, this.url, true);
29644 "Accept": "application/json",
29645 "Cache-Control": "no-cache",
29646 "X-Requested-With": "XMLHttpRequest"
29649 for (var headerName in headers) {
29650 var headerValue = headers[headerName];
29652 this.xhr.setRequestHeader(headerName, headerValue);
29658 this.xhr.onload = function()
29660 _this.xhrOnLoad(_this.xhr);
29663 this.xhr.onerror = function()
29665 _this.xhrOnError(_this.xhr);
29668 var formData = new FormData();
29670 formData.append('returnHTML', 'NO');
29672 formData.append('crop', crop);
29674 if(typeof(file.filename) != 'undefined'){
29675 formData.append('filename', file.filename);
29678 if(typeof(file.mimetype) != 'undefined'){
29679 formData.append('mimetype', file.mimetype);
29684 if(this.fireEvent('prepare', this, formData) != false){
29685 this.xhr.send(formData);
29695 * @class Roo.bootstrap.DocumentViewer
29696 * @extends Roo.bootstrap.Component
29697 * Bootstrap DocumentViewer class
29698 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29699 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29702 * Create a new DocumentViewer
29703 * @param {Object} config The config object
29706 Roo.bootstrap.DocumentViewer = function(config){
29707 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29712 * Fire after initEvent
29713 * @param {Roo.bootstrap.DocumentViewer} this
29719 * @param {Roo.bootstrap.DocumentViewer} this
29724 * Fire after download button
29725 * @param {Roo.bootstrap.DocumentViewer} this
29730 * Fire after trash button
29731 * @param {Roo.bootstrap.DocumentViewer} this
29738 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29740 showDownload : true,
29744 getAutoCreate : function()
29748 cls : 'roo-document-viewer',
29752 cls : 'roo-document-viewer-body',
29756 cls : 'roo-document-viewer-thumb',
29760 cls : 'roo-document-viewer-image'
29768 cls : 'roo-document-viewer-footer',
29771 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29775 cls : 'btn-group roo-document-viewer-download',
29779 cls : 'btn btn-default',
29780 html : '<i class="fa fa-download"></i>'
29786 cls : 'btn-group roo-document-viewer-trash',
29790 cls : 'btn btn-default',
29791 html : '<i class="fa fa-trash"></i>'
29804 initEvents : function()
29806 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29807 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29809 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29810 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29812 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29813 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29815 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29816 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29818 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29819 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29821 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29822 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29824 this.bodyEl.on('click', this.onClick, this);
29825 this.downloadBtn.on('click', this.onDownload, this);
29826 this.trashBtn.on('click', this.onTrash, this);
29828 this.downloadBtn.hide();
29829 this.trashBtn.hide();
29831 if(this.showDownload){
29832 this.downloadBtn.show();
29835 if(this.showTrash){
29836 this.trashBtn.show();
29839 if(!this.showDownload && !this.showTrash) {
29840 this.footerEl.hide();
29845 initial : function()
29847 this.fireEvent('initial', this);
29851 onClick : function(e)
29853 e.preventDefault();
29855 this.fireEvent('click', this);
29858 onDownload : function(e)
29860 e.preventDefault();
29862 this.fireEvent('download', this);
29865 onTrash : function(e)
29867 e.preventDefault();
29869 this.fireEvent('trash', this);
29881 * @class Roo.bootstrap.NavProgressBar
29882 * @extends Roo.bootstrap.Component
29883 * Bootstrap NavProgressBar class
29886 * Create a new nav progress bar
29887 * @param {Object} config The config object
29890 Roo.bootstrap.NavProgressBar = function(config){
29891 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29893 this.bullets = this.bullets || [];
29895 // Roo.bootstrap.NavProgressBar.register(this);
29899 * Fires when the active item changes
29900 * @param {Roo.bootstrap.NavProgressBar} this
29901 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29902 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29909 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29914 getAutoCreate : function()
29916 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29920 cls : 'roo-navigation-bar-group',
29924 cls : 'roo-navigation-top-bar'
29928 cls : 'roo-navigation-bullets-bar',
29932 cls : 'roo-navigation-bar'
29939 cls : 'roo-navigation-bottom-bar'
29949 initEvents: function()
29954 onRender : function(ct, position)
29956 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29958 if(this.bullets.length){
29959 Roo.each(this.bullets, function(b){
29968 addItem : function(cfg)
29970 var item = new Roo.bootstrap.NavProgressItem(cfg);
29972 item.parentId = this.id;
29973 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29976 var top = new Roo.bootstrap.Element({
29978 cls : 'roo-navigation-bar-text'
29981 var bottom = new Roo.bootstrap.Element({
29983 cls : 'roo-navigation-bar-text'
29986 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29987 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29989 var topText = new Roo.bootstrap.Element({
29991 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29994 var bottomText = new Roo.bootstrap.Element({
29996 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29999 topText.onRender(top.el, null);
30000 bottomText.onRender(bottom.el, null);
30003 item.bottomEl = bottom;
30006 this.barItems.push(item);
30011 getActive : function()
30013 var active = false;
30015 Roo.each(this.barItems, function(v){
30017 if (!v.isActive()) {
30029 setActiveItem : function(item)
30033 Roo.each(this.barItems, function(v){
30034 if (v.rid == item.rid) {
30038 if (v.isActive()) {
30039 v.setActive(false);
30044 item.setActive(true);
30046 this.fireEvent('changed', this, item, prev);
30049 getBarItem: function(rid)
30053 Roo.each(this.barItems, function(e) {
30054 if (e.rid != rid) {
30065 indexOfItem : function(item)
30069 Roo.each(this.barItems, function(v, i){
30071 if (v.rid != item.rid) {
30082 setActiveNext : function()
30084 var i = this.indexOfItem(this.getActive());
30086 if (i > this.barItems.length) {
30090 this.setActiveItem(this.barItems[i+1]);
30093 setActivePrev : function()
30095 var i = this.indexOfItem(this.getActive());
30101 this.setActiveItem(this.barItems[i-1]);
30104 format : function()
30106 if(!this.barItems.length){
30110 var width = 100 / this.barItems.length;
30112 Roo.each(this.barItems, function(i){
30113 i.el.setStyle('width', width + '%');
30114 i.topEl.el.setStyle('width', width + '%');
30115 i.bottomEl.el.setStyle('width', width + '%');
30124 * Nav Progress Item
30129 * @class Roo.bootstrap.NavProgressItem
30130 * @extends Roo.bootstrap.Component
30131 * Bootstrap NavProgressItem class
30132 * @cfg {String} rid the reference id
30133 * @cfg {Boolean} active (true|false) Is item active default false
30134 * @cfg {Boolean} disabled (true|false) Is item active default false
30135 * @cfg {String} html
30136 * @cfg {String} position (top|bottom) text position default bottom
30137 * @cfg {String} icon show icon instead of number
30140 * Create a new NavProgressItem
30141 * @param {Object} config The config object
30143 Roo.bootstrap.NavProgressItem = function(config){
30144 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30149 * The raw click event for the entire grid.
30150 * @param {Roo.bootstrap.NavProgressItem} this
30151 * @param {Roo.EventObject} e
30158 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30164 position : 'bottom',
30167 getAutoCreate : function()
30169 var iconCls = 'roo-navigation-bar-item-icon';
30171 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30175 cls: 'roo-navigation-bar-item',
30185 cfg.cls += ' active';
30188 cfg.cls += ' disabled';
30194 disable : function()
30196 this.setDisabled(true);
30199 enable : function()
30201 this.setDisabled(false);
30204 initEvents: function()
30206 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30208 this.iconEl.on('click', this.onClick, this);
30211 onClick : function(e)
30213 e.preventDefault();
30219 if(this.fireEvent('click', this, e) === false){
30223 this.parent().setActiveItem(this);
30226 isActive: function ()
30228 return this.active;
30231 setActive : function(state)
30233 if(this.active == state){
30237 this.active = state;
30240 this.el.addClass('active');
30244 this.el.removeClass('active');
30249 setDisabled : function(state)
30251 if(this.disabled == state){
30255 this.disabled = state;
30258 this.el.addClass('disabled');
30262 this.el.removeClass('disabled');
30265 tooltipEl : function()
30267 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30280 * @class Roo.bootstrap.FieldLabel
30281 * @extends Roo.bootstrap.Component
30282 * Bootstrap FieldLabel class
30283 * @cfg {String} html contents of the element
30284 * @cfg {String} tag tag of the element default label
30285 * @cfg {String} cls class of the element
30286 * @cfg {String} target label target
30287 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30288 * @cfg {String} invalidClass default "text-warning"
30289 * @cfg {String} validClass default "text-success"
30290 * @cfg {String} iconTooltip default "This field is required"
30291 * @cfg {String} indicatorpos (left|right) default left
30294 * Create a new FieldLabel
30295 * @param {Object} config The config object
30298 Roo.bootstrap.FieldLabel = function(config){
30299 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30304 * Fires after the field has been marked as invalid.
30305 * @param {Roo.form.FieldLabel} this
30306 * @param {String} msg The validation message
30311 * Fires after the field has been validated with no errors.
30312 * @param {Roo.form.FieldLabel} this
30318 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30325 invalidClass : 'has-warning',
30326 validClass : 'has-success',
30327 iconTooltip : 'This field is required',
30328 indicatorpos : 'left',
30330 getAutoCreate : function(){
30333 if (!this.allowBlank) {
30339 cls : 'roo-bootstrap-field-label ' + this.cls,
30344 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30345 tooltip : this.iconTooltip
30354 if(this.indicatorpos == 'right'){
30357 cls : 'roo-bootstrap-field-label ' + this.cls,
30366 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30367 tooltip : this.iconTooltip
30376 initEvents: function()
30378 Roo.bootstrap.Element.superclass.initEvents.call(this);
30380 this.indicator = this.indicatorEl();
30382 if(this.indicator){
30383 this.indicator.removeClass('visible');
30384 this.indicator.addClass('invisible');
30387 Roo.bootstrap.FieldLabel.register(this);
30390 indicatorEl : function()
30392 var indicator = this.el.select('i.roo-required-indicator',true).first();
30403 * Mark this field as valid
30405 markValid : function()
30407 if(this.indicator){
30408 this.indicator.removeClass('visible');
30409 this.indicator.addClass('invisible');
30412 this.el.removeClass(this.invalidClass);
30414 this.el.addClass(this.validClass);
30416 this.fireEvent('valid', this);
30420 * Mark this field as invalid
30421 * @param {String} msg The validation message
30423 markInvalid : function(msg)
30425 if(this.indicator){
30426 this.indicator.removeClass('invisible');
30427 this.indicator.addClass('visible');
30430 this.el.removeClass(this.validClass);
30432 this.el.addClass(this.invalidClass);
30434 this.fireEvent('invalid', this, msg);
30440 Roo.apply(Roo.bootstrap.FieldLabel, {
30445 * register a FieldLabel Group
30446 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30448 register : function(label)
30450 if(this.groups.hasOwnProperty(label.target)){
30454 this.groups[label.target] = label;
30458 * fetch a FieldLabel Group based on the target
30459 * @param {string} target
30460 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30462 get: function(target) {
30463 if (typeof(this.groups[target]) == 'undefined') {
30467 return this.groups[target] ;
30476 * page DateSplitField.
30482 * @class Roo.bootstrap.DateSplitField
30483 * @extends Roo.bootstrap.Component
30484 * Bootstrap DateSplitField class
30485 * @cfg {string} fieldLabel - the label associated
30486 * @cfg {Number} labelWidth set the width of label (0-12)
30487 * @cfg {String} labelAlign (top|left)
30488 * @cfg {Boolean} dayAllowBlank (true|false) default false
30489 * @cfg {Boolean} monthAllowBlank (true|false) default false
30490 * @cfg {Boolean} yearAllowBlank (true|false) default false
30491 * @cfg {string} dayPlaceholder
30492 * @cfg {string} monthPlaceholder
30493 * @cfg {string} yearPlaceholder
30494 * @cfg {string} dayFormat default 'd'
30495 * @cfg {string} monthFormat default 'm'
30496 * @cfg {string} yearFormat default 'Y'
30497 * @cfg {Number} labellg set the width of label (1-12)
30498 * @cfg {Number} labelmd set the width of label (1-12)
30499 * @cfg {Number} labelsm set the width of label (1-12)
30500 * @cfg {Number} labelxs set the width of label (1-12)
30504 * Create a new DateSplitField
30505 * @param {Object} config The config object
30508 Roo.bootstrap.DateSplitField = function(config){
30509 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30515 * getting the data of years
30516 * @param {Roo.bootstrap.DateSplitField} this
30517 * @param {Object} years
30522 * getting the data of days
30523 * @param {Roo.bootstrap.DateSplitField} this
30524 * @param {Object} days
30529 * Fires after the field has been marked as invalid.
30530 * @param {Roo.form.Field} this
30531 * @param {String} msg The validation message
30536 * Fires after the field has been validated with no errors.
30537 * @param {Roo.form.Field} this
30543 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30546 labelAlign : 'top',
30548 dayAllowBlank : false,
30549 monthAllowBlank : false,
30550 yearAllowBlank : false,
30551 dayPlaceholder : '',
30552 monthPlaceholder : '',
30553 yearPlaceholder : '',
30557 isFormField : true,
30563 getAutoCreate : function()
30567 cls : 'row roo-date-split-field-group',
30572 cls : 'form-hidden-field roo-date-split-field-group-value',
30578 var labelCls = 'col-md-12';
30579 var contentCls = 'col-md-4';
30581 if(this.fieldLabel){
30585 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30589 html : this.fieldLabel
30594 if(this.labelAlign == 'left'){
30596 if(this.labelWidth > 12){
30597 label.style = "width: " + this.labelWidth + 'px';
30600 if(this.labelWidth < 13 && this.labelmd == 0){
30601 this.labelmd = this.labelWidth;
30604 if(this.labellg > 0){
30605 labelCls = ' col-lg-' + this.labellg;
30606 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30609 if(this.labelmd > 0){
30610 labelCls = ' col-md-' + this.labelmd;
30611 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30614 if(this.labelsm > 0){
30615 labelCls = ' col-sm-' + this.labelsm;
30616 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30619 if(this.labelxs > 0){
30620 labelCls = ' col-xs-' + this.labelxs;
30621 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30625 label.cls += ' ' + labelCls;
30627 cfg.cn.push(label);
30630 Roo.each(['day', 'month', 'year'], function(t){
30633 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30640 inputEl: function ()
30642 return this.el.select('.roo-date-split-field-group-value', true).first();
30645 onRender : function(ct, position)
30649 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30651 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30653 this.dayField = new Roo.bootstrap.ComboBox({
30654 allowBlank : this.dayAllowBlank,
30655 alwaysQuery : true,
30656 displayField : 'value',
30659 forceSelection : true,
30661 placeholder : this.dayPlaceholder,
30662 selectOnFocus : true,
30663 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30664 triggerAction : 'all',
30666 valueField : 'value',
30667 store : new Roo.data.SimpleStore({
30668 data : (function() {
30670 _this.fireEvent('days', _this, days);
30673 fields : [ 'value' ]
30676 select : function (_self, record, index)
30678 _this.setValue(_this.getValue());
30683 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30685 this.monthField = new Roo.bootstrap.MonthField({
30686 after : '<i class=\"fa fa-calendar\"></i>',
30687 allowBlank : this.monthAllowBlank,
30688 placeholder : this.monthPlaceholder,
30691 render : function (_self)
30693 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30694 e.preventDefault();
30698 select : function (_self, oldvalue, newvalue)
30700 _this.setValue(_this.getValue());
30705 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30707 this.yearField = new Roo.bootstrap.ComboBox({
30708 allowBlank : this.yearAllowBlank,
30709 alwaysQuery : true,
30710 displayField : 'value',
30713 forceSelection : true,
30715 placeholder : this.yearPlaceholder,
30716 selectOnFocus : true,
30717 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30718 triggerAction : 'all',
30720 valueField : 'value',
30721 store : new Roo.data.SimpleStore({
30722 data : (function() {
30724 _this.fireEvent('years', _this, years);
30727 fields : [ 'value' ]
30730 select : function (_self, record, index)
30732 _this.setValue(_this.getValue());
30737 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30740 setValue : function(v, format)
30742 this.inputEl.dom.value = v;
30744 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30746 var d = Date.parseDate(v, f);
30753 this.setDay(d.format(this.dayFormat));
30754 this.setMonth(d.format(this.monthFormat));
30755 this.setYear(d.format(this.yearFormat));
30762 setDay : function(v)
30764 this.dayField.setValue(v);
30765 this.inputEl.dom.value = this.getValue();
30770 setMonth : function(v)
30772 this.monthField.setValue(v, true);
30773 this.inputEl.dom.value = this.getValue();
30778 setYear : function(v)
30780 this.yearField.setValue(v);
30781 this.inputEl.dom.value = this.getValue();
30786 getDay : function()
30788 return this.dayField.getValue();
30791 getMonth : function()
30793 return this.monthField.getValue();
30796 getYear : function()
30798 return this.yearField.getValue();
30801 getValue : function()
30803 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30805 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30815 this.inputEl.dom.value = '';
30820 validate : function()
30822 var d = this.dayField.validate();
30823 var m = this.monthField.validate();
30824 var y = this.yearField.validate();
30829 (!this.dayAllowBlank && !d) ||
30830 (!this.monthAllowBlank && !m) ||
30831 (!this.yearAllowBlank && !y)
30836 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30845 this.markInvalid();
30850 markValid : function()
30853 var label = this.el.select('label', true).first();
30854 var icon = this.el.select('i.fa-star', true).first();
30860 this.fireEvent('valid', this);
30864 * Mark this field as invalid
30865 * @param {String} msg The validation message
30867 markInvalid : function(msg)
30870 var label = this.el.select('label', true).first();
30871 var icon = this.el.select('i.fa-star', true).first();
30873 if(label && !icon){
30874 this.el.select('.roo-date-split-field-label', true).createChild({
30876 cls : 'text-danger fa fa-lg fa-star',
30877 tooltip : 'This field is required',
30878 style : 'margin-right:5px;'
30882 this.fireEvent('invalid', this, msg);
30885 clearInvalid : function()
30887 var label = this.el.select('label', true).first();
30888 var icon = this.el.select('i.fa-star', true).first();
30894 this.fireEvent('valid', this);
30897 getName: function()
30907 * http://masonry.desandro.com
30909 * The idea is to render all the bricks based on vertical width...
30911 * The original code extends 'outlayer' - we might need to use that....
30917 * @class Roo.bootstrap.LayoutMasonry
30918 * @extends Roo.bootstrap.Component
30919 * Bootstrap Layout Masonry class
30922 * Create a new Element
30923 * @param {Object} config The config object
30926 Roo.bootstrap.LayoutMasonry = function(config){
30928 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30932 Roo.bootstrap.LayoutMasonry.register(this);
30938 * Fire after layout the items
30939 * @param {Roo.bootstrap.LayoutMasonry} this
30940 * @param {Roo.EventObject} e
30947 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30950 * @cfg {Boolean} isLayoutInstant = no animation?
30952 isLayoutInstant : false, // needed?
30955 * @cfg {Number} boxWidth width of the columns
30960 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30965 * @cfg {Number} padWidth padding below box..
30970 * @cfg {Number} gutter gutter width..
30975 * @cfg {Number} maxCols maximum number of columns
30981 * @cfg {Boolean} isAutoInitial defalut true
30983 isAutoInitial : true,
30988 * @cfg {Boolean} isHorizontal defalut false
30990 isHorizontal : false,
30992 currentSize : null,
30998 bricks: null, //CompositeElement
31002 _isLayoutInited : false,
31004 // isAlternative : false, // only use for vertical layout...
31007 * @cfg {Number} alternativePadWidth padding below box..
31009 alternativePadWidth : 50,
31011 selectedBrick : [],
31013 getAutoCreate : function(){
31015 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31019 cls: 'blog-masonary-wrapper ' + this.cls,
31021 cls : 'mas-boxes masonary'
31028 getChildContainer: function( )
31030 if (this.boxesEl) {
31031 return this.boxesEl;
31034 this.boxesEl = this.el.select('.mas-boxes').first();
31036 return this.boxesEl;
31040 initEvents : function()
31044 if(this.isAutoInitial){
31045 Roo.log('hook children rendered');
31046 this.on('childrenrendered', function() {
31047 Roo.log('children rendered');
31053 initial : function()
31055 this.selectedBrick = [];
31057 this.currentSize = this.el.getBox(true);
31059 Roo.EventManager.onWindowResize(this.resize, this);
31061 if(!this.isAutoInitial){
31069 //this.layout.defer(500,this);
31073 resize : function()
31075 var cs = this.el.getBox(true);
31078 this.currentSize.width == cs.width &&
31079 this.currentSize.x == cs.x &&
31080 this.currentSize.height == cs.height &&
31081 this.currentSize.y == cs.y
31083 Roo.log("no change in with or X or Y");
31087 this.currentSize = cs;
31093 layout : function()
31095 this._resetLayout();
31097 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31099 this.layoutItems( isInstant );
31101 this._isLayoutInited = true;
31103 this.fireEvent('layout', this);
31107 _resetLayout : function()
31109 if(this.isHorizontal){
31110 this.horizontalMeasureColumns();
31114 this.verticalMeasureColumns();
31118 verticalMeasureColumns : function()
31120 this.getContainerWidth();
31122 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31123 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31127 var boxWidth = this.boxWidth + this.padWidth;
31129 if(this.containerWidth < this.boxWidth){
31130 boxWidth = this.containerWidth
31133 var containerWidth = this.containerWidth;
31135 var cols = Math.floor(containerWidth / boxWidth);
31137 this.cols = Math.max( cols, 1 );
31139 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31141 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31143 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31145 this.colWidth = boxWidth + avail - this.padWidth;
31147 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31148 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31151 horizontalMeasureColumns : function()
31153 this.getContainerWidth();
31155 var boxWidth = this.boxWidth;
31157 if(this.containerWidth < boxWidth){
31158 boxWidth = this.containerWidth;
31161 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31163 this.el.setHeight(boxWidth);
31167 getContainerWidth : function()
31169 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31172 layoutItems : function( isInstant )
31174 Roo.log(this.bricks);
31176 var items = Roo.apply([], this.bricks);
31178 if(this.isHorizontal){
31179 this._horizontalLayoutItems( items , isInstant );
31183 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31184 // this._verticalAlternativeLayoutItems( items , isInstant );
31188 this._verticalLayoutItems( items , isInstant );
31192 _verticalLayoutItems : function ( items , isInstant)
31194 if ( !items || !items.length ) {
31199 ['xs', 'xs', 'xs', 'tall'],
31200 ['xs', 'xs', 'tall'],
31201 ['xs', 'xs', 'sm'],
31202 ['xs', 'xs', 'xs'],
31208 ['sm', 'xs', 'xs'],
31212 ['tall', 'xs', 'xs', 'xs'],
31213 ['tall', 'xs', 'xs'],
31225 Roo.each(items, function(item, k){
31227 switch (item.size) {
31228 // these layouts take up a full box,
31239 boxes.push([item]);
31262 var filterPattern = function(box, length)
31270 var pattern = box.slice(0, length);
31274 Roo.each(pattern, function(i){
31275 format.push(i.size);
31278 Roo.each(standard, function(s){
31280 if(String(s) != String(format)){
31289 if(!match && length == 1){
31294 filterPattern(box, length - 1);
31298 queue.push(pattern);
31300 box = box.slice(length, box.length);
31302 filterPattern(box, 4);
31308 Roo.each(boxes, function(box, k){
31314 if(box.length == 1){
31319 filterPattern(box, 4);
31323 this._processVerticalLayoutQueue( queue, isInstant );
31327 // _verticalAlternativeLayoutItems : function( items , isInstant )
31329 // if ( !items || !items.length ) {
31333 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31337 _horizontalLayoutItems : function ( items , isInstant)
31339 if ( !items || !items.length || items.length < 3) {
31345 var eItems = items.slice(0, 3);
31347 items = items.slice(3, items.length);
31350 ['xs', 'xs', 'xs', 'wide'],
31351 ['xs', 'xs', 'wide'],
31352 ['xs', 'xs', 'sm'],
31353 ['xs', 'xs', 'xs'],
31359 ['sm', 'xs', 'xs'],
31363 ['wide', 'xs', 'xs', 'xs'],
31364 ['wide', 'xs', 'xs'],
31377 Roo.each(items, function(item, k){
31379 switch (item.size) {
31390 boxes.push([item]);
31414 var filterPattern = function(box, length)
31422 var pattern = box.slice(0, length);
31426 Roo.each(pattern, function(i){
31427 format.push(i.size);
31430 Roo.each(standard, function(s){
31432 if(String(s) != String(format)){
31441 if(!match && length == 1){
31446 filterPattern(box, length - 1);
31450 queue.push(pattern);
31452 box = box.slice(length, box.length);
31454 filterPattern(box, 4);
31460 Roo.each(boxes, function(box, k){
31466 if(box.length == 1){
31471 filterPattern(box, 4);
31478 var pos = this.el.getBox(true);
31482 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31484 var hit_end = false;
31486 Roo.each(queue, function(box){
31490 Roo.each(box, function(b){
31492 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31502 Roo.each(box, function(b){
31504 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31507 mx = Math.max(mx, b.x);
31511 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31515 Roo.each(box, function(b){
31517 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31531 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31534 /** Sets position of item in DOM
31535 * @param {Element} item
31536 * @param {Number} x - horizontal position
31537 * @param {Number} y - vertical position
31538 * @param {Boolean} isInstant - disables transitions
31540 _processVerticalLayoutQueue : function( queue, isInstant )
31542 var pos = this.el.getBox(true);
31547 for (var i = 0; i < this.cols; i++){
31551 Roo.each(queue, function(box, k){
31553 var col = k % this.cols;
31555 Roo.each(box, function(b,kk){
31557 b.el.position('absolute');
31559 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31560 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31562 if(b.size == 'md-left' || b.size == 'md-right'){
31563 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31564 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31567 b.el.setWidth(width);
31568 b.el.setHeight(height);
31570 b.el.select('iframe',true).setSize(width,height);
31574 for (var i = 0; i < this.cols; i++){
31576 if(maxY[i] < maxY[col]){
31581 col = Math.min(col, i);
31585 x = pos.x + col * (this.colWidth + this.padWidth);
31589 var positions = [];
31591 switch (box.length){
31593 positions = this.getVerticalOneBoxColPositions(x, y, box);
31596 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31599 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31602 positions = this.getVerticalFourBoxColPositions(x, y, box);
31608 Roo.each(box, function(b,kk){
31610 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31612 var sz = b.el.getSize();
31614 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31622 for (var i = 0; i < this.cols; i++){
31623 mY = Math.max(mY, maxY[i]);
31626 this.el.setHeight(mY - pos.y);
31630 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31632 // var pos = this.el.getBox(true);
31635 // var maxX = pos.right;
31637 // var maxHeight = 0;
31639 // Roo.each(items, function(item, k){
31643 // item.el.position('absolute');
31645 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31647 // item.el.setWidth(width);
31649 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31651 // item.el.setHeight(height);
31654 // item.el.setXY([x, y], isInstant ? false : true);
31656 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31659 // y = y + height + this.alternativePadWidth;
31661 // maxHeight = maxHeight + height + this.alternativePadWidth;
31665 // this.el.setHeight(maxHeight);
31669 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31671 var pos = this.el.getBox(true);
31676 var maxX = pos.right;
31678 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31680 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31682 Roo.each(queue, function(box, k){
31684 Roo.each(box, function(b, kk){
31686 b.el.position('absolute');
31688 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31689 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31691 if(b.size == 'md-left' || b.size == 'md-right'){
31692 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31693 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31696 b.el.setWidth(width);
31697 b.el.setHeight(height);
31705 var positions = [];
31707 switch (box.length){
31709 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31712 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31715 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31718 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31724 Roo.each(box, function(b,kk){
31726 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31728 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31736 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31738 Roo.each(eItems, function(b,k){
31740 b.size = (k == 0) ? 'sm' : 'xs';
31741 b.x = (k == 0) ? 2 : 1;
31742 b.y = (k == 0) ? 2 : 1;
31744 b.el.position('absolute');
31746 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31748 b.el.setWidth(width);
31750 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31752 b.el.setHeight(height);
31756 var positions = [];
31759 x : maxX - this.unitWidth * 2 - this.gutter,
31764 x : maxX - this.unitWidth,
31765 y : minY + (this.unitWidth + this.gutter) * 2
31769 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31773 Roo.each(eItems, function(b,k){
31775 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31781 getVerticalOneBoxColPositions : function(x, y, box)
31785 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31787 if(box[0].size == 'md-left'){
31791 if(box[0].size == 'md-right'){
31796 x : x + (this.unitWidth + this.gutter) * rand,
31803 getVerticalTwoBoxColPositions : function(x, y, box)
31807 if(box[0].size == 'xs'){
31811 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31815 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31829 x : x + (this.unitWidth + this.gutter) * 2,
31830 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31837 getVerticalThreeBoxColPositions : function(x, y, box)
31841 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31849 x : x + (this.unitWidth + this.gutter) * 1,
31854 x : x + (this.unitWidth + this.gutter) * 2,
31862 if(box[0].size == 'xs' && box[1].size == 'xs'){
31871 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31875 x : x + (this.unitWidth + this.gutter) * 1,
31889 x : x + (this.unitWidth + this.gutter) * 2,
31894 x : x + (this.unitWidth + this.gutter) * 2,
31895 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31902 getVerticalFourBoxColPositions : function(x, y, box)
31906 if(box[0].size == 'xs'){
31915 y : y + (this.unitHeight + this.gutter) * 1
31920 y : y + (this.unitHeight + this.gutter) * 2
31924 x : x + (this.unitWidth + this.gutter) * 1,
31938 x : x + (this.unitWidth + this.gutter) * 2,
31943 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31944 y : y + (this.unitHeight + this.gutter) * 1
31948 x : x + (this.unitWidth + this.gutter) * 2,
31949 y : y + (this.unitWidth + this.gutter) * 2
31956 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31960 if(box[0].size == 'md-left'){
31962 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31969 if(box[0].size == 'md-right'){
31971 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31972 y : minY + (this.unitWidth + this.gutter) * 1
31978 var rand = Math.floor(Math.random() * (4 - box[0].y));
31981 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31982 y : minY + (this.unitWidth + this.gutter) * rand
31989 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31993 if(box[0].size == 'xs'){
31996 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32001 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32010 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32015 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32016 y : minY + (this.unitWidth + this.gutter) * 2
32023 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32027 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32030 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32035 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32036 y : minY + (this.unitWidth + this.gutter) * 1
32040 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32041 y : minY + (this.unitWidth + this.gutter) * 2
32048 if(box[0].size == 'xs' && box[1].size == 'xs'){
32051 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32056 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32061 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32062 y : minY + (this.unitWidth + this.gutter) * 1
32070 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32075 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32076 y : minY + (this.unitWidth + this.gutter) * 2
32080 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32081 y : minY + (this.unitWidth + this.gutter) * 2
32088 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32092 if(box[0].size == 'xs'){
32095 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32100 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32105 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),
32110 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32111 y : minY + (this.unitWidth + this.gutter) * 1
32119 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32124 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32125 y : minY + (this.unitWidth + this.gutter) * 2
32129 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32130 y : minY + (this.unitWidth + this.gutter) * 2
32134 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),
32135 y : minY + (this.unitWidth + this.gutter) * 2
32143 * remove a Masonry Brick
32144 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32146 removeBrick : function(brick_id)
32152 for (var i = 0; i<this.bricks.length; i++) {
32153 if (this.bricks[i].id == brick_id) {
32154 this.bricks.splice(i,1);
32155 this.el.dom.removeChild(Roo.get(brick_id).dom);
32162 * adds a Masonry Brick
32163 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32165 addBrick : function(cfg)
32167 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32168 //this.register(cn);
32169 cn.parentId = this.id;
32170 cn.render(this.el);
32175 * register a Masonry Brick
32176 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32179 register : function(brick)
32181 this.bricks.push(brick);
32182 brick.masonryId = this.id;
32186 * clear all the Masonry Brick
32188 clearAll : function()
32191 //this.getChildContainer().dom.innerHTML = "";
32192 this.el.dom.innerHTML = '';
32195 getSelected : function()
32197 if (!this.selectedBrick) {
32201 return this.selectedBrick;
32205 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32209 * register a Masonry Layout
32210 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32213 register : function(layout)
32215 this.groups[layout.id] = layout;
32218 * fetch a Masonry Layout based on the masonry layout ID
32219 * @param {string} the masonry layout to add
32220 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32223 get: function(layout_id) {
32224 if (typeof(this.groups[layout_id]) == 'undefined') {
32227 return this.groups[layout_id] ;
32239 * http://masonry.desandro.com
32241 * The idea is to render all the bricks based on vertical width...
32243 * The original code extends 'outlayer' - we might need to use that....
32249 * @class Roo.bootstrap.LayoutMasonryAuto
32250 * @extends Roo.bootstrap.Component
32251 * Bootstrap Layout Masonry class
32254 * Create a new Element
32255 * @param {Object} config The config object
32258 Roo.bootstrap.LayoutMasonryAuto = function(config){
32259 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32262 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32265 * @cfg {Boolean} isFitWidth - resize the width..
32267 isFitWidth : false, // options..
32269 * @cfg {Boolean} isOriginLeft = left align?
32271 isOriginLeft : true,
32273 * @cfg {Boolean} isOriginTop = top align?
32275 isOriginTop : false,
32277 * @cfg {Boolean} isLayoutInstant = no animation?
32279 isLayoutInstant : false, // needed?
32281 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32283 isResizingContainer : true,
32285 * @cfg {Number} columnWidth width of the columns
32291 * @cfg {Number} maxCols maximum number of columns
32296 * @cfg {Number} padHeight padding below box..
32302 * @cfg {Boolean} isAutoInitial defalut true
32305 isAutoInitial : true,
32311 initialColumnWidth : 0,
32312 currentSize : null,
32314 colYs : null, // array.
32321 bricks: null, //CompositeElement
32322 cols : 0, // array?
32323 // element : null, // wrapped now this.el
32324 _isLayoutInited : null,
32327 getAutoCreate : function(){
32331 cls: 'blog-masonary-wrapper ' + this.cls,
32333 cls : 'mas-boxes masonary'
32340 getChildContainer: function( )
32342 if (this.boxesEl) {
32343 return this.boxesEl;
32346 this.boxesEl = this.el.select('.mas-boxes').first();
32348 return this.boxesEl;
32352 initEvents : function()
32356 if(this.isAutoInitial){
32357 Roo.log('hook children rendered');
32358 this.on('childrenrendered', function() {
32359 Roo.log('children rendered');
32366 initial : function()
32368 this.reloadItems();
32370 this.currentSize = this.el.getBox(true);
32372 /// was window resize... - let's see if this works..
32373 Roo.EventManager.onWindowResize(this.resize, this);
32375 if(!this.isAutoInitial){
32380 this.layout.defer(500,this);
32383 reloadItems: function()
32385 this.bricks = this.el.select('.masonry-brick', true);
32387 this.bricks.each(function(b) {
32388 //Roo.log(b.getSize());
32389 if (!b.attr('originalwidth')) {
32390 b.attr('originalwidth', b.getSize().width);
32395 Roo.log(this.bricks.elements.length);
32398 resize : function()
32401 var cs = this.el.getBox(true);
32403 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32404 Roo.log("no change in with or X");
32407 this.currentSize = cs;
32411 layout : function()
32414 this._resetLayout();
32415 //this._manageStamps();
32417 // don't animate first layout
32418 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32419 this.layoutItems( isInstant );
32421 // flag for initalized
32422 this._isLayoutInited = true;
32425 layoutItems : function( isInstant )
32427 //var items = this._getItemsForLayout( this.items );
32428 // original code supports filtering layout items.. we just ignore it..
32430 this._layoutItems( this.bricks , isInstant );
32432 this._postLayout();
32434 _layoutItems : function ( items , isInstant)
32436 //this.fireEvent( 'layout', this, items );
32439 if ( !items || !items.elements.length ) {
32440 // no items, emit event with empty array
32445 items.each(function(item) {
32446 Roo.log("layout item");
32448 // get x/y object from method
32449 var position = this._getItemLayoutPosition( item );
32451 position.item = item;
32452 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32453 queue.push( position );
32456 this._processLayoutQueue( queue );
32458 /** Sets position of item in DOM
32459 * @param {Element} item
32460 * @param {Number} x - horizontal position
32461 * @param {Number} y - vertical position
32462 * @param {Boolean} isInstant - disables transitions
32464 _processLayoutQueue : function( queue )
32466 for ( var i=0, len = queue.length; i < len; i++ ) {
32467 var obj = queue[i];
32468 obj.item.position('absolute');
32469 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32475 * Any logic you want to do after each layout,
32476 * i.e. size the container
32478 _postLayout : function()
32480 this.resizeContainer();
32483 resizeContainer : function()
32485 if ( !this.isResizingContainer ) {
32488 var size = this._getContainerSize();
32490 this.el.setSize(size.width,size.height);
32491 this.boxesEl.setSize(size.width,size.height);
32497 _resetLayout : function()
32499 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32500 this.colWidth = this.el.getWidth();
32501 //this.gutter = this.el.getWidth();
32503 this.measureColumns();
32509 this.colYs.push( 0 );
32515 measureColumns : function()
32517 this.getContainerWidth();
32518 // if columnWidth is 0, default to outerWidth of first item
32519 if ( !this.columnWidth ) {
32520 var firstItem = this.bricks.first();
32521 Roo.log(firstItem);
32522 this.columnWidth = this.containerWidth;
32523 if (firstItem && firstItem.attr('originalwidth') ) {
32524 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32526 // columnWidth fall back to item of first element
32527 Roo.log("set column width?");
32528 this.initialColumnWidth = this.columnWidth ;
32530 // if first elem has no width, default to size of container
32535 if (this.initialColumnWidth) {
32536 this.columnWidth = this.initialColumnWidth;
32541 // column width is fixed at the top - however if container width get's smaller we should
32544 // this bit calcs how man columns..
32546 var columnWidth = this.columnWidth += this.gutter;
32548 // calculate columns
32549 var containerWidth = this.containerWidth + this.gutter;
32551 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32552 // fix rounding errors, typically with gutters
32553 var excess = columnWidth - containerWidth % columnWidth;
32556 // if overshoot is less than a pixel, round up, otherwise floor it
32557 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32558 cols = Math[ mathMethod ]( cols );
32559 this.cols = Math.max( cols, 1 );
32560 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32562 // padding positioning..
32563 var totalColWidth = this.cols * this.columnWidth;
32564 var padavail = this.containerWidth - totalColWidth;
32565 // so for 2 columns - we need 3 'pads'
32567 var padNeeded = (1+this.cols) * this.padWidth;
32569 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32571 this.columnWidth += padExtra
32572 //this.padWidth = Math.floor(padavail / ( this.cols));
32574 // adjust colum width so that padding is fixed??
32576 // we have 3 columns ... total = width * 3
32577 // we have X left over... that should be used by
32579 //if (this.expandC) {
32587 getContainerWidth : function()
32589 /* // container is parent if fit width
32590 var container = this.isFitWidth ? this.element.parentNode : this.element;
32591 // check that this.size and size are there
32592 // IE8 triggers resize on body size change, so they might not be
32594 var size = getSize( container ); //FIXME
32595 this.containerWidth = size && size.innerWidth; //FIXME
32598 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32602 _getItemLayoutPosition : function( item ) // what is item?
32604 // we resize the item to our columnWidth..
32606 item.setWidth(this.columnWidth);
32607 item.autoBoxAdjust = false;
32609 var sz = item.getSize();
32611 // how many columns does this brick span
32612 var remainder = this.containerWidth % this.columnWidth;
32614 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32615 // round if off by 1 pixel, otherwise use ceil
32616 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32617 colSpan = Math.min( colSpan, this.cols );
32619 // normally this should be '1' as we dont' currently allow multi width columns..
32621 var colGroup = this._getColGroup( colSpan );
32622 // get the minimum Y value from the columns
32623 var minimumY = Math.min.apply( Math, colGroup );
32624 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32626 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32628 // position the brick
32630 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32631 y: this.currentSize.y + minimumY + this.padHeight
32635 // apply setHeight to necessary columns
32636 var setHeight = minimumY + sz.height + this.padHeight;
32637 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32639 var setSpan = this.cols + 1 - colGroup.length;
32640 for ( var i = 0; i < setSpan; i++ ) {
32641 this.colYs[ shortColIndex + i ] = setHeight ;
32648 * @param {Number} colSpan - number of columns the element spans
32649 * @returns {Array} colGroup
32651 _getColGroup : function( colSpan )
32653 if ( colSpan < 2 ) {
32654 // if brick spans only one column, use all the column Ys
32659 // how many different places could this brick fit horizontally
32660 var groupCount = this.cols + 1 - colSpan;
32661 // for each group potential horizontal position
32662 for ( var i = 0; i < groupCount; i++ ) {
32663 // make an array of colY values for that one group
32664 var groupColYs = this.colYs.slice( i, i + colSpan );
32665 // and get the max value of the array
32666 colGroup[i] = Math.max.apply( Math, groupColYs );
32671 _manageStamp : function( stamp )
32673 var stampSize = stamp.getSize();
32674 var offset = stamp.getBox();
32675 // get the columns that this stamp affects
32676 var firstX = this.isOriginLeft ? offset.x : offset.right;
32677 var lastX = firstX + stampSize.width;
32678 var firstCol = Math.floor( firstX / this.columnWidth );
32679 firstCol = Math.max( 0, firstCol );
32681 var lastCol = Math.floor( lastX / this.columnWidth );
32682 // lastCol should not go over if multiple of columnWidth #425
32683 lastCol -= lastX % this.columnWidth ? 0 : 1;
32684 lastCol = Math.min( this.cols - 1, lastCol );
32686 // set colYs to bottom of the stamp
32687 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32690 for ( var i = firstCol; i <= lastCol; i++ ) {
32691 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32696 _getContainerSize : function()
32698 this.maxY = Math.max.apply( Math, this.colYs );
32703 if ( this.isFitWidth ) {
32704 size.width = this._getContainerFitWidth();
32710 _getContainerFitWidth : function()
32712 var unusedCols = 0;
32713 // count unused columns
32716 if ( this.colYs[i] !== 0 ) {
32721 // fit container to columns that have been used
32722 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32725 needsResizeLayout : function()
32727 var previousWidth = this.containerWidth;
32728 this.getContainerWidth();
32729 return previousWidth !== this.containerWidth;
32744 * @class Roo.bootstrap.MasonryBrick
32745 * @extends Roo.bootstrap.Component
32746 * Bootstrap MasonryBrick class
32749 * Create a new MasonryBrick
32750 * @param {Object} config The config object
32753 Roo.bootstrap.MasonryBrick = function(config){
32755 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32757 Roo.bootstrap.MasonryBrick.register(this);
32763 * When a MasonryBrick is clcik
32764 * @param {Roo.bootstrap.MasonryBrick} this
32765 * @param {Roo.EventObject} e
32771 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32774 * @cfg {String} title
32778 * @cfg {String} html
32782 * @cfg {String} bgimage
32786 * @cfg {String} videourl
32790 * @cfg {String} cls
32794 * @cfg {String} href
32798 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32803 * @cfg {String} placetitle (center|bottom)
32808 * @cfg {Boolean} isFitContainer defalut true
32810 isFitContainer : true,
32813 * @cfg {Boolean} preventDefault defalut false
32815 preventDefault : false,
32818 * @cfg {Boolean} inverse defalut false
32820 maskInverse : false,
32822 getAutoCreate : function()
32824 if(!this.isFitContainer){
32825 return this.getSplitAutoCreate();
32828 var cls = 'masonry-brick masonry-brick-full';
32830 if(this.href.length){
32831 cls += ' masonry-brick-link';
32834 if(this.bgimage.length){
32835 cls += ' masonry-brick-image';
32838 if(this.maskInverse){
32839 cls += ' mask-inverse';
32842 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32843 cls += ' enable-mask';
32847 cls += ' masonry-' + this.size + '-brick';
32850 if(this.placetitle.length){
32852 switch (this.placetitle) {
32854 cls += ' masonry-center-title';
32857 cls += ' masonry-bottom-title';
32864 if(!this.html.length && !this.bgimage.length){
32865 cls += ' masonry-center-title';
32868 if(!this.html.length && this.bgimage.length){
32869 cls += ' masonry-bottom-title';
32874 cls += ' ' + this.cls;
32878 tag: (this.href.length) ? 'a' : 'div',
32883 cls: 'masonry-brick-mask'
32887 cls: 'masonry-brick-paragraph',
32893 if(this.href.length){
32894 cfg.href = this.href;
32897 var cn = cfg.cn[1].cn;
32899 if(this.title.length){
32902 cls: 'masonry-brick-title',
32907 if(this.html.length){
32910 cls: 'masonry-brick-text',
32915 if (!this.title.length && !this.html.length) {
32916 cfg.cn[1].cls += ' hide';
32919 if(this.bgimage.length){
32922 cls: 'masonry-brick-image-view',
32927 if(this.videourl.length){
32928 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32929 // youtube support only?
32932 cls: 'masonry-brick-image-view',
32935 allowfullscreen : true
32943 getSplitAutoCreate : function()
32945 var cls = 'masonry-brick masonry-brick-split';
32947 if(this.href.length){
32948 cls += ' masonry-brick-link';
32951 if(this.bgimage.length){
32952 cls += ' masonry-brick-image';
32956 cls += ' masonry-' + this.size + '-brick';
32959 switch (this.placetitle) {
32961 cls += ' masonry-center-title';
32964 cls += ' masonry-bottom-title';
32967 if(!this.bgimage.length){
32968 cls += ' masonry-center-title';
32971 if(this.bgimage.length){
32972 cls += ' masonry-bottom-title';
32978 cls += ' ' + this.cls;
32982 tag: (this.href.length) ? 'a' : 'div',
32987 cls: 'masonry-brick-split-head',
32991 cls: 'masonry-brick-paragraph',
32998 cls: 'masonry-brick-split-body',
33004 if(this.href.length){
33005 cfg.href = this.href;
33008 if(this.title.length){
33009 cfg.cn[0].cn[0].cn.push({
33011 cls: 'masonry-brick-title',
33016 if(this.html.length){
33017 cfg.cn[1].cn.push({
33019 cls: 'masonry-brick-text',
33024 if(this.bgimage.length){
33025 cfg.cn[0].cn.push({
33027 cls: 'masonry-brick-image-view',
33032 if(this.videourl.length){
33033 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33034 // youtube support only?
33035 cfg.cn[0].cn.cn.push({
33037 cls: 'masonry-brick-image-view',
33040 allowfullscreen : true
33047 initEvents: function()
33049 switch (this.size) {
33082 this.el.on('touchstart', this.onTouchStart, this);
33083 this.el.on('touchmove', this.onTouchMove, this);
33084 this.el.on('touchend', this.onTouchEnd, this);
33085 this.el.on('contextmenu', this.onContextMenu, this);
33087 this.el.on('mouseenter' ,this.enter, this);
33088 this.el.on('mouseleave', this.leave, this);
33089 this.el.on('click', this.onClick, this);
33092 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33093 this.parent().bricks.push(this);
33098 onClick: function(e, el)
33100 var time = this.endTimer - this.startTimer;
33101 // Roo.log(e.preventDefault());
33104 e.preventDefault();
33109 if(!this.preventDefault){
33113 e.preventDefault();
33115 if (this.activeClass != '') {
33116 this.selectBrick();
33119 this.fireEvent('click', this, e);
33122 enter: function(e, el)
33124 e.preventDefault();
33126 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33130 if(this.bgimage.length && this.html.length){
33131 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33135 leave: function(e, el)
33137 e.preventDefault();
33139 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33143 if(this.bgimage.length && this.html.length){
33144 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33148 onTouchStart: function(e, el)
33150 // e.preventDefault();
33152 this.touchmoved = false;
33154 if(!this.isFitContainer){
33158 if(!this.bgimage.length || !this.html.length){
33162 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33164 this.timer = new Date().getTime();
33168 onTouchMove: function(e, el)
33170 this.touchmoved = true;
33173 onContextMenu : function(e,el)
33175 e.preventDefault();
33176 e.stopPropagation();
33180 onTouchEnd: function(e, el)
33182 // e.preventDefault();
33184 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33191 if(!this.bgimage.length || !this.html.length){
33193 if(this.href.length){
33194 window.location.href = this.href;
33200 if(!this.isFitContainer){
33204 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33206 window.location.href = this.href;
33209 //selection on single brick only
33210 selectBrick : function() {
33212 if (!this.parentId) {
33216 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33217 var index = m.selectedBrick.indexOf(this.id);
33220 m.selectedBrick.splice(index,1);
33221 this.el.removeClass(this.activeClass);
33225 for(var i = 0; i < m.selectedBrick.length; i++) {
33226 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33227 b.el.removeClass(b.activeClass);
33230 m.selectedBrick = [];
33232 m.selectedBrick.push(this.id);
33233 this.el.addClass(this.activeClass);
33237 isSelected : function(){
33238 return this.el.hasClass(this.activeClass);
33243 Roo.apply(Roo.bootstrap.MasonryBrick, {
33246 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33248 * register a Masonry Brick
33249 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33252 register : function(brick)
33254 //this.groups[brick.id] = brick;
33255 this.groups.add(brick.id, brick);
33258 * fetch a masonry brick based on the masonry brick ID
33259 * @param {string} the masonry brick to add
33260 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33263 get: function(brick_id)
33265 // if (typeof(this.groups[brick_id]) == 'undefined') {
33268 // return this.groups[brick_id] ;
33270 if(this.groups.key(brick_id)) {
33271 return this.groups.key(brick_id);
33289 * @class Roo.bootstrap.Brick
33290 * @extends Roo.bootstrap.Component
33291 * Bootstrap Brick class
33294 * Create a new Brick
33295 * @param {Object} config The config object
33298 Roo.bootstrap.Brick = function(config){
33299 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33305 * When a Brick is click
33306 * @param {Roo.bootstrap.Brick} this
33307 * @param {Roo.EventObject} e
33313 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33316 * @cfg {String} title
33320 * @cfg {String} html
33324 * @cfg {String} bgimage
33328 * @cfg {String} cls
33332 * @cfg {String} href
33336 * @cfg {String} video
33340 * @cfg {Boolean} square
33344 getAutoCreate : function()
33346 var cls = 'roo-brick';
33348 if(this.href.length){
33349 cls += ' roo-brick-link';
33352 if(this.bgimage.length){
33353 cls += ' roo-brick-image';
33356 if(!this.html.length && !this.bgimage.length){
33357 cls += ' roo-brick-center-title';
33360 if(!this.html.length && this.bgimage.length){
33361 cls += ' roo-brick-bottom-title';
33365 cls += ' ' + this.cls;
33369 tag: (this.href.length) ? 'a' : 'div',
33374 cls: 'roo-brick-paragraph',
33380 if(this.href.length){
33381 cfg.href = this.href;
33384 var cn = cfg.cn[0].cn;
33386 if(this.title.length){
33389 cls: 'roo-brick-title',
33394 if(this.html.length){
33397 cls: 'roo-brick-text',
33404 if(this.bgimage.length){
33407 cls: 'roo-brick-image-view',
33415 initEvents: function()
33417 if(this.title.length || this.html.length){
33418 this.el.on('mouseenter' ,this.enter, this);
33419 this.el.on('mouseleave', this.leave, this);
33422 Roo.EventManager.onWindowResize(this.resize, this);
33424 if(this.bgimage.length){
33425 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33426 this.imageEl.on('load', this.onImageLoad, this);
33433 onImageLoad : function()
33438 resize : function()
33440 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33442 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33444 if(this.bgimage.length){
33445 var image = this.el.select('.roo-brick-image-view', true).first();
33447 image.setWidth(paragraph.getWidth());
33450 image.setHeight(paragraph.getWidth());
33453 this.el.setHeight(image.getHeight());
33454 paragraph.setHeight(image.getHeight());
33460 enter: function(e, el)
33462 e.preventDefault();
33464 if(this.bgimage.length){
33465 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33466 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33470 leave: function(e, el)
33472 e.preventDefault();
33474 if(this.bgimage.length){
33475 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33476 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33491 * @class Roo.bootstrap.NumberField
33492 * @extends Roo.bootstrap.Input
33493 * Bootstrap NumberField class
33499 * Create a new NumberField
33500 * @param {Object} config The config object
33503 Roo.bootstrap.NumberField = function(config){
33504 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33507 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33510 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33512 allowDecimals : true,
33514 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33516 decimalSeparator : ".",
33518 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33520 decimalPrecision : 2,
33522 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33524 allowNegative : true,
33527 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33531 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33533 minValue : Number.NEGATIVE_INFINITY,
33535 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33537 maxValue : Number.MAX_VALUE,
33539 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33541 minText : "The minimum value for this field is {0}",
33543 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33545 maxText : "The maximum value for this field is {0}",
33547 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33548 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33550 nanText : "{0} is not a valid number",
33552 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33554 thousandsDelimiter : false,
33556 * @cfg {String} valueAlign alignment of value
33558 valueAlign : "left",
33560 getAutoCreate : function()
33562 var hiddenInput = {
33566 cls: 'hidden-number-input'
33570 hiddenInput.name = this.name;
33575 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33577 this.name = hiddenInput.name;
33579 if(cfg.cn.length > 0) {
33580 cfg.cn.push(hiddenInput);
33587 initEvents : function()
33589 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33591 var allowed = "0123456789";
33593 if(this.allowDecimals){
33594 allowed += this.decimalSeparator;
33597 if(this.allowNegative){
33601 if(this.thousandsDelimiter) {
33605 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33607 var keyPress = function(e){
33609 var k = e.getKey();
33611 var c = e.getCharCode();
33614 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33615 allowed.indexOf(String.fromCharCode(c)) === -1
33621 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33625 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33630 this.el.on("keypress", keyPress, this);
33633 validateValue : function(value)
33636 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33640 var num = this.parseValue(value);
33643 this.markInvalid(String.format(this.nanText, value));
33647 if(num < this.minValue){
33648 this.markInvalid(String.format(this.minText, this.minValue));
33652 if(num > this.maxValue){
33653 this.markInvalid(String.format(this.maxText, this.maxValue));
33660 getValue : function()
33662 var v = this.hiddenEl().getValue();
33664 return this.fixPrecision(this.parseValue(v));
33667 parseValue : function(value)
33669 if(this.thousandsDelimiter) {
33671 r = new RegExp(",", "g");
33672 value = value.replace(r, "");
33675 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33676 return isNaN(value) ? '' : value;
33679 fixPrecision : function(value)
33681 if(this.thousandsDelimiter) {
33683 r = new RegExp(",", "g");
33684 value = value.replace(r, "");
33687 var nan = isNaN(value);
33689 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33690 return nan ? '' : value;
33692 return parseFloat(value).toFixed(this.decimalPrecision);
33695 setValue : function(v)
33697 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33703 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33705 this.inputEl().dom.value = (v == '') ? '' :
33706 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33708 if(!this.allowZero && v === '0') {
33709 this.hiddenEl().dom.value = '';
33710 this.inputEl().dom.value = '';
33717 decimalPrecisionFcn : function(v)
33719 return Math.floor(v);
33722 beforeBlur : function()
33724 var v = this.parseValue(this.getRawValue());
33726 if(v || v === 0 || v === ''){
33731 hiddenEl : function()
33733 return this.el.select('input.hidden-number-input',true).first();
33745 * @class Roo.bootstrap.DocumentSlider
33746 * @extends Roo.bootstrap.Component
33747 * Bootstrap DocumentSlider class
33750 * Create a new DocumentViewer
33751 * @param {Object} config The config object
33754 Roo.bootstrap.DocumentSlider = function(config){
33755 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33762 * Fire after initEvent
33763 * @param {Roo.bootstrap.DocumentSlider} this
33768 * Fire after update
33769 * @param {Roo.bootstrap.DocumentSlider} this
33775 * @param {Roo.bootstrap.DocumentSlider} this
33781 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33787 getAutoCreate : function()
33791 cls : 'roo-document-slider',
33795 cls : 'roo-document-slider-header',
33799 cls : 'roo-document-slider-header-title'
33805 cls : 'roo-document-slider-body',
33809 cls : 'roo-document-slider-prev',
33813 cls : 'fa fa-chevron-left'
33819 cls : 'roo-document-slider-thumb',
33823 cls : 'roo-document-slider-image'
33829 cls : 'roo-document-slider-next',
33833 cls : 'fa fa-chevron-right'
33845 initEvents : function()
33847 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33848 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33850 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33851 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33853 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33854 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33856 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33857 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33859 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33860 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33862 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33863 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33865 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33866 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33868 this.thumbEl.on('click', this.onClick, this);
33870 this.prevIndicator.on('click', this.prev, this);
33872 this.nextIndicator.on('click', this.next, this);
33876 initial : function()
33878 if(this.files.length){
33879 this.indicator = 1;
33883 this.fireEvent('initial', this);
33886 update : function()
33888 this.imageEl.attr('src', this.files[this.indicator - 1]);
33890 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33892 this.prevIndicator.show();
33894 if(this.indicator == 1){
33895 this.prevIndicator.hide();
33898 this.nextIndicator.show();
33900 if(this.indicator == this.files.length){
33901 this.nextIndicator.hide();
33904 this.thumbEl.scrollTo('top');
33906 this.fireEvent('update', this);
33909 onClick : function(e)
33911 e.preventDefault();
33913 this.fireEvent('click', this);
33918 e.preventDefault();
33920 this.indicator = Math.max(1, this.indicator - 1);
33927 e.preventDefault();
33929 this.indicator = Math.min(this.files.length, this.indicator + 1);
33943 * @class Roo.bootstrap.RadioSet
33944 * @extends Roo.bootstrap.Input
33945 * Bootstrap RadioSet class
33946 * @cfg {String} indicatorpos (left|right) default left
33947 * @cfg {Boolean} inline (true|false) inline the element (default true)
33948 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33950 * Create a new RadioSet
33951 * @param {Object} config The config object
33954 Roo.bootstrap.RadioSet = function(config){
33956 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33960 Roo.bootstrap.RadioSet.register(this);
33965 * Fires when the element is checked or unchecked.
33966 * @param {Roo.bootstrap.RadioSet} this This radio
33967 * @param {Roo.bootstrap.Radio} item The checked item
33972 * Fires when the element is click.
33973 * @param {Roo.bootstrap.RadioSet} this This radio set
33974 * @param {Roo.bootstrap.Radio} item The checked item
33975 * @param {Roo.EventObject} e The event object
33982 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33990 indicatorpos : 'left',
33992 getAutoCreate : function()
33996 cls : 'roo-radio-set-label',
34000 html : this.fieldLabel
34005 if(this.indicatorpos == 'left'){
34008 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34009 tooltip : 'This field is required'
34014 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34015 tooltip : 'This field is required'
34021 cls : 'roo-radio-set-items'
34024 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34026 if (align === 'left' && this.fieldLabel.length) {
34029 cls : "roo-radio-set-right",
34035 if(this.labelWidth > 12){
34036 label.style = "width: " + this.labelWidth + 'px';
34039 if(this.labelWidth < 13 && this.labelmd == 0){
34040 this.labelmd = this.labelWidth;
34043 if(this.labellg > 0){
34044 label.cls += ' col-lg-' + this.labellg;
34045 items.cls += ' col-lg-' + (12 - this.labellg);
34048 if(this.labelmd > 0){
34049 label.cls += ' col-md-' + this.labelmd;
34050 items.cls += ' col-md-' + (12 - this.labelmd);
34053 if(this.labelsm > 0){
34054 label.cls += ' col-sm-' + this.labelsm;
34055 items.cls += ' col-sm-' + (12 - this.labelsm);
34058 if(this.labelxs > 0){
34059 label.cls += ' col-xs-' + this.labelxs;
34060 items.cls += ' col-xs-' + (12 - this.labelxs);
34066 cls : 'roo-radio-set',
34070 cls : 'roo-radio-set-input',
34073 value : this.value ? this.value : ''
34080 if(this.weight.length){
34081 cfg.cls += ' roo-radio-' + this.weight;
34085 cfg.cls += ' roo-radio-set-inline';
34089 ['xs','sm','md','lg'].map(function(size){
34090 if (settings[size]) {
34091 cfg.cls += ' col-' + size + '-' + settings[size];
34099 initEvents : function()
34101 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34102 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34104 if(!this.fieldLabel.length){
34105 this.labelEl.hide();
34108 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34109 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34111 this.indicator = this.indicatorEl();
34113 if(this.indicator){
34114 this.indicator.addClass('invisible');
34117 this.originalValue = this.getValue();
34121 inputEl: function ()
34123 return this.el.select('.roo-radio-set-input', true).first();
34126 getChildContainer : function()
34128 return this.itemsEl;
34131 register : function(item)
34133 this.radioes.push(item);
34137 validate : function()
34139 if(this.getVisibilityEl().hasClass('hidden')){
34145 Roo.each(this.radioes, function(i){
34154 if(this.allowBlank) {
34158 if(this.disabled || valid){
34163 this.markInvalid();
34168 markValid : function()
34170 if(this.labelEl.isVisible(true)){
34171 this.indicatorEl().removeClass('visible');
34172 this.indicatorEl().addClass('invisible');
34175 this.el.removeClass([this.invalidClass, this.validClass]);
34176 this.el.addClass(this.validClass);
34178 this.fireEvent('valid', this);
34181 markInvalid : function(msg)
34183 if(this.allowBlank || this.disabled){
34187 if(this.labelEl.isVisible(true)){
34188 this.indicatorEl().removeClass('invisible');
34189 this.indicatorEl().addClass('visible');
34192 this.el.removeClass([this.invalidClass, this.validClass]);
34193 this.el.addClass(this.invalidClass);
34195 this.fireEvent('invalid', this, msg);
34199 setValue : function(v, suppressEvent)
34201 if(this.value === v){
34208 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34211 Roo.each(this.radioes, function(i){
34213 i.el.removeClass('checked');
34216 Roo.each(this.radioes, function(i){
34218 if(i.value === v || i.value.toString() === v.toString()){
34220 i.el.addClass('checked');
34222 if(suppressEvent !== true){
34223 this.fireEvent('check', this, i);
34234 clearInvalid : function(){
34236 if(!this.el || this.preventMark){
34240 this.el.removeClass([this.invalidClass]);
34242 this.fireEvent('valid', this);
34247 Roo.apply(Roo.bootstrap.RadioSet, {
34251 register : function(set)
34253 this.groups[set.name] = set;
34256 get: function(name)
34258 if (typeof(this.groups[name]) == 'undefined') {
34262 return this.groups[name] ;
34268 * Ext JS Library 1.1.1
34269 * Copyright(c) 2006-2007, Ext JS, LLC.
34271 * Originally Released Under LGPL - original licence link has changed is not relivant.
34274 * <script type="text/javascript">
34279 * @class Roo.bootstrap.SplitBar
34280 * @extends Roo.util.Observable
34281 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34285 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34286 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34287 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34288 split.minSize = 100;
34289 split.maxSize = 600;
34290 split.animate = true;
34291 split.on('moved', splitterMoved);
34294 * Create a new SplitBar
34295 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34296 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34297 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34298 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34299 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34300 position of the SplitBar).
34302 Roo.bootstrap.SplitBar = function(cfg){
34307 // dragElement : elm
34308 // resizingElement: el,
34310 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34311 // placement : Roo.bootstrap.SplitBar.LEFT ,
34312 // existingProxy ???
34315 this.el = Roo.get(cfg.dragElement, true);
34316 this.el.dom.unselectable = "on";
34318 this.resizingEl = Roo.get(cfg.resizingElement, true);
34322 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34323 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34326 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34329 * The minimum size of the resizing element. (Defaults to 0)
34335 * The maximum size of the resizing element. (Defaults to 2000)
34338 this.maxSize = 2000;
34341 * Whether to animate the transition to the new size
34344 this.animate = false;
34347 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34350 this.useShim = false;
34355 if(!cfg.existingProxy){
34357 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34359 this.proxy = Roo.get(cfg.existingProxy).dom;
34362 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34365 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34368 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34371 this.dragSpecs = {};
34374 * @private The adapter to use to positon and resize elements
34376 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34377 this.adapter.init(this);
34379 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34381 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34382 this.el.addClass("roo-splitbar-h");
34385 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34386 this.el.addClass("roo-splitbar-v");
34392 * Fires when the splitter is moved (alias for {@link #event-moved})
34393 * @param {Roo.bootstrap.SplitBar} this
34394 * @param {Number} newSize the new width or height
34399 * Fires when the splitter is moved
34400 * @param {Roo.bootstrap.SplitBar} this
34401 * @param {Number} newSize the new width or height
34405 * @event beforeresize
34406 * Fires before the splitter is dragged
34407 * @param {Roo.bootstrap.SplitBar} this
34409 "beforeresize" : true,
34411 "beforeapply" : true
34414 Roo.util.Observable.call(this);
34417 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34418 onStartProxyDrag : function(x, y){
34419 this.fireEvent("beforeresize", this);
34421 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34423 o.enableDisplayMode("block");
34424 // all splitbars share the same overlay
34425 Roo.bootstrap.SplitBar.prototype.overlay = o;
34427 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34428 this.overlay.show();
34429 Roo.get(this.proxy).setDisplayed("block");
34430 var size = this.adapter.getElementSize(this);
34431 this.activeMinSize = this.getMinimumSize();;
34432 this.activeMaxSize = this.getMaximumSize();;
34433 var c1 = size - this.activeMinSize;
34434 var c2 = Math.max(this.activeMaxSize - size, 0);
34435 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34436 this.dd.resetConstraints();
34437 this.dd.setXConstraint(
34438 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34439 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34441 this.dd.setYConstraint(0, 0);
34443 this.dd.resetConstraints();
34444 this.dd.setXConstraint(0, 0);
34445 this.dd.setYConstraint(
34446 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34447 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34450 this.dragSpecs.startSize = size;
34451 this.dragSpecs.startPoint = [x, y];
34452 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34456 * @private Called after the drag operation by the DDProxy
34458 onEndProxyDrag : function(e){
34459 Roo.get(this.proxy).setDisplayed(false);
34460 var endPoint = Roo.lib.Event.getXY(e);
34462 this.overlay.hide();
34465 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34466 newSize = this.dragSpecs.startSize +
34467 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34468 endPoint[0] - this.dragSpecs.startPoint[0] :
34469 this.dragSpecs.startPoint[0] - endPoint[0]
34472 newSize = this.dragSpecs.startSize +
34473 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34474 endPoint[1] - this.dragSpecs.startPoint[1] :
34475 this.dragSpecs.startPoint[1] - endPoint[1]
34478 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34479 if(newSize != this.dragSpecs.startSize){
34480 if(this.fireEvent('beforeapply', this, newSize) !== false){
34481 this.adapter.setElementSize(this, newSize);
34482 this.fireEvent("moved", this, newSize);
34483 this.fireEvent("resize", this, newSize);
34489 * Get the adapter this SplitBar uses
34490 * @return The adapter object
34492 getAdapter : function(){
34493 return this.adapter;
34497 * Set the adapter this SplitBar uses
34498 * @param {Object} adapter A SplitBar adapter object
34500 setAdapter : function(adapter){
34501 this.adapter = adapter;
34502 this.adapter.init(this);
34506 * Gets the minimum size for the resizing element
34507 * @return {Number} The minimum size
34509 getMinimumSize : function(){
34510 return this.minSize;
34514 * Sets the minimum size for the resizing element
34515 * @param {Number} minSize The minimum size
34517 setMinimumSize : function(minSize){
34518 this.minSize = minSize;
34522 * Gets the maximum size for the resizing element
34523 * @return {Number} The maximum size
34525 getMaximumSize : function(){
34526 return this.maxSize;
34530 * Sets the maximum size for the resizing element
34531 * @param {Number} maxSize The maximum size
34533 setMaximumSize : function(maxSize){
34534 this.maxSize = maxSize;
34538 * Sets the initialize size for the resizing element
34539 * @param {Number} size The initial size
34541 setCurrentSize : function(size){
34542 var oldAnimate = this.animate;
34543 this.animate = false;
34544 this.adapter.setElementSize(this, size);
34545 this.animate = oldAnimate;
34549 * Destroy this splitbar.
34550 * @param {Boolean} removeEl True to remove the element
34552 destroy : function(removeEl){
34554 this.shim.remove();
34557 this.proxy.parentNode.removeChild(this.proxy);
34565 * @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.
34567 Roo.bootstrap.SplitBar.createProxy = function(dir){
34568 var proxy = new Roo.Element(document.createElement("div"));
34569 proxy.unselectable();
34570 var cls = 'roo-splitbar-proxy';
34571 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34572 document.body.appendChild(proxy.dom);
34577 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34578 * Default Adapter. It assumes the splitter and resizing element are not positioned
34579 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34581 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34584 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34585 // do nothing for now
34586 init : function(s){
34590 * Called before drag operations to get the current size of the resizing element.
34591 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34593 getElementSize : function(s){
34594 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34595 return s.resizingEl.getWidth();
34597 return s.resizingEl.getHeight();
34602 * Called after drag operations to set the size of the resizing element.
34603 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34604 * @param {Number} newSize The new size to set
34605 * @param {Function} onComplete A function to be invoked when resizing is complete
34607 setElementSize : function(s, newSize, onComplete){
34608 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34610 s.resizingEl.setWidth(newSize);
34612 onComplete(s, newSize);
34615 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34620 s.resizingEl.setHeight(newSize);
34622 onComplete(s, newSize);
34625 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34632 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34633 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34634 * Adapter that moves the splitter element to align with the resized sizing element.
34635 * Used with an absolute positioned SplitBar.
34636 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34637 * document.body, make sure you assign an id to the body element.
34639 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34640 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34641 this.container = Roo.get(container);
34644 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34645 init : function(s){
34646 this.basic.init(s);
34649 getElementSize : function(s){
34650 return this.basic.getElementSize(s);
34653 setElementSize : function(s, newSize, onComplete){
34654 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34657 moveSplitter : function(s){
34658 var yes = Roo.bootstrap.SplitBar;
34659 switch(s.placement){
34661 s.el.setX(s.resizingEl.getRight());
34664 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34667 s.el.setY(s.resizingEl.getBottom());
34670 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34677 * Orientation constant - Create a vertical SplitBar
34681 Roo.bootstrap.SplitBar.VERTICAL = 1;
34684 * Orientation constant - Create a horizontal SplitBar
34688 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34691 * Placement constant - The resizing element is to the left of the splitter element
34695 Roo.bootstrap.SplitBar.LEFT = 1;
34698 * Placement constant - The resizing element is to the right of the splitter element
34702 Roo.bootstrap.SplitBar.RIGHT = 2;
34705 * Placement constant - The resizing element is positioned above the splitter element
34709 Roo.bootstrap.SplitBar.TOP = 3;
34712 * Placement constant - The resizing element is positioned under splitter element
34716 Roo.bootstrap.SplitBar.BOTTOM = 4;
34717 Roo.namespace("Roo.bootstrap.layout");/*
34719 * Ext JS Library 1.1.1
34720 * Copyright(c) 2006-2007, Ext JS, LLC.
34722 * Originally Released Under LGPL - original licence link has changed is not relivant.
34725 * <script type="text/javascript">
34729 * @class Roo.bootstrap.layout.Manager
34730 * @extends Roo.bootstrap.Component
34731 * Base class for layout managers.
34733 Roo.bootstrap.layout.Manager = function(config)
34735 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34741 /** false to disable window resize monitoring @type Boolean */
34742 this.monitorWindowResize = true;
34747 * Fires when a layout is performed.
34748 * @param {Roo.LayoutManager} this
34752 * @event regionresized
34753 * Fires when the user resizes a region.
34754 * @param {Roo.LayoutRegion} region The resized region
34755 * @param {Number} newSize The new size (width for east/west, height for north/south)
34757 "regionresized" : true,
34759 * @event regioncollapsed
34760 * Fires when a region is collapsed.
34761 * @param {Roo.LayoutRegion} region The collapsed region
34763 "regioncollapsed" : true,
34765 * @event regionexpanded
34766 * Fires when a region is expanded.
34767 * @param {Roo.LayoutRegion} region The expanded region
34769 "regionexpanded" : true
34771 this.updating = false;
34774 this.el = Roo.get(config.el);
34780 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34785 monitorWindowResize : true,
34791 onRender : function(ct, position)
34794 this.el = Roo.get(ct);
34797 //this.fireEvent('render',this);
34801 initEvents: function()
34805 // ie scrollbar fix
34806 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34807 document.body.scroll = "no";
34808 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34809 this.el.position('relative');
34811 this.id = this.el.id;
34812 this.el.addClass("roo-layout-container");
34813 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34814 if(this.el.dom != document.body ) {
34815 this.el.on('resize', this.layout,this);
34816 this.el.on('show', this.layout,this);
34822 * Returns true if this layout is currently being updated
34823 * @return {Boolean}
34825 isUpdating : function(){
34826 return this.updating;
34830 * Suspend the LayoutManager from doing auto-layouts while
34831 * making multiple add or remove calls
34833 beginUpdate : function(){
34834 this.updating = true;
34838 * Restore auto-layouts and optionally disable the manager from performing a layout
34839 * @param {Boolean} noLayout true to disable a layout update
34841 endUpdate : function(noLayout){
34842 this.updating = false;
34848 layout: function(){
34852 onRegionResized : function(region, newSize){
34853 this.fireEvent("regionresized", region, newSize);
34857 onRegionCollapsed : function(region){
34858 this.fireEvent("regioncollapsed", region);
34861 onRegionExpanded : function(region){
34862 this.fireEvent("regionexpanded", region);
34866 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34867 * performs box-model adjustments.
34868 * @return {Object} The size as an object {width: (the width), height: (the height)}
34870 getViewSize : function()
34873 if(this.el.dom != document.body){
34874 size = this.el.getSize();
34876 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34878 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34879 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34884 * Returns the Element this layout is bound to.
34885 * @return {Roo.Element}
34887 getEl : function(){
34892 * Returns the specified region.
34893 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34894 * @return {Roo.LayoutRegion}
34896 getRegion : function(target){
34897 return this.regions[target.toLowerCase()];
34900 onWindowResize : function(){
34901 if(this.monitorWindowResize){
34908 * Ext JS Library 1.1.1
34909 * Copyright(c) 2006-2007, Ext JS, LLC.
34911 * Originally Released Under LGPL - original licence link has changed is not relivant.
34914 * <script type="text/javascript">
34917 * @class Roo.bootstrap.layout.Border
34918 * @extends Roo.bootstrap.layout.Manager
34919 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34920 * please see: examples/bootstrap/nested.html<br><br>
34922 <b>The container the layout is rendered into can be either the body element or any other element.
34923 If it is not the body element, the container needs to either be an absolute positioned element,
34924 or you will need to add "position:relative" to the css of the container. You will also need to specify
34925 the container size if it is not the body element.</b>
34928 * Create a new Border
34929 * @param {Object} config Configuration options
34931 Roo.bootstrap.layout.Border = function(config){
34932 config = config || {};
34933 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34937 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34938 if(config[region]){
34939 config[region].region = region;
34940 this.addRegion(config[region]);
34946 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34948 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34950 * Creates and adds a new region if it doesn't already exist.
34951 * @param {String} target The target region key (north, south, east, west or center).
34952 * @param {Object} config The regions config object
34953 * @return {BorderLayoutRegion} The new region
34955 addRegion : function(config)
34957 if(!this.regions[config.region]){
34958 var r = this.factory(config);
34959 this.bindRegion(r);
34961 return this.regions[config.region];
34965 bindRegion : function(r){
34966 this.regions[r.config.region] = r;
34968 r.on("visibilitychange", this.layout, this);
34969 r.on("paneladded", this.layout, this);
34970 r.on("panelremoved", this.layout, this);
34971 r.on("invalidated", this.layout, this);
34972 r.on("resized", this.onRegionResized, this);
34973 r.on("collapsed", this.onRegionCollapsed, this);
34974 r.on("expanded", this.onRegionExpanded, this);
34978 * Performs a layout update.
34980 layout : function()
34982 if(this.updating) {
34986 // render all the rebions if they have not been done alreayd?
34987 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34988 if(this.regions[region] && !this.regions[region].bodyEl){
34989 this.regions[region].onRender(this.el)
34993 var size = this.getViewSize();
34994 var w = size.width;
34995 var h = size.height;
35000 //var x = 0, y = 0;
35002 var rs = this.regions;
35003 var north = rs["north"];
35004 var south = rs["south"];
35005 var west = rs["west"];
35006 var east = rs["east"];
35007 var center = rs["center"];
35008 //if(this.hideOnLayout){ // not supported anymore
35009 //c.el.setStyle("display", "none");
35011 if(north && north.isVisible()){
35012 var b = north.getBox();
35013 var m = north.getMargins();
35014 b.width = w - (m.left+m.right);
35017 centerY = b.height + b.y + m.bottom;
35018 centerH -= centerY;
35019 north.updateBox(this.safeBox(b));
35021 if(south && south.isVisible()){
35022 var b = south.getBox();
35023 var m = south.getMargins();
35024 b.width = w - (m.left+m.right);
35026 var totalHeight = (b.height + m.top + m.bottom);
35027 b.y = h - totalHeight + m.top;
35028 centerH -= totalHeight;
35029 south.updateBox(this.safeBox(b));
35031 if(west && west.isVisible()){
35032 var b = west.getBox();
35033 var m = west.getMargins();
35034 b.height = centerH - (m.top+m.bottom);
35036 b.y = centerY + m.top;
35037 var totalWidth = (b.width + m.left + m.right);
35038 centerX += totalWidth;
35039 centerW -= totalWidth;
35040 west.updateBox(this.safeBox(b));
35042 if(east && east.isVisible()){
35043 var b = east.getBox();
35044 var m = east.getMargins();
35045 b.height = centerH - (m.top+m.bottom);
35046 var totalWidth = (b.width + m.left + m.right);
35047 b.x = w - totalWidth + m.left;
35048 b.y = centerY + m.top;
35049 centerW -= totalWidth;
35050 east.updateBox(this.safeBox(b));
35053 var m = center.getMargins();
35055 x: centerX + m.left,
35056 y: centerY + m.top,
35057 width: centerW - (m.left+m.right),
35058 height: centerH - (m.top+m.bottom)
35060 //if(this.hideOnLayout){
35061 //center.el.setStyle("display", "block");
35063 center.updateBox(this.safeBox(centerBox));
35066 this.fireEvent("layout", this);
35070 safeBox : function(box){
35071 box.width = Math.max(0, box.width);
35072 box.height = Math.max(0, box.height);
35077 * Adds a ContentPanel (or subclass) to this layout.
35078 * @param {String} target The target region key (north, south, east, west or center).
35079 * @param {Roo.ContentPanel} panel The panel to add
35080 * @return {Roo.ContentPanel} The added panel
35082 add : function(target, panel){
35084 target = target.toLowerCase();
35085 return this.regions[target].add(panel);
35089 * Remove a ContentPanel (or subclass) to this layout.
35090 * @param {String} target The target region key (north, south, east, west or center).
35091 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35092 * @return {Roo.ContentPanel} The removed panel
35094 remove : function(target, panel){
35095 target = target.toLowerCase();
35096 return this.regions[target].remove(panel);
35100 * Searches all regions for a panel with the specified id
35101 * @param {String} panelId
35102 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35104 findPanel : function(panelId){
35105 var rs = this.regions;
35106 for(var target in rs){
35107 if(typeof rs[target] != "function"){
35108 var p = rs[target].getPanel(panelId);
35118 * Searches all regions for a panel with the specified id and activates (shows) it.
35119 * @param {String/ContentPanel} panelId The panels id or the panel itself
35120 * @return {Roo.ContentPanel} The shown panel or null
35122 showPanel : function(panelId) {
35123 var rs = this.regions;
35124 for(var target in rs){
35125 var r = rs[target];
35126 if(typeof r != "function"){
35127 if(r.hasPanel(panelId)){
35128 return r.showPanel(panelId);
35136 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35137 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35140 restoreState : function(provider){
35142 provider = Roo.state.Manager;
35144 var sm = new Roo.LayoutStateManager();
35145 sm.init(this, provider);
35151 * Adds a xtype elements to the layout.
35155 xtype : 'ContentPanel',
35162 xtype : 'NestedLayoutPanel',
35168 items : [ ... list of content panels or nested layout panels.. ]
35172 * @param {Object} cfg Xtype definition of item to add.
35174 addxtype : function(cfg)
35176 // basically accepts a pannel...
35177 // can accept a layout region..!?!?
35178 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35181 // theory? children can only be panels??
35183 //if (!cfg.xtype.match(/Panel$/)) {
35188 if (typeof(cfg.region) == 'undefined') {
35189 Roo.log("Failed to add Panel, region was not set");
35193 var region = cfg.region;
35199 xitems = cfg.items;
35206 case 'Content': // ContentPanel (el, cfg)
35207 case 'Scroll': // ContentPanel (el, cfg)
35209 cfg.autoCreate = true;
35210 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35212 // var el = this.el.createChild();
35213 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35216 this.add(region, ret);
35220 case 'TreePanel': // our new panel!
35221 cfg.el = this.el.createChild();
35222 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35223 this.add(region, ret);
35228 // create a new Layout (which is a Border Layout...
35230 var clayout = cfg.layout;
35231 clayout.el = this.el.createChild();
35232 clayout.items = clayout.items || [];
35236 // replace this exitems with the clayout ones..
35237 xitems = clayout.items;
35239 // force background off if it's in center...
35240 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35241 cfg.background = false;
35243 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35246 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35247 //console.log('adding nested layout panel ' + cfg.toSource());
35248 this.add(region, ret);
35249 nb = {}; /// find first...
35254 // needs grid and region
35256 //var el = this.getRegion(region).el.createChild();
35258 *var el = this.el.createChild();
35259 // create the grid first...
35260 cfg.grid.container = el;
35261 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35264 if (region == 'center' && this.active ) {
35265 cfg.background = false;
35268 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35270 this.add(region, ret);
35272 if (cfg.background) {
35273 // render grid on panel activation (if panel background)
35274 ret.on('activate', function(gp) {
35275 if (!gp.grid.rendered) {
35276 // gp.grid.render(el);
35280 // cfg.grid.render(el);
35286 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35287 // it was the old xcomponent building that caused this before.
35288 // espeically if border is the top element in the tree.
35298 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35300 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35301 this.add(region, ret);
35305 throw "Can not add '" + cfg.xtype + "' to Border";
35311 this.beginUpdate();
35315 Roo.each(xitems, function(i) {
35316 region = nb && i.region ? i.region : false;
35318 var add = ret.addxtype(i);
35321 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35322 if (!i.background) {
35323 abn[region] = nb[region] ;
35330 // make the last non-background panel active..
35331 //if (nb) { Roo.log(abn); }
35334 for(var r in abn) {
35335 region = this.getRegion(r);
35337 // tried using nb[r], but it does not work..
35339 region.showPanel(abn[r]);
35350 factory : function(cfg)
35353 var validRegions = Roo.bootstrap.layout.Border.regions;
35355 var target = cfg.region;
35358 var r = Roo.bootstrap.layout;
35362 return new r.North(cfg);
35364 return new r.South(cfg);
35366 return new r.East(cfg);
35368 return new r.West(cfg);
35370 return new r.Center(cfg);
35372 throw 'Layout region "'+target+'" not supported.';
35379 * Ext JS Library 1.1.1
35380 * Copyright(c) 2006-2007, Ext JS, LLC.
35382 * Originally Released Under LGPL - original licence link has changed is not relivant.
35385 * <script type="text/javascript">
35389 * @class Roo.bootstrap.layout.Basic
35390 * @extends Roo.util.Observable
35391 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35392 * and does not have a titlebar, tabs or any other features. All it does is size and position
35393 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35394 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35395 * @cfg {string} region the region that it inhabits..
35396 * @cfg {bool} skipConfig skip config?
35400 Roo.bootstrap.layout.Basic = function(config){
35402 this.mgr = config.mgr;
35404 this.position = config.region;
35406 var skipConfig = config.skipConfig;
35410 * @scope Roo.BasicLayoutRegion
35414 * @event beforeremove
35415 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35416 * @param {Roo.LayoutRegion} this
35417 * @param {Roo.ContentPanel} panel The panel
35418 * @param {Object} e The cancel event object
35420 "beforeremove" : true,
35422 * @event invalidated
35423 * Fires when the layout for this region is changed.
35424 * @param {Roo.LayoutRegion} this
35426 "invalidated" : true,
35428 * @event visibilitychange
35429 * Fires when this region is shown or hidden
35430 * @param {Roo.LayoutRegion} this
35431 * @param {Boolean} visibility true or false
35433 "visibilitychange" : true,
35435 * @event paneladded
35436 * Fires when a panel is added.
35437 * @param {Roo.LayoutRegion} this
35438 * @param {Roo.ContentPanel} panel The panel
35440 "paneladded" : true,
35442 * @event panelremoved
35443 * Fires when a panel is removed.
35444 * @param {Roo.LayoutRegion} this
35445 * @param {Roo.ContentPanel} panel The panel
35447 "panelremoved" : true,
35449 * @event beforecollapse
35450 * Fires when this region before collapse.
35451 * @param {Roo.LayoutRegion} this
35453 "beforecollapse" : true,
35456 * Fires when this region is collapsed.
35457 * @param {Roo.LayoutRegion} this
35459 "collapsed" : true,
35462 * Fires when this region is expanded.
35463 * @param {Roo.LayoutRegion} this
35468 * Fires when this region is slid into view.
35469 * @param {Roo.LayoutRegion} this
35471 "slideshow" : true,
35474 * Fires when this region slides out of view.
35475 * @param {Roo.LayoutRegion} this
35477 "slidehide" : true,
35479 * @event panelactivated
35480 * Fires when a panel is activated.
35481 * @param {Roo.LayoutRegion} this
35482 * @param {Roo.ContentPanel} panel The activated panel
35484 "panelactivated" : true,
35487 * Fires when the user resizes this region.
35488 * @param {Roo.LayoutRegion} this
35489 * @param {Number} newSize The new size (width for east/west, height for north/south)
35493 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35494 this.panels = new Roo.util.MixedCollection();
35495 this.panels.getKey = this.getPanelId.createDelegate(this);
35497 this.activePanel = null;
35498 // ensure listeners are added...
35500 if (config.listeners || config.events) {
35501 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35502 listeners : config.listeners || {},
35503 events : config.events || {}
35507 if(skipConfig !== true){
35508 this.applyConfig(config);
35512 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35514 getPanelId : function(p){
35518 applyConfig : function(config){
35519 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35520 this.config = config;
35525 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35526 * the width, for horizontal (north, south) the height.
35527 * @param {Number} newSize The new width or height
35529 resizeTo : function(newSize){
35530 var el = this.el ? this.el :
35531 (this.activePanel ? this.activePanel.getEl() : null);
35533 switch(this.position){
35536 el.setWidth(newSize);
35537 this.fireEvent("resized", this, newSize);
35541 el.setHeight(newSize);
35542 this.fireEvent("resized", this, newSize);
35548 getBox : function(){
35549 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35552 getMargins : function(){
35553 return this.margins;
35556 updateBox : function(box){
35558 var el = this.activePanel.getEl();
35559 el.dom.style.left = box.x + "px";
35560 el.dom.style.top = box.y + "px";
35561 this.activePanel.setSize(box.width, box.height);
35565 * Returns the container element for this region.
35566 * @return {Roo.Element}
35568 getEl : function(){
35569 return this.activePanel;
35573 * Returns true if this region is currently visible.
35574 * @return {Boolean}
35576 isVisible : function(){
35577 return this.activePanel ? true : false;
35580 setActivePanel : function(panel){
35581 panel = this.getPanel(panel);
35582 if(this.activePanel && this.activePanel != panel){
35583 this.activePanel.setActiveState(false);
35584 this.activePanel.getEl().setLeftTop(-10000,-10000);
35586 this.activePanel = panel;
35587 panel.setActiveState(true);
35589 panel.setSize(this.box.width, this.box.height);
35591 this.fireEvent("panelactivated", this, panel);
35592 this.fireEvent("invalidated");
35596 * Show the specified panel.
35597 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35598 * @return {Roo.ContentPanel} The shown panel or null
35600 showPanel : function(panel){
35601 panel = this.getPanel(panel);
35603 this.setActivePanel(panel);
35609 * Get the active panel for this region.
35610 * @return {Roo.ContentPanel} The active panel or null
35612 getActivePanel : function(){
35613 return this.activePanel;
35617 * Add the passed ContentPanel(s)
35618 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35619 * @return {Roo.ContentPanel} The panel added (if only one was added)
35621 add : function(panel){
35622 if(arguments.length > 1){
35623 for(var i = 0, len = arguments.length; i < len; i++) {
35624 this.add(arguments[i]);
35628 if(this.hasPanel(panel)){
35629 this.showPanel(panel);
35632 var el = panel.getEl();
35633 if(el.dom.parentNode != this.mgr.el.dom){
35634 this.mgr.el.dom.appendChild(el.dom);
35636 if(panel.setRegion){
35637 panel.setRegion(this);
35639 this.panels.add(panel);
35640 el.setStyle("position", "absolute");
35641 if(!panel.background){
35642 this.setActivePanel(panel);
35643 if(this.config.initialSize && this.panels.getCount()==1){
35644 this.resizeTo(this.config.initialSize);
35647 this.fireEvent("paneladded", this, panel);
35652 * Returns true if the panel is in this region.
35653 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35654 * @return {Boolean}
35656 hasPanel : function(panel){
35657 if(typeof panel == "object"){ // must be panel obj
35658 panel = panel.getId();
35660 return this.getPanel(panel) ? true : false;
35664 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35665 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35666 * @param {Boolean} preservePanel Overrides the config preservePanel option
35667 * @return {Roo.ContentPanel} The panel that was removed
35669 remove : function(panel, preservePanel){
35670 panel = this.getPanel(panel);
35675 this.fireEvent("beforeremove", this, panel, e);
35676 if(e.cancel === true){
35679 var panelId = panel.getId();
35680 this.panels.removeKey(panelId);
35685 * Returns the panel specified or null if it's not in this region.
35686 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35687 * @return {Roo.ContentPanel}
35689 getPanel : function(id){
35690 if(typeof id == "object"){ // must be panel obj
35693 return this.panels.get(id);
35697 * Returns this regions position (north/south/east/west/center).
35700 getPosition: function(){
35701 return this.position;
35705 * Ext JS Library 1.1.1
35706 * Copyright(c) 2006-2007, Ext JS, LLC.
35708 * Originally Released Under LGPL - original licence link has changed is not relivant.
35711 * <script type="text/javascript">
35715 * @class Roo.bootstrap.layout.Region
35716 * @extends Roo.bootstrap.layout.Basic
35717 * This class represents a region in a layout manager.
35719 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35720 * @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})
35721 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35722 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35723 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35724 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35725 * @cfg {String} title The title for the region (overrides panel titles)
35726 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35727 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35728 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35729 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35730 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35731 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35732 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35733 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35734 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35735 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35737 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35738 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35739 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35740 * @cfg {Number} width For East/West panels
35741 * @cfg {Number} height For North/South panels
35742 * @cfg {Boolean} split To show the splitter
35743 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35745 * @cfg {string} cls Extra CSS classes to add to region
35747 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35748 * @cfg {string} region the region that it inhabits..
35751 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35752 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35754 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35755 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35756 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35758 Roo.bootstrap.layout.Region = function(config)
35760 this.applyConfig(config);
35762 var mgr = config.mgr;
35763 var pos = config.region;
35764 config.skipConfig = true;
35765 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35768 this.onRender(mgr.el);
35771 this.visible = true;
35772 this.collapsed = false;
35773 this.unrendered_panels = [];
35776 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35778 position: '', // set by wrapper (eg. north/south etc..)
35779 unrendered_panels : null, // unrendered panels.
35780 createBody : function(){
35781 /** This region's body element
35782 * @type Roo.Element */
35783 this.bodyEl = this.el.createChild({
35785 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35789 onRender: function(ctr, pos)
35791 var dh = Roo.DomHelper;
35792 /** This region's container element
35793 * @type Roo.Element */
35794 this.el = dh.append(ctr.dom, {
35796 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35798 /** This region's title element
35799 * @type Roo.Element */
35801 this.titleEl = dh.append(this.el.dom,
35804 unselectable: "on",
35805 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35807 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35808 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35811 this.titleEl.enableDisplayMode();
35812 /** This region's title text element
35813 * @type HTMLElement */
35814 this.titleTextEl = this.titleEl.dom.firstChild;
35815 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35817 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35818 this.closeBtn.enableDisplayMode();
35819 this.closeBtn.on("click", this.closeClicked, this);
35820 this.closeBtn.hide();
35822 this.createBody(this.config);
35823 if(this.config.hideWhenEmpty){
35825 this.on("paneladded", this.validateVisibility, this);
35826 this.on("panelremoved", this.validateVisibility, this);
35828 if(this.autoScroll){
35829 this.bodyEl.setStyle("overflow", "auto");
35831 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35833 //if(c.titlebar !== false){
35834 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35835 this.titleEl.hide();
35837 this.titleEl.show();
35838 if(this.config.title){
35839 this.titleTextEl.innerHTML = this.config.title;
35843 if(this.config.collapsed){
35844 this.collapse(true);
35846 if(this.config.hidden){
35850 if (this.unrendered_panels && this.unrendered_panels.length) {
35851 for (var i =0;i< this.unrendered_panels.length; i++) {
35852 this.add(this.unrendered_panels[i]);
35854 this.unrendered_panels = null;
35860 applyConfig : function(c)
35863 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35864 var dh = Roo.DomHelper;
35865 if(c.titlebar !== false){
35866 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35867 this.collapseBtn.on("click", this.collapse, this);
35868 this.collapseBtn.enableDisplayMode();
35870 if(c.showPin === true || this.showPin){
35871 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35872 this.stickBtn.enableDisplayMode();
35873 this.stickBtn.on("click", this.expand, this);
35874 this.stickBtn.hide();
35879 /** This region's collapsed element
35880 * @type Roo.Element */
35883 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35884 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35887 if(c.floatable !== false){
35888 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35889 this.collapsedEl.on("click", this.collapseClick, this);
35892 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35893 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35894 id: "message", unselectable: "on", style:{"float":"left"}});
35895 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35897 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35898 this.expandBtn.on("click", this.expand, this);
35902 if(this.collapseBtn){
35903 this.collapseBtn.setVisible(c.collapsible == true);
35906 this.cmargins = c.cmargins || this.cmargins ||
35907 (this.position == "west" || this.position == "east" ?
35908 {top: 0, left: 2, right:2, bottom: 0} :
35909 {top: 2, left: 0, right:0, bottom: 2});
35911 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35914 this.bottomTabs = c.tabPosition != "top";
35916 this.autoScroll = c.autoScroll || false;
35921 this.duration = c.duration || .30;
35922 this.slideDuration = c.slideDuration || .45;
35927 * Returns true if this region is currently visible.
35928 * @return {Boolean}
35930 isVisible : function(){
35931 return this.visible;
35935 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35936 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35938 //setCollapsedTitle : function(title){
35939 // title = title || " ";
35940 // if(this.collapsedTitleTextEl){
35941 // this.collapsedTitleTextEl.innerHTML = title;
35945 getBox : function(){
35947 // if(!this.collapsed){
35948 b = this.el.getBox(false, true);
35950 // b = this.collapsedEl.getBox(false, true);
35955 getMargins : function(){
35956 return this.margins;
35957 //return this.collapsed ? this.cmargins : this.margins;
35960 highlight : function(){
35961 this.el.addClass("x-layout-panel-dragover");
35964 unhighlight : function(){
35965 this.el.removeClass("x-layout-panel-dragover");
35968 updateBox : function(box)
35970 if (!this.bodyEl) {
35971 return; // not rendered yet..
35975 if(!this.collapsed){
35976 this.el.dom.style.left = box.x + "px";
35977 this.el.dom.style.top = box.y + "px";
35978 this.updateBody(box.width, box.height);
35980 this.collapsedEl.dom.style.left = box.x + "px";
35981 this.collapsedEl.dom.style.top = box.y + "px";
35982 this.collapsedEl.setSize(box.width, box.height);
35985 this.tabs.autoSizeTabs();
35989 updateBody : function(w, h)
35992 this.el.setWidth(w);
35993 w -= this.el.getBorderWidth("rl");
35994 if(this.config.adjustments){
35995 w += this.config.adjustments[0];
35998 if(h !== null && h > 0){
35999 this.el.setHeight(h);
36000 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36001 h -= this.el.getBorderWidth("tb");
36002 if(this.config.adjustments){
36003 h += this.config.adjustments[1];
36005 this.bodyEl.setHeight(h);
36007 h = this.tabs.syncHeight(h);
36010 if(this.panelSize){
36011 w = w !== null ? w : this.panelSize.width;
36012 h = h !== null ? h : this.panelSize.height;
36014 if(this.activePanel){
36015 var el = this.activePanel.getEl();
36016 w = w !== null ? w : el.getWidth();
36017 h = h !== null ? h : el.getHeight();
36018 this.panelSize = {width: w, height: h};
36019 this.activePanel.setSize(w, h);
36021 if(Roo.isIE && this.tabs){
36022 this.tabs.el.repaint();
36027 * Returns the container element for this region.
36028 * @return {Roo.Element}
36030 getEl : function(){
36035 * Hides this region.
36038 //if(!this.collapsed){
36039 this.el.dom.style.left = "-2000px";
36042 // this.collapsedEl.dom.style.left = "-2000px";
36043 // this.collapsedEl.hide();
36045 this.visible = false;
36046 this.fireEvent("visibilitychange", this, false);
36050 * Shows this region if it was previously hidden.
36053 //if(!this.collapsed){
36056 // this.collapsedEl.show();
36058 this.visible = true;
36059 this.fireEvent("visibilitychange", this, true);
36062 closeClicked : function(){
36063 if(this.activePanel){
36064 this.remove(this.activePanel);
36068 collapseClick : function(e){
36070 e.stopPropagation();
36073 e.stopPropagation();
36079 * Collapses this region.
36080 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36083 collapse : function(skipAnim, skipCheck = false){
36084 if(this.collapsed) {
36088 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36090 this.collapsed = true;
36092 this.split.el.hide();
36094 if(this.config.animate && skipAnim !== true){
36095 this.fireEvent("invalidated", this);
36096 this.animateCollapse();
36098 this.el.setLocation(-20000,-20000);
36100 this.collapsedEl.show();
36101 this.fireEvent("collapsed", this);
36102 this.fireEvent("invalidated", this);
36108 animateCollapse : function(){
36113 * Expands this region if it was previously collapsed.
36114 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36115 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36118 expand : function(e, skipAnim){
36120 e.stopPropagation();
36122 if(!this.collapsed || this.el.hasActiveFx()) {
36126 this.afterSlideIn();
36129 this.collapsed = false;
36130 if(this.config.animate && skipAnim !== true){
36131 this.animateExpand();
36135 this.split.el.show();
36137 this.collapsedEl.setLocation(-2000,-2000);
36138 this.collapsedEl.hide();
36139 this.fireEvent("invalidated", this);
36140 this.fireEvent("expanded", this);
36144 animateExpand : function(){
36148 initTabs : function()
36150 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36152 var ts = new Roo.bootstrap.panel.Tabs({
36153 el: this.bodyEl.dom,
36154 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36155 disableTooltips: this.config.disableTabTips,
36156 toolbar : this.config.toolbar
36159 if(this.config.hideTabs){
36160 ts.stripWrap.setDisplayed(false);
36163 ts.resizeTabs = this.config.resizeTabs === true;
36164 ts.minTabWidth = this.config.minTabWidth || 40;
36165 ts.maxTabWidth = this.config.maxTabWidth || 250;
36166 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36167 ts.monitorResize = false;
36168 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36169 ts.bodyEl.addClass('roo-layout-tabs-body');
36170 this.panels.each(this.initPanelAsTab, this);
36173 initPanelAsTab : function(panel){
36174 var ti = this.tabs.addTab(
36178 this.config.closeOnTab && panel.isClosable(),
36181 if(panel.tabTip !== undefined){
36182 ti.setTooltip(panel.tabTip);
36184 ti.on("activate", function(){
36185 this.setActivePanel(panel);
36188 if(this.config.closeOnTab){
36189 ti.on("beforeclose", function(t, e){
36191 this.remove(panel);
36195 panel.tabItem = ti;
36200 updatePanelTitle : function(panel, title)
36202 if(this.activePanel == panel){
36203 this.updateTitle(title);
36206 var ti = this.tabs.getTab(panel.getEl().id);
36208 if(panel.tabTip !== undefined){
36209 ti.setTooltip(panel.tabTip);
36214 updateTitle : function(title){
36215 if(this.titleTextEl && !this.config.title){
36216 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36220 setActivePanel : function(panel)
36222 panel = this.getPanel(panel);
36223 if(this.activePanel && this.activePanel != panel){
36224 if(this.activePanel.setActiveState(false) === false){
36228 this.activePanel = panel;
36229 panel.setActiveState(true);
36230 if(this.panelSize){
36231 panel.setSize(this.panelSize.width, this.panelSize.height);
36234 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36236 this.updateTitle(panel.getTitle());
36238 this.fireEvent("invalidated", this);
36240 this.fireEvent("panelactivated", this, panel);
36244 * Shows the specified panel.
36245 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36246 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36248 showPanel : function(panel)
36250 panel = this.getPanel(panel);
36253 var tab = this.tabs.getTab(panel.getEl().id);
36254 if(tab.isHidden()){
36255 this.tabs.unhideTab(tab.id);
36259 this.setActivePanel(panel);
36266 * Get the active panel for this region.
36267 * @return {Roo.ContentPanel} The active panel or null
36269 getActivePanel : function(){
36270 return this.activePanel;
36273 validateVisibility : function(){
36274 if(this.panels.getCount() < 1){
36275 this.updateTitle(" ");
36276 this.closeBtn.hide();
36279 if(!this.isVisible()){
36286 * Adds the passed ContentPanel(s) to this region.
36287 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36288 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36290 add : function(panel)
36292 if(arguments.length > 1){
36293 for(var i = 0, len = arguments.length; i < len; i++) {
36294 this.add(arguments[i]);
36299 // if we have not been rendered yet, then we can not really do much of this..
36300 if (!this.bodyEl) {
36301 this.unrendered_panels.push(panel);
36308 if(this.hasPanel(panel)){
36309 this.showPanel(panel);
36312 panel.setRegion(this);
36313 this.panels.add(panel);
36314 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36315 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36316 // and hide them... ???
36317 this.bodyEl.dom.appendChild(panel.getEl().dom);
36318 if(panel.background !== true){
36319 this.setActivePanel(panel);
36321 this.fireEvent("paneladded", this, panel);
36328 this.initPanelAsTab(panel);
36332 if(panel.background !== true){
36333 this.tabs.activate(panel.getEl().id);
36335 this.fireEvent("paneladded", this, panel);
36340 * Hides the tab for the specified panel.
36341 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36343 hidePanel : function(panel){
36344 if(this.tabs && (panel = this.getPanel(panel))){
36345 this.tabs.hideTab(panel.getEl().id);
36350 * Unhides the tab for a previously hidden panel.
36351 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36353 unhidePanel : function(panel){
36354 if(this.tabs && (panel = this.getPanel(panel))){
36355 this.tabs.unhideTab(panel.getEl().id);
36359 clearPanels : function(){
36360 while(this.panels.getCount() > 0){
36361 this.remove(this.panels.first());
36366 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36367 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36368 * @param {Boolean} preservePanel Overrides the config preservePanel option
36369 * @return {Roo.ContentPanel} The panel that was removed
36371 remove : function(panel, preservePanel)
36373 panel = this.getPanel(panel);
36378 this.fireEvent("beforeremove", this, panel, e);
36379 if(e.cancel === true){
36382 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36383 var panelId = panel.getId();
36384 this.panels.removeKey(panelId);
36386 document.body.appendChild(panel.getEl().dom);
36389 this.tabs.removeTab(panel.getEl().id);
36390 }else if (!preservePanel){
36391 this.bodyEl.dom.removeChild(panel.getEl().dom);
36393 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36394 var p = this.panels.first();
36395 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36396 tempEl.appendChild(p.getEl().dom);
36397 this.bodyEl.update("");
36398 this.bodyEl.dom.appendChild(p.getEl().dom);
36400 this.updateTitle(p.getTitle());
36402 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36403 this.setActivePanel(p);
36405 panel.setRegion(null);
36406 if(this.activePanel == panel){
36407 this.activePanel = null;
36409 if(this.config.autoDestroy !== false && preservePanel !== true){
36410 try{panel.destroy();}catch(e){}
36412 this.fireEvent("panelremoved", this, panel);
36417 * Returns the TabPanel component used by this region
36418 * @return {Roo.TabPanel}
36420 getTabs : function(){
36424 createTool : function(parentEl, className){
36425 var btn = Roo.DomHelper.append(parentEl, {
36427 cls: "x-layout-tools-button",
36430 cls: "roo-layout-tools-button-inner " + className,
36434 btn.addClassOnOver("roo-layout-tools-button-over");
36439 * Ext JS Library 1.1.1
36440 * Copyright(c) 2006-2007, Ext JS, LLC.
36442 * Originally Released Under LGPL - original licence link has changed is not relivant.
36445 * <script type="text/javascript">
36451 * @class Roo.SplitLayoutRegion
36452 * @extends Roo.LayoutRegion
36453 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36455 Roo.bootstrap.layout.Split = function(config){
36456 this.cursor = config.cursor;
36457 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36460 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36462 splitTip : "Drag to resize.",
36463 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36464 useSplitTips : false,
36466 applyConfig : function(config){
36467 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36470 onRender : function(ctr,pos) {
36472 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36473 if(!this.config.split){
36478 var splitEl = Roo.DomHelper.append(ctr.dom, {
36480 id: this.el.id + "-split",
36481 cls: "roo-layout-split roo-layout-split-"+this.position,
36484 /** The SplitBar for this region
36485 * @type Roo.SplitBar */
36486 // does not exist yet...
36487 Roo.log([this.position, this.orientation]);
36489 this.split = new Roo.bootstrap.SplitBar({
36490 dragElement : splitEl,
36491 resizingElement: this.el,
36492 orientation : this.orientation
36495 this.split.on("moved", this.onSplitMove, this);
36496 this.split.useShim = this.config.useShim === true;
36497 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36498 if(this.useSplitTips){
36499 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36501 //if(config.collapsible){
36502 // this.split.el.on("dblclick", this.collapse, this);
36505 if(typeof this.config.minSize != "undefined"){
36506 this.split.minSize = this.config.minSize;
36508 if(typeof this.config.maxSize != "undefined"){
36509 this.split.maxSize = this.config.maxSize;
36511 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36512 this.hideSplitter();
36517 getHMaxSize : function(){
36518 var cmax = this.config.maxSize || 10000;
36519 var center = this.mgr.getRegion("center");
36520 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36523 getVMaxSize : function(){
36524 var cmax = this.config.maxSize || 10000;
36525 var center = this.mgr.getRegion("center");
36526 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36529 onSplitMove : function(split, newSize){
36530 this.fireEvent("resized", this, newSize);
36534 * Returns the {@link Roo.SplitBar} for this region.
36535 * @return {Roo.SplitBar}
36537 getSplitBar : function(){
36542 this.hideSplitter();
36543 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36546 hideSplitter : function(){
36548 this.split.el.setLocation(-2000,-2000);
36549 this.split.el.hide();
36555 this.split.el.show();
36557 Roo.bootstrap.layout.Split.superclass.show.call(this);
36560 beforeSlide: function(){
36561 if(Roo.isGecko){// firefox overflow auto bug workaround
36562 this.bodyEl.clip();
36564 this.tabs.bodyEl.clip();
36566 if(this.activePanel){
36567 this.activePanel.getEl().clip();
36569 if(this.activePanel.beforeSlide){
36570 this.activePanel.beforeSlide();
36576 afterSlide : function(){
36577 if(Roo.isGecko){// firefox overflow auto bug workaround
36578 this.bodyEl.unclip();
36580 this.tabs.bodyEl.unclip();
36582 if(this.activePanel){
36583 this.activePanel.getEl().unclip();
36584 if(this.activePanel.afterSlide){
36585 this.activePanel.afterSlide();
36591 initAutoHide : function(){
36592 if(this.autoHide !== false){
36593 if(!this.autoHideHd){
36594 var st = new Roo.util.DelayedTask(this.slideIn, this);
36595 this.autoHideHd = {
36596 "mouseout": function(e){
36597 if(!e.within(this.el, true)){
36601 "mouseover" : function(e){
36607 this.el.on(this.autoHideHd);
36611 clearAutoHide : function(){
36612 if(this.autoHide !== false){
36613 this.el.un("mouseout", this.autoHideHd.mouseout);
36614 this.el.un("mouseover", this.autoHideHd.mouseover);
36618 clearMonitor : function(){
36619 Roo.get(document).un("click", this.slideInIf, this);
36622 // these names are backwards but not changed for compat
36623 slideOut : function(){
36624 if(this.isSlid || this.el.hasActiveFx()){
36627 this.isSlid = true;
36628 if(this.collapseBtn){
36629 this.collapseBtn.hide();
36631 this.closeBtnState = this.closeBtn.getStyle('display');
36632 this.closeBtn.hide();
36634 this.stickBtn.show();
36637 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36638 this.beforeSlide();
36639 this.el.setStyle("z-index", 10001);
36640 this.el.slideIn(this.getSlideAnchor(), {
36641 callback: function(){
36643 this.initAutoHide();
36644 Roo.get(document).on("click", this.slideInIf, this);
36645 this.fireEvent("slideshow", this);
36652 afterSlideIn : function(){
36653 this.clearAutoHide();
36654 this.isSlid = false;
36655 this.clearMonitor();
36656 this.el.setStyle("z-index", "");
36657 if(this.collapseBtn){
36658 this.collapseBtn.show();
36660 this.closeBtn.setStyle('display', this.closeBtnState);
36662 this.stickBtn.hide();
36664 this.fireEvent("slidehide", this);
36667 slideIn : function(cb){
36668 if(!this.isSlid || this.el.hasActiveFx()){
36672 this.isSlid = false;
36673 this.beforeSlide();
36674 this.el.slideOut(this.getSlideAnchor(), {
36675 callback: function(){
36676 this.el.setLeftTop(-10000, -10000);
36678 this.afterSlideIn();
36686 slideInIf : function(e){
36687 if(!e.within(this.el)){
36692 animateCollapse : function(){
36693 this.beforeSlide();
36694 this.el.setStyle("z-index", 20000);
36695 var anchor = this.getSlideAnchor();
36696 this.el.slideOut(anchor, {
36697 callback : function(){
36698 this.el.setStyle("z-index", "");
36699 this.collapsedEl.slideIn(anchor, {duration:.3});
36701 this.el.setLocation(-10000,-10000);
36703 this.fireEvent("collapsed", this);
36710 animateExpand : function(){
36711 this.beforeSlide();
36712 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36713 this.el.setStyle("z-index", 20000);
36714 this.collapsedEl.hide({
36717 this.el.slideIn(this.getSlideAnchor(), {
36718 callback : function(){
36719 this.el.setStyle("z-index", "");
36722 this.split.el.show();
36724 this.fireEvent("invalidated", this);
36725 this.fireEvent("expanded", this);
36753 getAnchor : function(){
36754 return this.anchors[this.position];
36757 getCollapseAnchor : function(){
36758 return this.canchors[this.position];
36761 getSlideAnchor : function(){
36762 return this.sanchors[this.position];
36765 getAlignAdj : function(){
36766 var cm = this.cmargins;
36767 switch(this.position){
36783 getExpandAdj : function(){
36784 var c = this.collapsedEl, cm = this.cmargins;
36785 switch(this.position){
36787 return [-(cm.right+c.getWidth()+cm.left), 0];
36790 return [cm.right+c.getWidth()+cm.left, 0];
36793 return [0, -(cm.top+cm.bottom+c.getHeight())];
36796 return [0, cm.top+cm.bottom+c.getHeight()];
36802 * Ext JS Library 1.1.1
36803 * Copyright(c) 2006-2007, Ext JS, LLC.
36805 * Originally Released Under LGPL - original licence link has changed is not relivant.
36808 * <script type="text/javascript">
36811 * These classes are private internal classes
36813 Roo.bootstrap.layout.Center = function(config){
36814 config.region = "center";
36815 Roo.bootstrap.layout.Region.call(this, config);
36816 this.visible = true;
36817 this.minWidth = config.minWidth || 20;
36818 this.minHeight = config.minHeight || 20;
36821 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36823 // center panel can't be hidden
36827 // center panel can't be hidden
36830 getMinWidth: function(){
36831 return this.minWidth;
36834 getMinHeight: function(){
36835 return this.minHeight;
36848 Roo.bootstrap.layout.North = function(config)
36850 config.region = 'north';
36851 config.cursor = 'n-resize';
36853 Roo.bootstrap.layout.Split.call(this, config);
36857 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36858 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36859 this.split.el.addClass("roo-layout-split-v");
36861 var size = config.initialSize || config.height;
36862 if(typeof size != "undefined"){
36863 this.el.setHeight(size);
36866 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36868 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36872 getBox : function(){
36873 if(this.collapsed){
36874 return this.collapsedEl.getBox();
36876 var box = this.el.getBox();
36878 box.height += this.split.el.getHeight();
36883 updateBox : function(box){
36884 if(this.split && !this.collapsed){
36885 box.height -= this.split.el.getHeight();
36886 this.split.el.setLeft(box.x);
36887 this.split.el.setTop(box.y+box.height);
36888 this.split.el.setWidth(box.width);
36890 if(this.collapsed){
36891 this.updateBody(box.width, null);
36893 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36901 Roo.bootstrap.layout.South = function(config){
36902 config.region = 'south';
36903 config.cursor = 's-resize';
36904 Roo.bootstrap.layout.Split.call(this, config);
36906 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36907 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36908 this.split.el.addClass("roo-layout-split-v");
36910 var size = config.initialSize || config.height;
36911 if(typeof size != "undefined"){
36912 this.el.setHeight(size);
36916 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36917 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36918 getBox : function(){
36919 if(this.collapsed){
36920 return this.collapsedEl.getBox();
36922 var box = this.el.getBox();
36924 var sh = this.split.el.getHeight();
36931 updateBox : function(box){
36932 if(this.split && !this.collapsed){
36933 var sh = this.split.el.getHeight();
36936 this.split.el.setLeft(box.x);
36937 this.split.el.setTop(box.y-sh);
36938 this.split.el.setWidth(box.width);
36940 if(this.collapsed){
36941 this.updateBody(box.width, null);
36943 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36947 Roo.bootstrap.layout.East = function(config){
36948 config.region = "east";
36949 config.cursor = "e-resize";
36950 Roo.bootstrap.layout.Split.call(this, config);
36952 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36953 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36954 this.split.el.addClass("roo-layout-split-h");
36956 var size = config.initialSize || config.width;
36957 if(typeof size != "undefined"){
36958 this.el.setWidth(size);
36961 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36962 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36963 getBox : function(){
36964 if(this.collapsed){
36965 return this.collapsedEl.getBox();
36967 var box = this.el.getBox();
36969 var sw = this.split.el.getWidth();
36976 updateBox : function(box){
36977 if(this.split && !this.collapsed){
36978 var sw = this.split.el.getWidth();
36980 this.split.el.setLeft(box.x);
36981 this.split.el.setTop(box.y);
36982 this.split.el.setHeight(box.height);
36985 if(this.collapsed){
36986 this.updateBody(null, box.height);
36988 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36992 Roo.bootstrap.layout.West = function(config){
36993 config.region = "west";
36994 config.cursor = "w-resize";
36996 Roo.bootstrap.layout.Split.call(this, config);
36998 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36999 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37000 this.split.el.addClass("roo-layout-split-h");
37004 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37005 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37007 onRender: function(ctr, pos)
37009 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37010 var size = this.config.initialSize || this.config.width;
37011 if(typeof size != "undefined"){
37012 this.el.setWidth(size);
37016 getBox : function(){
37017 if(this.collapsed){
37018 return this.collapsedEl.getBox();
37020 var box = this.el.getBox();
37022 box.width += this.split.el.getWidth();
37027 updateBox : function(box){
37028 if(this.split && !this.collapsed){
37029 var sw = this.split.el.getWidth();
37031 this.split.el.setLeft(box.x+box.width);
37032 this.split.el.setTop(box.y);
37033 this.split.el.setHeight(box.height);
37035 if(this.collapsed){
37036 this.updateBody(null, box.height);
37038 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37041 Roo.namespace("Roo.bootstrap.panel");/*
37043 * Ext JS Library 1.1.1
37044 * Copyright(c) 2006-2007, Ext JS, LLC.
37046 * Originally Released Under LGPL - original licence link has changed is not relivant.
37049 * <script type="text/javascript">
37052 * @class Roo.ContentPanel
37053 * @extends Roo.util.Observable
37054 * A basic ContentPanel element.
37055 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37056 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37057 * @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
37058 * @cfg {Boolean} closable True if the panel can be closed/removed
37059 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37060 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37061 * @cfg {Toolbar} toolbar A toolbar for this panel
37062 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37063 * @cfg {String} title The title for this panel
37064 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37065 * @cfg {String} url Calls {@link #setUrl} with this value
37066 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37067 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37068 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37069 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37070 * @cfg {Boolean} badges render the badges
37073 * Create a new ContentPanel.
37074 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37075 * @param {String/Object} config A string to set only the title or a config object
37076 * @param {String} content (optional) Set the HTML content for this panel
37077 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37079 Roo.bootstrap.panel.Content = function( config){
37081 this.tpl = config.tpl || false;
37083 var el = config.el;
37084 var content = config.content;
37086 if(config.autoCreate){ // xtype is available if this is called from factory
37089 this.el = Roo.get(el);
37090 if(!this.el && config && config.autoCreate){
37091 if(typeof config.autoCreate == "object"){
37092 if(!config.autoCreate.id){
37093 config.autoCreate.id = config.id||el;
37095 this.el = Roo.DomHelper.append(document.body,
37096 config.autoCreate, true);
37098 var elcfg = { tag: "div",
37099 cls: "roo-layout-inactive-content",
37103 elcfg.html = config.html;
37107 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37110 this.closable = false;
37111 this.loaded = false;
37112 this.active = false;
37115 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37117 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37119 this.wrapEl = this.el; //this.el.wrap();
37121 if (config.toolbar.items) {
37122 ti = config.toolbar.items ;
37123 delete config.toolbar.items ;
37127 this.toolbar.render(this.wrapEl, 'before');
37128 for(var i =0;i < ti.length;i++) {
37129 // Roo.log(['add child', items[i]]);
37130 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37132 this.toolbar.items = nitems;
37133 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37134 delete config.toolbar;
37138 // xtype created footer. - not sure if will work as we normally have to render first..
37139 if (this.footer && !this.footer.el && this.footer.xtype) {
37140 if (!this.wrapEl) {
37141 this.wrapEl = this.el.wrap();
37144 this.footer.container = this.wrapEl.createChild();
37146 this.footer = Roo.factory(this.footer, Roo);
37151 if(typeof config == "string"){
37152 this.title = config;
37154 Roo.apply(this, config);
37158 this.resizeEl = Roo.get(this.resizeEl, true);
37160 this.resizeEl = this.el;
37162 // handle view.xtype
37170 * Fires when this panel is activated.
37171 * @param {Roo.ContentPanel} this
37175 * @event deactivate
37176 * Fires when this panel is activated.
37177 * @param {Roo.ContentPanel} this
37179 "deactivate" : true,
37183 * Fires when this panel is resized if fitToFrame is true.
37184 * @param {Roo.ContentPanel} this
37185 * @param {Number} width The width after any component adjustments
37186 * @param {Number} height The height after any component adjustments
37192 * Fires when this tab is created
37193 * @param {Roo.ContentPanel} this
37204 if(this.autoScroll){
37205 this.resizeEl.setStyle("overflow", "auto");
37207 // fix randome scrolling
37208 //this.el.on('scroll', function() {
37209 // Roo.log('fix random scolling');
37210 // this.scrollTo('top',0);
37213 content = content || this.content;
37215 this.setContent(content);
37217 if(config && config.url){
37218 this.setUrl(this.url, this.params, this.loadOnce);
37223 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37225 if (this.view && typeof(this.view.xtype) != 'undefined') {
37226 this.view.el = this.el.appendChild(document.createElement("div"));
37227 this.view = Roo.factory(this.view);
37228 this.view.render && this.view.render(false, '');
37232 this.fireEvent('render', this);
37235 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37239 setRegion : function(region){
37240 this.region = region;
37241 this.setActiveClass(region && !this.background);
37245 setActiveClass: function(state)
37248 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37249 this.el.setStyle('position','relative');
37251 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37252 this.el.setStyle('position', 'absolute');
37257 * Returns the toolbar for this Panel if one was configured.
37258 * @return {Roo.Toolbar}
37260 getToolbar : function(){
37261 return this.toolbar;
37264 setActiveState : function(active)
37266 this.active = active;
37267 this.setActiveClass(active);
37269 if(this.fireEvent("deactivate", this) === false){
37274 this.fireEvent("activate", this);
37278 * Updates this panel's element
37279 * @param {String} content The new content
37280 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37282 setContent : function(content, loadScripts){
37283 this.el.update(content, loadScripts);
37286 ignoreResize : function(w, h){
37287 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37290 this.lastSize = {width: w, height: h};
37295 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37296 * @return {Roo.UpdateManager} The UpdateManager
37298 getUpdateManager : function(){
37299 return this.el.getUpdateManager();
37302 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37303 * @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:
37306 url: "your-url.php",
37307 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37308 callback: yourFunction,
37309 scope: yourObject, //(optional scope)
37312 text: "Loading...",
37317 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37318 * 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.
37319 * @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}
37320 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37321 * @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.
37322 * @return {Roo.ContentPanel} this
37325 var um = this.el.getUpdateManager();
37326 um.update.apply(um, arguments);
37332 * 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.
37333 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37334 * @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)
37335 * @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)
37336 * @return {Roo.UpdateManager} The UpdateManager
37338 setUrl : function(url, params, loadOnce){
37339 if(this.refreshDelegate){
37340 this.removeListener("activate", this.refreshDelegate);
37342 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37343 this.on("activate", this.refreshDelegate);
37344 return this.el.getUpdateManager();
37347 _handleRefresh : function(url, params, loadOnce){
37348 if(!loadOnce || !this.loaded){
37349 var updater = this.el.getUpdateManager();
37350 updater.update(url, params, this._setLoaded.createDelegate(this));
37354 _setLoaded : function(){
37355 this.loaded = true;
37359 * Returns this panel's id
37362 getId : function(){
37367 * Returns this panel's element - used by regiosn to add.
37368 * @return {Roo.Element}
37370 getEl : function(){
37371 return this.wrapEl || this.el;
37376 adjustForComponents : function(width, height)
37378 //Roo.log('adjustForComponents ');
37379 if(this.resizeEl != this.el){
37380 width -= this.el.getFrameWidth('lr');
37381 height -= this.el.getFrameWidth('tb');
37384 var te = this.toolbar.getEl();
37385 te.setWidth(width);
37386 height -= te.getHeight();
37389 var te = this.footer.getEl();
37390 te.setWidth(width);
37391 height -= te.getHeight();
37395 if(this.adjustments){
37396 width += this.adjustments[0];
37397 height += this.adjustments[1];
37399 return {"width": width, "height": height};
37402 setSize : function(width, height){
37403 if(this.fitToFrame && !this.ignoreResize(width, height)){
37404 if(this.fitContainer && this.resizeEl != this.el){
37405 this.el.setSize(width, height);
37407 var size = this.adjustForComponents(width, height);
37408 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37409 this.fireEvent('resize', this, size.width, size.height);
37414 * Returns this panel's title
37417 getTitle : function(){
37419 if (typeof(this.title) != 'object') {
37424 for (var k in this.title) {
37425 if (!this.title.hasOwnProperty(k)) {
37429 if (k.indexOf('-') >= 0) {
37430 var s = k.split('-');
37431 for (var i = 0; i<s.length; i++) {
37432 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37435 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37442 * Set this panel's title
37443 * @param {String} title
37445 setTitle : function(title){
37446 this.title = title;
37448 this.region.updatePanelTitle(this, title);
37453 * Returns true is this panel was configured to be closable
37454 * @return {Boolean}
37456 isClosable : function(){
37457 return this.closable;
37460 beforeSlide : function(){
37462 this.resizeEl.clip();
37465 afterSlide : function(){
37467 this.resizeEl.unclip();
37471 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37472 * Will fail silently if the {@link #setUrl} method has not been called.
37473 * This does not activate the panel, just updates its content.
37475 refresh : function(){
37476 if(this.refreshDelegate){
37477 this.loaded = false;
37478 this.refreshDelegate();
37483 * Destroys this panel
37485 destroy : function(){
37486 this.el.removeAllListeners();
37487 var tempEl = document.createElement("span");
37488 tempEl.appendChild(this.el.dom);
37489 tempEl.innerHTML = "";
37495 * form - if the content panel contains a form - this is a reference to it.
37496 * @type {Roo.form.Form}
37500 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37501 * This contains a reference to it.
37507 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37517 * @param {Object} cfg Xtype definition of item to add.
37521 getChildContainer: function () {
37522 return this.getEl();
37527 var ret = new Roo.factory(cfg);
37532 if (cfg.xtype.match(/^Form$/)) {
37535 //if (this.footer) {
37536 // el = this.footer.container.insertSibling(false, 'before');
37538 el = this.el.createChild();
37541 this.form = new Roo.form.Form(cfg);
37544 if ( this.form.allItems.length) {
37545 this.form.render(el.dom);
37549 // should only have one of theses..
37550 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37551 // views.. should not be just added - used named prop 'view''
37553 cfg.el = this.el.appendChild(document.createElement("div"));
37556 var ret = new Roo.factory(cfg);
37558 ret.render && ret.render(false, ''); // render blank..
37568 * @class Roo.bootstrap.panel.Grid
37569 * @extends Roo.bootstrap.panel.Content
37571 * Create a new GridPanel.
37572 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37573 * @param {Object} config A the config object
37579 Roo.bootstrap.panel.Grid = function(config)
37583 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37584 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37586 config.el = this.wrapper;
37587 //this.el = this.wrapper;
37589 if (config.container) {
37590 // ctor'ed from a Border/panel.grid
37593 this.wrapper.setStyle("overflow", "hidden");
37594 this.wrapper.addClass('roo-grid-container');
37599 if(config.toolbar){
37600 var tool_el = this.wrapper.createChild();
37601 this.toolbar = Roo.factory(config.toolbar);
37603 if (config.toolbar.items) {
37604 ti = config.toolbar.items ;
37605 delete config.toolbar.items ;
37609 this.toolbar.render(tool_el);
37610 for(var i =0;i < ti.length;i++) {
37611 // Roo.log(['add child', items[i]]);
37612 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37614 this.toolbar.items = nitems;
37616 delete config.toolbar;
37619 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37620 config.grid.scrollBody = true;;
37621 config.grid.monitorWindowResize = false; // turn off autosizing
37622 config.grid.autoHeight = false;
37623 config.grid.autoWidth = false;
37625 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37627 if (config.background) {
37628 // render grid on panel activation (if panel background)
37629 this.on('activate', function(gp) {
37630 if (!gp.grid.rendered) {
37631 gp.grid.render(this.wrapper);
37632 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37637 this.grid.render(this.wrapper);
37638 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37641 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37642 // ??? needed ??? config.el = this.wrapper;
37647 // xtype created footer. - not sure if will work as we normally have to render first..
37648 if (this.footer && !this.footer.el && this.footer.xtype) {
37650 var ctr = this.grid.getView().getFooterPanel(true);
37651 this.footer.dataSource = this.grid.dataSource;
37652 this.footer = Roo.factory(this.footer, Roo);
37653 this.footer.render(ctr);
37663 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37664 getId : function(){
37665 return this.grid.id;
37669 * Returns the grid for this panel
37670 * @return {Roo.bootstrap.Table}
37672 getGrid : function(){
37676 setSize : function(width, height){
37677 if(!this.ignoreResize(width, height)){
37678 var grid = this.grid;
37679 var size = this.adjustForComponents(width, height);
37680 var gridel = grid.getGridEl();
37681 gridel.setSize(size.width, size.height);
37683 var thd = grid.getGridEl().select('thead',true).first();
37684 var tbd = grid.getGridEl().select('tbody', true).first();
37686 tbd.setSize(width, height - thd.getHeight());
37695 beforeSlide : function(){
37696 this.grid.getView().scroller.clip();
37699 afterSlide : function(){
37700 this.grid.getView().scroller.unclip();
37703 destroy : function(){
37704 this.grid.destroy();
37706 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37711 * @class Roo.bootstrap.panel.Nest
37712 * @extends Roo.bootstrap.panel.Content
37714 * Create a new Panel, that can contain a layout.Border.
37717 * @param {Roo.BorderLayout} layout The layout for this panel
37718 * @param {String/Object} config A string to set only the title or a config object
37720 Roo.bootstrap.panel.Nest = function(config)
37722 // construct with only one argument..
37723 /* FIXME - implement nicer consturctors
37724 if (layout.layout) {
37726 layout = config.layout;
37727 delete config.layout;
37729 if (layout.xtype && !layout.getEl) {
37730 // then layout needs constructing..
37731 layout = Roo.factory(layout, Roo);
37735 config.el = config.layout.getEl();
37737 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37739 config.layout.monitorWindowResize = false; // turn off autosizing
37740 this.layout = config.layout;
37741 this.layout.getEl().addClass("roo-layout-nested-layout");
37748 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37750 setSize : function(width, height){
37751 if(!this.ignoreResize(width, height)){
37752 var size = this.adjustForComponents(width, height);
37753 var el = this.layout.getEl();
37754 if (size.height < 1) {
37755 el.setWidth(size.width);
37757 el.setSize(size.width, size.height);
37759 var touch = el.dom.offsetWidth;
37760 this.layout.layout();
37761 // ie requires a double layout on the first pass
37762 if(Roo.isIE && !this.initialized){
37763 this.initialized = true;
37764 this.layout.layout();
37769 // activate all subpanels if not currently active..
37771 setActiveState : function(active){
37772 this.active = active;
37773 this.setActiveClass(active);
37776 this.fireEvent("deactivate", this);
37780 this.fireEvent("activate", this);
37781 // not sure if this should happen before or after..
37782 if (!this.layout) {
37783 return; // should not happen..
37786 for (var r in this.layout.regions) {
37787 reg = this.layout.getRegion(r);
37788 if (reg.getActivePanel()) {
37789 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37790 reg.setActivePanel(reg.getActivePanel());
37793 if (!reg.panels.length) {
37796 reg.showPanel(reg.getPanel(0));
37805 * Returns the nested BorderLayout for this panel
37806 * @return {Roo.BorderLayout}
37808 getLayout : function(){
37809 return this.layout;
37813 * Adds a xtype elements to the layout of the nested panel
37817 xtype : 'ContentPanel',
37824 xtype : 'NestedLayoutPanel',
37830 items : [ ... list of content panels or nested layout panels.. ]
37834 * @param {Object} cfg Xtype definition of item to add.
37836 addxtype : function(cfg) {
37837 return this.layout.addxtype(cfg);
37842 * Ext JS Library 1.1.1
37843 * Copyright(c) 2006-2007, Ext JS, LLC.
37845 * Originally Released Under LGPL - original licence link has changed is not relivant.
37848 * <script type="text/javascript">
37851 * @class Roo.TabPanel
37852 * @extends Roo.util.Observable
37853 * A lightweight tab container.
37857 // basic tabs 1, built from existing content
37858 var tabs = new Roo.TabPanel("tabs1");
37859 tabs.addTab("script", "View Script");
37860 tabs.addTab("markup", "View Markup");
37861 tabs.activate("script");
37863 // more advanced tabs, built from javascript
37864 var jtabs = new Roo.TabPanel("jtabs");
37865 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37867 // set up the UpdateManager
37868 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37869 var updater = tab2.getUpdateManager();
37870 updater.setDefaultUrl("ajax1.htm");
37871 tab2.on('activate', updater.refresh, updater, true);
37873 // Use setUrl for Ajax loading
37874 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37875 tab3.setUrl("ajax2.htm", null, true);
37878 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37881 jtabs.activate("jtabs-1");
37884 * Create a new TabPanel.
37885 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37886 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37888 Roo.bootstrap.panel.Tabs = function(config){
37890 * The container element for this TabPanel.
37891 * @type Roo.Element
37893 this.el = Roo.get(config.el);
37896 if(typeof config == "boolean"){
37897 this.tabPosition = config ? "bottom" : "top";
37899 Roo.apply(this, config);
37903 if(this.tabPosition == "bottom"){
37904 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37905 this.el.addClass("roo-tabs-bottom");
37907 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37908 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37909 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37911 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37913 if(this.tabPosition != "bottom"){
37914 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37915 * @type Roo.Element
37917 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37918 this.el.addClass("roo-tabs-top");
37922 this.bodyEl.setStyle("position", "relative");
37924 this.active = null;
37925 this.activateDelegate = this.activate.createDelegate(this);
37930 * Fires when the active tab changes
37931 * @param {Roo.TabPanel} this
37932 * @param {Roo.TabPanelItem} activePanel The new active tab
37936 * @event beforetabchange
37937 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37938 * @param {Roo.TabPanel} this
37939 * @param {Object} e Set cancel to true on this object to cancel the tab change
37940 * @param {Roo.TabPanelItem} tab The tab being changed to
37942 "beforetabchange" : true
37945 Roo.EventManager.onWindowResize(this.onResize, this);
37946 this.cpad = this.el.getPadding("lr");
37947 this.hiddenCount = 0;
37950 // toolbar on the tabbar support...
37951 if (this.toolbar) {
37952 alert("no toolbar support yet");
37953 this.toolbar = false;
37955 var tcfg = this.toolbar;
37956 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37957 this.toolbar = new Roo.Toolbar(tcfg);
37958 if (Roo.isSafari) {
37959 var tbl = tcfg.container.child('table', true);
37960 tbl.setAttribute('width', '100%');
37968 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37971 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37973 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37975 tabPosition : "top",
37977 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37979 currentTabWidth : 0,
37981 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37985 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37989 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37991 preferredTabWidth : 175,
37993 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37995 resizeTabs : false,
37997 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37999 monitorResize : true,
38001 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38006 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38007 * @param {String} id The id of the div to use <b>or create</b>
38008 * @param {String} text The text for the tab
38009 * @param {String} content (optional) Content to put in the TabPanelItem body
38010 * @param {Boolean} closable (optional) True to create a close icon on the tab
38011 * @return {Roo.TabPanelItem} The created TabPanelItem
38013 addTab : function(id, text, content, closable, tpl)
38015 var item = new Roo.bootstrap.panel.TabItem({
38019 closable : closable,
38022 this.addTabItem(item);
38024 item.setContent(content);
38030 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38031 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38032 * @return {Roo.TabPanelItem}
38034 getTab : function(id){
38035 return this.items[id];
38039 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38040 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38042 hideTab : function(id){
38043 var t = this.items[id];
38046 this.hiddenCount++;
38047 this.autoSizeTabs();
38052 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38053 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38055 unhideTab : function(id){
38056 var t = this.items[id];
38058 t.setHidden(false);
38059 this.hiddenCount--;
38060 this.autoSizeTabs();
38065 * Adds an existing {@link Roo.TabPanelItem}.
38066 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38068 addTabItem : function(item){
38069 this.items[item.id] = item;
38070 this.items.push(item);
38071 // if(this.resizeTabs){
38072 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38073 // this.autoSizeTabs();
38075 // item.autoSize();
38080 * Removes a {@link Roo.TabPanelItem}.
38081 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38083 removeTab : function(id){
38084 var items = this.items;
38085 var tab = items[id];
38086 if(!tab) { return; }
38087 var index = items.indexOf(tab);
38088 if(this.active == tab && items.length > 1){
38089 var newTab = this.getNextAvailable(index);
38094 this.stripEl.dom.removeChild(tab.pnode.dom);
38095 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38096 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38098 items.splice(index, 1);
38099 delete this.items[tab.id];
38100 tab.fireEvent("close", tab);
38101 tab.purgeListeners();
38102 this.autoSizeTabs();
38105 getNextAvailable : function(start){
38106 var items = this.items;
38108 // look for a next tab that will slide over to
38109 // replace the one being removed
38110 while(index < items.length){
38111 var item = items[++index];
38112 if(item && !item.isHidden()){
38116 // if one isn't found select the previous tab (on the left)
38119 var item = items[--index];
38120 if(item && !item.isHidden()){
38128 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38129 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38131 disableTab : function(id){
38132 var tab = this.items[id];
38133 if(tab && this.active != tab){
38139 * Enables a {@link Roo.TabPanelItem} that is disabled.
38140 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38142 enableTab : function(id){
38143 var tab = this.items[id];
38148 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38149 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38150 * @return {Roo.TabPanelItem} The TabPanelItem.
38152 activate : function(id){
38153 var tab = this.items[id];
38157 if(tab == this.active || tab.disabled){
38161 this.fireEvent("beforetabchange", this, e, tab);
38162 if(e.cancel !== true && !tab.disabled){
38164 this.active.hide();
38166 this.active = this.items[id];
38167 this.active.show();
38168 this.fireEvent("tabchange", this, this.active);
38174 * Gets the active {@link Roo.TabPanelItem}.
38175 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38177 getActiveTab : function(){
38178 return this.active;
38182 * Updates the tab body element to fit the height of the container element
38183 * for overflow scrolling
38184 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38186 syncHeight : function(targetHeight){
38187 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38188 var bm = this.bodyEl.getMargins();
38189 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38190 this.bodyEl.setHeight(newHeight);
38194 onResize : function(){
38195 if(this.monitorResize){
38196 this.autoSizeTabs();
38201 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38203 beginUpdate : function(){
38204 this.updating = true;
38208 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38210 endUpdate : function(){
38211 this.updating = false;
38212 this.autoSizeTabs();
38216 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38218 autoSizeTabs : function(){
38219 var count = this.items.length;
38220 var vcount = count - this.hiddenCount;
38221 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38224 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38225 var availWidth = Math.floor(w / vcount);
38226 var b = this.stripBody;
38227 if(b.getWidth() > w){
38228 var tabs = this.items;
38229 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38230 if(availWidth < this.minTabWidth){
38231 /*if(!this.sleft){ // incomplete scrolling code
38232 this.createScrollButtons();
38235 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38238 if(this.currentTabWidth < this.preferredTabWidth){
38239 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38245 * Returns the number of tabs in this TabPanel.
38248 getCount : function(){
38249 return this.items.length;
38253 * Resizes all the tabs to the passed width
38254 * @param {Number} The new width
38256 setTabWidth : function(width){
38257 this.currentTabWidth = width;
38258 for(var i = 0, len = this.items.length; i < len; i++) {
38259 if(!this.items[i].isHidden()) {
38260 this.items[i].setWidth(width);
38266 * Destroys this TabPanel
38267 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38269 destroy : function(removeEl){
38270 Roo.EventManager.removeResizeListener(this.onResize, this);
38271 for(var i = 0, len = this.items.length; i < len; i++){
38272 this.items[i].purgeListeners();
38274 if(removeEl === true){
38275 this.el.update("");
38280 createStrip : function(container)
38282 var strip = document.createElement("nav");
38283 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38284 container.appendChild(strip);
38288 createStripList : function(strip)
38290 // div wrapper for retard IE
38291 // returns the "tr" element.
38292 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38293 //'<div class="x-tabs-strip-wrap">'+
38294 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38295 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38296 return strip.firstChild; //.firstChild.firstChild.firstChild;
38298 createBody : function(container)
38300 var body = document.createElement("div");
38301 Roo.id(body, "tab-body");
38302 //Roo.fly(body).addClass("x-tabs-body");
38303 Roo.fly(body).addClass("tab-content");
38304 container.appendChild(body);
38307 createItemBody :function(bodyEl, id){
38308 var body = Roo.getDom(id);
38310 body = document.createElement("div");
38313 //Roo.fly(body).addClass("x-tabs-item-body");
38314 Roo.fly(body).addClass("tab-pane");
38315 bodyEl.insertBefore(body, bodyEl.firstChild);
38319 createStripElements : function(stripEl, text, closable, tpl)
38321 var td = document.createElement("li"); // was td..
38324 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38327 stripEl.appendChild(td);
38329 td.className = "x-tabs-closable";
38330 if(!this.closeTpl){
38331 this.closeTpl = new Roo.Template(
38332 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38333 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38334 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38337 var el = this.closeTpl.overwrite(td, {"text": text});
38338 var close = el.getElementsByTagName("div")[0];
38339 var inner = el.getElementsByTagName("em")[0];
38340 return {"el": el, "close": close, "inner": inner};
38343 // not sure what this is..
38344 // if(!this.tabTpl){
38345 //this.tabTpl = new Roo.Template(
38346 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38347 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38349 // this.tabTpl = new Roo.Template(
38350 // '<a href="#">' +
38351 // '<span unselectable="on"' +
38352 // (this.disableTooltips ? '' : ' title="{text}"') +
38353 // ' >{text}</span></a>'
38359 var template = tpl || this.tabTpl || false;
38363 template = new Roo.Template(
38365 '<span unselectable="on"' +
38366 (this.disableTooltips ? '' : ' title="{text}"') +
38367 ' >{text}</span></a>'
38371 switch (typeof(template)) {
38375 template = new Roo.Template(template);
38381 var el = template.overwrite(td, {"text": text});
38383 var inner = el.getElementsByTagName("span")[0];
38385 return {"el": el, "inner": inner};
38393 * @class Roo.TabPanelItem
38394 * @extends Roo.util.Observable
38395 * Represents an individual item (tab plus body) in a TabPanel.
38396 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38397 * @param {String} id The id of this TabPanelItem
38398 * @param {String} text The text for the tab of this TabPanelItem
38399 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38401 Roo.bootstrap.panel.TabItem = function(config){
38403 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38404 * @type Roo.TabPanel
38406 this.tabPanel = config.panel;
38408 * The id for this TabPanelItem
38411 this.id = config.id;
38413 this.disabled = false;
38415 this.text = config.text;
38417 this.loaded = false;
38418 this.closable = config.closable;
38421 * The body element for this TabPanelItem.
38422 * @type Roo.Element
38424 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38425 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38426 this.bodyEl.setStyle("display", "block");
38427 this.bodyEl.setStyle("zoom", "1");
38428 //this.hideAction();
38430 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38432 this.el = Roo.get(els.el);
38433 this.inner = Roo.get(els.inner, true);
38434 this.textEl = Roo.get(this.el.dom.firstChild, true);
38435 this.pnode = Roo.get(els.el.parentNode, true);
38436 // this.el.on("mousedown", this.onTabMouseDown, this);
38437 this.el.on("click", this.onTabClick, this);
38439 if(config.closable){
38440 var c = Roo.get(els.close, true);
38441 c.dom.title = this.closeText;
38442 c.addClassOnOver("close-over");
38443 c.on("click", this.closeClick, this);
38449 * Fires when this tab becomes the active tab.
38450 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38451 * @param {Roo.TabPanelItem} this
38455 * @event beforeclose
38456 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38457 * @param {Roo.TabPanelItem} this
38458 * @param {Object} e Set cancel to true on this object to cancel the close.
38460 "beforeclose": true,
38463 * Fires when this tab is closed.
38464 * @param {Roo.TabPanelItem} this
38468 * @event deactivate
38469 * Fires when this tab is no longer the active tab.
38470 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38471 * @param {Roo.TabPanelItem} this
38473 "deactivate" : true
38475 this.hidden = false;
38477 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38480 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38482 purgeListeners : function(){
38483 Roo.util.Observable.prototype.purgeListeners.call(this);
38484 this.el.removeAllListeners();
38487 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38490 this.pnode.addClass("active");
38493 this.tabPanel.stripWrap.repaint();
38495 this.fireEvent("activate", this.tabPanel, this);
38499 * Returns true if this tab is the active tab.
38500 * @return {Boolean}
38502 isActive : function(){
38503 return this.tabPanel.getActiveTab() == this;
38507 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38510 this.pnode.removeClass("active");
38512 this.fireEvent("deactivate", this.tabPanel, this);
38515 hideAction : function(){
38516 this.bodyEl.hide();
38517 this.bodyEl.setStyle("position", "absolute");
38518 this.bodyEl.setLeft("-20000px");
38519 this.bodyEl.setTop("-20000px");
38522 showAction : function(){
38523 this.bodyEl.setStyle("position", "relative");
38524 this.bodyEl.setTop("");
38525 this.bodyEl.setLeft("");
38526 this.bodyEl.show();
38530 * Set the tooltip for the tab.
38531 * @param {String} tooltip The tab's tooltip
38533 setTooltip : function(text){
38534 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38535 this.textEl.dom.qtip = text;
38536 this.textEl.dom.removeAttribute('title');
38538 this.textEl.dom.title = text;
38542 onTabClick : function(e){
38543 e.preventDefault();
38544 this.tabPanel.activate(this.id);
38547 onTabMouseDown : function(e){
38548 e.preventDefault();
38549 this.tabPanel.activate(this.id);
38552 getWidth : function(){
38553 return this.inner.getWidth();
38556 setWidth : function(width){
38557 var iwidth = width - this.pnode.getPadding("lr");
38558 this.inner.setWidth(iwidth);
38559 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38560 this.pnode.setWidth(width);
38564 * Show or hide the tab
38565 * @param {Boolean} hidden True to hide or false to show.
38567 setHidden : function(hidden){
38568 this.hidden = hidden;
38569 this.pnode.setStyle("display", hidden ? "none" : "");
38573 * Returns true if this tab is "hidden"
38574 * @return {Boolean}
38576 isHidden : function(){
38577 return this.hidden;
38581 * Returns the text for this tab
38584 getText : function(){
38588 autoSize : function(){
38589 //this.el.beginMeasure();
38590 this.textEl.setWidth(1);
38592 * #2804 [new] Tabs in Roojs
38593 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38595 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38596 //this.el.endMeasure();
38600 * Sets the text for the tab (Note: this also sets the tooltip text)
38601 * @param {String} text The tab's text and tooltip
38603 setText : function(text){
38605 this.textEl.update(text);
38606 this.setTooltip(text);
38607 //if(!this.tabPanel.resizeTabs){
38608 // this.autoSize();
38612 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38614 activate : function(){
38615 this.tabPanel.activate(this.id);
38619 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38621 disable : function(){
38622 if(this.tabPanel.active != this){
38623 this.disabled = true;
38624 this.pnode.addClass("disabled");
38629 * Enables this TabPanelItem if it was previously disabled.
38631 enable : function(){
38632 this.disabled = false;
38633 this.pnode.removeClass("disabled");
38637 * Sets the content for this TabPanelItem.
38638 * @param {String} content The content
38639 * @param {Boolean} loadScripts true to look for and load scripts
38641 setContent : function(content, loadScripts){
38642 this.bodyEl.update(content, loadScripts);
38646 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38647 * @return {Roo.UpdateManager} The UpdateManager
38649 getUpdateManager : function(){
38650 return this.bodyEl.getUpdateManager();
38654 * Set a URL to be used to load the content for this TabPanelItem.
38655 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38656 * @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)
38657 * @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)
38658 * @return {Roo.UpdateManager} The UpdateManager
38660 setUrl : function(url, params, loadOnce){
38661 if(this.refreshDelegate){
38662 this.un('activate', this.refreshDelegate);
38664 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38665 this.on("activate", this.refreshDelegate);
38666 return this.bodyEl.getUpdateManager();
38670 _handleRefresh : function(url, params, loadOnce){
38671 if(!loadOnce || !this.loaded){
38672 var updater = this.bodyEl.getUpdateManager();
38673 updater.update(url, params, this._setLoaded.createDelegate(this));
38678 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38679 * Will fail silently if the setUrl method has not been called.
38680 * This does not activate the panel, just updates its content.
38682 refresh : function(){
38683 if(this.refreshDelegate){
38684 this.loaded = false;
38685 this.refreshDelegate();
38690 _setLoaded : function(){
38691 this.loaded = true;
38695 closeClick : function(e){
38698 this.fireEvent("beforeclose", this, o);
38699 if(o.cancel !== true){
38700 this.tabPanel.removeTab(this.id);
38704 * The text displayed in the tooltip for the close icon.
38707 closeText : "Close this tab"
38710 * This script refer to:
38711 * Title: International Telephone Input
38712 * Author: Jack O'Connor
38713 * Code version: v12.1.12
38714 * Availability: https://github.com/jackocnr/intl-tel-input.git
38717 Roo.bootstrap.PhoneInputData = function() {
38720 "Afghanistan (افغانستان)",
38725 "Albania (Shqipëri)",
38730 "Algeria (الجزائر)",
38755 "Antigua and Barbuda",
38765 "Armenia (Հայաստան)",
38781 "Austria (Österreich)",
38786 "Azerbaijan (Azərbaycan)",
38796 "Bahrain (البحرين)",
38801 "Bangladesh (বাংলাদেশ)",
38811 "Belarus (Беларусь)",
38816 "Belgium (België)",
38846 "Bosnia and Herzegovina (Босна и Херцеговина)",
38861 "British Indian Ocean Territory",
38866 "British Virgin Islands",
38876 "Bulgaria (България)",
38886 "Burundi (Uburundi)",
38891 "Cambodia (កម្ពុជា)",
38896 "Cameroon (Cameroun)",
38905 ["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"]
38908 "Cape Verde (Kabu Verdi)",
38913 "Caribbean Netherlands",
38924 "Central African Republic (République centrafricaine)",
38944 "Christmas Island",
38950 "Cocos (Keeling) Islands",
38961 "Comoros (جزر القمر)",
38966 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38971 "Congo (Republic) (Congo-Brazzaville)",
38991 "Croatia (Hrvatska)",
39012 "Czech Republic (Česká republika)",
39017 "Denmark (Danmark)",
39032 "Dominican Republic (República Dominicana)",
39036 ["809", "829", "849"]
39054 "Equatorial Guinea (Guinea Ecuatorial)",
39074 "Falkland Islands (Islas Malvinas)",
39079 "Faroe Islands (Føroyar)",
39100 "French Guiana (Guyane française)",
39105 "French Polynesia (Polynésie française)",
39120 "Georgia (საქართველო)",
39125 "Germany (Deutschland)",
39145 "Greenland (Kalaallit Nunaat)",
39182 "Guinea-Bissau (Guiné Bissau)",
39207 "Hungary (Magyarország)",
39212 "Iceland (Ísland)",
39232 "Iraq (العراق)",
39248 "Israel (ישראל)",
39275 "Jordan (الأردن)",
39280 "Kazakhstan (Казахстан)",
39301 "Kuwait (الكويت)",
39306 "Kyrgyzstan (Кыргызстан)",
39316 "Latvia (Latvija)",
39321 "Lebanon (لبنان)",
39336 "Libya (ليبيا)",
39346 "Lithuania (Lietuva)",
39361 "Macedonia (FYROM) (Македонија)",
39366 "Madagascar (Madagasikara)",
39396 "Marshall Islands",
39406 "Mauritania (موريتانيا)",
39411 "Mauritius (Moris)",
39432 "Moldova (Republica Moldova)",
39442 "Mongolia (Монгол)",
39447 "Montenegro (Crna Gora)",
39457 "Morocco (المغرب)",
39463 "Mozambique (Moçambique)",
39468 "Myanmar (Burma) (မြန်မာ)",
39473 "Namibia (Namibië)",
39488 "Netherlands (Nederland)",
39493 "New Caledonia (Nouvelle-Calédonie)",
39528 "North Korea (조선 민주주의 인민 공화국)",
39533 "Northern Mariana Islands",
39549 "Pakistan (پاکستان)",
39559 "Palestine (فلسطين)",
39569 "Papua New Guinea",
39611 "Réunion (La Réunion)",
39617 "Romania (România)",
39633 "Saint Barthélemy",
39644 "Saint Kitts and Nevis",
39654 "Saint Martin (Saint-Martin (partie française))",
39660 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39665 "Saint Vincent and the Grenadines",
39680 "São Tomé and Príncipe (São Tomé e Príncipe)",
39685 "Saudi Arabia (المملكة العربية السعودية)",
39690 "Senegal (Sénégal)",
39720 "Slovakia (Slovensko)",
39725 "Slovenia (Slovenija)",
39735 "Somalia (Soomaaliya)",
39745 "South Korea (대한민국)",
39750 "South Sudan (جنوب السودان)",
39760 "Sri Lanka (ශ්රී ලංකාව)",
39765 "Sudan (السودان)",
39775 "Svalbard and Jan Mayen",
39786 "Sweden (Sverige)",
39791 "Switzerland (Schweiz)",
39796 "Syria (سوريا)",
39841 "Trinidad and Tobago",
39846 "Tunisia (تونس)",
39851 "Turkey (Türkiye)",
39861 "Turks and Caicos Islands",
39871 "U.S. Virgin Islands",
39881 "Ukraine (Україна)",
39886 "United Arab Emirates (الإمارات العربية المتحدة)",
39908 "Uzbekistan (Oʻzbekiston)",
39918 "Vatican City (Città del Vaticano)",
39929 "Vietnam (Việt Nam)",
39934 "Wallis and Futuna (Wallis-et-Futuna)",
39939 "Western Sahara (الصحراء الغربية)",
39945 "Yemen (اليمن)",
39969 * This script refer to:
39970 * Title: International Telephone Input
39971 * Author: Jack O'Connor
39972 * Code version: v12.1.12
39973 * Availability: https://github.com/jackocnr/intl-tel-input.git
39977 * @class Roo.bootstrap.PhoneInput
39978 * @extends Roo.bootstrap.TriggerField
39979 * An input with International dial-code selection
39981 * @cfg {String} defaultDialCode default '+852'
39982 * @cfg {Array} preferedCountries default []
39985 * Create a new PhoneInput.
39986 * @param {Object} config Configuration options
39989 Roo.bootstrap.PhoneInput = function(config) {
39990 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39993 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39995 listWidth: undefined,
39997 selectedClass: 'active',
39999 invalidClass : "has-warning",
40001 validClass: 'has-success',
40003 allowed: '0123456789',
40008 * @cfg {String} defaultDialCode The default dial code when initializing the input
40010 defaultDialCode: '+852',
40013 * @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
40015 preferedCountries: false,
40017 getAutoCreate : function()
40019 var data = Roo.bootstrap.PhoneInputData();
40020 var align = this.labelAlign || this.parentLabelAlign();
40023 this.allCountries = [];
40024 this.dialCodeMapping = [];
40026 for (var i = 0; i < data.length; i++) {
40028 this.allCountries[i] = {
40032 priority: c[3] || 0,
40033 areaCodes: c[4] || null
40035 this.dialCodeMapping[c[2]] = {
40038 priority: c[3] || 0,
40039 areaCodes: c[4] || null
40051 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40052 maxlength: this.max_length,
40053 cls : 'form-control tel-input',
40054 autocomplete: 'new-password'
40057 var hiddenInput = {
40060 cls: 'hidden-tel-input'
40064 hiddenInput.name = this.name;
40067 if (this.disabled) {
40068 input.disabled = true;
40071 var flag_container = {
40088 cls: this.hasFeedback ? 'has-feedback' : '',
40094 cls: 'dial-code-holder',
40101 cls: 'roo-select2-container input-group',
40108 if (this.fieldLabel.length) {
40111 tooltip: 'This field is required'
40117 cls: 'control-label',
40123 html: this.fieldLabel
40126 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40132 if(this.indicatorpos == 'right') {
40133 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40140 if(align == 'left') {
40148 if(this.labelWidth > 12){
40149 label.style = "width: " + this.labelWidth + 'px';
40151 if(this.labelWidth < 13 && this.labelmd == 0){
40152 this.labelmd = this.labelWidth;
40154 if(this.labellg > 0){
40155 label.cls += ' col-lg-' + this.labellg;
40156 input.cls += ' col-lg-' + (12 - this.labellg);
40158 if(this.labelmd > 0){
40159 label.cls += ' col-md-' + this.labelmd;
40160 container.cls += ' col-md-' + (12 - this.labelmd);
40162 if(this.labelsm > 0){
40163 label.cls += ' col-sm-' + this.labelsm;
40164 container.cls += ' col-sm-' + (12 - this.labelsm);
40166 if(this.labelxs > 0){
40167 label.cls += ' col-xs-' + this.labelxs;
40168 container.cls += ' col-xs-' + (12 - this.labelxs);
40178 var settings = this;
40180 ['xs','sm','md','lg'].map(function(size){
40181 if (settings[size]) {
40182 cfg.cls += ' col-' + size + '-' + settings[size];
40186 this.store = new Roo.data.Store({
40187 proxy : new Roo.data.MemoryProxy({}),
40188 reader : new Roo.data.JsonReader({
40199 'name' : 'dialCode',
40203 'name' : 'priority',
40207 'name' : 'areaCodes',
40214 if(!this.preferedCountries) {
40215 this.preferedCountries = [
40222 var p = this.preferedCountries.reverse();
40225 for (var i = 0; i < p.length; i++) {
40226 for (var j = 0; j < this.allCountries.length; j++) {
40227 if(this.allCountries[j].iso2 == p[i]) {
40228 var t = this.allCountries[j];
40229 this.allCountries.splice(j,1);
40230 this.allCountries.unshift(t);
40236 this.store.proxy.data = {
40238 data: this.allCountries
40244 initEvents : function()
40247 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40249 this.indicator = this.indicatorEl();
40250 this.flag = this.flagEl();
40251 this.dialCodeHolder = this.dialCodeHolderEl();
40253 this.trigger = this.el.select('div.flag-box',true).first();
40254 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40259 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40260 _this.list.setWidth(lw);
40263 this.list.on('mouseover', this.onViewOver, this);
40264 this.list.on('mousemove', this.onViewMove, this);
40265 this.inputEl().on("keyup", this.onKeyUp, this);
40266 this.inputEl().on("keypress", this.onKeyPress, this);
40268 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40270 this.view = new Roo.View(this.list, this.tpl, {
40271 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40274 this.view.on('click', this.onViewClick, this);
40275 this.setValue(this.defaultDialCode);
40278 onTriggerClick : function(e)
40280 Roo.log('trigger click');
40285 if(this.isExpanded()){
40287 this.hasFocus = false;
40289 this.store.load({});
40290 this.hasFocus = true;
40295 isExpanded : function()
40297 return this.list.isVisible();
40300 collapse : function()
40302 if(!this.isExpanded()){
40306 Roo.get(document).un('mousedown', this.collapseIf, this);
40307 Roo.get(document).un('mousewheel', this.collapseIf, this);
40308 this.fireEvent('collapse', this);
40312 expand : function()
40316 if(this.isExpanded() || !this.hasFocus){
40320 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40321 this.list.setWidth(lw);
40324 this.restrictHeight();
40326 Roo.get(document).on('mousedown', this.collapseIf, this);
40327 Roo.get(document).on('mousewheel', this.collapseIf, this);
40329 this.fireEvent('expand', this);
40332 restrictHeight : function()
40334 this.list.alignTo(this.inputEl(), this.listAlign);
40335 this.list.alignTo(this.inputEl(), this.listAlign);
40338 onViewOver : function(e, t)
40340 if(this.inKeyMode){
40343 var item = this.view.findItemFromChild(t);
40346 var index = this.view.indexOf(item);
40347 this.select(index, false);
40352 onViewClick : function(view, doFocus, el, e)
40354 var index = this.view.getSelectedIndexes()[0];
40356 var r = this.store.getAt(index);
40359 this.onSelect(r, index);
40361 if(doFocus !== false && !this.blockFocus){
40362 this.inputEl().focus();
40366 onViewMove : function(e, t)
40368 this.inKeyMode = false;
40371 select : function(index, scrollIntoView)
40373 this.selectedIndex = index;
40374 this.view.select(index);
40375 if(scrollIntoView !== false){
40376 var el = this.view.getNode(index);
40378 this.list.scrollChildIntoView(el, false);
40383 createList : function()
40385 this.list = Roo.get(document.body).createChild({
40387 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40388 style: 'display:none'
40391 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40394 collapseIf : function(e)
40396 var in_combo = e.within(this.el);
40397 var in_list = e.within(this.list);
40398 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40400 if (in_combo || in_list || is_list) {
40406 onSelect : function(record, index)
40408 if(this.fireEvent('beforeselect', this, record, index) !== false){
40410 this.setFlagClass(record.data.iso2);
40411 this.setDialCode(record.data.dialCode);
40412 this.hasFocus = false;
40414 this.fireEvent('select', this, record, index);
40418 flagEl : function()
40420 var flag = this.el.select('div.flag',true).first();
40427 dialCodeHolderEl : function()
40429 var d = this.el.select('input.dial-code-holder',true).first();
40436 setDialCode : function(v)
40438 this.dialCodeHolder.dom.value = '+'+v;
40441 setFlagClass : function(n)
40443 this.flag.dom.className = 'flag '+n;
40446 getValue : function()
40448 var v = this.inputEl().getValue();
40449 if(this.dialCodeHolder) {
40450 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40455 setValue : function(v)
40457 var d = this.getDialCode(v);
40459 //invalid dial code
40460 if(v.length == 0 || !d || d.length == 0) {
40462 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40463 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40469 this.setFlagClass(this.dialCodeMapping[d].iso2);
40470 this.setDialCode(d);
40471 this.inputEl().dom.value = v.replace('+'+d,'');
40472 this.hiddenEl().dom.value = this.getValue();
40477 getDialCode : function(v)
40481 if (v.length == 0) {
40482 return this.dialCodeHolder.dom.value;
40486 if (v.charAt(0) != "+") {
40489 var numericChars = "";
40490 for (var i = 1; i < v.length; i++) {
40491 var c = v.charAt(i);
40494 if (this.dialCodeMapping[numericChars]) {
40495 dialCode = v.substr(1, i);
40497 if (numericChars.length == 4) {
40507 this.setValue(this.defaultDialCode);
40511 hiddenEl : function()
40513 return this.el.select('input.hidden-tel-input',true).first();
40516 // after setting val
40517 onKeyUp : function(e){
40518 this.setValue(this.getValue());
40521 onKeyPress : function(e){
40522 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40529 * @class Roo.bootstrap.MoneyField
40530 * @extends Roo.bootstrap.ComboBox
40531 * Bootstrap MoneyField class
40534 * Create a new MoneyField.
40535 * @param {Object} config Configuration options
40538 Roo.bootstrap.MoneyField = function(config) {
40540 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40544 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40547 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40549 allowDecimals : true,
40551 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40553 decimalSeparator : ".",
40555 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40557 decimalPrecision : 0,
40559 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40561 allowNegative : true,
40563 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40567 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40569 minValue : Number.NEGATIVE_INFINITY,
40571 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40573 maxValue : Number.MAX_VALUE,
40575 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40577 minText : "The minimum value for this field is {0}",
40579 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40581 maxText : "The maximum value for this field is {0}",
40583 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40584 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40586 nanText : "{0} is not a valid number",
40588 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40592 * @cfg {String} defaults currency of the MoneyField
40593 * value should be in lkey
40595 defaultCurrency : false,
40597 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40599 thousandsDelimiter : false,
40601 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40612 getAutoCreate : function()
40614 var align = this.labelAlign || this.parentLabelAlign();
40626 cls : 'form-control roo-money-amount-input',
40627 autocomplete: 'new-password'
40630 var hiddenInput = {
40634 cls: 'hidden-number-input'
40637 if(this.max_length) {
40638 input.maxlength = this.max_length;
40642 hiddenInput.name = this.name;
40645 if (this.disabled) {
40646 input.disabled = true;
40649 var clg = 12 - this.inputlg;
40650 var cmd = 12 - this.inputmd;
40651 var csm = 12 - this.inputsm;
40652 var cxs = 12 - this.inputxs;
40656 cls : 'row roo-money-field',
40660 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40664 cls: 'roo-select2-container input-group',
40668 cls : 'form-control roo-money-currency-input',
40669 autocomplete: 'new-password',
40671 name : this.currencyName
40675 cls : 'input-group-addon',
40689 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40693 cls: this.hasFeedback ? 'has-feedback' : '',
40704 if (this.fieldLabel.length) {
40707 tooltip: 'This field is required'
40713 cls: 'control-label',
40719 html: this.fieldLabel
40722 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40728 if(this.indicatorpos == 'right') {
40729 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40736 if(align == 'left') {
40744 if(this.labelWidth > 12){
40745 label.style = "width: " + this.labelWidth + 'px';
40747 if(this.labelWidth < 13 && this.labelmd == 0){
40748 this.labelmd = this.labelWidth;
40750 if(this.labellg > 0){
40751 label.cls += ' col-lg-' + this.labellg;
40752 input.cls += ' col-lg-' + (12 - this.labellg);
40754 if(this.labelmd > 0){
40755 label.cls += ' col-md-' + this.labelmd;
40756 container.cls += ' col-md-' + (12 - this.labelmd);
40758 if(this.labelsm > 0){
40759 label.cls += ' col-sm-' + this.labelsm;
40760 container.cls += ' col-sm-' + (12 - this.labelsm);
40762 if(this.labelxs > 0){
40763 label.cls += ' col-xs-' + this.labelxs;
40764 container.cls += ' col-xs-' + (12 - this.labelxs);
40775 var settings = this;
40777 ['xs','sm','md','lg'].map(function(size){
40778 if (settings[size]) {
40779 cfg.cls += ' col-' + size + '-' + settings[size];
40786 initEvents : function()
40788 this.indicator = this.indicatorEl();
40790 this.initCurrencyEvent();
40792 this.initNumberEvent();
40795 initCurrencyEvent : function()
40798 throw "can not find store for combo";
40801 this.store = Roo.factory(this.store, Roo.data);
40802 this.store.parent = this;
40806 this.triggerEl = this.el.select('.input-group-addon', true).first();
40808 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40813 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40814 _this.list.setWidth(lw);
40817 this.list.on('mouseover', this.onViewOver, this);
40818 this.list.on('mousemove', this.onViewMove, this);
40819 this.list.on('scroll', this.onViewScroll, this);
40822 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40825 this.view = new Roo.View(this.list, this.tpl, {
40826 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40829 this.view.on('click', this.onViewClick, this);
40831 this.store.on('beforeload', this.onBeforeLoad, this);
40832 this.store.on('load', this.onLoad, this);
40833 this.store.on('loadexception', this.onLoadException, this);
40835 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40836 "up" : function(e){
40837 this.inKeyMode = true;
40841 "down" : function(e){
40842 if(!this.isExpanded()){
40843 this.onTriggerClick();
40845 this.inKeyMode = true;
40850 "enter" : function(e){
40853 if(this.fireEvent("specialkey", this, e)){
40854 this.onViewClick(false);
40860 "esc" : function(e){
40864 "tab" : function(e){
40867 if(this.fireEvent("specialkey", this, e)){
40868 this.onViewClick(false);
40876 doRelay : function(foo, bar, hname){
40877 if(hname == 'down' || this.scope.isExpanded()){
40878 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40886 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40890 initNumberEvent : function(e)
40892 this.inputEl().on("keydown" , this.fireKey, this);
40893 this.inputEl().on("focus", this.onFocus, this);
40894 this.inputEl().on("blur", this.onBlur, this);
40896 this.inputEl().relayEvent('keyup', this);
40898 if(this.indicator){
40899 this.indicator.addClass('invisible');
40902 this.originalValue = this.getValue();
40904 if(this.validationEvent == 'keyup'){
40905 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40906 this.inputEl().on('keyup', this.filterValidation, this);
40908 else if(this.validationEvent !== false){
40909 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40912 if(this.selectOnFocus){
40913 this.on("focus", this.preFocus, this);
40916 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40917 this.inputEl().on("keypress", this.filterKeys, this);
40919 this.inputEl().relayEvent('keypress', this);
40922 var allowed = "0123456789";
40924 if(this.allowDecimals){
40925 allowed += this.decimalSeparator;
40928 if(this.allowNegative){
40932 if(this.thousandsDelimiter) {
40936 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40938 var keyPress = function(e){
40940 var k = e.getKey();
40942 var c = e.getCharCode();
40945 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40946 allowed.indexOf(String.fromCharCode(c)) === -1
40952 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40956 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40961 this.inputEl().on("keypress", keyPress, this);
40965 onTriggerClick : function(e)
40972 this.loadNext = false;
40974 if(this.isExpanded()){
40979 this.hasFocus = true;
40981 if(this.triggerAction == 'all') {
40982 this.doQuery(this.allQuery, true);
40986 this.doQuery(this.getRawValue());
40989 getCurrency : function()
40991 var v = this.currencyEl().getValue();
40996 restrictHeight : function()
40998 this.list.alignTo(this.currencyEl(), this.listAlign);
40999 this.list.alignTo(this.currencyEl(), this.listAlign);
41002 onViewClick : function(view, doFocus, el, e)
41004 var index = this.view.getSelectedIndexes()[0];
41006 var r = this.store.getAt(index);
41009 this.onSelect(r, index);
41013 onSelect : function(record, index){
41015 if(this.fireEvent('beforeselect', this, record, index) !== false){
41017 this.setFromCurrencyData(index > -1 ? record.data : false);
41021 this.fireEvent('select', this, record, index);
41025 setFromCurrencyData : function(o)
41029 this.lastCurrency = o;
41031 if (this.currencyField) {
41032 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41034 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41037 this.lastSelectionText = currency;
41039 //setting default currency
41040 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41041 this.setCurrency(this.defaultCurrency);
41045 this.setCurrency(currency);
41048 setFromData : function(o)
41052 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41054 this.setFromCurrencyData(c);
41059 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41061 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41064 this.setValue(value);
41068 setCurrency : function(v)
41070 this.currencyValue = v;
41073 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41078 setValue : function(v)
41080 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41086 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41088 this.inputEl().dom.value = (v == '') ? '' :
41089 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41091 if(!this.allowZero && v === '0') {
41092 this.hiddenEl().dom.value = '';
41093 this.inputEl().dom.value = '';
41100 getRawValue : function()
41102 var v = this.inputEl().getValue();
41107 getValue : function()
41109 return this.fixPrecision(this.parseValue(this.getRawValue()));
41112 parseValue : function(value)
41114 if(this.thousandsDelimiter) {
41116 r = new RegExp(",", "g");
41117 value = value.replace(r, "");
41120 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41121 return isNaN(value) ? '' : value;
41125 fixPrecision : function(value)
41127 if(this.thousandsDelimiter) {
41129 r = new RegExp(",", "g");
41130 value = value.replace(r, "");
41133 var nan = isNaN(value);
41135 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41136 return nan ? '' : value;
41138 return parseFloat(value).toFixed(this.decimalPrecision);
41141 decimalPrecisionFcn : function(v)
41143 return Math.floor(v);
41146 validateValue : function(value)
41148 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41152 var num = this.parseValue(value);
41155 this.markInvalid(String.format(this.nanText, value));
41159 if(num < this.minValue){
41160 this.markInvalid(String.format(this.minText, this.minValue));
41164 if(num > this.maxValue){
41165 this.markInvalid(String.format(this.maxText, this.maxValue));
41172 validate : function()
41174 if(this.disabled || this.allowBlank){
41179 var currency = this.getCurrency();
41181 if(this.validateValue(this.getRawValue()) && currency.length){
41186 this.markInvalid();
41190 getName: function()
41195 beforeBlur : function()
41201 var v = this.parseValue(this.getRawValue());
41208 onBlur : function()
41212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41213 //this.el.removeClass(this.focusClass);
41216 this.hasFocus = false;
41218 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41222 var v = this.getValue();
41224 if(String(v) !== String(this.startValue)){
41225 this.fireEvent('change', this, v, this.startValue);
41228 this.fireEvent("blur", this);
41231 inputEl : function()
41233 return this.el.select('.roo-money-amount-input', true).first();
41236 currencyEl : function()
41238 return this.el.select('.roo-money-currency-input', true).first();
41241 hiddenEl : function()
41243 return this.el.select('input.hidden-number-input',true).first();