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 fa
593 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594 * @cfg {String} badge text for badge
595 * @cfg {String} theme (default|glow)
596 * @cfg {Boolean} inverse dark themed version
597 * @cfg {Boolean} toggle is it a slidy toggle button
598 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599 * @cfg {String} ontext text for on slidy toggle state
600 * @cfg {String} offtext text for off slidy toggle state
601 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
602 * @cfg {Boolean} removeClass remove the standard class..
603 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
606 * Create a new button
607 * @param {Object} config The config object
611 Roo.bootstrap.Button = function(config){
612 Roo.bootstrap.Button.superclass.constructor.call(this, config);
613 this.weightClass = ["btn-default btn-outline-secondary",
625 * When a butotn is pressed
626 * @param {Roo.bootstrap.Button} btn
627 * @param {Roo.EventObject} e
632 * After the button has been toggles
633 * @param {Roo.bootstrap.Button} btn
634 * @param {Roo.EventObject} e
635 * @param {boolean} pressed (also available as button.pressed)
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
662 preventDefault: true,
670 getAutoCreate : function(){
678 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
684 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
686 if (this.toggle == true) {
689 cls: 'slider-frame roo-button',
694 'data-off-text':'OFF',
695 cls: 'slider-button',
701 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702 cfg.cls += ' '+this.weight;
711 cfg["aria-hidden"] = true;
713 cfg.html = "×";
719 if (this.theme==='default') {
720 cfg.cls = 'btn roo-button';
722 //if (this.parentType != 'Navbar') {
723 this.weight = this.weight.length ? this.weight : 'default';
725 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
727 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729 cfg.cls += ' btn-' + outline + weight;
730 if (this.weight == 'default') {
732 cfg.cls += ' btn-' + this.weight;
735 } else if (this.theme==='glow') {
738 cfg.cls = 'btn-glow roo-button';
740 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
742 cfg.cls += ' ' + this.weight;
748 this.cls += ' inverse';
752 if (this.active || this.pressed === true) {
753 cfg.cls += ' active';
757 cfg.disabled = 'disabled';
761 Roo.log('changing to ul' );
763 this.glyphicon = 'caret';
764 if (Roo.bootstrap.version == 4) {
765 this.fa = 'caret-down';
770 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
772 //gsRoo.log(this.parentType);
773 if (this.parentType === 'Navbar' && !this.parent().bar) {
774 Roo.log('changing to li?');
783 href : this.href || '#'
786 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
787 cfg.cls += ' dropdown';
794 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
796 if (this.glyphicon) {
797 cfg.html = ' ' + cfg.html;
802 cls: 'glyphicon glyphicon-' + this.glyphicon
807 cfg.html = ' ' + cfg.html;
812 cls: 'fa fas fa-' + this.fa
822 // cfg.cls='btn roo-button';
826 var value = cfg.html;
831 cls: 'glyphicon glyphicon-' + this.glyphicon,
838 cls: 'fa fas fa-' + this.fa,
843 var bw = this.badge_weight.length ? this.badge_weight :
844 (this.weight.length ? this.weight : 'secondary');
845 bw = bw == 'default' ? 'secondary' : bw;
851 cls: 'badge badge-' + bw,
860 cfg.cls += ' dropdown';
861 cfg.html = typeof(cfg.html) != 'undefined' ?
862 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
865 if (cfg.tag !== 'a' && this.href !== '') {
866 throw "Tag must be a to set href.";
867 } else if (this.href.length > 0) {
868 cfg.href = this.href;
871 if(this.removeClass){
876 cfg.target = this.target;
881 initEvents: function() {
882 // Roo.log('init events?');
883 // Roo.log(this.el.dom);
886 if (typeof (this.menu) != 'undefined') {
887 this.menu.parentType = this.xtype;
888 this.menu.triggerEl = this.el;
889 this.addxtype(Roo.apply({}, this.menu));
893 if (this.el.hasClass('roo-button')) {
894 this.el.on('click', this.onClick, this);
896 this.el.select('.roo-button').on('click', this.onClick, this);
899 if(this.removeClass){
900 this.el.on('click', this.onClick, this);
903 this.el.enableDisplayMode();
906 onClick : function(e)
912 Roo.log('button on click ');
913 if(this.preventDefault){
917 if (this.pressed === true || this.pressed === false) {
918 this.toggleActive(e);
922 this.fireEvent('click', this, e);
926 * Enables this button
930 this.disabled = false;
931 this.el.removeClass('disabled');
935 * Disable this button
939 this.disabled = true;
940 this.el.addClass('disabled');
943 * sets the active state on/off,
944 * @param {Boolean} state (optional) Force a particular state
946 setActive : function(v) {
948 this.el[v ? 'addClass' : 'removeClass']('active');
952 * toggles the current active state
954 toggleActive : function(e)
956 this.setActive(!this.pressed);
957 this.fireEvent('toggle', this, e, !this.pressed);
960 * get the current active state
961 * @return {boolean} true if it's active
963 isActive : function()
965 return this.el.hasClass('active');
968 * set the text of the first selected button
970 setText : function(str)
972 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
975 * get the text of the first selected button
979 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
982 setWeight : function(str)
984 this.el.removeClass(this.weightClass);
986 var outline = this.outline ? 'outline-' : '';
987 if (str == 'default') {
988 this.el.addClass('btn-default btn-outline-secondary');
991 this.el.addClass('btn-' + outline + str);
1005 * @class Roo.bootstrap.Column
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Column class
1008 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1018 * @cfg {Boolean} hidden (true|false) hide the element
1019 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020 * @cfg {String} fa (ban|check|...) font awesome icon
1021 * @cfg {Number} fasize (1|2|....) font awsome size
1023 * @cfg {String} icon (info-sign|check|...) glyphicon name
1025 * @cfg {String} html content of column.
1028 * Create a new Column
1029 * @param {Object} config The config object
1032 Roo.bootstrap.Column = function(config){
1033 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1054 getAutoCreate : function(){
1055 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1063 ['xs','sm','md','lg'].map(function(size){
1064 //Roo.log( size + ':' + settings[size]);
1066 if (settings[size+'off'] !== false) {
1067 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1070 if (settings[size] === false) {
1074 if (!settings[size]) { // 0 = hidden
1075 cfg.cls += ' hidden-' + size;
1078 cfg.cls += ' col-' + size + '-' + settings[size];
1083 cfg.cls += ' hidden';
1086 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087 cfg.cls +=' alert alert-' + this.alert;
1091 if (this.html.length) {
1092 cfg.html = this.html;
1096 if (this.fasize > 1) {
1097 fasize = ' fa-' + this.fasize + 'x';
1099 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1104 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1123 * @class Roo.bootstrap.Container
1124 * @extends Roo.bootstrap.Component
1125 * Bootstrap Container class
1126 * @cfg {Boolean} jumbotron is it a jumbotron element
1127 * @cfg {String} html content of element
1128 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1130 * @cfg {String} header content of header (for panel)
1131 * @cfg {String} footer content of footer (for panel)
1132 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133 * @cfg {String} tag (header|aside|section) type of HTML tag.
1134 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135 * @cfg {String} fa font awesome icon
1136 * @cfg {String} icon (info-sign|check|...) glyphicon name
1137 * @cfg {Boolean} hidden (true|false) hide the element
1138 * @cfg {Boolean} expandable (true|false) default false
1139 * @cfg {Boolean} expanded (true|false) default true
1140 * @cfg {String} rheader contet on the right of header
1141 * @cfg {Boolean} clickable (true|false) default false
1145 * Create a new Container
1146 * @param {Object} config The config object
1149 Roo.bootstrap.Container = function(config){
1150 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1156 * After the panel has been expand
1158 * @param {Roo.bootstrap.Container} this
1163 * After the panel has been collapsed
1165 * @param {Roo.bootstrap.Container} this
1170 * When a element is chick
1171 * @param {Roo.bootstrap.Container} this
1172 * @param {Roo.EventObject} e
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1196 getChildContainer : function() {
1202 if (this.panel.length) {
1203 return this.el.select('.panel-body',true).first();
1210 getAutoCreate : function(){
1213 tag : this.tag || 'div',
1217 if (this.jumbotron) {
1218 cfg.cls = 'jumbotron';
1223 // - this is applied by the parent..
1225 // cfg.cls = this.cls + '';
1228 if (this.sticky.length) {
1230 var bd = Roo.get(document.body);
1231 if (!bd.hasClass('bootstrap-sticky')) {
1232 bd.addClass('bootstrap-sticky');
1233 Roo.select('html',true).setStyle('height', '100%');
1236 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1240 if (this.well.length) {
1241 switch (this.well) {
1244 cfg.cls +=' well well-' +this.well;
1253 cfg.cls += ' hidden';
1257 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258 cfg.cls +=' alert alert-' + this.alert;
1263 if (this.panel.length) {
1264 cfg.cls += ' panel panel-' + this.panel;
1266 if (this.header.length) {
1270 if(this.expandable){
1272 cfg.cls = cfg.cls + ' expandable';
1276 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1284 cls : 'panel-title',
1285 html : (this.expandable ? ' ' : '') + this.header
1289 cls: 'panel-header-right',
1295 cls : 'panel-heading',
1296 style : this.expandable ? 'cursor: pointer' : '',
1304 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1309 if (this.footer.length) {
1311 cls : 'panel-footer',
1320 body.html = this.html || cfg.html;
1321 // prefix with the icons..
1323 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1331 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332 cfg.cls = 'container';
1338 initEvents: function()
1340 if(this.expandable){
1341 var headerEl = this.headerEl();
1344 headerEl.on('click', this.onToggleClick, this);
1349 this.el.on('click', this.onClick, this);
1354 onToggleClick : function()
1356 var headerEl = this.headerEl();
1372 if(this.fireEvent('expand', this)) {
1374 this.expanded = true;
1376 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1378 this.el.select('.panel-body',true).first().removeClass('hide');
1380 var toggleEl = this.toggleEl();
1386 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1391 collapse : function()
1393 if(this.fireEvent('collapse', this)) {
1395 this.expanded = false;
1397 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398 this.el.select('.panel-body',true).first().addClass('hide');
1400 var toggleEl = this.toggleEl();
1406 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1410 toggleEl : function()
1412 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1416 return this.el.select('.panel-heading .fa',true).first();
1419 headerEl : function()
1421 if(!this.el || !this.panel.length || !this.header.length){
1425 return this.el.select('.panel-heading',true).first()
1430 if(!this.el || !this.panel.length){
1434 return this.el.select('.panel-body',true).first()
1437 titleEl : function()
1439 if(!this.el || !this.panel.length || !this.header.length){
1443 return this.el.select('.panel-title',true).first();
1446 setTitle : function(v)
1448 var titleEl = this.titleEl();
1454 titleEl.dom.innerHTML = v;
1457 getTitle : function()
1460 var titleEl = this.titleEl();
1466 return titleEl.dom.innerHTML;
1469 setRightTitle : function(v)
1471 var t = this.el.select('.panel-header-right',true).first();
1477 t.dom.innerHTML = v;
1480 onClick : function(e)
1484 this.fireEvent('click', this, e);
1497 * @class Roo.bootstrap.Img
1498 * @extends Roo.bootstrap.Component
1499 * Bootstrap Img class
1500 * @cfg {Boolean} imgResponsive false | true
1501 * @cfg {String} border rounded | circle | thumbnail
1502 * @cfg {String} src image source
1503 * @cfg {String} alt image alternative text
1504 * @cfg {String} href a tag href
1505 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506 * @cfg {String} xsUrl xs image source
1507 * @cfg {String} smUrl sm image source
1508 * @cfg {String} mdUrl md image source
1509 * @cfg {String} lgUrl lg image source
1512 * Create a new Input
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Img = function(config){
1517 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1523 * The img click event for the img.
1524 * @param {Roo.EventObject} e
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1532 imgResponsive: true,
1542 getAutoCreate : function()
1544 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545 return this.createSingleImg();
1550 cls: 'roo-image-responsive-group',
1555 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1557 if(!_this[size + 'Url']){
1563 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564 html: _this.html || cfg.html,
1565 src: _this[size + 'Url']
1568 img.cls += ' roo-image-responsive-' + size;
1570 var s = ['xs', 'sm', 'md', 'lg'];
1572 s.splice(s.indexOf(size), 1);
1574 Roo.each(s, function(ss){
1575 img.cls += ' hidden-' + ss;
1578 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579 cfg.cls += ' img-' + _this.border;
1583 cfg.alt = _this.alt;
1596 a.target = _this.target;
1600 cfg.cn.push((_this.href) ? a : img);
1607 createSingleImg : function()
1611 cls: (this.imgResponsive) ? 'img-responsive' : '',
1613 src : 'about:blank' // just incase src get's set to undefined?!?
1616 cfg.html = this.html || cfg.html;
1618 cfg.src = this.src || cfg.src;
1620 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621 cfg.cls += ' img-' + this.border;
1638 a.target = this.target;
1643 return (this.href) ? a : cfg;
1646 initEvents: function()
1649 this.el.on('click', this.onClick, this);
1654 onClick : function(e)
1656 Roo.log('img onclick');
1657 this.fireEvent('click', this, e);
1660 * Sets the url of the image - used to update it
1661 * @param {String} url the url of the image
1664 setSrc : function(url)
1668 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669 this.el.dom.src = url;
1673 this.el.select('img', true).first().dom.src = url;
1689 * @class Roo.bootstrap.Link
1690 * @extends Roo.bootstrap.Component
1691 * Bootstrap Link Class
1692 * @cfg {String} alt image alternative text
1693 * @cfg {String} href a tag href
1694 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695 * @cfg {String} html the content of the link.
1696 * @cfg {String} anchor name for the anchor link
1697 * @cfg {String} fa - favicon
1699 * @cfg {Boolean} preventDefault (true | false) default false
1703 * Create a new Input
1704 * @param {Object} config The config object
1707 Roo.bootstrap.Link = function(config){
1708 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1714 * The img click event for the img.
1715 * @param {Roo.EventObject} e
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1725 preventDefault: false,
1731 getAutoCreate : function()
1733 var html = this.html || '';
1735 if (this.fa !== false) {
1736 html = '<i class="fa fa-' + this.fa + '"></i>';
1741 // anchor's do not require html/href...
1742 if (this.anchor === false) {
1744 cfg.href = this.href || '#';
1746 cfg.name = this.anchor;
1747 if (this.html !== false || this.fa !== false) {
1750 if (this.href !== false) {
1751 cfg.href = this.href;
1755 if(this.alt !== false){
1760 if(this.target !== false) {
1761 cfg.target = this.target;
1767 initEvents: function() {
1769 if(!this.href || this.preventDefault){
1770 this.el.on('click', this.onClick, this);
1774 onClick : function(e)
1776 if(this.preventDefault){
1779 //Roo.log('img onclick');
1780 this.fireEvent('click', this, e);
1793 * @class Roo.bootstrap.Header
1794 * @extends Roo.bootstrap.Component
1795 * Bootstrap Header class
1796 * @cfg {String} html content of header
1797 * @cfg {Number} level (1|2|3|4|5|6) default 1
1800 * Create a new Header
1801 * @param {Object} config The config object
1805 Roo.bootstrap.Header = function(config){
1806 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1817 getAutoCreate : function(){
1822 tag: 'h' + (1 *this.level),
1823 html: this.html || ''
1835 * Ext JS Library 1.1.1
1836 * Copyright(c) 2006-2007, Ext JS, LLC.
1838 * Originally Released Under LGPL - original licence link has changed is not relivant.
1841 * <script type="text/javascript">
1845 * @class Roo.bootstrap.MenuMgr
1846 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849 Roo.bootstrap.MenuMgr = function(){
1850 var menus, active, groups = {}, attached = false, lastShow = new Date();
1852 // private - called when first menu is created
1855 active = new Roo.util.MixedCollection();
1856 Roo.get(document).addKeyListener(27, function(){
1857 if(active.length > 0){
1865 if(active && active.length > 0){
1866 var c = active.clone();
1876 if(active.length < 1){
1877 Roo.get(document).un("mouseup", onMouseDown);
1885 var last = active.last();
1886 lastShow = new Date();
1889 Roo.get(document).on("mouseup", onMouseDown);
1894 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895 m.parentMenu.activeChild = m;
1896 }else if(last && last.isVisible()){
1897 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1902 function onBeforeHide(m){
1904 m.activeChild.hide();
1906 if(m.autoHideTimer){
1907 clearTimeout(m.autoHideTimer);
1908 delete m.autoHideTimer;
1913 function onBeforeShow(m){
1914 var pm = m.parentMenu;
1915 if(!pm && !m.allowOtherMenus){
1917 }else if(pm && pm.activeChild && active != m){
1918 pm.activeChild.hide();
1922 // private this should really trigger on mouseup..
1923 function onMouseDown(e){
1924 Roo.log("on Mouse Up");
1926 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927 Roo.log("MenuManager hideAll");
1936 function onBeforeCheck(mi, state){
1938 var g = groups[mi.group];
1939 for(var i = 0, l = g.length; i < l; i++){
1941 g[i].setChecked(false);
1950 * Hides all menus that are currently visible
1952 hideAll : function(){
1957 register : function(menu){
1961 menus[menu.id] = menu;
1962 menu.on("beforehide", onBeforeHide);
1963 menu.on("hide", onHide);
1964 menu.on("beforeshow", onBeforeShow);
1965 menu.on("show", onShow);
1967 if(g && menu.events["checkchange"]){
1971 groups[g].push(menu);
1972 menu.on("checkchange", onCheck);
1977 * Returns a {@link Roo.menu.Menu} object
1978 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979 * be used to generate and return a new Menu instance.
1981 get : function(menu){
1982 if(typeof menu == "string"){ // menu id
1984 }else if(menu.events){ // menu instance
1987 /*else if(typeof menu.length == 'number'){ // array of menu items?
1988 return new Roo.bootstrap.Menu({items:menu});
1989 }else{ // otherwise, must be a config
1990 return new Roo.bootstrap.Menu(menu);
1997 unregister : function(menu){
1998 delete menus[menu.id];
1999 menu.un("beforehide", onBeforeHide);
2000 menu.un("hide", onHide);
2001 menu.un("beforeshow", onBeforeShow);
2002 menu.un("show", onShow);
2004 if(g && menu.events["checkchange"]){
2005 groups[g].remove(menu);
2006 menu.un("checkchange", onCheck);
2011 registerCheckable : function(menuItem){
2012 var g = menuItem.group;
2017 groups[g].push(menuItem);
2018 menuItem.on("beforecheckchange", onBeforeCheck);
2023 unregisterCheckable : function(menuItem){
2024 var g = menuItem.group;
2026 groups[g].remove(menuItem);
2027 menuItem.un("beforecheckchange", onBeforeCheck);
2039 * @class Roo.bootstrap.Menu
2040 * @extends Roo.bootstrap.Component
2041 * Bootstrap Menu class - container for MenuItems
2042 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043 * @cfg {bool} hidden if the menu should be hidden when rendered.
2044 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2045 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2049 * @param {Object} config The config object
2053 Roo.bootstrap.Menu = function(config){
2054 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055 if (this.registerMenu && this.type != 'treeview') {
2056 Roo.bootstrap.MenuMgr.register(this);
2063 * Fires before this menu is displayed
2064 * @param {Roo.menu.Menu} this
2069 * Fires before this menu is hidden
2070 * @param {Roo.menu.Menu} this
2075 * Fires after this menu is displayed
2076 * @param {Roo.menu.Menu} this
2081 * Fires after this menu is hidden
2082 * @param {Roo.menu.Menu} this
2087 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088 * @param {Roo.menu.Menu} this
2089 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090 * @param {Roo.EventObject} e
2095 * Fires when the mouse is hovering over this menu
2096 * @param {Roo.menu.Menu} this
2097 * @param {Roo.EventObject} e
2098 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2103 * Fires when the mouse exits this menu
2104 * @param {Roo.menu.Menu} this
2105 * @param {Roo.EventObject} e
2106 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2111 * Fires when a menu item contained in this menu is clicked
2112 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113 * @param {Roo.EventObject} e
2117 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2124 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2127 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2129 registerMenu : true,
2131 menuItems :false, // stores the menu items..
2141 getChildContainer : function() {
2145 getAutoCreate : function(){
2147 //if (['right'].indexOf(this.align)!==-1) {
2148 // cfg.cn[1].cls += ' pull-right'
2154 cls : 'dropdown-menu' ,
2155 style : 'z-index:1000'
2159 if (this.type === 'submenu') {
2160 cfg.cls = 'submenu active';
2162 if (this.type === 'treeview') {
2163 cfg.cls = 'treeview-menu';
2168 initEvents : function() {
2170 // Roo.log("ADD event");
2171 // Roo.log(this.triggerEl.dom);
2173 this.triggerEl.on('click', this.onTriggerClick, this);
2175 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178 if (this.triggerEl.hasClass('nav-item')) {
2179 // dropdown toggle on the 'a' in BS4?
2180 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2182 this.triggerEl.addClass('dropdown-toggle');
2185 this.el.on('touchstart' , this.onTouch, this);
2187 this.el.on('click' , this.onClick, this);
2189 this.el.on("mouseover", this.onMouseOver, this);
2190 this.el.on("mouseout", this.onMouseOut, this);
2194 findTargetItem : function(e)
2196 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2200 //Roo.log(t); Roo.log(t.id);
2202 //Roo.log(this.menuitems);
2203 return this.menuitems.get(t.id);
2205 //return this.items.get(t.menuItemId);
2211 onTouch : function(e)
2213 Roo.log("menu.onTouch");
2214 //e.stopEvent(); this make the user popdown broken
2218 onClick : function(e)
2220 Roo.log("menu.onClick");
2222 var t = this.findTargetItem(e);
2223 if(!t || t.isContainer){
2228 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2229 if(t == this.activeItem && t.shouldDeactivate(e)){
2230 this.activeItem.deactivate();
2231 delete this.activeItem;
2235 this.setActiveItem(t, true);
2243 Roo.log('pass click event');
2247 this.fireEvent("click", this, t, e);
2251 if(!t.href.length || t.href == '#'){
2252 (function() { _this.hide(); }).defer(100);
2257 onMouseOver : function(e){
2258 var t = this.findTargetItem(e);
2261 // if(t.canActivate && !t.disabled){
2262 // this.setActiveItem(t, true);
2266 this.fireEvent("mouseover", this, e, t);
2268 isVisible : function(){
2269 return !this.hidden;
2271 onMouseOut : function(e){
2272 var t = this.findTargetItem(e);
2275 // if(t == this.activeItem && t.shouldDeactivate(e)){
2276 // this.activeItem.deactivate();
2277 // delete this.activeItem;
2280 this.fireEvent("mouseout", this, e, t);
2285 * Displays this menu relative to another element
2286 * @param {String/HTMLElement/Roo.Element} element The element to align to
2287 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288 * the element (defaults to this.defaultAlign)
2289 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2291 show : function(el, pos, parentMenu){
2292 this.parentMenu = parentMenu;
2296 this.fireEvent("beforeshow", this);
2297 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2300 * Displays this menu at a specific xy position
2301 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2304 showAt : function(xy, parentMenu, /* private: */_e){
2305 this.parentMenu = parentMenu;
2310 this.fireEvent("beforeshow", this);
2311 //xy = this.el.adjustForConstraints(xy);
2315 this.hideMenuItems();
2316 this.hidden = false;
2317 this.triggerEl.addClass('open');
2318 this.el.addClass('show');
2320 // reassign x when hitting right
2321 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2325 // reassign y when hitting bottom
2326 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2330 // but the list may align on trigger left or trigger top... should it be a properity?
2332 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2337 this.fireEvent("show", this);
2343 this.doFocus.defer(50, this);
2347 doFocus : function(){
2349 this.focusEl.focus();
2354 * Hides this menu and optionally all parent menus
2355 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2357 hide : function(deep)
2360 this.hideMenuItems();
2361 if(this.el && this.isVisible()){
2362 this.fireEvent("beforehide", this);
2363 if(this.activeItem){
2364 this.activeItem.deactivate();
2365 this.activeItem = null;
2367 this.triggerEl.removeClass('open');;
2368 this.el.removeClass('show');
2370 this.fireEvent("hide", this);
2372 if(deep === true && this.parentMenu){
2373 this.parentMenu.hide(true);
2377 onTriggerClick : function(e)
2379 Roo.log('trigger click');
2381 var target = e.getTarget();
2383 Roo.log(target.nodeName.toLowerCase());
2385 if(target.nodeName.toLowerCase() === 'i'){
2391 onTriggerPress : function(e)
2393 Roo.log('trigger press');
2394 //Roo.log(e.getTarget());
2395 // Roo.log(this.triggerEl.dom);
2397 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398 var pel = Roo.get(e.getTarget());
2399 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400 Roo.log('is treeview or dropdown?');
2404 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2408 if (this.isVisible()) {
2413 this.show(this.triggerEl, false, false);
2416 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2423 hideMenuItems : function()
2425 Roo.log("hide Menu Items");
2429 //$(backdrop).remove()
2430 this.el.select('.open',true).each(function(aa) {
2432 aa.removeClass('open');
2433 //var parent = getParent($(this))
2434 //var relatedTarget = { relatedTarget: this }
2436 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437 //if (e.isDefaultPrevented()) return
2438 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2441 addxtypeChild : function (tree, cntr) {
2442 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2444 this.menuitems.add(comp);
2456 this.getEl().dom.innerHTML = '';
2457 this.menuitems.clear();
2471 * @class Roo.bootstrap.MenuItem
2472 * @extends Roo.bootstrap.Component
2473 * Bootstrap MenuItem class
2474 * @cfg {String} html the menu label
2475 * @cfg {String} href the link
2476 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478 * @cfg {Boolean} active used on sidebars to highlight active itesm
2479 * @cfg {String} fa favicon to show on left of menu item.
2480 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484 * Create a new MenuItem
2485 * @param {Object} config The config object
2489 Roo.bootstrap.MenuItem = function(config){
2490 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2495 * The raw click event for the entire grid.
2496 * @param {Roo.bootstrap.MenuItem} this
2497 * @param {Roo.EventObject} e
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2507 preventDefault: false,
2508 isContainer : false,
2512 getAutoCreate : function(){
2514 if(this.isContainer){
2517 cls: 'dropdown-menu-item '
2527 cls : 'dropdown-item',
2532 if (this.fa !== false) {
2535 cls : 'fa fa-' + this.fa
2544 cls: 'dropdown-menu-item',
2547 if (this.parent().type == 'treeview') {
2548 cfg.cls = 'treeview-menu';
2551 cfg.cls += ' active';
2556 anc.href = this.href || cfg.cn[0].href ;
2557 ctag.html = this.html || cfg.cn[0].html ;
2561 initEvents: function()
2563 if (this.parent().type == 'treeview') {
2564 this.el.select('a').on('click', this.onClick, this);
2568 this.menu.parentType = this.xtype;
2569 this.menu.triggerEl = this.el;
2570 this.menu = this.addxtype(Roo.apply({}, this.menu));
2574 onClick : function(e)
2576 Roo.log('item on click ');
2578 if(this.preventDefault){
2581 //this.parent().hideMenuItems();
2583 this.fireEvent('click', this, e);
2602 * @class Roo.bootstrap.MenuSeparator
2603 * @extends Roo.bootstrap.Component
2604 * Bootstrap MenuSeparator class
2607 * Create a new MenuItem
2608 * @param {Object} config The config object
2612 Roo.bootstrap.MenuSeparator = function(config){
2613 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2618 getAutoCreate : function(){
2637 * @class Roo.bootstrap.Modal
2638 * @extends Roo.bootstrap.Component
2639 * Bootstrap Modal class
2640 * @cfg {String} title Title of dialog
2641 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2643 * @cfg {Boolean} specificTitle default false
2644 * @cfg {Array} buttons Array of buttons or standard button set..
2645 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646 * @cfg {Boolean} animate default true
2647 * @cfg {Boolean} allow_close default true
2648 * @cfg {Boolean} fitwindow default false
2649 * @cfg {String} size (sm|lg) default empty
2650 * @cfg {Number} max_width set the max width of modal
2654 * Create a new Modal Dialog
2655 * @param {Object} config The config object
2658 Roo.bootstrap.Modal = function(config){
2659 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2664 * The raw btnclick event for the button
2665 * @param {Roo.EventObject} e
2670 * Fire when dialog resize
2671 * @param {Roo.bootstrap.Modal} this
2672 * @param {Roo.EventObject} e
2676 this.buttons = this.buttons || [];
2679 this.tmpl = Roo.factory(this.tmpl);
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2686 title : 'test dialog',
2696 specificTitle: false,
2698 buttonPosition: 'right',
2721 onRender : function(ct, position)
2723 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2726 var cfg = Roo.apply({}, this.getAutoCreate());
2729 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2731 //if (!cfg.name.length) {
2735 cfg.cls += ' ' + this.cls;
2738 cfg.style = this.style;
2740 this.el = Roo.get(document.body).createChild(cfg, position);
2742 //var type = this.el.dom.type;
2745 if(this.tabIndex !== undefined){
2746 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2749 this.dialogEl = this.el.select('.modal-dialog',true).first();
2750 this.bodyEl = this.el.select('.modal-body',true).first();
2751 this.closeEl = this.el.select('.modal-header .close', true).first();
2752 this.headerEl = this.el.select('.modal-header',true).first();
2753 this.titleEl = this.el.select('.modal-title',true).first();
2754 this.footerEl = this.el.select('.modal-footer',true).first();
2756 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2758 //this.el.addClass("x-dlg-modal");
2760 if (this.buttons.length) {
2761 Roo.each(this.buttons, function(bb) {
2762 var b = Roo.apply({}, bb);
2763 b.xns = b.xns || Roo.bootstrap;
2764 b.xtype = b.xtype || 'Button';
2765 if (typeof(b.listeners) == 'undefined') {
2766 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2769 var btn = Roo.factory(b);
2771 btn.render(this.getButtonContainer());
2775 // render the children.
2778 if(typeof(this.items) != 'undefined'){
2779 var items = this.items;
2782 for(var i =0;i < items.length;i++) {
2783 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787 this.items = nitems;
2789 // where are these used - they used to be body/close/footer
2793 //this.el.addClass([this.fieldClass, this.cls]);
2797 getAutoCreate : function()
2801 html : this.html || ''
2806 cls : 'modal-title',
2810 if(this.specificTitle){
2816 if (this.allow_close && Roo.bootstrap.version == 3) {
2826 if (this.allow_close && Roo.bootstrap.version == 4) {
2836 if(this.size.length){
2837 size = 'modal-' + this.size;
2840 var footer = Roo.bootstrap.version == 3 ?
2842 cls : 'modal-footer',
2846 cls: 'btn-' + this.buttonPosition
2851 { // BS4 uses mr-auto on left buttons....
2852 cls : 'modal-footer'
2863 cls: "modal-dialog " + size,
2866 cls : "modal-content",
2869 cls : 'modal-header',
2884 modal.cls += ' fade';
2890 getChildContainer : function() {
2895 getButtonContainer : function() {
2897 return Roo.bootstrap.version == 4 ?
2898 this.el.select('.modal-footer',true).first()
2899 : this.el.select('.modal-footer div',true).first();
2902 initEvents : function()
2904 if (this.allow_close) {
2905 this.closeEl.on('click', this.hide, this);
2907 Roo.EventManager.onWindowResize(this.resize, this, true);
2914 this.maskEl.setSize(
2915 Roo.lib.Dom.getViewWidth(true),
2916 Roo.lib.Dom.getViewHeight(true)
2919 if (this.fitwindow) {
2921 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2927 if(this.max_width !== 0) {
2929 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2932 this.setSize(w, this.height);
2936 if(this.max_height) {
2937 this.setSize(w,Math.min(
2939 Roo.lib.Dom.getViewportHeight(true) - 60
2945 if(!this.fit_content) {
2946 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2950 this.setSize(w, Math.min(
2952 this.headerEl.getHeight() +
2953 this.footerEl.getHeight() +
2954 this.getChildHeight(this.bodyEl.dom.childNodes),
2955 Roo.lib.Dom.getViewportHeight(true) - 60)
2961 setSize : function(w,h)
2972 if (!this.rendered) {
2976 //this.el.setStyle('display', 'block');
2977 this.el.removeClass('hideing');
2978 this.el.dom.style.display='block';
2980 Roo.get(document.body).addClass('modal-open');
2982 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2985 this.el.addClass('show');
2986 this.el.addClass('in');
2989 this.el.addClass('show');
2990 this.el.addClass('in');
2993 // not sure how we can show data in here..
2995 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2998 Roo.get(document.body).addClass("x-body-masked");
3000 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3001 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002 this.maskEl.dom.style.display = 'block';
3003 this.maskEl.addClass('show');
3008 this.fireEvent('show', this);
3010 // set zindex here - otherwise it appears to be ignored...
3011 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3014 this.items.forEach( function(e) {
3015 e.layout ? e.layout() : false;
3023 if(this.fireEvent("beforehide", this) !== false){
3025 this.maskEl.removeClass('show');
3027 this.maskEl.dom.style.display = '';
3028 Roo.get(document.body).removeClass("x-body-masked");
3029 this.el.removeClass('in');
3030 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3032 if(this.animate){ // why
3033 this.el.addClass('hideing');
3034 this.el.removeClass('show');
3036 if (!this.el.hasClass('hideing')) {
3037 return; // it's been shown again...
3040 this.el.dom.style.display='';
3042 Roo.get(document.body).removeClass('modal-open');
3043 this.el.removeClass('hideing');
3047 this.el.removeClass('show');
3048 this.el.dom.style.display='';
3049 Roo.get(document.body).removeClass('modal-open');
3052 this.fireEvent('hide', this);
3055 isVisible : function()
3058 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3062 addButton : function(str, cb)
3066 var b = Roo.apply({}, { html : str } );
3067 b.xns = b.xns || Roo.bootstrap;
3068 b.xtype = b.xtype || 'Button';
3069 if (typeof(b.listeners) == 'undefined') {
3070 b.listeners = { click : cb.createDelegate(this) };
3073 var btn = Roo.factory(b);
3075 btn.render(this.getButtonContainer());
3081 setDefaultButton : function(btn)
3083 //this.el.select('.modal-footer').()
3087 resizeTo: function(w,h)
3091 this.dialogEl.setWidth(w);
3092 if (this.diff === false) {
3093 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3096 this.bodyEl.setHeight(h - this.diff);
3098 this.fireEvent('resize', this);
3101 setContentSize : function(w, h)
3105 onButtonClick: function(btn,e)
3108 this.fireEvent('btnclick', btn.name, e);
3111 * Set the title of the Dialog
3112 * @param {String} str new Title
3114 setTitle: function(str) {
3115 this.titleEl.dom.innerHTML = str;
3118 * Set the body of the Dialog
3119 * @param {String} str new Title
3121 setBody: function(str) {
3122 this.bodyEl.dom.innerHTML = str;
3125 * Set the body of the Dialog using the template
3126 * @param {Obj} data - apply this data to the template and replace the body contents.
3128 applyBody: function(obj)
3131 Roo.log("Error - using apply Body without a template");
3134 this.tmpl.overwrite(this.bodyEl, obj);
3137 getChildHeight : function(child_nodes)
3141 child_nodes.length == 0
3146 var child_height = 0;
3148 for(var i = 0; i < child_nodes.length; i++) {
3151 * for modal with tabs...
3152 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3154 var layout_childs = child_nodes[i].childNodes;
3156 for(var j = 0; j < layout_childs.length; j++) {
3158 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3160 var layout_body_childs = layout_childs[j].childNodes;
3162 for(var k = 0; k < layout_body_childs.length; k++) {
3164 if(layout_body_childs[k].classList.contains('navbar')) {
3165 child_height += layout_body_childs[k].offsetHeight;
3169 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3171 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3173 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3175 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3191 child_height += child_nodes[i].offsetHeight;
3192 // Roo.log(child_nodes[i].offsetHeight);
3195 return child_height;
3201 Roo.apply(Roo.bootstrap.Modal, {
3203 * Button config that displays a single OK button
3212 * Button config that displays Yes and No buttons
3228 * Button config that displays OK and Cancel buttons
3243 * Button config that displays Yes, No and Cancel buttons
3267 * messagebox - can be used as a replace
3271 * @class Roo.MessageBox
3272 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3281 // process text value...
3285 // Show a dialog using config options:
3287 title:'Save Changes?',
3288 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289 buttons: Roo.Msg.YESNOCANCEL,
3296 Roo.bootstrap.MessageBox = function(){
3297 var dlg, opt, mask, waitTimer;
3298 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299 var buttons, activeTextEl, bwidth;
3303 var handleButton = function(button){
3305 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3309 var handleHide = function(){
3311 dlg.el.removeClass(opt.cls);
3314 // Roo.TaskMgr.stop(waitTimer);
3315 // waitTimer = null;
3320 var updateButtons = function(b){
3323 buttons["ok"].hide();
3324 buttons["cancel"].hide();
3325 buttons["yes"].hide();
3326 buttons["no"].hide();
3327 //dlg.footer.dom.style.display = 'none';
3330 dlg.footerEl.dom.style.display = '';
3331 for(var k in buttons){
3332 if(typeof buttons[k] != "function"){
3335 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336 width += buttons[k].el.getWidth()+15;
3346 var handleEsc = function(d, k, e){
3347 if(opt && opt.closable !== false){
3357 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358 * @return {Roo.BasicDialog} The BasicDialog element
3360 getDialog : function(){
3362 dlg = new Roo.bootstrap.Modal( {
3365 //constraintoviewport:false,
3367 //collapsible : false,
3372 //buttonAlign:"center",
3373 closeClick : function(){
3374 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3377 handleButton("cancel");
3382 dlg.on("hide", handleHide);
3384 //dlg.addKeyListener(27, handleEsc);
3386 this.buttons = buttons;
3387 var bt = this.buttonText;
3388 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393 bodyEl = dlg.bodyEl.createChild({
3395 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396 '<textarea class="roo-mb-textarea"></textarea>' +
3397 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3399 msgEl = bodyEl.dom.firstChild;
3400 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401 textboxEl.enableDisplayMode();
3402 textboxEl.addKeyListener([10,13], function(){
3403 if(dlg.isVisible() && opt && opt.buttons){
3406 }else if(opt.buttons.yes){
3407 handleButton("yes");
3411 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412 textareaEl.enableDisplayMode();
3413 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414 progressEl.enableDisplayMode();
3416 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417 var pf = progressEl.dom.firstChild;
3419 pp = Roo.get(pf.firstChild);
3420 pp.setHeight(pf.offsetHeight);
3428 * Updates the message box body text
3429 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430 * the XHTML-compliant non-breaking space character '&#160;')
3431 * @return {Roo.MessageBox} This message box
3433 updateText : function(text)
3435 if(!dlg.isVisible() && !opt.width){
3436 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439 msgEl.innerHTML = text || ' ';
3441 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444 Math.min(opt.width || cw , this.maxWidth),
3445 Math.max(opt.minWidth || this.minWidth, bwidth)
3448 activeTextEl.setWidth(w);
3450 if(dlg.isVisible()){
3451 dlg.fixedcenter = false;
3453 // to big, make it scroll. = But as usual stupid IE does not support
3456 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460 bodyEl.dom.style.height = '';
3461 bodyEl.dom.style.overflowY = '';
3464 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466 bodyEl.dom.style.overflowX = '';
3469 dlg.setContentSize(w, bodyEl.getHeight());
3470 if(dlg.isVisible()){
3471 dlg.fixedcenter = true;
3477 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3478 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481 * @return {Roo.MessageBox} This message box
3483 updateProgress : function(value, text){
3485 this.updateText(text);
3488 if (pp) { // weird bug on my firefox - for some reason this is not defined
3489 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3496 * Returns true if the message box is currently displayed
3497 * @return {Boolean} True if the message box is visible, else false
3499 isVisible : function(){
3500 return dlg && dlg.isVisible();
3504 * Hides the message box if it is displayed
3507 if(this.isVisible()){
3513 * Displays a new message box, or reinitializes an existing message box, based on the config options
3514 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515 * The following config object properties are supported:
3517 Property Type Description
3518 ---------- --------------- ------------------------------------------------------------------------------------
3519 animEl String/Element An id or Element from which the message box should animate as it opens and
3520 closes (defaults to undefined)
3521 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable Boolean False to hide the top-right close button (defaults to true). Note that
3524 progress and wait dialogs will ignore this property and always hide the
3525 close button as they can only be closed programmatically.
3526 cls String A custom CSS class to apply to the message box element
3527 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3528 displayed (defaults to 75)
3529 fn Function A callback function to execute after closing the dialog. The arguments to the
3530 function will be btn (the name of the button that was clicked, if applicable,
3531 e.g. "ok"), and text (the value of the active text field, if applicable).
3532 Progress and wait dialogs will ignore this option since they do not respond to
3533 user actions and can only be closed programmatically, so any required function
3534 should be called by the same code after it closes the dialog.
3535 icon String A CSS class that provides a background image to be used as an icon for
3536 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3538 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3539 modal Boolean False to allow user interaction with the page while the message box is
3540 displayed (defaults to true)
3541 msg String A string that will replace the existing message box body text (defaults
3542 to the XHTML-compliant non-breaking space character ' ')
3543 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3544 progress Boolean True to display a progress bar (defaults to false)
3545 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3548 title String The title text
3549 value String The string value to set into the active textbox element if displayed
3550 wait Boolean True to display a progress bar (defaults to false)
3551 width Number The width of the dialog in pixels
3558 msg: 'Please enter your address:',
3560 buttons: Roo.MessageBox.OKCANCEL,
3563 animEl: 'addAddressBtn'
3566 * @param {Object} config Configuration options
3567 * @return {Roo.MessageBox} This message box
3569 show : function(options)
3572 // this causes nightmares if you show one dialog after another
3573 // especially on callbacks..
3575 if(this.isVisible()){
3578 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3580 Roo.log("New Dialog Message:" + options.msg )
3581 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3585 var d = this.getDialog();
3587 d.setTitle(opt.title || " ");
3588 d.closeEl.setDisplayed(opt.closable !== false);
3589 activeTextEl = textboxEl;
3590 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3595 textareaEl.setHeight(typeof opt.multiline == "number" ?
3596 opt.multiline : this.defaultTextHeight);
3597 activeTextEl = textareaEl;
3606 progressEl.setDisplayed(opt.progress === true);
3607 this.updateProgress(0);
3608 activeTextEl.dom.value = opt.value || "";
3610 dlg.setDefaultButton(activeTextEl);
3612 var bs = opt.buttons;
3616 }else if(bs && bs.yes){
3617 db = buttons["yes"];
3619 dlg.setDefaultButton(db);
3621 bwidth = updateButtons(opt.buttons);
3622 this.updateText(opt.msg);
3624 d.el.addClass(opt.cls);
3626 d.proxyDrag = opt.proxyDrag === true;
3627 d.modal = opt.modal !== false;
3628 d.mask = opt.modal !== false ? mask : false;
3630 // force it to the end of the z-index stack so it gets a cursor in FF
3631 document.body.appendChild(dlg.el.dom);
3632 d.animateTarget = null;
3633 d.show(options.animEl);
3639 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3640 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3641 * and closing the message box when the process is complete.
3642 * @param {String} title The title bar text
3643 * @param {String} msg The message box body text
3644 * @return {Roo.MessageBox} This message box
3646 progress : function(title, msg){
3653 minWidth: this.minProgressWidth,
3660 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3661 * If a callback function is passed it will be called after the user clicks the button, and the
3662 * id of the button that was clicked will be passed as the only parameter to the callback
3663 * (could also be the top-right close button).
3664 * @param {String} title The title bar text
3665 * @param {String} msg The message box body text
3666 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3667 * @param {Object} scope (optional) The scope of the callback function
3668 * @return {Roo.MessageBox} This message box
3670 alert : function(title, msg, fn, scope)
3685 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3686 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3687 * You are responsible for closing the message box when the process is complete.
3688 * @param {String} msg The message box body text
3689 * @param {String} title (optional) The title bar text
3690 * @return {Roo.MessageBox} This message box
3692 wait : function(msg, title){
3703 waitTimer = Roo.TaskMgr.start({
3705 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3713 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3714 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3715 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3716 * @param {String} title The title bar text
3717 * @param {String} msg The message box body text
3718 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3719 * @param {Object} scope (optional) The scope of the callback function
3720 * @return {Roo.MessageBox} This message box
3722 confirm : function(title, msg, fn, scope){
3726 buttons: this.YESNO,
3735 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3736 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3737 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3738 * (could also be the top-right close button) and the text that was entered will be passed as the two
3739 * parameters to the callback.
3740 * @param {String} title The title bar text
3741 * @param {String} msg The message box body text
3742 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3743 * @param {Object} scope (optional) The scope of the callback function
3744 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3745 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3746 * @return {Roo.MessageBox} This message box
3748 prompt : function(title, msg, fn, scope, multiline){
3752 buttons: this.OKCANCEL,
3757 multiline: multiline,
3764 * Button config that displays a single OK button
3769 * Button config that displays Yes and No buttons
3772 YESNO : {yes:true, no:true},
3774 * Button config that displays OK and Cancel buttons
3777 OKCANCEL : {ok:true, cancel:true},
3779 * Button config that displays Yes, No and Cancel buttons
3782 YESNOCANCEL : {yes:true, no:true, cancel:true},
3785 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3788 defaultTextHeight : 75,
3790 * The maximum width in pixels of the message box (defaults to 600)
3795 * The minimum width in pixels of the message box (defaults to 100)
3800 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3801 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3804 minProgressWidth : 250,
3806 * An object containing the default button text strings that can be overriden for localized language support.
3807 * Supported properties are: ok, cancel, yes and no.
3808 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3821 * Shorthand for {@link Roo.MessageBox}
3823 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3824 Roo.Msg = Roo.Msg || Roo.MessageBox;
3833 * @class Roo.bootstrap.Navbar
3834 * @extends Roo.bootstrap.Component
3835 * Bootstrap Navbar class
3838 * Create a new Navbar
3839 * @param {Object} config The config object
3843 Roo.bootstrap.Navbar = function(config){
3844 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3848 * @event beforetoggle
3849 * Fire before toggle the menu
3850 * @param {Roo.EventObject} e
3852 "beforetoggle" : true
3856 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3865 getAutoCreate : function(){
3868 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3872 initEvents :function ()
3874 //Roo.log(this.el.select('.navbar-toggle',true));
3875 this.el.select('.navbar-toggle',true).on('click', function() {
3876 if(this.fireEvent('beforetoggle', this) !== false){
3877 var ce = this.el.select('.navbar-collapse',true).first();
3878 ce.toggleClass('in'); // old...
3879 if (ce.hasClass('collapse')) {
3881 ce.removeClass('collapse');
3882 ce.addClass('show');
3883 var h = ce.getHeight();
3885 ce.removeClass('show');
3886 // at this point we should be able to see it..
3887 ce.addClass('collapsing');
3889 ce.setHeight(0); // resize it ...
3890 ce.on('transitionend', function() {
3891 Roo.log('done transition');
3892 ce.removeClass('collapsing');
3893 ce.addClass('show');
3894 ce.removeClass('collapse');
3896 ce.dom.style.height = '';
3897 }, this, { single: true} );
3901 ce.setHeight(ce.getHeight());
3902 ce.removeClass('show');
3903 ce.addClass('collapsing');
3905 ce.on('transitionend', function() {
3906 ce.dom.style.height = '';
3907 ce.removeClass('collapsing');
3908 ce.addClass('collapse');
3909 }, this, { single: true} );
3921 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3923 var size = this.el.getSize();
3924 this.maskEl.setSize(size.width, size.height);
3925 this.maskEl.enableDisplayMode("block");
3934 getChildContainer : function()
3936 if (this.el.select('.collapse').getCount()) {
3937 return this.el.select('.collapse',true).first();
3970 * @class Roo.bootstrap.NavSimplebar
3971 * @extends Roo.bootstrap.Navbar
3972 * Bootstrap Sidebar class
3974 * @cfg {Boolean} inverse is inverted color
3976 * @cfg {String} type (nav | pills | tabs)
3977 * @cfg {Boolean} arrangement stacked | justified
3978 * @cfg {String} align (left | right) alignment
3980 * @cfg {Boolean} main (true|false) main nav bar? default false
3981 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3983 * @cfg {String} tag (header|footer|nav|div) default is nav
3985 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3989 * Create a new Sidebar
3990 * @param {Object} config The config object
3994 Roo.bootstrap.NavSimplebar = function(config){
3995 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3998 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4014 getAutoCreate : function(){
4018 tag : this.tag || 'div',
4019 cls : 'navbar navbar-expand-lg'
4021 if (['light','white'].indexOf(this.weight) > -1) {
4022 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4024 cfg.cls += ' bg-' + this.weight;
4036 this.type = this.type || 'nav';
4037 if (['tabs','pills'].indexOf(this.type)!==-1) {
4038 cfg.cn[0].cls += ' nav-' + this.type
4042 if (this.type!=='nav') {
4043 Roo.log('nav type must be nav/tabs/pills')
4045 cfg.cn[0].cls += ' navbar-nav'
4051 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4052 cfg.cn[0].cls += ' nav-' + this.arrangement;
4056 if (this.align === 'right') {
4057 cfg.cn[0].cls += ' navbar-right';
4061 cfg.cls += ' navbar-inverse';
4085 * navbar-expand-md fixed-top
4089 * @class Roo.bootstrap.NavHeaderbar
4090 * @extends Roo.bootstrap.NavSimplebar
4091 * Bootstrap Sidebar class
4093 * @cfg {String} brand what is brand
4094 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4095 * @cfg {String} brand_href href of the brand
4096 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4097 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4098 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4099 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4102 * Create a new Sidebar
4103 * @param {Object} config The config object
4107 Roo.bootstrap.NavHeaderbar = function(config){
4108 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4112 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4119 desktopCenter : false,
4122 getAutoCreate : function(){
4125 tag: this.nav || 'nav',
4126 cls: 'navbar navbar-expand-md',
4132 if (this.desktopCenter) {
4133 cn.push({cls : 'container', cn : []});
4141 cls: 'navbar-toggle navbar-toggler',
4142 'data-toggle': 'collapse',
4147 html: 'Toggle navigation'
4151 cls: 'icon-bar navbar-toggler-icon'
4164 cn.push( Roo.bootstrap.version == 4 ? btn : {
4166 cls: 'navbar-header',
4175 cls: 'collapse navbar-collapse',
4179 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4181 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4182 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4184 // tag can override this..
4186 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4189 if (this.brand !== '') {
4190 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4191 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4193 href: this.brand_href ? this.brand_href : '#',
4194 cls: 'navbar-brand',
4202 cfg.cls += ' main-nav';
4210 getHeaderChildContainer : function()
4212 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4213 return this.el.select('.navbar-header',true).first();
4216 return this.getChildContainer();
4220 initEvents : function()
4222 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4224 if (this.autohide) {
4229 Roo.get(document).on('scroll',function(e) {
4230 var ns = Roo.get(document).getScroll().top;
4231 var os = prevScroll;
4235 ft.removeClass('slideDown');
4236 ft.addClass('slideUp');
4239 ft.removeClass('slideUp');
4240 ft.addClass('slideDown');
4261 * @class Roo.bootstrap.NavSidebar
4262 * @extends Roo.bootstrap.Navbar
4263 * Bootstrap Sidebar class
4266 * Create a new Sidebar
4267 * @param {Object} config The config object
4271 Roo.bootstrap.NavSidebar = function(config){
4272 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4275 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4277 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4279 getAutoCreate : function(){
4284 cls: 'sidebar sidebar-nav'
4306 * @class Roo.bootstrap.NavGroup
4307 * @extends Roo.bootstrap.Component
4308 * Bootstrap NavGroup class
4309 * @cfg {String} align (left|right)
4310 * @cfg {Boolean} inverse
4311 * @cfg {String} type (nav|pills|tab) default nav
4312 * @cfg {String} navId - reference Id for navbar.
4316 * Create a new nav group
4317 * @param {Object} config The config object
4320 Roo.bootstrap.NavGroup = function(config){
4321 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4324 Roo.bootstrap.NavGroup.register(this);
4328 * Fires when the active item changes
4329 * @param {Roo.bootstrap.NavGroup} this
4330 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4331 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4338 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4349 getAutoCreate : function()
4351 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4358 if (['tabs','pills'].indexOf(this.type)!==-1) {
4359 cfg.cls += ' nav-' + this.type
4361 if (this.type!=='nav') {
4362 Roo.log('nav type must be nav/tabs/pills')
4364 cfg.cls += ' navbar-nav'
4367 if (this.parent() && this.parent().sidebar) {
4370 cls: 'dashboard-menu sidebar-menu'
4376 if (this.form === true) {
4382 if (this.align === 'right') {
4383 cfg.cls += ' navbar-right ml-md-auto';
4385 cfg.cls += ' navbar-left';
4389 if (this.align === 'right') {
4390 cfg.cls += ' navbar-right ml-md-auto';
4392 cfg.cls += ' mr-auto';
4396 cfg.cls += ' navbar-inverse';
4404 * sets the active Navigation item
4405 * @param {Roo.bootstrap.NavItem} the new current navitem
4407 setActiveItem : function(item)
4410 Roo.each(this.navItems, function(v){
4415 v.setActive(false, true);
4422 item.setActive(true, true);
4423 this.fireEvent('changed', this, item, prev);
4428 * gets the active Navigation item
4429 * @return {Roo.bootstrap.NavItem} the current navitem
4431 getActive : function()
4435 Roo.each(this.navItems, function(v){
4446 indexOfNav : function()
4450 Roo.each(this.navItems, function(v,i){
4461 * adds a Navigation item
4462 * @param {Roo.bootstrap.NavItem} the navitem to add
4464 addItem : function(cfg)
4466 var cn = new Roo.bootstrap.NavItem(cfg);
4468 cn.parentId = this.id;
4469 cn.onRender(this.el, null);
4473 * register a Navigation item
4474 * @param {Roo.bootstrap.NavItem} the navitem to add
4476 register : function(item)
4478 this.navItems.push( item);
4479 item.navId = this.navId;
4484 * clear all the Navigation item
4487 clearAll : function()
4490 this.el.dom.innerHTML = '';
4493 getNavItem: function(tabId)
4496 Roo.each(this.navItems, function(e) {
4497 if (e.tabId == tabId) {
4507 setActiveNext : function()
4509 var i = this.indexOfNav(this.getActive());
4510 if (i > this.navItems.length) {
4513 this.setActiveItem(this.navItems[i+1]);
4515 setActivePrev : function()
4517 var i = this.indexOfNav(this.getActive());
4521 this.setActiveItem(this.navItems[i-1]);
4523 clearWasActive : function(except) {
4524 Roo.each(this.navItems, function(e) {
4525 if (e.tabId != except.tabId && e.was_active) {
4526 e.was_active = false;
4533 getWasActive : function ()
4536 Roo.each(this.navItems, function(e) {
4551 Roo.apply(Roo.bootstrap.NavGroup, {
4555 * register a Navigation Group
4556 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4558 register : function(navgrp)
4560 this.groups[navgrp.navId] = navgrp;
4564 * fetch a Navigation Group based on the navigation ID
4565 * @param {string} the navgroup to add
4566 * @returns {Roo.bootstrap.NavGroup} the navgroup
4568 get: function(navId) {
4569 if (typeof(this.groups[navId]) == 'undefined') {
4571 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4573 return this.groups[navId] ;
4588 * @class Roo.bootstrap.NavItem
4589 * @extends Roo.bootstrap.Component
4590 * Bootstrap Navbar.NavItem class
4591 * @cfg {String} href link to
4592 * @cfg {String} html content of button
4593 * @cfg {String} badge text inside badge
4594 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4595 * @cfg {String} glyphicon DEPRICATED - use fa
4596 * @cfg {String} icon DEPRICATED - use fa
4597 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4598 * @cfg {Boolean} active Is item active
4599 * @cfg {Boolean} disabled Is item disabled
4601 * @cfg {Boolean} preventDefault (true | false) default false
4602 * @cfg {String} tabId the tab that this item activates.
4603 * @cfg {String} tagtype (a|span) render as a href or span?
4604 * @cfg {Boolean} animateRef (true|false) link to element default false
4607 * Create a new Navbar Item
4608 * @param {Object} config The config object
4610 Roo.bootstrap.NavItem = function(config){
4611 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4616 * The raw click event for the entire grid.
4617 * @param {Roo.EventObject} e
4622 * Fires when the active item active state changes
4623 * @param {Roo.bootstrap.NavItem} this
4624 * @param {boolean} state the new state
4630 * Fires when scroll to element
4631 * @param {Roo.bootstrap.NavItem} this
4632 * @param {Object} options
4633 * @param {Roo.EventObject} e
4641 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4650 preventDefault : false,
4657 getAutoCreate : function(){
4666 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4668 if (this.disabled) {
4669 cfg.cls += ' disabled';
4672 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4676 href : this.href || "#",
4677 html: this.html || ''
4680 if (this.tagtype == 'a') {
4681 cfg.cn[0].cls = 'nav-link';
4684 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4687 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4689 if(this.glyphicon) {
4690 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4695 cfg.cn[0].html += " <span class='caret'></span>";
4699 if (this.badge !== '') {
4701 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4709 initEvents: function()
4711 if (typeof (this.menu) != 'undefined') {
4712 this.menu.parentType = this.xtype;
4713 this.menu.triggerEl = this.el;
4714 this.menu = this.addxtype(Roo.apply({}, this.menu));
4717 this.el.select('a',true).on('click', this.onClick, this);
4719 if(this.tagtype == 'span'){
4720 this.el.select('span',true).on('click', this.onClick, this);
4723 // at this point parent should be available..
4724 this.parent().register(this);
4727 onClick : function(e)
4729 if (e.getTarget('.dropdown-menu-item')) {
4730 // did you click on a menu itemm.... - then don't trigger onclick..
4735 this.preventDefault ||
4738 Roo.log("NavItem - prevent Default?");
4742 if (this.disabled) {
4746 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4747 if (tg && tg.transition) {
4748 Roo.log("waiting for the transitionend");
4754 //Roo.log("fire event clicked");
4755 if(this.fireEvent('click', this, e) === false){
4759 if(this.tagtype == 'span'){
4763 //Roo.log(this.href);
4764 var ael = this.el.select('a',true).first();
4767 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4768 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4769 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4770 return; // ignore... - it's a 'hash' to another page.
4772 Roo.log("NavItem - prevent Default?");
4774 this.scrollToElement(e);
4778 var p = this.parent();
4780 if (['tabs','pills'].indexOf(p.type)!==-1) {
4781 if (typeof(p.setActiveItem) !== 'undefined') {
4782 p.setActiveItem(this);
4786 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4787 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4788 // remove the collapsed menu expand...
4789 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4793 isActive: function () {
4796 setActive : function(state, fire, is_was_active)
4798 if (this.active && !state && this.navId) {
4799 this.was_active = true;
4800 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4802 nv.clearWasActive(this);
4806 this.active = state;
4809 this.el.removeClass('active');
4810 } else if (!this.el.hasClass('active')) {
4811 this.el.addClass('active');
4814 this.fireEvent('changed', this, state);
4817 // show a panel if it's registered and related..
4819 if (!this.navId || !this.tabId || !state || is_was_active) {
4823 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4827 var pan = tg.getPanelByName(this.tabId);
4831 // if we can not flip to new panel - go back to old nav highlight..
4832 if (false == tg.showPanel(pan)) {
4833 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4835 var onav = nv.getWasActive();
4837 onav.setActive(true, false, true);
4846 // this should not be here...
4847 setDisabled : function(state)
4849 this.disabled = state;
4851 this.el.removeClass('disabled');
4852 } else if (!this.el.hasClass('disabled')) {
4853 this.el.addClass('disabled');
4859 * Fetch the element to display the tooltip on.
4860 * @return {Roo.Element} defaults to this.el
4862 tooltipEl : function()
4864 return this.el.select('' + this.tagtype + '', true).first();
4867 scrollToElement : function(e)
4869 var c = document.body;
4872 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4874 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4875 c = document.documentElement;
4878 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4884 var o = target.calcOffsetsTo(c);
4891 this.fireEvent('scrollto', this, options, e);
4893 Roo.get(c).scrollTo('top', options.value, true);
4906 * <span> icon </span>
4907 * <span> text </span>
4908 * <span>badge </span>
4912 * @class Roo.bootstrap.NavSidebarItem
4913 * @extends Roo.bootstrap.NavItem
4914 * Bootstrap Navbar.NavSidebarItem class
4915 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4916 * {Boolean} open is the menu open
4917 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4918 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4919 * {String} buttonSize (sm|md|lg)the extra classes for the button
4920 * {Boolean} showArrow show arrow next to the text (default true)
4922 * Create a new Navbar Button
4923 * @param {Object} config The config object
4925 Roo.bootstrap.NavSidebarItem = function(config){
4926 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4931 * The raw click event for the entire grid.
4932 * @param {Roo.EventObject} e
4937 * Fires when the active item active state changes
4938 * @param {Roo.bootstrap.NavSidebarItem} this
4939 * @param {boolean} state the new state
4947 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4949 badgeWeight : 'default',
4955 buttonWeight : 'default',
4961 getAutoCreate : function(){
4966 href : this.href || '#',
4972 if(this.buttonView){
4975 href : this.href || '#',
4976 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4989 cfg.cls += ' active';
4992 if (this.disabled) {
4993 cfg.cls += ' disabled';
4996 cfg.cls += ' open x-open';
4999 if (this.glyphicon || this.icon) {
5000 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5001 a.cn.push({ tag : 'i', cls : c }) ;
5004 if(!this.buttonView){
5007 html : this.html || ''
5014 if (this.badge !== '') {
5015 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5021 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5024 a.cls += ' dropdown-toggle treeview' ;
5030 initEvents : function()
5032 if (typeof (this.menu) != 'undefined') {
5033 this.menu.parentType = this.xtype;
5034 this.menu.triggerEl = this.el;
5035 this.menu = this.addxtype(Roo.apply({}, this.menu));
5038 this.el.on('click', this.onClick, this);
5040 if(this.badge !== ''){
5041 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5046 onClick : function(e)
5053 if(this.preventDefault){
5057 this.fireEvent('click', this);
5060 disable : function()
5062 this.setDisabled(true);
5067 this.setDisabled(false);
5070 setDisabled : function(state)
5072 if(this.disabled == state){
5076 this.disabled = state;
5079 this.el.addClass('disabled');
5083 this.el.removeClass('disabled');
5088 setActive : function(state)
5090 if(this.active == state){
5094 this.active = state;
5097 this.el.addClass('active');
5101 this.el.removeClass('active');
5106 isActive: function ()
5111 setBadge : function(str)
5117 this.badgeEl.dom.innerHTML = str;
5134 * @class Roo.bootstrap.Row
5135 * @extends Roo.bootstrap.Component
5136 * Bootstrap Row class (contains columns...)
5140 * @param {Object} config The config object
5143 Roo.bootstrap.Row = function(config){
5144 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5147 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5149 getAutoCreate : function(){
5168 * @class Roo.bootstrap.Element
5169 * @extends Roo.bootstrap.Component
5170 * Bootstrap Element class
5171 * @cfg {String} html contents of the element
5172 * @cfg {String} tag tag of the element
5173 * @cfg {String} cls class of the element
5174 * @cfg {Boolean} preventDefault (true|false) default false
5175 * @cfg {Boolean} clickable (true|false) default false
5178 * Create a new Element
5179 * @param {Object} config The config object
5182 Roo.bootstrap.Element = function(config){
5183 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5189 * When a element is chick
5190 * @param {Roo.bootstrap.Element} this
5191 * @param {Roo.EventObject} e
5197 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5202 preventDefault: false,
5205 getAutoCreate : function(){
5209 // cls: this.cls, double assign in parent class Component.js :: onRender
5216 initEvents: function()
5218 Roo.bootstrap.Element.superclass.initEvents.call(this);
5221 this.el.on('click', this.onClick, this);
5226 onClick : function(e)
5228 if(this.preventDefault){
5232 this.fireEvent('click', this, e);
5235 getValue : function()
5237 return this.el.dom.innerHTML;
5240 setValue : function(value)
5242 this.el.dom.innerHTML = value;
5257 * @class Roo.bootstrap.Pagination
5258 * @extends Roo.bootstrap.Component
5259 * Bootstrap Pagination class
5260 * @cfg {String} size xs | sm | md | lg
5261 * @cfg {Boolean} inverse false | true
5264 * Create a new Pagination
5265 * @param {Object} config The config object
5268 Roo.bootstrap.Pagination = function(config){
5269 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5272 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5278 getAutoCreate : function(){
5284 cfg.cls += ' inverse';
5290 cfg.cls += " " + this.cls;
5308 * @class Roo.bootstrap.PaginationItem
5309 * @extends Roo.bootstrap.Component
5310 * Bootstrap PaginationItem class
5311 * @cfg {String} html text
5312 * @cfg {String} href the link
5313 * @cfg {Boolean} preventDefault (true | false) default true
5314 * @cfg {Boolean} active (true | false) default false
5315 * @cfg {Boolean} disabled default false
5319 * Create a new PaginationItem
5320 * @param {Object} config The config object
5324 Roo.bootstrap.PaginationItem = function(config){
5325 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5330 * The raw click event for the entire grid.
5331 * @param {Roo.EventObject} e
5337 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5341 preventDefault: true,
5346 getAutoCreate : function(){
5352 href : this.href ? this.href : '#',
5353 html : this.html ? this.html : ''
5363 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5367 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5373 initEvents: function() {
5375 this.el.on('click', this.onClick, this);
5378 onClick : function(e)
5380 Roo.log('PaginationItem on click ');
5381 if(this.preventDefault){
5389 this.fireEvent('click', this, e);
5405 * @class Roo.bootstrap.Slider
5406 * @extends Roo.bootstrap.Component
5407 * Bootstrap Slider class
5410 * Create a new Slider
5411 * @param {Object} config The config object
5414 Roo.bootstrap.Slider = function(config){
5415 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5418 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5420 getAutoCreate : function(){
5424 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5428 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5440 * Ext JS Library 1.1.1
5441 * Copyright(c) 2006-2007, Ext JS, LLC.
5443 * Originally Released Under LGPL - original licence link has changed is not relivant.
5446 * <script type="text/javascript">
5451 * @class Roo.grid.ColumnModel
5452 * @extends Roo.util.Observable
5453 * This is the default implementation of a ColumnModel used by the Grid. It defines
5454 * the columns in the grid.
5457 var colModel = new Roo.grid.ColumnModel([
5458 {header: "Ticker", width: 60, sortable: true, locked: true},
5459 {header: "Company Name", width: 150, sortable: true},
5460 {header: "Market Cap.", width: 100, sortable: true},
5461 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5462 {header: "Employees", width: 100, sortable: true, resizable: false}
5467 * The config options listed for this class are options which may appear in each
5468 * individual column definition.
5469 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5471 * @param {Object} config An Array of column config objects. See this class's
5472 * config objects for details.
5474 Roo.grid.ColumnModel = function(config){
5476 * The config passed into the constructor
5478 this.config = config;
5481 // if no id, create one
5482 // if the column does not have a dataIndex mapping,
5483 // map it to the order it is in the config
5484 for(var i = 0, len = config.length; i < len; i++){
5486 if(typeof c.dataIndex == "undefined"){
5489 if(typeof c.renderer == "string"){
5490 c.renderer = Roo.util.Format[c.renderer];
5492 if(typeof c.id == "undefined"){
5495 if(c.editor && c.editor.xtype){
5496 c.editor = Roo.factory(c.editor, Roo.grid);
5498 if(c.editor && c.editor.isFormField){
5499 c.editor = new Roo.grid.GridEditor(c.editor);
5501 this.lookup[c.id] = c;
5505 * The width of columns which have no width specified (defaults to 100)
5508 this.defaultWidth = 100;
5511 * Default sortable of columns which have no sortable specified (defaults to false)
5514 this.defaultSortable = false;
5518 * @event widthchange
5519 * Fires when the width of a column changes.
5520 * @param {ColumnModel} this
5521 * @param {Number} columnIndex The column index
5522 * @param {Number} newWidth The new width
5524 "widthchange": true,
5526 * @event headerchange
5527 * Fires when the text of a header changes.
5528 * @param {ColumnModel} this
5529 * @param {Number} columnIndex The column index
5530 * @param {Number} newText The new header text
5532 "headerchange": true,
5534 * @event hiddenchange
5535 * Fires when a column is hidden or "unhidden".
5536 * @param {ColumnModel} this
5537 * @param {Number} columnIndex The column index
5538 * @param {Boolean} hidden true if hidden, false otherwise
5540 "hiddenchange": true,
5542 * @event columnmoved
5543 * Fires when a column is moved.
5544 * @param {ColumnModel} this
5545 * @param {Number} oldIndex
5546 * @param {Number} newIndex
5548 "columnmoved" : true,
5550 * @event columlockchange
5551 * Fires when a column's locked state is changed
5552 * @param {ColumnModel} this
5553 * @param {Number} colIndex
5554 * @param {Boolean} locked true if locked
5556 "columnlockchange" : true
5558 Roo.grid.ColumnModel.superclass.constructor.call(this);
5560 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5562 * @cfg {String} header The header text to display in the Grid view.
5565 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5566 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5567 * specified, the column's index is used as an index into the Record's data Array.
5570 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5571 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5574 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5575 * Defaults to the value of the {@link #defaultSortable} property.
5576 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5579 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5582 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5585 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5588 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5591 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5592 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5593 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5594 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5597 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5600 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5603 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5606 * @cfg {String} cursor (Optional)
5609 * @cfg {String} tooltip (Optional)
5612 * @cfg {Number} xs (Optional)
5615 * @cfg {Number} sm (Optional)
5618 * @cfg {Number} md (Optional)
5621 * @cfg {Number} lg (Optional)
5624 * Returns the id of the column at the specified index.
5625 * @param {Number} index The column index
5626 * @return {String} the id
5628 getColumnId : function(index){
5629 return this.config[index].id;
5633 * Returns the column for a specified id.
5634 * @param {String} id The column id
5635 * @return {Object} the column
5637 getColumnById : function(id){
5638 return this.lookup[id];
5643 * Returns the column for a specified dataIndex.
5644 * @param {String} dataIndex The column dataIndex
5645 * @return {Object|Boolean} the column or false if not found
5647 getColumnByDataIndex: function(dataIndex){
5648 var index = this.findColumnIndex(dataIndex);
5649 return index > -1 ? this.config[index] : false;
5653 * Returns the index for a specified column id.
5654 * @param {String} id The column id
5655 * @return {Number} the index, or -1 if not found
5657 getIndexById : function(id){
5658 for(var i = 0, len = this.config.length; i < len; i++){
5659 if(this.config[i].id == id){
5667 * Returns the index for a specified column dataIndex.
5668 * @param {String} dataIndex The column dataIndex
5669 * @return {Number} the index, or -1 if not found
5672 findColumnIndex : function(dataIndex){
5673 for(var i = 0, len = this.config.length; i < len; i++){
5674 if(this.config[i].dataIndex == dataIndex){
5682 moveColumn : function(oldIndex, newIndex){
5683 var c = this.config[oldIndex];
5684 this.config.splice(oldIndex, 1);
5685 this.config.splice(newIndex, 0, c);
5686 this.dataMap = null;
5687 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5690 isLocked : function(colIndex){
5691 return this.config[colIndex].locked === true;
5694 setLocked : function(colIndex, value, suppressEvent){
5695 if(this.isLocked(colIndex) == value){
5698 this.config[colIndex].locked = value;
5700 this.fireEvent("columnlockchange", this, colIndex, value);
5704 getTotalLockedWidth : function(){
5706 for(var i = 0; i < this.config.length; i++){
5707 if(this.isLocked(i) && !this.isHidden(i)){
5708 this.totalWidth += this.getColumnWidth(i);
5714 getLockedCount : function(){
5715 for(var i = 0, len = this.config.length; i < len; i++){
5716 if(!this.isLocked(i)){
5721 return this.config.length;
5725 * Returns the number of columns.
5728 getColumnCount : function(visibleOnly){
5729 if(visibleOnly === true){
5731 for(var i = 0, len = this.config.length; i < len; i++){
5732 if(!this.isHidden(i)){
5738 return this.config.length;
5742 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5743 * @param {Function} fn
5744 * @param {Object} scope (optional)
5745 * @return {Array} result
5747 getColumnsBy : function(fn, scope){
5749 for(var i = 0, len = this.config.length; i < len; i++){
5750 var c = this.config[i];
5751 if(fn.call(scope||this, c, i) === true){
5759 * Returns true if the specified column is sortable.
5760 * @param {Number} col The column index
5763 isSortable : function(col){
5764 if(typeof this.config[col].sortable == "undefined"){
5765 return this.defaultSortable;
5767 return this.config[col].sortable;
5771 * Returns the rendering (formatting) function defined for the column.
5772 * @param {Number} col The column index.
5773 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5775 getRenderer : function(col){
5776 if(!this.config[col].renderer){
5777 return Roo.grid.ColumnModel.defaultRenderer;
5779 return this.config[col].renderer;
5783 * Sets the rendering (formatting) function for a column.
5784 * @param {Number} col The column index
5785 * @param {Function} fn The function to use to process the cell's raw data
5786 * to return HTML markup for the grid view. The render function is called with
5787 * the following parameters:<ul>
5788 * <li>Data value.</li>
5789 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5790 * <li>css A CSS style string to apply to the table cell.</li>
5791 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5792 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5793 * <li>Row index</li>
5794 * <li>Column index</li>
5795 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5797 setRenderer : function(col, fn){
5798 this.config[col].renderer = fn;
5802 * Returns the width for the specified column.
5803 * @param {Number} col The column index
5806 getColumnWidth : function(col){
5807 return this.config[col].width * 1 || this.defaultWidth;
5811 * Sets the width for a column.
5812 * @param {Number} col The column index
5813 * @param {Number} width The new width
5815 setColumnWidth : function(col, width, suppressEvent){
5816 this.config[col].width = width;
5817 this.totalWidth = null;
5819 this.fireEvent("widthchange", this, col, width);
5824 * Returns the total width of all columns.
5825 * @param {Boolean} includeHidden True to include hidden column widths
5828 getTotalWidth : function(includeHidden){
5829 if(!this.totalWidth){
5830 this.totalWidth = 0;
5831 for(var i = 0, len = this.config.length; i < len; i++){
5832 if(includeHidden || !this.isHidden(i)){
5833 this.totalWidth += this.getColumnWidth(i);
5837 return this.totalWidth;
5841 * Returns the header for the specified column.
5842 * @param {Number} col The column index
5845 getColumnHeader : function(col){
5846 return this.config[col].header;
5850 * Sets the header for a column.
5851 * @param {Number} col The column index
5852 * @param {String} header The new header
5854 setColumnHeader : function(col, header){
5855 this.config[col].header = header;
5856 this.fireEvent("headerchange", this, col, header);
5860 * Returns the tooltip for the specified column.
5861 * @param {Number} col The column index
5864 getColumnTooltip : function(col){
5865 return this.config[col].tooltip;
5868 * Sets the tooltip for a column.
5869 * @param {Number} col The column index
5870 * @param {String} tooltip The new tooltip
5872 setColumnTooltip : function(col, tooltip){
5873 this.config[col].tooltip = tooltip;
5877 * Returns the dataIndex for the specified column.
5878 * @param {Number} col The column index
5881 getDataIndex : function(col){
5882 return this.config[col].dataIndex;
5886 * Sets the dataIndex for a column.
5887 * @param {Number} col The column index
5888 * @param {Number} dataIndex The new dataIndex
5890 setDataIndex : function(col, dataIndex){
5891 this.config[col].dataIndex = dataIndex;
5897 * Returns true if the cell is editable.
5898 * @param {Number} colIndex The column index
5899 * @param {Number} rowIndex The row index - this is nto actually used..?
5902 isCellEditable : function(colIndex, rowIndex){
5903 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5907 * Returns the editor defined for the cell/column.
5908 * return false or null to disable editing.
5909 * @param {Number} colIndex The column index
5910 * @param {Number} rowIndex The row index
5913 getCellEditor : function(colIndex, rowIndex){
5914 return this.config[colIndex].editor;
5918 * Sets if a column is editable.
5919 * @param {Number} col The column index
5920 * @param {Boolean} editable True if the column is editable
5922 setEditable : function(col, editable){
5923 this.config[col].editable = editable;
5928 * Returns true if the column is hidden.
5929 * @param {Number} colIndex The column index
5932 isHidden : function(colIndex){
5933 return this.config[colIndex].hidden;
5938 * Returns true if the column width cannot be changed
5940 isFixed : function(colIndex){
5941 return this.config[colIndex].fixed;
5945 * Returns true if the column can be resized
5948 isResizable : function(colIndex){
5949 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5952 * Sets if a column is hidden.
5953 * @param {Number} colIndex The column index
5954 * @param {Boolean} hidden True if the column is hidden
5956 setHidden : function(colIndex, hidden){
5957 this.config[colIndex].hidden = hidden;
5958 this.totalWidth = null;
5959 this.fireEvent("hiddenchange", this, colIndex, hidden);
5963 * Sets the editor for a column.
5964 * @param {Number} col The column index
5965 * @param {Object} editor The editor object
5967 setEditor : function(col, editor){
5968 this.config[col].editor = editor;
5972 Roo.grid.ColumnModel.defaultRenderer = function(value)
5974 if(typeof value == "object") {
5977 if(typeof value == "string" && value.length < 1){
5981 return String.format("{0}", value);
5984 // Alias for backwards compatibility
5985 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5988 * Ext JS Library 1.1.1
5989 * Copyright(c) 2006-2007, Ext JS, LLC.
5991 * Originally Released Under LGPL - original licence link has changed is not relivant.
5994 * <script type="text/javascript">
5998 * @class Roo.LoadMask
5999 * A simple utility class for generically masking elements while loading data. If the element being masked has
6000 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6001 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6002 * element's UpdateManager load indicator and will be destroyed after the initial load.
6004 * Create a new LoadMask
6005 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6006 * @param {Object} config The config object
6008 Roo.LoadMask = function(el, config){
6009 this.el = Roo.get(el);
6010 Roo.apply(this, config);
6012 this.store.on('beforeload', this.onBeforeLoad, this);
6013 this.store.on('load', this.onLoad, this);
6014 this.store.on('loadexception', this.onLoadException, this);
6015 this.removeMask = false;
6017 var um = this.el.getUpdateManager();
6018 um.showLoadIndicator = false; // disable the default indicator
6019 um.on('beforeupdate', this.onBeforeLoad, this);
6020 um.on('update', this.onLoad, this);
6021 um.on('failure', this.onLoad, this);
6022 this.removeMask = true;
6026 Roo.LoadMask.prototype = {
6028 * @cfg {Boolean} removeMask
6029 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6030 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6034 * The text to display in a centered loading message box (defaults to 'Loading...')
6038 * @cfg {String} msgCls
6039 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6041 msgCls : 'x-mask-loading',
6044 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6050 * Disables the mask to prevent it from being displayed
6052 disable : function(){
6053 this.disabled = true;
6057 * Enables the mask so that it can be displayed
6059 enable : function(){
6060 this.disabled = false;
6063 onLoadException : function()
6067 if (typeof(arguments[3]) != 'undefined') {
6068 Roo.MessageBox.alert("Error loading",arguments[3]);
6072 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6073 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6080 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6085 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6089 onBeforeLoad : function(){
6091 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6096 destroy : function(){
6098 this.store.un('beforeload', this.onBeforeLoad, this);
6099 this.store.un('load', this.onLoad, this);
6100 this.store.un('loadexception', this.onLoadException, this);
6102 var um = this.el.getUpdateManager();
6103 um.un('beforeupdate', this.onBeforeLoad, this);
6104 um.un('update', this.onLoad, this);
6105 um.un('failure', this.onLoad, this);
6116 * @class Roo.bootstrap.Table
6117 * @extends Roo.bootstrap.Component
6118 * Bootstrap Table class
6119 * @cfg {String} cls table class
6120 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6121 * @cfg {String} bgcolor Specifies the background color for a table
6122 * @cfg {Number} border Specifies whether the table cells should have borders or not
6123 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6124 * @cfg {Number} cellspacing Specifies the space between cells
6125 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6126 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6127 * @cfg {String} sortable Specifies that the table should be sortable
6128 * @cfg {String} summary Specifies a summary of the content of a table
6129 * @cfg {Number} width Specifies the width of a table
6130 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6132 * @cfg {boolean} striped Should the rows be alternative striped
6133 * @cfg {boolean} bordered Add borders to the table
6134 * @cfg {boolean} hover Add hover highlighting
6135 * @cfg {boolean} condensed Format condensed
6136 * @cfg {boolean} responsive Format condensed
6137 * @cfg {Boolean} loadMask (true|false) default false
6138 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6139 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6140 * @cfg {Boolean} rowSelection (true|false) default false
6141 * @cfg {Boolean} cellSelection (true|false) default false
6142 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6143 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6144 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6145 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6149 * Create a new Table
6150 * @param {Object} config The config object
6153 Roo.bootstrap.Table = function(config){
6154 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6159 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6160 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6161 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6162 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6164 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6166 this.sm.grid = this;
6167 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6168 this.sm = this.selModel;
6169 this.sm.xmodule = this.xmodule || false;
6172 if (this.cm && typeof(this.cm.config) == 'undefined') {
6173 this.colModel = new Roo.grid.ColumnModel(this.cm);
6174 this.cm = this.colModel;
6175 this.cm.xmodule = this.xmodule || false;
6178 this.store= Roo.factory(this.store, Roo.data);
6179 this.ds = this.store;
6180 this.ds.xmodule = this.xmodule || false;
6183 if (this.footer && this.store) {
6184 this.footer.dataSource = this.ds;
6185 this.footer = Roo.factory(this.footer);
6192 * Fires when a cell is clicked
6193 * @param {Roo.bootstrap.Table} this
6194 * @param {Roo.Element} el
6195 * @param {Number} rowIndex
6196 * @param {Number} columnIndex
6197 * @param {Roo.EventObject} e
6201 * @event celldblclick
6202 * Fires when a cell is double clicked
6203 * @param {Roo.bootstrap.Table} this
6204 * @param {Roo.Element} el
6205 * @param {Number} rowIndex
6206 * @param {Number} columnIndex
6207 * @param {Roo.EventObject} e
6209 "celldblclick" : true,
6212 * Fires when a row is clicked
6213 * @param {Roo.bootstrap.Table} this
6214 * @param {Roo.Element} el
6215 * @param {Number} rowIndex
6216 * @param {Roo.EventObject} e
6220 * @event rowdblclick
6221 * Fires when a row is double clicked
6222 * @param {Roo.bootstrap.Table} this
6223 * @param {Roo.Element} el
6224 * @param {Number} rowIndex
6225 * @param {Roo.EventObject} e
6227 "rowdblclick" : true,
6230 * Fires when a mouseover occur
6231 * @param {Roo.bootstrap.Table} this
6232 * @param {Roo.Element} el
6233 * @param {Number} rowIndex
6234 * @param {Number} columnIndex
6235 * @param {Roo.EventObject} e
6240 * Fires when a mouseout occur
6241 * @param {Roo.bootstrap.Table} this
6242 * @param {Roo.Element} el
6243 * @param {Number} rowIndex
6244 * @param {Number} columnIndex
6245 * @param {Roo.EventObject} e
6250 * Fires when a row is rendered, so you can change add a style to it.
6251 * @param {Roo.bootstrap.Table} this
6252 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6256 * @event rowsrendered
6257 * Fires when all the rows have been rendered
6258 * @param {Roo.bootstrap.Table} this
6260 'rowsrendered' : true,
6262 * @event contextmenu
6263 * The raw contextmenu event for the entire grid.
6264 * @param {Roo.EventObject} e
6266 "contextmenu" : true,
6268 * @event rowcontextmenu
6269 * Fires when a row is right clicked
6270 * @param {Roo.bootstrap.Table} this
6271 * @param {Number} rowIndex
6272 * @param {Roo.EventObject} e
6274 "rowcontextmenu" : true,
6276 * @event cellcontextmenu
6277 * Fires when a cell is right clicked
6278 * @param {Roo.bootstrap.Table} this
6279 * @param {Number} rowIndex
6280 * @param {Number} cellIndex
6281 * @param {Roo.EventObject} e
6283 "cellcontextmenu" : true,
6285 * @event headercontextmenu
6286 * Fires when a header is right clicked
6287 * @param {Roo.bootstrap.Table} this
6288 * @param {Number} columnIndex
6289 * @param {Roo.EventObject} e
6291 "headercontextmenu" : true
6295 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6321 rowSelection : false,
6322 cellSelection : false,
6325 // Roo.Element - the tbody
6327 // Roo.Element - thead element
6330 container: false, // used by gridpanel...
6336 auto_hide_footer : false,
6338 getAutoCreate : function()
6340 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6347 if (this.scrollBody) {
6348 cfg.cls += ' table-body-fixed';
6351 cfg.cls += ' table-striped';
6355 cfg.cls += ' table-hover';
6357 if (this.bordered) {
6358 cfg.cls += ' table-bordered';
6360 if (this.condensed) {
6361 cfg.cls += ' table-condensed';
6363 if (this.responsive) {
6364 cfg.cls += ' table-responsive';
6368 cfg.cls+= ' ' +this.cls;
6371 // this lot should be simplifed...
6384 ].forEach(function(k) {
6392 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6395 if(this.store || this.cm){
6396 if(this.headerShow){
6397 cfg.cn.push(this.renderHeader());
6400 cfg.cn.push(this.renderBody());
6402 if(this.footerShow){
6403 cfg.cn.push(this.renderFooter());
6405 // where does this come from?
6406 //cfg.cls+= ' TableGrid';
6409 return { cn : [ cfg ] };
6412 initEvents : function()
6414 if(!this.store || !this.cm){
6417 if (this.selModel) {
6418 this.selModel.initEvents();
6422 //Roo.log('initEvents with ds!!!!');
6424 this.mainBody = this.el.select('tbody', true).first();
6425 this.mainHead = this.el.select('thead', true).first();
6426 this.mainFoot = this.el.select('tfoot', true).first();
6432 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6433 e.on('click', _this.sort, _this);
6436 this.mainBody.on("click", this.onClick, this);
6437 this.mainBody.on("dblclick", this.onDblClick, this);
6439 // why is this done????? = it breaks dialogs??
6440 //this.parent().el.setStyle('position', 'relative');
6444 this.footer.parentId = this.id;
6445 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6448 this.el.select('tfoot tr td').first().addClass('hide');
6453 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6456 this.store.on('load', this.onLoad, this);
6457 this.store.on('beforeload', this.onBeforeLoad, this);
6458 this.store.on('update', this.onUpdate, this);
6459 this.store.on('add', this.onAdd, this);
6460 this.store.on("clear", this.clear, this);
6462 this.el.on("contextmenu", this.onContextMenu, this);
6464 this.mainBody.on('scroll', this.onBodyScroll, this);
6466 this.cm.on("headerchange", this.onHeaderChange, this);
6468 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6472 onContextMenu : function(e, t)
6474 this.processEvent("contextmenu", e);
6477 processEvent : function(name, e)
6479 if (name != 'touchstart' ) {
6480 this.fireEvent(name, e);
6483 var t = e.getTarget();
6485 var cell = Roo.get(t);
6491 if(cell.findParent('tfoot', false, true)){
6495 if(cell.findParent('thead', false, true)){
6497 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6498 cell = Roo.get(t).findParent('th', false, true);
6500 Roo.log("failed to find th in thead?");
6501 Roo.log(e.getTarget());
6506 var cellIndex = cell.dom.cellIndex;
6508 var ename = name == 'touchstart' ? 'click' : name;
6509 this.fireEvent("header" + ename, this, cellIndex, e);
6514 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6515 cell = Roo.get(t).findParent('td', false, true);
6517 Roo.log("failed to find th in tbody?");
6518 Roo.log(e.getTarget());
6523 var row = cell.findParent('tr', false, true);
6524 var cellIndex = cell.dom.cellIndex;
6525 var rowIndex = row.dom.rowIndex - 1;
6529 this.fireEvent("row" + name, this, rowIndex, e);
6533 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6539 onMouseover : function(e, el)
6541 var cell = Roo.get(el);
6547 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6548 cell = cell.findParent('td', false, true);
6551 var row = cell.findParent('tr', false, true);
6552 var cellIndex = cell.dom.cellIndex;
6553 var rowIndex = row.dom.rowIndex - 1; // start from 0
6555 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6559 onMouseout : function(e, el)
6561 var cell = Roo.get(el);
6567 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6568 cell = cell.findParent('td', false, true);
6571 var row = cell.findParent('tr', false, true);
6572 var cellIndex = cell.dom.cellIndex;
6573 var rowIndex = row.dom.rowIndex - 1; // start from 0
6575 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6579 onClick : function(e, el)
6581 var cell = Roo.get(el);
6583 if(!cell || (!this.cellSelection && !this.rowSelection)){
6587 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6588 cell = cell.findParent('td', false, true);
6591 if(!cell || typeof(cell) == 'undefined'){
6595 var row = cell.findParent('tr', false, true);
6597 if(!row || typeof(row) == 'undefined'){
6601 var cellIndex = cell.dom.cellIndex;
6602 var rowIndex = this.getRowIndex(row);
6604 // why??? - should these not be based on SelectionModel?
6605 if(this.cellSelection){
6606 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6609 if(this.rowSelection){
6610 this.fireEvent('rowclick', this, row, rowIndex, e);
6616 onDblClick : function(e,el)
6618 var cell = Roo.get(el);
6620 if(!cell || (!this.cellSelection && !this.rowSelection)){
6624 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6625 cell = cell.findParent('td', false, true);
6628 if(!cell || typeof(cell) == 'undefined'){
6632 var row = cell.findParent('tr', false, true);
6634 if(!row || typeof(row) == 'undefined'){
6638 var cellIndex = cell.dom.cellIndex;
6639 var rowIndex = this.getRowIndex(row);
6641 if(this.cellSelection){
6642 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6645 if(this.rowSelection){
6646 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6650 sort : function(e,el)
6652 var col = Roo.get(el);
6654 if(!col.hasClass('sortable')){
6658 var sort = col.attr('sort');
6661 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6665 this.store.sortInfo = {field : sort, direction : dir};
6668 Roo.log("calling footer first");
6669 this.footer.onClick('first');
6672 this.store.load({ params : { start : 0 } });
6676 renderHeader : function()
6684 this.totalWidth = 0;
6686 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6688 var config = cm.config[i];
6692 cls : 'x-hcol-' + i,
6694 html: cm.getColumnHeader(i)
6699 if(typeof(config.sortable) != 'undefined' && config.sortable){
6701 c.html = '<i class="glyphicon"></i>' + c.html;
6704 if(typeof(config.lgHeader) != 'undefined'){
6705 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6708 if(typeof(config.mdHeader) != 'undefined'){
6709 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6712 if(typeof(config.smHeader) != 'undefined'){
6713 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6716 if(typeof(config.xsHeader) != 'undefined'){
6717 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6724 if(typeof(config.tooltip) != 'undefined'){
6725 c.tooltip = config.tooltip;
6728 if(typeof(config.colspan) != 'undefined'){
6729 c.colspan = config.colspan;
6732 if(typeof(config.hidden) != 'undefined' && config.hidden){
6733 c.style += ' display:none;';
6736 if(typeof(config.dataIndex) != 'undefined'){
6737 c.sort = config.dataIndex;
6742 if(typeof(config.align) != 'undefined' && config.align.length){
6743 c.style += ' text-align:' + config.align + ';';
6746 if(typeof(config.width) != 'undefined'){
6747 c.style += ' width:' + config.width + 'px;';
6748 this.totalWidth += config.width;
6750 this.totalWidth += 100; // assume minimum of 100 per column?
6753 if(typeof(config.cls) != 'undefined'){
6754 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6757 ['xs','sm','md','lg'].map(function(size){
6759 if(typeof(config[size]) == 'undefined'){
6763 if (!config[size]) { // 0 = hidden
6764 c.cls += ' hidden-' + size;
6768 c.cls += ' col-' + size + '-' + config[size];
6778 renderBody : function()
6788 colspan : this.cm.getColumnCount()
6798 renderFooter : function()
6808 colspan : this.cm.getColumnCount()
6822 // Roo.log('ds onload');
6827 var ds = this.store;
6829 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6830 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6831 if (_this.store.sortInfo) {
6833 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6834 e.select('i', true).addClass(['glyphicon-arrow-up']);
6837 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6838 e.select('i', true).addClass(['glyphicon-arrow-down']);
6843 var tbody = this.mainBody;
6845 if(ds.getCount() > 0){
6846 ds.data.each(function(d,rowIndex){
6847 var row = this.renderRow(cm, ds, rowIndex);
6849 tbody.createChild(row);
6853 if(row.cellObjects.length){
6854 Roo.each(row.cellObjects, function(r){
6855 _this.renderCellObject(r);
6862 var tfoot = this.el.select('tfoot', true).first();
6864 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6866 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6868 var total = this.ds.getTotalCount();
6870 if(this.footer.pageSize < total){
6871 this.mainFoot.show();
6875 Roo.each(this.el.select('tbody td', true).elements, function(e){
6876 e.on('mouseover', _this.onMouseover, _this);
6879 Roo.each(this.el.select('tbody td', true).elements, function(e){
6880 e.on('mouseout', _this.onMouseout, _this);
6882 this.fireEvent('rowsrendered', this);
6888 onUpdate : function(ds,record)
6890 this.refreshRow(record);
6894 onRemove : function(ds, record, index, isUpdate){
6895 if(isUpdate !== true){
6896 this.fireEvent("beforerowremoved", this, index, record);
6898 var bt = this.mainBody.dom;
6900 var rows = this.el.select('tbody > tr', true).elements;
6902 if(typeof(rows[index]) != 'undefined'){
6903 bt.removeChild(rows[index].dom);
6906 // if(bt.rows[index]){
6907 // bt.removeChild(bt.rows[index]);
6910 if(isUpdate !== true){
6911 //this.stripeRows(index);
6912 //this.syncRowHeights(index, index);
6914 this.fireEvent("rowremoved", this, index, record);
6918 onAdd : function(ds, records, rowIndex)
6920 //Roo.log('on Add called');
6921 // - note this does not handle multiple adding very well..
6922 var bt = this.mainBody.dom;
6923 for (var i =0 ; i < records.length;i++) {
6924 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6925 //Roo.log(records[i]);
6926 //Roo.log(this.store.getAt(rowIndex+i));
6927 this.insertRow(this.store, rowIndex + i, false);
6934 refreshRow : function(record){
6935 var ds = this.store, index;
6936 if(typeof record == 'number'){
6938 record = ds.getAt(index);
6940 index = ds.indexOf(record);
6942 this.insertRow(ds, index, true);
6944 this.onRemove(ds, record, index+1, true);
6946 //this.syncRowHeights(index, index);
6948 this.fireEvent("rowupdated", this, index, record);
6951 insertRow : function(dm, rowIndex, isUpdate){
6954 this.fireEvent("beforerowsinserted", this, rowIndex);
6956 //var s = this.getScrollState();
6957 var row = this.renderRow(this.cm, this.store, rowIndex);
6958 // insert before rowIndex..
6959 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6963 if(row.cellObjects.length){
6964 Roo.each(row.cellObjects, function(r){
6965 _this.renderCellObject(r);
6970 this.fireEvent("rowsinserted", this, rowIndex);
6971 //this.syncRowHeights(firstRow, lastRow);
6972 //this.stripeRows(firstRow);
6979 getRowDom : function(rowIndex)
6981 var rows = this.el.select('tbody > tr', true).elements;
6983 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6986 // returns the object tree for a tr..
6989 renderRow : function(cm, ds, rowIndex)
6991 var d = ds.getAt(rowIndex);
6995 cls : 'x-row-' + rowIndex,
6999 var cellObjects = [];
7001 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7002 var config = cm.config[i];
7004 var renderer = cm.getRenderer(i);
7008 if(typeof(renderer) !== 'undefined'){
7009 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7011 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7012 // and are rendered into the cells after the row is rendered - using the id for the element.
7014 if(typeof(value) === 'object'){
7024 rowIndex : rowIndex,
7029 this.fireEvent('rowclass', this, rowcfg);
7033 cls : rowcfg.rowClass + ' x-col-' + i,
7035 html: (typeof(value) === 'object') ? '' : value
7042 if(typeof(config.colspan) != 'undefined'){
7043 td.colspan = config.colspan;
7046 if(typeof(config.hidden) != 'undefined' && config.hidden){
7047 td.style += ' display:none;';
7050 if(typeof(config.align) != 'undefined' && config.align.length){
7051 td.style += ' text-align:' + config.align + ';';
7053 if(typeof(config.valign) != 'undefined' && config.valign.length){
7054 td.style += ' vertical-align:' + config.valign + ';';
7057 if(typeof(config.width) != 'undefined'){
7058 td.style += ' width:' + config.width + 'px;';
7061 if(typeof(config.cursor) != 'undefined'){
7062 td.style += ' cursor:' + config.cursor + ';';
7065 if(typeof(config.cls) != 'undefined'){
7066 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7069 ['xs','sm','md','lg'].map(function(size){
7071 if(typeof(config[size]) == 'undefined'){
7075 if (!config[size]) { // 0 = hidden
7076 td.cls += ' hidden-' + size;
7080 td.cls += ' col-' + size + '-' + config[size];
7088 row.cellObjects = cellObjects;
7096 onBeforeLoad : function()
7105 this.el.select('tbody', true).first().dom.innerHTML = '';
7108 * Show or hide a row.
7109 * @param {Number} rowIndex to show or hide
7110 * @param {Boolean} state hide
7112 setRowVisibility : function(rowIndex, state)
7114 var bt = this.mainBody.dom;
7116 var rows = this.el.select('tbody > tr', true).elements;
7118 if(typeof(rows[rowIndex]) == 'undefined'){
7121 rows[rowIndex].dom.style.display = state ? '' : 'none';
7125 getSelectionModel : function(){
7127 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7129 return this.selModel;
7132 * Render the Roo.bootstrap object from renderder
7134 renderCellObject : function(r)
7138 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7140 var t = r.cfg.render(r.container);
7143 Roo.each(r.cfg.cn, function(c){
7145 container: t.getChildContainer(),
7148 _this.renderCellObject(child);
7153 getRowIndex : function(row)
7157 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7168 * Returns the grid's underlying element = used by panel.Grid
7169 * @return {Element} The element
7171 getGridEl : function(){
7175 * Forces a resize - used by panel.Grid
7176 * @return {Element} The element
7178 autoSize : function()
7180 //var ctr = Roo.get(this.container.dom.parentElement);
7181 var ctr = Roo.get(this.el.dom);
7183 var thd = this.getGridEl().select('thead',true).first();
7184 var tbd = this.getGridEl().select('tbody', true).first();
7185 var tfd = this.getGridEl().select('tfoot', true).first();
7187 var cw = ctr.getWidth();
7191 tbd.setSize(ctr.getWidth(),
7192 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7194 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7197 cw = Math.max(cw, this.totalWidth);
7198 this.getGridEl().select('tr',true).setWidth(cw);
7199 // resize 'expandable coloumn?
7201 return; // we doe not have a view in this design..
7204 onBodyScroll: function()
7206 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7208 this.mainHead.setStyle({
7209 'position' : 'relative',
7210 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7216 var scrollHeight = this.mainBody.dom.scrollHeight;
7218 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7220 var height = this.mainBody.getHeight();
7222 if(scrollHeight - height == scrollTop) {
7224 var total = this.ds.getTotalCount();
7226 if(this.footer.cursor + this.footer.pageSize < total){
7228 this.footer.ds.load({
7230 start : this.footer.cursor + this.footer.pageSize,
7231 limit : this.footer.pageSize
7241 onHeaderChange : function()
7243 var header = this.renderHeader();
7244 var table = this.el.select('table', true).first();
7246 this.mainHead.remove();
7247 this.mainHead = table.createChild(header, this.mainBody, false);
7250 onHiddenChange : function(colModel, colIndex, hidden)
7252 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7253 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7255 this.CSS.updateRule(thSelector, "display", "");
7256 this.CSS.updateRule(tdSelector, "display", "");
7259 this.CSS.updateRule(thSelector, "display", "none");
7260 this.CSS.updateRule(tdSelector, "display", "none");
7263 this.onHeaderChange();
7267 setColumnWidth: function(col_index, width)
7269 // width = "md-2 xs-2..."
7270 if(!this.colModel.config[col_index]) {
7274 var w = width.split(" ");
7276 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7278 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7281 for(var j = 0; j < w.length; j++) {
7287 var size_cls = w[j].split("-");
7289 if(!Number.isInteger(size_cls[1] * 1)) {
7293 if(!this.colModel.config[col_index][size_cls[0]]) {
7297 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7301 h_row[0].classList.replace(
7302 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7303 "col-"+size_cls[0]+"-"+size_cls[1]
7306 for(var i = 0; i < rows.length; i++) {
7308 var size_cls = w[j].split("-");
7310 if(!Number.isInteger(size_cls[1] * 1)) {
7314 if(!this.colModel.config[col_index][size_cls[0]]) {
7318 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7322 rows[i].classList.replace(
7323 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7324 "col-"+size_cls[0]+"-"+size_cls[1]
7328 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7343 * @class Roo.bootstrap.TableCell
7344 * @extends Roo.bootstrap.Component
7345 * Bootstrap TableCell class
7346 * @cfg {String} html cell contain text
7347 * @cfg {String} cls cell class
7348 * @cfg {String} tag cell tag (td|th) default td
7349 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7350 * @cfg {String} align Aligns the content in a cell
7351 * @cfg {String} axis Categorizes cells
7352 * @cfg {String} bgcolor Specifies the background color of a cell
7353 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7354 * @cfg {Number} colspan Specifies the number of columns a cell should span
7355 * @cfg {String} headers Specifies one or more header cells a cell is related to
7356 * @cfg {Number} height Sets the height of a cell
7357 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7358 * @cfg {Number} rowspan Sets the number of rows a cell should span
7359 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7360 * @cfg {String} valign Vertical aligns the content in a cell
7361 * @cfg {Number} width Specifies the width of a cell
7364 * Create a new TableCell
7365 * @param {Object} config The config object
7368 Roo.bootstrap.TableCell = function(config){
7369 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7372 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7392 getAutoCreate : function(){
7393 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7413 cfg.align=this.align
7419 cfg.bgcolor=this.bgcolor
7422 cfg.charoff=this.charoff
7425 cfg.colspan=this.colspan
7428 cfg.headers=this.headers
7431 cfg.height=this.height
7434 cfg.nowrap=this.nowrap
7437 cfg.rowspan=this.rowspan
7440 cfg.scope=this.scope
7443 cfg.valign=this.valign
7446 cfg.width=this.width
7465 * @class Roo.bootstrap.TableRow
7466 * @extends Roo.bootstrap.Component
7467 * Bootstrap TableRow class
7468 * @cfg {String} cls row class
7469 * @cfg {String} align Aligns the content in a table row
7470 * @cfg {String} bgcolor Specifies a background color for a table row
7471 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7472 * @cfg {String} valign Vertical aligns the content in a table row
7475 * Create a new TableRow
7476 * @param {Object} config The config object
7479 Roo.bootstrap.TableRow = function(config){
7480 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7483 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7491 getAutoCreate : function(){
7492 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7502 cfg.align = this.align;
7505 cfg.bgcolor = this.bgcolor;
7508 cfg.charoff = this.charoff;
7511 cfg.valign = this.valign;
7529 * @class Roo.bootstrap.TableBody
7530 * @extends Roo.bootstrap.Component
7531 * Bootstrap TableBody class
7532 * @cfg {String} cls element class
7533 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7534 * @cfg {String} align Aligns the content inside the element
7535 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7536 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7539 * Create a new TableBody
7540 * @param {Object} config The config object
7543 Roo.bootstrap.TableBody = function(config){
7544 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7547 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7555 getAutoCreate : function(){
7556 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7570 cfg.align = this.align;
7573 cfg.charoff = this.charoff;
7576 cfg.valign = this.valign;
7583 // initEvents : function()
7590 // this.store = Roo.factory(this.store, Roo.data);
7591 // this.store.on('load', this.onLoad, this);
7593 // this.store.load();
7597 // onLoad: function ()
7599 // this.fireEvent('load', this);
7609 * Ext JS Library 1.1.1
7610 * Copyright(c) 2006-2007, Ext JS, LLC.
7612 * Originally Released Under LGPL - original licence link has changed is not relivant.
7615 * <script type="text/javascript">
7618 // as we use this in bootstrap.
7619 Roo.namespace('Roo.form');
7621 * @class Roo.form.Action
7622 * Internal Class used to handle form actions
7624 * @param {Roo.form.BasicForm} el The form element or its id
7625 * @param {Object} config Configuration options
7630 // define the action interface
7631 Roo.form.Action = function(form, options){
7633 this.options = options || {};
7636 * Client Validation Failed
7639 Roo.form.Action.CLIENT_INVALID = 'client';
7641 * Server Validation Failed
7644 Roo.form.Action.SERVER_INVALID = 'server';
7646 * Connect to Server Failed
7649 Roo.form.Action.CONNECT_FAILURE = 'connect';
7651 * Reading Data from Server Failed
7654 Roo.form.Action.LOAD_FAILURE = 'load';
7656 Roo.form.Action.prototype = {
7658 failureType : undefined,
7659 response : undefined,
7663 run : function(options){
7668 success : function(response){
7673 handleResponse : function(response){
7677 // default connection failure
7678 failure : function(response){
7680 this.response = response;
7681 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7682 this.form.afterAction(this, false);
7685 processResponse : function(response){
7686 this.response = response;
7687 if(!response.responseText){
7690 this.result = this.handleResponse(response);
7694 // utility functions used internally
7695 getUrl : function(appendParams){
7696 var url = this.options.url || this.form.url || this.form.el.dom.action;
7698 var p = this.getParams();
7700 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7706 getMethod : function(){
7707 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7710 getParams : function(){
7711 var bp = this.form.baseParams;
7712 var p = this.options.params;
7714 if(typeof p == "object"){
7715 p = Roo.urlEncode(Roo.applyIf(p, bp));
7716 }else if(typeof p == 'string' && bp){
7717 p += '&' + Roo.urlEncode(bp);
7720 p = Roo.urlEncode(bp);
7725 createCallback : function(){
7727 success: this.success,
7728 failure: this.failure,
7730 timeout: (this.form.timeout*1000),
7731 upload: this.form.fileUpload ? this.success : undefined
7736 Roo.form.Action.Submit = function(form, options){
7737 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7740 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7743 haveProgress : false,
7744 uploadComplete : false,
7746 // uploadProgress indicator.
7747 uploadProgress : function()
7749 if (!this.form.progressUrl) {
7753 if (!this.haveProgress) {
7754 Roo.MessageBox.progress("Uploading", "Uploading");
7756 if (this.uploadComplete) {
7757 Roo.MessageBox.hide();
7761 this.haveProgress = true;
7763 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7765 var c = new Roo.data.Connection();
7767 url : this.form.progressUrl,
7772 success : function(req){
7773 //console.log(data);
7777 rdata = Roo.decode(req.responseText)
7779 Roo.log("Invalid data from server..");
7783 if (!rdata || !rdata.success) {
7785 Roo.MessageBox.alert(Roo.encode(rdata));
7788 var data = rdata.data;
7790 if (this.uploadComplete) {
7791 Roo.MessageBox.hide();
7796 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7797 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7800 this.uploadProgress.defer(2000,this);
7803 failure: function(data) {
7804 Roo.log('progress url failed ');
7815 // run get Values on the form, so it syncs any secondary forms.
7816 this.form.getValues();
7818 var o = this.options;
7819 var method = this.getMethod();
7820 var isPost = method == 'POST';
7821 if(o.clientValidation === false || this.form.isValid()){
7823 if (this.form.progressUrl) {
7824 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7825 (new Date() * 1) + '' + Math.random());
7830 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7831 form:this.form.el.dom,
7832 url:this.getUrl(!isPost),
7834 params:isPost ? this.getParams() : null,
7835 isUpload: this.form.fileUpload
7838 this.uploadProgress();
7840 }else if (o.clientValidation !== false){ // client validation failed
7841 this.failureType = Roo.form.Action.CLIENT_INVALID;
7842 this.form.afterAction(this, false);
7846 success : function(response)
7848 this.uploadComplete= true;
7849 if (this.haveProgress) {
7850 Roo.MessageBox.hide();
7854 var result = this.processResponse(response);
7855 if(result === true || result.success){
7856 this.form.afterAction(this, true);
7860 this.form.markInvalid(result.errors);
7861 this.failureType = Roo.form.Action.SERVER_INVALID;
7863 this.form.afterAction(this, false);
7865 failure : function(response)
7867 this.uploadComplete= true;
7868 if (this.haveProgress) {
7869 Roo.MessageBox.hide();
7872 this.response = response;
7873 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7874 this.form.afterAction(this, false);
7877 handleResponse : function(response){
7878 if(this.form.errorReader){
7879 var rs = this.form.errorReader.read(response);
7882 for(var i = 0, len = rs.records.length; i < len; i++) {
7883 var r = rs.records[i];
7887 if(errors.length < 1){
7891 success : rs.success,
7897 ret = Roo.decode(response.responseText);
7901 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7911 Roo.form.Action.Load = function(form, options){
7912 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7913 this.reader = this.form.reader;
7916 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7921 Roo.Ajax.request(Roo.apply(
7922 this.createCallback(), {
7923 method:this.getMethod(),
7924 url:this.getUrl(false),
7925 params:this.getParams()
7929 success : function(response){
7931 var result = this.processResponse(response);
7932 if(result === true || !result.success || !result.data){
7933 this.failureType = Roo.form.Action.LOAD_FAILURE;
7934 this.form.afterAction(this, false);
7937 this.form.clearInvalid();
7938 this.form.setValues(result.data);
7939 this.form.afterAction(this, true);
7942 handleResponse : function(response){
7943 if(this.form.reader){
7944 var rs = this.form.reader.read(response);
7945 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7947 success : rs.success,
7951 return Roo.decode(response.responseText);
7955 Roo.form.Action.ACTION_TYPES = {
7956 'load' : Roo.form.Action.Load,
7957 'submit' : Roo.form.Action.Submit
7966 * @class Roo.bootstrap.Form
7967 * @extends Roo.bootstrap.Component
7968 * Bootstrap Form class
7969 * @cfg {String} method GET | POST (default POST)
7970 * @cfg {String} labelAlign top | left (default top)
7971 * @cfg {String} align left | right - for navbars
7972 * @cfg {Boolean} loadMask load mask when submit (default true)
7977 * @param {Object} config The config object
7981 Roo.bootstrap.Form = function(config){
7983 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7985 Roo.bootstrap.Form.popover.apply();
7989 * @event clientvalidation
7990 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7991 * @param {Form} this
7992 * @param {Boolean} valid true if the form has passed client-side validation
7994 clientvalidation: true,
7996 * @event beforeaction
7997 * Fires before any action is performed. Return false to cancel the action.
7998 * @param {Form} this
7999 * @param {Action} action The action to be performed
8003 * @event actionfailed
8004 * Fires when an action fails.
8005 * @param {Form} this
8006 * @param {Action} action The action that failed
8008 actionfailed : true,
8010 * @event actioncomplete
8011 * Fires when an action is completed.
8012 * @param {Form} this
8013 * @param {Action} action The action that completed
8015 actioncomplete : true
8019 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8022 * @cfg {String} method
8023 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8028 * The URL to use for form actions if one isn't supplied in the action options.
8031 * @cfg {Boolean} fileUpload
8032 * Set to true if this form is a file upload.
8036 * @cfg {Object} baseParams
8037 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8041 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8045 * @cfg {Sting} align (left|right) for navbar forms
8050 activeAction : null,
8053 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8054 * element by passing it or its id or mask the form itself by passing in true.
8057 waitMsgTarget : false,
8062 * @cfg {Boolean} errorMask (true|false) default false
8067 * @cfg {Number} maskOffset Default 100
8072 * @cfg {Boolean} maskBody
8076 getAutoCreate : function(){
8080 method : this.method || 'POST',
8081 id : this.id || Roo.id(),
8084 if (this.parent().xtype.match(/^Nav/)) {
8085 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8089 if (this.labelAlign == 'left' ) {
8090 cfg.cls += ' form-horizontal';
8096 initEvents : function()
8098 this.el.on('submit', this.onSubmit, this);
8099 // this was added as random key presses on the form where triggering form submit.
8100 this.el.on('keypress', function(e) {
8101 if (e.getCharCode() != 13) {
8104 // we might need to allow it for textareas.. and some other items.
8105 // check e.getTarget().
8107 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8111 Roo.log("keypress blocked");
8119 onSubmit : function(e){
8124 * Returns true if client-side validation on the form is successful.
8127 isValid : function(){
8128 var items = this.getItems();
8132 items.each(function(f){
8138 Roo.log('invalid field: ' + f.name);
8142 if(!target && f.el.isVisible(true)){
8148 if(this.errorMask && !valid){
8149 Roo.bootstrap.Form.popover.mask(this, target);
8156 * Returns true if any fields in this form have changed since their original load.
8159 isDirty : function(){
8161 var items = this.getItems();
8162 items.each(function(f){
8172 * Performs a predefined action (submit or load) or custom actions you define on this form.
8173 * @param {String} actionName The name of the action type
8174 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8175 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8176 * accept other config options):
8178 Property Type Description
8179 ---------------- --------------- ----------------------------------------------------------------------------------
8180 url String The url for the action (defaults to the form's url)
8181 method String The form method to use (defaults to the form's method, or POST if not defined)
8182 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8183 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8184 validate the form on the client (defaults to false)
8186 * @return {BasicForm} this
8188 doAction : function(action, options){
8189 if(typeof action == 'string'){
8190 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8192 if(this.fireEvent('beforeaction', this, action) !== false){
8193 this.beforeAction(action);
8194 action.run.defer(100, action);
8200 beforeAction : function(action){
8201 var o = action.options;
8206 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8208 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8211 // not really supported yet.. ??
8213 //if(this.waitMsgTarget === true){
8214 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8215 //}else if(this.waitMsgTarget){
8216 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8217 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8219 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8225 afterAction : function(action, success){
8226 this.activeAction = null;
8227 var o = action.options;
8232 Roo.get(document.body).unmask();
8238 //if(this.waitMsgTarget === true){
8239 // this.el.unmask();
8240 //}else if(this.waitMsgTarget){
8241 // this.waitMsgTarget.unmask();
8243 // Roo.MessageBox.updateProgress(1);
8244 // Roo.MessageBox.hide();
8251 Roo.callback(o.success, o.scope, [this, action]);
8252 this.fireEvent('actioncomplete', this, action);
8256 // failure condition..
8257 // we have a scenario where updates need confirming.
8258 // eg. if a locking scenario exists..
8259 // we look for { errors : { needs_confirm : true }} in the response.
8261 (typeof(action.result) != 'undefined') &&
8262 (typeof(action.result.errors) != 'undefined') &&
8263 (typeof(action.result.errors.needs_confirm) != 'undefined')
8266 Roo.log("not supported yet");
8269 Roo.MessageBox.confirm(
8270 "Change requires confirmation",
8271 action.result.errorMsg,
8276 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8286 Roo.callback(o.failure, o.scope, [this, action]);
8287 // show an error message if no failed handler is set..
8288 if (!this.hasListener('actionfailed')) {
8289 Roo.log("need to add dialog support");
8291 Roo.MessageBox.alert("Error",
8292 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8293 action.result.errorMsg :
8294 "Saving Failed, please check your entries or try again"
8299 this.fireEvent('actionfailed', this, action);
8304 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8305 * @param {String} id The value to search for
8308 findField : function(id){
8309 var items = this.getItems();
8310 var field = items.get(id);
8312 items.each(function(f){
8313 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8320 return field || null;
8323 * Mark fields in this form invalid in bulk.
8324 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8325 * @return {BasicForm} this
8327 markInvalid : function(errors){
8328 if(errors instanceof Array){
8329 for(var i = 0, len = errors.length; i < len; i++){
8330 var fieldError = errors[i];
8331 var f = this.findField(fieldError.id);
8333 f.markInvalid(fieldError.msg);
8339 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8340 field.markInvalid(errors[id]);
8344 //Roo.each(this.childForms || [], function (f) {
8345 // f.markInvalid(errors);
8352 * Set values for fields in this form in bulk.
8353 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8354 * @return {BasicForm} this
8356 setValues : function(values){
8357 if(values instanceof Array){ // array of objects
8358 for(var i = 0, len = values.length; i < len; i++){
8360 var f = this.findField(v.id);
8362 f.setValue(v.value);
8363 if(this.trackResetOnLoad){
8364 f.originalValue = f.getValue();
8368 }else{ // object hash
8371 if(typeof values[id] != 'function' && (field = this.findField(id))){
8373 if (field.setFromData &&
8375 field.displayField &&
8376 // combos' with local stores can
8377 // be queried via setValue()
8378 // to set their value..
8379 (field.store && !field.store.isLocal)
8383 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8384 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8385 field.setFromData(sd);
8387 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8389 field.setFromData(values);
8392 field.setValue(values[id]);
8396 if(this.trackResetOnLoad){
8397 field.originalValue = field.getValue();
8403 //Roo.each(this.childForms || [], function (f) {
8404 // f.setValues(values);
8411 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8412 * they are returned as an array.
8413 * @param {Boolean} asString
8416 getValues : function(asString){
8417 //if (this.childForms) {
8418 // copy values from the child forms
8419 // Roo.each(this.childForms, function (f) {
8420 // this.setValues(f.getValues());
8426 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8427 if(asString === true){
8430 return Roo.urlDecode(fs);
8434 * Returns the fields in this form as an object with key/value pairs.
8435 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8438 getFieldValues : function(with_hidden)
8440 var items = this.getItems();
8442 items.each(function(f){
8448 var v = f.getValue();
8450 if (f.inputType =='radio') {
8451 if (typeof(ret[f.getName()]) == 'undefined') {
8452 ret[f.getName()] = ''; // empty..
8455 if (!f.el.dom.checked) {
8463 if(f.xtype == 'MoneyField'){
8464 ret[f.currencyName] = f.getCurrency();
8467 // not sure if this supported any more..
8468 if ((typeof(v) == 'object') && f.getRawValue) {
8469 v = f.getRawValue() ; // dates..
8471 // combo boxes where name != hiddenName...
8472 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8473 ret[f.name] = f.getRawValue();
8475 ret[f.getName()] = v;
8482 * Clears all invalid messages in this form.
8483 * @return {BasicForm} this
8485 clearInvalid : function(){
8486 var items = this.getItems();
8488 items.each(function(f){
8497 * @return {BasicForm} this
8500 var items = this.getItems();
8501 items.each(function(f){
8505 Roo.each(this.childForms || [], function (f) {
8513 getItems : function()
8515 var r=new Roo.util.MixedCollection(false, function(o){
8516 return o.id || (o.id = Roo.id());
8518 var iter = function(el) {
8525 Roo.each(el.items,function(e) {
8534 hideFields : function(items)
8536 Roo.each(items, function(i){
8538 var f = this.findField(i);
8549 showFields : function(items)
8551 Roo.each(items, function(i){
8553 var f = this.findField(i);
8566 Roo.apply(Roo.bootstrap.Form, {
8593 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8594 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8595 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8596 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8599 this.maskEl.top.enableDisplayMode("block");
8600 this.maskEl.left.enableDisplayMode("block");
8601 this.maskEl.bottom.enableDisplayMode("block");
8602 this.maskEl.right.enableDisplayMode("block");
8604 this.toolTip = new Roo.bootstrap.Tooltip({
8605 cls : 'roo-form-error-popover',
8607 'left' : ['r-l', [-2,0], 'right'],
8608 'right' : ['l-r', [2,0], 'left'],
8609 'bottom' : ['tl-bl', [0,2], 'top'],
8610 'top' : [ 'bl-tl', [0,-2], 'bottom']
8614 this.toolTip.render(Roo.get(document.body));
8616 this.toolTip.el.enableDisplayMode("block");
8618 Roo.get(document.body).on('click', function(){
8622 Roo.get(document.body).on('touchstart', function(){
8626 this.isApplied = true
8629 mask : function(form, target)
8633 this.target = target;
8635 if(!this.form.errorMask || !target.el){
8639 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8641 Roo.log(scrollable);
8643 var ot = this.target.el.calcOffsetsTo(scrollable);
8645 var scrollTo = ot[1] - this.form.maskOffset;
8647 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8649 scrollable.scrollTo('top', scrollTo);
8651 var box = this.target.el.getBox();
8653 var zIndex = Roo.bootstrap.Modal.zIndex++;
8656 this.maskEl.top.setStyle('position', 'absolute');
8657 this.maskEl.top.setStyle('z-index', zIndex);
8658 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8659 this.maskEl.top.setLeft(0);
8660 this.maskEl.top.setTop(0);
8661 this.maskEl.top.show();
8663 this.maskEl.left.setStyle('position', 'absolute');
8664 this.maskEl.left.setStyle('z-index', zIndex);
8665 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8666 this.maskEl.left.setLeft(0);
8667 this.maskEl.left.setTop(box.y - this.padding);
8668 this.maskEl.left.show();
8670 this.maskEl.bottom.setStyle('position', 'absolute');
8671 this.maskEl.bottom.setStyle('z-index', zIndex);
8672 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8673 this.maskEl.bottom.setLeft(0);
8674 this.maskEl.bottom.setTop(box.bottom + this.padding);
8675 this.maskEl.bottom.show();
8677 this.maskEl.right.setStyle('position', 'absolute');
8678 this.maskEl.right.setStyle('z-index', zIndex);
8679 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8680 this.maskEl.right.setLeft(box.right + this.padding);
8681 this.maskEl.right.setTop(box.y - this.padding);
8682 this.maskEl.right.show();
8684 this.toolTip.bindEl = this.target.el;
8686 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8688 var tip = this.target.blankText;
8690 if(this.target.getValue() !== '' ) {
8692 if (this.target.invalidText.length) {
8693 tip = this.target.invalidText;
8694 } else if (this.target.regexText.length){
8695 tip = this.target.regexText;
8699 this.toolTip.show(tip);
8701 this.intervalID = window.setInterval(function() {
8702 Roo.bootstrap.Form.popover.unmask();
8705 window.onwheel = function(){ return false;};
8707 (function(){ this.isMasked = true; }).defer(500, this);
8713 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8717 this.maskEl.top.setStyle('position', 'absolute');
8718 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8719 this.maskEl.top.hide();
8721 this.maskEl.left.setStyle('position', 'absolute');
8722 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8723 this.maskEl.left.hide();
8725 this.maskEl.bottom.setStyle('position', 'absolute');
8726 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8727 this.maskEl.bottom.hide();
8729 this.maskEl.right.setStyle('position', 'absolute');
8730 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8731 this.maskEl.right.hide();
8733 this.toolTip.hide();
8735 this.toolTip.el.hide();
8737 window.onwheel = function(){ return true;};
8739 if(this.intervalID){
8740 window.clearInterval(this.intervalID);
8741 this.intervalID = false;
8744 this.isMasked = false;
8754 * Ext JS Library 1.1.1
8755 * Copyright(c) 2006-2007, Ext JS, LLC.
8757 * Originally Released Under LGPL - original licence link has changed is not relivant.
8760 * <script type="text/javascript">
8763 * @class Roo.form.VTypes
8764 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8767 Roo.form.VTypes = function(){
8768 // closure these in so they are only created once.
8769 var alpha = /^[a-zA-Z_]+$/;
8770 var alphanum = /^[a-zA-Z0-9_]+$/;
8771 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8772 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8774 // All these messages and functions are configurable
8777 * The function used to validate email addresses
8778 * @param {String} value The email address
8780 'email' : function(v){
8781 return email.test(v);
8784 * The error text to display when the email validation function returns false
8787 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8789 * The keystroke filter mask to be applied on email input
8792 'emailMask' : /[a-z0-9_\.\-@]/i,
8795 * The function used to validate URLs
8796 * @param {String} value The URL
8798 'url' : function(v){
8802 * The error text to display when the url validation function returns false
8805 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8808 * The function used to validate alpha values
8809 * @param {String} value The value
8811 'alpha' : function(v){
8812 return alpha.test(v);
8815 * The error text to display when the alpha validation function returns false
8818 'alphaText' : 'This field should only contain letters and _',
8820 * The keystroke filter mask to be applied on alpha input
8823 'alphaMask' : /[a-z_]/i,
8826 * The function used to validate alphanumeric values
8827 * @param {String} value The value
8829 'alphanum' : function(v){
8830 return alphanum.test(v);
8833 * The error text to display when the alphanumeric validation function returns false
8836 'alphanumText' : 'This field should only contain letters, numbers and _',
8838 * The keystroke filter mask to be applied on alphanumeric input
8841 'alphanumMask' : /[a-z0-9_]/i
8851 * @class Roo.bootstrap.Input
8852 * @extends Roo.bootstrap.Component
8853 * Bootstrap Input class
8854 * @cfg {Boolean} disabled is it disabled
8855 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8856 * @cfg {String} name name of the input
8857 * @cfg {string} fieldLabel - the label associated
8858 * @cfg {string} placeholder - placeholder to put in text.
8859 * @cfg {string} before - input group add on before
8860 * @cfg {string} after - input group add on after
8861 * @cfg {string} size - (lg|sm) or leave empty..
8862 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8863 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8864 * @cfg {Number} md colspan out of 12 for computer-sized screens
8865 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8866 * @cfg {string} value default value of the input
8867 * @cfg {Number} labelWidth set the width of label
8868 * @cfg {Number} labellg set the width of label (1-12)
8869 * @cfg {Number} labelmd set the width of label (1-12)
8870 * @cfg {Number} labelsm set the width of label (1-12)
8871 * @cfg {Number} labelxs set the width of label (1-12)
8872 * @cfg {String} labelAlign (top|left)
8873 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8874 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8875 * @cfg {String} indicatorpos (left|right) default left
8876 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8877 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8879 * @cfg {String} align (left|center|right) Default left
8880 * @cfg {Boolean} forceFeedback (true|false) Default false
8883 * Create a new Input
8884 * @param {Object} config The config object
8887 Roo.bootstrap.Input = function(config){
8889 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8894 * Fires when this field receives input focus.
8895 * @param {Roo.form.Field} this
8900 * Fires when this field loses input focus.
8901 * @param {Roo.form.Field} this
8906 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8907 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8908 * @param {Roo.form.Field} this
8909 * @param {Roo.EventObject} e The event object
8914 * Fires just before the field blurs if the field value has changed.
8915 * @param {Roo.form.Field} this
8916 * @param {Mixed} newValue The new value
8917 * @param {Mixed} oldValue The original value
8922 * Fires after the field has been marked as invalid.
8923 * @param {Roo.form.Field} this
8924 * @param {String} msg The validation message
8929 * Fires after the field has been validated with no errors.
8930 * @param {Roo.form.Field} this
8935 * Fires after the key up
8936 * @param {Roo.form.Field} this
8937 * @param {Roo.EventObject} e The event Object
8943 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8945 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8946 automatic validation (defaults to "keyup").
8948 validationEvent : "keyup",
8950 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8952 validateOnBlur : true,
8954 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8956 validationDelay : 250,
8958 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8960 focusClass : "x-form-focus", // not needed???
8964 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8966 invalidClass : "has-warning",
8969 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8971 validClass : "has-success",
8974 * @cfg {Boolean} hasFeedback (true|false) default true
8979 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8981 invalidFeedbackClass : "glyphicon-warning-sign",
8984 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8986 validFeedbackClass : "glyphicon-ok",
8989 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8991 selectOnFocus : false,
8994 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8998 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9003 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9005 disableKeyFilter : false,
9008 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9012 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9016 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9018 blankText : "Please complete this mandatory field",
9021 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9025 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9027 maxLength : Number.MAX_VALUE,
9029 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9031 minLengthText : "The minimum length for this field is {0}",
9033 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9035 maxLengthText : "The maximum length for this field is {0}",
9039 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9040 * If available, this function will be called only after the basic validators all return true, and will be passed the
9041 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9045 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9046 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9047 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9051 * @cfg {String} regexText -- Depricated - use Invalid Text
9056 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9062 autocomplete: false,
9081 formatedValue : false,
9082 forceFeedback : false,
9084 indicatorpos : 'left',
9094 parentLabelAlign : function()
9097 while (parent.parent()) {
9098 parent = parent.parent();
9099 if (typeof(parent.labelAlign) !='undefined') {
9100 return parent.labelAlign;
9107 getAutoCreate : function()
9109 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9115 if(this.inputType != 'hidden'){
9116 cfg.cls = 'form-group' //input-group
9122 type : this.inputType,
9124 cls : 'form-control',
9125 placeholder : this.placeholder || '',
9126 autocomplete : this.autocomplete || 'new-password'
9129 if(this.capture.length){
9130 input.capture = this.capture;
9133 if(this.accept.length){
9134 input.accept = this.accept + "/*";
9138 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9141 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9142 input.maxLength = this.maxLength;
9145 if (this.disabled) {
9146 input.disabled=true;
9149 if (this.readOnly) {
9150 input.readonly=true;
9154 input.name = this.name;
9158 input.cls += ' input-' + this.size;
9162 ['xs','sm','md','lg'].map(function(size){
9163 if (settings[size]) {
9164 cfg.cls += ' col-' + size + '-' + settings[size];
9168 var inputblock = input;
9172 cls: 'glyphicon form-control-feedback'
9175 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9178 cls : 'has-feedback',
9186 if (this.before || this.after) {
9189 cls : 'input-group',
9193 if (this.before && typeof(this.before) == 'string') {
9195 inputblock.cn.push({
9197 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9201 if (this.before && typeof(this.before) == 'object') {
9202 this.before = Roo.factory(this.before);
9204 inputblock.cn.push({
9206 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9207 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9211 inputblock.cn.push(input);
9213 if (this.after && typeof(this.after) == 'string') {
9214 inputblock.cn.push({
9216 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9220 if (this.after && typeof(this.after) == 'object') {
9221 this.after = Roo.factory(this.after);
9223 inputblock.cn.push({
9225 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9226 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9230 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9231 inputblock.cls += ' has-feedback';
9232 inputblock.cn.push(feedback);
9237 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9238 tooltip : 'This field is required'
9240 if (Roo.bootstrap.version == 4) {
9243 style : 'display-none'
9246 if (align ==='left' && this.fieldLabel.length) {
9248 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9255 cls : 'control-label col-form-label',
9256 html : this.fieldLabel
9267 var labelCfg = cfg.cn[1];
9268 var contentCfg = cfg.cn[2];
9270 if(this.indicatorpos == 'right'){
9275 cls : 'control-label col-form-label',
9279 html : this.fieldLabel
9293 labelCfg = cfg.cn[0];
9294 contentCfg = cfg.cn[1];
9298 if(this.labelWidth > 12){
9299 labelCfg.style = "width: " + this.labelWidth + 'px';
9302 if(this.labelWidth < 13 && this.labelmd == 0){
9303 this.labelmd = this.labelWidth;
9306 if(this.labellg > 0){
9307 labelCfg.cls += ' col-lg-' + this.labellg;
9308 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9311 if(this.labelmd > 0){
9312 labelCfg.cls += ' col-md-' + this.labelmd;
9313 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9316 if(this.labelsm > 0){
9317 labelCfg.cls += ' col-sm-' + this.labelsm;
9318 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9321 if(this.labelxs > 0){
9322 labelCfg.cls += ' col-xs-' + this.labelxs;
9323 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9327 } else if ( this.fieldLabel.length) {
9332 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9333 tooltip : 'This field is required'
9337 //cls : 'input-group-addon',
9338 html : this.fieldLabel
9346 if(this.indicatorpos == 'right'){
9351 //cls : 'input-group-addon',
9352 html : this.fieldLabel
9357 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9358 tooltip : 'This field is required'
9378 if (this.parentType === 'Navbar' && this.parent().bar) {
9379 cfg.cls += ' navbar-form';
9382 if (this.parentType === 'NavGroup') {
9383 cfg.cls += ' navbar-form';
9391 * return the real input element.
9393 inputEl: function ()
9395 return this.el.select('input.form-control',true).first();
9398 tooltipEl : function()
9400 return this.inputEl();
9403 indicatorEl : function()
9405 if (Roo.bootstrap.version == 4) {
9406 return false; // not enabled in v4 yet.
9409 var indicator = this.el.select('i.roo-required-indicator',true).first();
9419 setDisabled : function(v)
9421 var i = this.inputEl().dom;
9423 i.removeAttribute('disabled');
9427 i.setAttribute('disabled','true');
9429 initEvents : function()
9432 this.inputEl().on("keydown" , this.fireKey, this);
9433 this.inputEl().on("focus", this.onFocus, this);
9434 this.inputEl().on("blur", this.onBlur, this);
9436 this.inputEl().relayEvent('keyup', this);
9438 this.indicator = this.indicatorEl();
9441 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9444 // reference to original value for reset
9445 this.originalValue = this.getValue();
9446 //Roo.form.TextField.superclass.initEvents.call(this);
9447 if(this.validationEvent == 'keyup'){
9448 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9449 this.inputEl().on('keyup', this.filterValidation, this);
9451 else if(this.validationEvent !== false){
9452 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9455 if(this.selectOnFocus){
9456 this.on("focus", this.preFocus, this);
9459 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9460 this.inputEl().on("keypress", this.filterKeys, this);
9462 this.inputEl().relayEvent('keypress', this);
9465 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9466 this.el.on("click", this.autoSize, this);
9469 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9470 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9473 if (typeof(this.before) == 'object') {
9474 this.before.render(this.el.select('.roo-input-before',true).first());
9476 if (typeof(this.after) == 'object') {
9477 this.after.render(this.el.select('.roo-input-after',true).first());
9480 this.inputEl().on('change', this.onChange, this);
9483 filterValidation : function(e){
9484 if(!e.isNavKeyPress()){
9485 this.validationTask.delay(this.validationDelay);
9489 * Validates the field value
9490 * @return {Boolean} True if the value is valid, else false
9492 validate : function(){
9493 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9494 if(this.disabled || this.validateValue(this.getRawValue())){
9505 * Validates a value according to the field's validation rules and marks the field as invalid
9506 * if the validation fails
9507 * @param {Mixed} value The value to validate
9508 * @return {Boolean} True if the value is valid, else false
9510 validateValue : function(value)
9512 if(this.getVisibilityEl().hasClass('hidden')){
9516 if(value.length < 1) { // if it's blank
9517 if(this.allowBlank){
9523 if(value.length < this.minLength){
9526 if(value.length > this.maxLength){
9530 var vt = Roo.form.VTypes;
9531 if(!vt[this.vtype](value, this)){
9535 if(typeof this.validator == "function"){
9536 var msg = this.validator(value);
9540 if (typeof(msg) == 'string') {
9541 this.invalidText = msg;
9545 if(this.regex && !this.regex.test(value)){
9553 fireKey : function(e){
9554 //Roo.log('field ' + e.getKey());
9555 if(e.isNavKeyPress()){
9556 this.fireEvent("specialkey", this, e);
9559 focus : function (selectText){
9561 this.inputEl().focus();
9562 if(selectText === true){
9563 this.inputEl().dom.select();
9569 onFocus : function(){
9570 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9571 // this.el.addClass(this.focusClass);
9574 this.hasFocus = true;
9575 this.startValue = this.getValue();
9576 this.fireEvent("focus", this);
9580 beforeBlur : Roo.emptyFn,
9584 onBlur : function(){
9586 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9587 //this.el.removeClass(this.focusClass);
9589 this.hasFocus = false;
9590 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9593 var v = this.getValue();
9594 if(String(v) !== String(this.startValue)){
9595 this.fireEvent('change', this, v, this.startValue);
9597 this.fireEvent("blur", this);
9600 onChange : function(e)
9602 var v = this.getValue();
9603 if(String(v) !== String(this.startValue)){
9604 this.fireEvent('change', this, v, this.startValue);
9610 * Resets the current field value to the originally loaded value and clears any validation messages
9613 this.setValue(this.originalValue);
9617 * Returns the name of the field
9618 * @return {Mixed} name The name field
9620 getName: function(){
9624 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9625 * @return {Mixed} value The field value
9627 getValue : function(){
9629 var v = this.inputEl().getValue();
9634 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9635 * @return {Mixed} value The field value
9637 getRawValue : function(){
9638 var v = this.inputEl().getValue();
9644 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9645 * @param {Mixed} value The value to set
9647 setRawValue : function(v){
9648 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9651 selectText : function(start, end){
9652 var v = this.getRawValue();
9654 start = start === undefined ? 0 : start;
9655 end = end === undefined ? v.length : end;
9656 var d = this.inputEl().dom;
9657 if(d.setSelectionRange){
9658 d.setSelectionRange(start, end);
9659 }else if(d.createTextRange){
9660 var range = d.createTextRange();
9661 range.moveStart("character", start);
9662 range.moveEnd("character", v.length-end);
9669 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9670 * @param {Mixed} value The value to set
9672 setValue : function(v){
9675 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9681 processValue : function(value){
9682 if(this.stripCharsRe){
9683 var newValue = value.replace(this.stripCharsRe, '');
9684 if(newValue !== value){
9685 this.setRawValue(newValue);
9692 preFocus : function(){
9694 if(this.selectOnFocus){
9695 this.inputEl().dom.select();
9698 filterKeys : function(e){
9700 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9703 var c = e.getCharCode(), cc = String.fromCharCode(c);
9704 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9707 if(!this.maskRe.test(cc)){
9712 * Clear any invalid styles/messages for this field
9714 clearInvalid : function(){
9716 if(!this.el || this.preventMark){ // not rendered
9721 this.el.removeClass(this.invalidClass);
9723 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9725 var feedback = this.el.select('.form-control-feedback', true).first();
9728 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9734 this.indicator.removeClass('visible');
9735 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9738 this.fireEvent('valid', this);
9742 * Mark this field as valid
9744 markValid : function()
9746 if(!this.el || this.preventMark){ // not rendered...
9750 this.el.removeClass([this.invalidClass, this.validClass]);
9752 var feedback = this.el.select('.form-control-feedback', true).first();
9755 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9759 this.indicator.removeClass('visible');
9760 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9767 if(this.allowBlank && !this.getRawValue().length){
9771 this.el.addClass(this.validClass);
9773 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9775 var feedback = this.el.select('.form-control-feedback', true).first();
9778 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9779 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9784 this.fireEvent('valid', this);
9788 * Mark this field as invalid
9789 * @param {String} msg The validation message
9791 markInvalid : function(msg)
9793 if(!this.el || this.preventMark){ // not rendered
9797 this.el.removeClass([this.invalidClass, this.validClass]);
9799 var feedback = this.el.select('.form-control-feedback', true).first();
9802 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9809 if(this.allowBlank && !this.getRawValue().length){
9814 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9815 this.indicator.addClass('visible');
9818 this.el.addClass(this.invalidClass);
9820 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9822 var feedback = this.el.select('.form-control-feedback', true).first();
9825 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9827 if(this.getValue().length || this.forceFeedback){
9828 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9835 this.fireEvent('invalid', this, msg);
9838 SafariOnKeyDown : function(event)
9840 // this is a workaround for a password hang bug on chrome/ webkit.
9841 if (this.inputEl().dom.type != 'password') {
9845 var isSelectAll = false;
9847 if(this.inputEl().dom.selectionEnd > 0){
9848 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9850 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9851 event.preventDefault();
9856 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9858 event.preventDefault();
9859 // this is very hacky as keydown always get's upper case.
9861 var cc = String.fromCharCode(event.getCharCode());
9862 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9866 adjustWidth : function(tag, w){
9867 tag = tag.toLowerCase();
9868 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9869 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9873 if(tag == 'textarea'){
9876 }else if(Roo.isOpera){
9880 if(tag == 'textarea'){
9888 setFieldLabel : function(v)
9894 if(this.indicatorEl()){
9895 var ar = this.el.select('label > span',true);
9897 if (ar.elements.length) {
9898 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9899 this.fieldLabel = v;
9903 var br = this.el.select('label',true);
9905 if(br.elements.length) {
9906 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9907 this.fieldLabel = v;
9911 Roo.log('Cannot Found any of label > span || label in input');
9915 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9916 this.fieldLabel = v;
9931 * @class Roo.bootstrap.TextArea
9932 * @extends Roo.bootstrap.Input
9933 * Bootstrap TextArea class
9934 * @cfg {Number} cols Specifies the visible width of a text area
9935 * @cfg {Number} rows Specifies the visible number of lines in a text area
9936 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9937 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9938 * @cfg {string} html text
9941 * Create a new TextArea
9942 * @param {Object} config The config object
9945 Roo.bootstrap.TextArea = function(config){
9946 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9950 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9960 getAutoCreate : function(){
9962 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9968 if(this.inputType != 'hidden'){
9969 cfg.cls = 'form-group' //input-group
9977 value : this.value || '',
9978 html: this.html || '',
9979 cls : 'form-control',
9980 placeholder : this.placeholder || ''
9984 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9985 input.maxLength = this.maxLength;
9989 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9993 input.cols = this.cols;
9996 if (this.readOnly) {
9997 input.readonly = true;
10001 input.name = this.name;
10005 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10009 ['xs','sm','md','lg'].map(function(size){
10010 if (settings[size]) {
10011 cfg.cls += ' col-' + size + '-' + settings[size];
10015 var inputblock = input;
10017 if(this.hasFeedback && !this.allowBlank){
10021 cls: 'glyphicon form-control-feedback'
10025 cls : 'has-feedback',
10034 if (this.before || this.after) {
10037 cls : 'input-group',
10041 inputblock.cn.push({
10043 cls : 'input-group-addon',
10048 inputblock.cn.push(input);
10050 if(this.hasFeedback && !this.allowBlank){
10051 inputblock.cls += ' has-feedback';
10052 inputblock.cn.push(feedback);
10056 inputblock.cn.push({
10058 cls : 'input-group-addon',
10065 if (align ==='left' && this.fieldLabel.length) {
10070 cls : 'control-label',
10071 html : this.fieldLabel
10082 if(this.labelWidth > 12){
10083 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10086 if(this.labelWidth < 13 && this.labelmd == 0){
10087 this.labelmd = this.labelWidth;
10090 if(this.labellg > 0){
10091 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10092 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10095 if(this.labelmd > 0){
10096 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10097 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10100 if(this.labelsm > 0){
10101 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10102 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10105 if(this.labelxs > 0){
10106 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10107 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10110 } else if ( this.fieldLabel.length) {
10115 //cls : 'input-group-addon',
10116 html : this.fieldLabel
10134 if (this.disabled) {
10135 input.disabled=true;
10142 * return the real textarea element.
10144 inputEl: function ()
10146 return this.el.select('textarea.form-control',true).first();
10150 * Clear any invalid styles/messages for this field
10152 clearInvalid : function()
10155 if(!this.el || this.preventMark){ // not rendered
10159 var label = this.el.select('label', true).first();
10160 var icon = this.el.select('i.fa-star', true).first();
10166 this.el.removeClass(this.invalidClass);
10168 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10170 var feedback = this.el.select('.form-control-feedback', true).first();
10173 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10178 this.fireEvent('valid', this);
10182 * Mark this field as valid
10184 markValid : function()
10186 if(!this.el || this.preventMark){ // not rendered
10190 this.el.removeClass([this.invalidClass, this.validClass]);
10192 var feedback = this.el.select('.form-control-feedback', true).first();
10195 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10198 if(this.disabled || this.allowBlank){
10202 var label = this.el.select('label', true).first();
10203 var icon = this.el.select('i.fa-star', true).first();
10209 this.el.addClass(this.validClass);
10211 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10213 var feedback = this.el.select('.form-control-feedback', true).first();
10216 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10217 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10222 this.fireEvent('valid', this);
10226 * Mark this field as invalid
10227 * @param {String} msg The validation message
10229 markInvalid : function(msg)
10231 if(!this.el || this.preventMark){ // not rendered
10235 this.el.removeClass([this.invalidClass, this.validClass]);
10237 var feedback = this.el.select('.form-control-feedback', true).first();
10240 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10243 if(this.disabled || this.allowBlank){
10247 var label = this.el.select('label', true).first();
10248 var icon = this.el.select('i.fa-star', true).first();
10250 if(!this.getValue().length && label && !icon){
10251 this.el.createChild({
10253 cls : 'text-danger fa fa-lg fa-star',
10254 tooltip : 'This field is required',
10255 style : 'margin-right:5px;'
10259 this.el.addClass(this.invalidClass);
10261 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10263 var feedback = this.el.select('.form-control-feedback', true).first();
10266 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10268 if(this.getValue().length || this.forceFeedback){
10269 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10276 this.fireEvent('invalid', this, msg);
10284 * trigger field - base class for combo..
10289 * @class Roo.bootstrap.TriggerField
10290 * @extends Roo.bootstrap.Input
10291 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10292 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10293 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10294 * for which you can provide a custom implementation. For example:
10296 var trigger = new Roo.bootstrap.TriggerField();
10297 trigger.onTriggerClick = myTriggerFn;
10298 trigger.applyTo('my-field');
10301 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10302 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10303 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10304 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10305 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10308 * Create a new TriggerField.
10309 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10310 * to the base TextField)
10312 Roo.bootstrap.TriggerField = function(config){
10313 this.mimicing = false;
10314 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10317 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10319 * @cfg {String} triggerClass A CSS class to apply to the trigger
10322 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10327 * @cfg {Boolean} removable (true|false) special filter default false
10331 /** @cfg {Boolean} grow @hide */
10332 /** @cfg {Number} growMin @hide */
10333 /** @cfg {Number} growMax @hide */
10339 autoSize: Roo.emptyFn,
10343 deferHeight : true,
10346 actionMode : 'wrap',
10351 getAutoCreate : function(){
10353 var align = this.labelAlign || this.parentLabelAlign();
10358 cls: 'form-group' //input-group
10365 type : this.inputType,
10366 cls : 'form-control',
10367 autocomplete: 'new-password',
10368 placeholder : this.placeholder || ''
10372 input.name = this.name;
10375 input.cls += ' input-' + this.size;
10378 if (this.disabled) {
10379 input.disabled=true;
10382 var inputblock = input;
10384 if(this.hasFeedback && !this.allowBlank){
10388 cls: 'glyphicon form-control-feedback'
10391 if(this.removable && !this.editable && !this.tickable){
10393 cls : 'has-feedback',
10399 cls : 'roo-combo-removable-btn close'
10406 cls : 'has-feedback',
10415 if(this.removable && !this.editable && !this.tickable){
10417 cls : 'roo-removable',
10423 cls : 'roo-combo-removable-btn close'
10430 if (this.before || this.after) {
10433 cls : 'input-group',
10437 inputblock.cn.push({
10439 cls : 'input-group-addon input-group-prepend input-group-text',
10444 inputblock.cn.push(input);
10446 if(this.hasFeedback && !this.allowBlank){
10447 inputblock.cls += ' has-feedback';
10448 inputblock.cn.push(feedback);
10452 inputblock.cn.push({
10454 cls : 'input-group-addon input-group-append input-group-text',
10463 var ibwrap = inputblock;
10468 cls: 'roo-select2-choices',
10472 cls: 'roo-select2-search-field',
10484 cls: 'roo-select2-container input-group',
10489 cls: 'form-hidden-field'
10495 if(!this.multiple && this.showToggleBtn){
10501 if (this.caret != false) {
10504 cls: 'fa fa-' + this.caret
10511 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10516 cls: 'combobox-clear',
10530 combobox.cls += ' roo-select2-container-multi';
10534 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10535 tooltip : 'This field is required'
10537 if (Roo.bootstrap.version == 4) {
10540 style : 'display:none'
10545 if (align ==='left' && this.fieldLabel.length) {
10547 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10554 cls : 'control-label',
10555 html : this.fieldLabel
10567 var labelCfg = cfg.cn[1];
10568 var contentCfg = cfg.cn[2];
10570 if(this.indicatorpos == 'right'){
10575 cls : 'control-label',
10579 html : this.fieldLabel
10593 labelCfg = cfg.cn[0];
10594 contentCfg = cfg.cn[1];
10597 if(this.labelWidth > 12){
10598 labelCfg.style = "width: " + this.labelWidth + 'px';
10601 if(this.labelWidth < 13 && this.labelmd == 0){
10602 this.labelmd = this.labelWidth;
10605 if(this.labellg > 0){
10606 labelCfg.cls += ' col-lg-' + this.labellg;
10607 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10610 if(this.labelmd > 0){
10611 labelCfg.cls += ' col-md-' + this.labelmd;
10612 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10615 if(this.labelsm > 0){
10616 labelCfg.cls += ' col-sm-' + this.labelsm;
10617 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10620 if(this.labelxs > 0){
10621 labelCfg.cls += ' col-xs-' + this.labelxs;
10622 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10625 } else if ( this.fieldLabel.length) {
10626 // Roo.log(" label");
10631 //cls : 'input-group-addon',
10632 html : this.fieldLabel
10640 if(this.indicatorpos == 'right'){
10648 html : this.fieldLabel
10662 // Roo.log(" no label && no align");
10669 ['xs','sm','md','lg'].map(function(size){
10670 if (settings[size]) {
10671 cfg.cls += ' col-' + size + '-' + settings[size];
10682 onResize : function(w, h){
10683 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10684 // if(typeof w == 'number'){
10685 // var x = w - this.trigger.getWidth();
10686 // this.inputEl().setWidth(this.adjustWidth('input', x));
10687 // this.trigger.setStyle('left', x+'px');
10692 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10695 getResizeEl : function(){
10696 return this.inputEl();
10700 getPositionEl : function(){
10701 return this.inputEl();
10705 alignErrorIcon : function(){
10706 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10710 initEvents : function(){
10714 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10715 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10716 if(!this.multiple && this.showToggleBtn){
10717 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10718 if(this.hideTrigger){
10719 this.trigger.setDisplayed(false);
10721 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10725 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10728 if(this.removable && !this.editable && !this.tickable){
10729 var close = this.closeTriggerEl();
10732 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10733 close.on('click', this.removeBtnClick, this, close);
10737 //this.trigger.addClassOnOver('x-form-trigger-over');
10738 //this.trigger.addClassOnClick('x-form-trigger-click');
10741 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10745 closeTriggerEl : function()
10747 var close = this.el.select('.roo-combo-removable-btn', true).first();
10748 return close ? close : false;
10751 removeBtnClick : function(e, h, el)
10753 e.preventDefault();
10755 if(this.fireEvent("remove", this) !== false){
10757 this.fireEvent("afterremove", this)
10761 createList : function()
10763 this.list = Roo.get(document.body).createChild({
10764 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10765 cls: 'typeahead typeahead-long dropdown-menu',
10766 style: 'display:none'
10769 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10774 initTrigger : function(){
10779 onDestroy : function(){
10781 this.trigger.removeAllListeners();
10782 // this.trigger.remove();
10785 // this.wrap.remove();
10787 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10791 onFocus : function(){
10792 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10794 if(!this.mimicing){
10795 this.wrap.addClass('x-trigger-wrap-focus');
10796 this.mimicing = true;
10797 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10798 if(this.monitorTab){
10799 this.el.on("keydown", this.checkTab, this);
10806 checkTab : function(e){
10807 if(e.getKey() == e.TAB){
10808 this.triggerBlur();
10813 onBlur : function(){
10818 mimicBlur : function(e, t){
10820 if(!this.wrap.contains(t) && this.validateBlur()){
10821 this.triggerBlur();
10827 triggerBlur : function(){
10828 this.mimicing = false;
10829 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10830 if(this.monitorTab){
10831 this.el.un("keydown", this.checkTab, this);
10833 //this.wrap.removeClass('x-trigger-wrap-focus');
10834 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10838 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10839 validateBlur : function(e, t){
10844 onDisable : function(){
10845 this.inputEl().dom.disabled = true;
10846 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10848 // this.wrap.addClass('x-item-disabled');
10853 onEnable : function(){
10854 this.inputEl().dom.disabled = false;
10855 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10857 // this.el.removeClass('x-item-disabled');
10862 onShow : function(){
10863 var ae = this.getActionEl();
10866 ae.dom.style.display = '';
10867 ae.dom.style.visibility = 'visible';
10873 onHide : function(){
10874 var ae = this.getActionEl();
10875 ae.dom.style.display = 'none';
10879 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10880 * by an implementing function.
10882 * @param {EventObject} e
10884 onTriggerClick : Roo.emptyFn
10888 * Ext JS Library 1.1.1
10889 * Copyright(c) 2006-2007, Ext JS, LLC.
10891 * Originally Released Under LGPL - original licence link has changed is not relivant.
10894 * <script type="text/javascript">
10899 * @class Roo.data.SortTypes
10901 * Defines the default sorting (casting?) comparison functions used when sorting data.
10903 Roo.data.SortTypes = {
10905 * Default sort that does nothing
10906 * @param {Mixed} s The value being converted
10907 * @return {Mixed} The comparison value
10909 none : function(s){
10914 * The regular expression used to strip tags
10918 stripTagsRE : /<\/?[^>]+>/gi,
10921 * Strips all HTML tags to sort on text only
10922 * @param {Mixed} s The value being converted
10923 * @return {String} The comparison value
10925 asText : function(s){
10926 return String(s).replace(this.stripTagsRE, "");
10930 * Strips all HTML tags to sort on text only - Case insensitive
10931 * @param {Mixed} s The value being converted
10932 * @return {String} The comparison value
10934 asUCText : function(s){
10935 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10939 * Case insensitive string
10940 * @param {Mixed} s The value being converted
10941 * @return {String} The comparison value
10943 asUCString : function(s) {
10944 return String(s).toUpperCase();
10949 * @param {Mixed} s The value being converted
10950 * @return {Number} The comparison value
10952 asDate : function(s) {
10956 if(s instanceof Date){
10957 return s.getTime();
10959 return Date.parse(String(s));
10964 * @param {Mixed} s The value being converted
10965 * @return {Float} The comparison value
10967 asFloat : function(s) {
10968 var val = parseFloat(String(s).replace(/,/g, ""));
10977 * @param {Mixed} s The value being converted
10978 * @return {Number} The comparison value
10980 asInt : function(s) {
10981 var val = parseInt(String(s).replace(/,/g, ""));
10989 * Ext JS Library 1.1.1
10990 * Copyright(c) 2006-2007, Ext JS, LLC.
10992 * Originally Released Under LGPL - original licence link has changed is not relivant.
10995 * <script type="text/javascript">
10999 * @class Roo.data.Record
11000 * Instances of this class encapsulate both record <em>definition</em> information, and record
11001 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11002 * to access Records cached in an {@link Roo.data.Store} object.<br>
11004 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11005 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11008 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11010 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11011 * {@link #create}. The parameters are the same.
11012 * @param {Array} data An associative Array of data values keyed by the field name.
11013 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11014 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11015 * not specified an integer id is generated.
11017 Roo.data.Record = function(data, id){
11018 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11023 * Generate a constructor for a specific record layout.
11024 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11025 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11026 * Each field definition object may contain the following properties: <ul>
11027 * <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,
11028 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11029 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11030 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11031 * is being used, then this is a string containing the javascript expression to reference the data relative to
11032 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11033 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11034 * this may be omitted.</p></li>
11035 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11036 * <ul><li>auto (Default, implies no conversion)</li>
11041 * <li>date</li></ul></p></li>
11042 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11043 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11044 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11045 * by the Reader into an object that will be stored in the Record. It is passed the
11046 * following parameters:<ul>
11047 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11049 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11051 * <br>usage:<br><pre><code>
11052 var TopicRecord = Roo.data.Record.create(
11053 {name: 'title', mapping: 'topic_title'},
11054 {name: 'author', mapping: 'username'},
11055 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11056 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11057 {name: 'lastPoster', mapping: 'user2'},
11058 {name: 'excerpt', mapping: 'post_text'}
11061 var myNewRecord = new TopicRecord({
11062 title: 'Do my job please',
11065 lastPost: new Date(),
11066 lastPoster: 'Animal',
11067 excerpt: 'No way dude!'
11069 myStore.add(myNewRecord);
11074 Roo.data.Record.create = function(o){
11075 var f = function(){
11076 f.superclass.constructor.apply(this, arguments);
11078 Roo.extend(f, Roo.data.Record);
11079 var p = f.prototype;
11080 p.fields = new Roo.util.MixedCollection(false, function(field){
11083 for(var i = 0, len = o.length; i < len; i++){
11084 p.fields.add(new Roo.data.Field(o[i]));
11086 f.getField = function(name){
11087 return p.fields.get(name);
11092 Roo.data.Record.AUTO_ID = 1000;
11093 Roo.data.Record.EDIT = 'edit';
11094 Roo.data.Record.REJECT = 'reject';
11095 Roo.data.Record.COMMIT = 'commit';
11097 Roo.data.Record.prototype = {
11099 * Readonly flag - true if this record has been modified.
11108 join : function(store){
11109 this.store = store;
11113 * Set the named field to the specified value.
11114 * @param {String} name The name of the field to set.
11115 * @param {Object} value The value to set the field to.
11117 set : function(name, value){
11118 if(this.data[name] == value){
11122 if(!this.modified){
11123 this.modified = {};
11125 if(typeof this.modified[name] == 'undefined'){
11126 this.modified[name] = this.data[name];
11128 this.data[name] = value;
11129 if(!this.editing && this.store){
11130 this.store.afterEdit(this);
11135 * Get the value of the named field.
11136 * @param {String} name The name of the field to get the value of.
11137 * @return {Object} The value of the field.
11139 get : function(name){
11140 return this.data[name];
11144 beginEdit : function(){
11145 this.editing = true;
11146 this.modified = {};
11150 cancelEdit : function(){
11151 this.editing = false;
11152 delete this.modified;
11156 endEdit : function(){
11157 this.editing = false;
11158 if(this.dirty && this.store){
11159 this.store.afterEdit(this);
11164 * Usually called by the {@link Roo.data.Store} which owns the Record.
11165 * Rejects all changes made to the Record since either creation, or the last commit operation.
11166 * Modified fields are reverted to their original values.
11168 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11169 * of reject operations.
11171 reject : function(){
11172 var m = this.modified;
11174 if(typeof m[n] != "function"){
11175 this.data[n] = m[n];
11178 this.dirty = false;
11179 delete this.modified;
11180 this.editing = false;
11182 this.store.afterReject(this);
11187 * Usually called by the {@link Roo.data.Store} which owns the Record.
11188 * Commits all changes made to the Record since either creation, or the last commit operation.
11190 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11191 * of commit operations.
11193 commit : function(){
11194 this.dirty = false;
11195 delete this.modified;
11196 this.editing = false;
11198 this.store.afterCommit(this);
11203 hasError : function(){
11204 return this.error != null;
11208 clearError : function(){
11213 * Creates a copy of this record.
11214 * @param {String} id (optional) A new record id if you don't want to use this record's id
11217 copy : function(newId) {
11218 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11222 * Ext JS Library 1.1.1
11223 * Copyright(c) 2006-2007, Ext JS, LLC.
11225 * Originally Released Under LGPL - original licence link has changed is not relivant.
11228 * <script type="text/javascript">
11234 * @class Roo.data.Store
11235 * @extends Roo.util.Observable
11236 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11237 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11239 * 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
11240 * has no knowledge of the format of the data returned by the Proxy.<br>
11242 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11243 * instances from the data object. These records are cached and made available through accessor functions.
11245 * Creates a new Store.
11246 * @param {Object} config A config object containing the objects needed for the Store to access data,
11247 * and read the data into Records.
11249 Roo.data.Store = function(config){
11250 this.data = new Roo.util.MixedCollection(false);
11251 this.data.getKey = function(o){
11254 this.baseParams = {};
11256 this.paramNames = {
11261 "multisort" : "_multisort"
11264 if(config && config.data){
11265 this.inlineData = config.data;
11266 delete config.data;
11269 Roo.apply(this, config);
11271 if(this.reader){ // reader passed
11272 this.reader = Roo.factory(this.reader, Roo.data);
11273 this.reader.xmodule = this.xmodule || false;
11274 if(!this.recordType){
11275 this.recordType = this.reader.recordType;
11277 if(this.reader.onMetaChange){
11278 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11282 if(this.recordType){
11283 this.fields = this.recordType.prototype.fields;
11285 this.modified = [];
11289 * @event datachanged
11290 * Fires when the data cache has changed, and a widget which is using this Store
11291 * as a Record cache should refresh its view.
11292 * @param {Store} this
11294 datachanged : true,
11296 * @event metachange
11297 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11298 * @param {Store} this
11299 * @param {Object} meta The JSON metadata
11304 * Fires when Records have been added to the Store
11305 * @param {Store} this
11306 * @param {Roo.data.Record[]} records The array of Records added
11307 * @param {Number} index The index at which the record(s) were added
11312 * Fires when a Record has been removed from the Store
11313 * @param {Store} this
11314 * @param {Roo.data.Record} record The Record that was removed
11315 * @param {Number} index The index at which the record was removed
11320 * Fires when a Record has been updated
11321 * @param {Store} this
11322 * @param {Roo.data.Record} record The Record that was updated
11323 * @param {String} operation The update operation being performed. Value may be one of:
11325 Roo.data.Record.EDIT
11326 Roo.data.Record.REJECT
11327 Roo.data.Record.COMMIT
11333 * Fires when the data cache has been cleared.
11334 * @param {Store} this
11338 * @event beforeload
11339 * Fires before a request is made for a new data object. If the beforeload handler returns false
11340 * the load action will be canceled.
11341 * @param {Store} this
11342 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11346 * @event beforeloadadd
11347 * Fires after a new set of Records has been loaded.
11348 * @param {Store} this
11349 * @param {Roo.data.Record[]} records The Records that were loaded
11350 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11352 beforeloadadd : true,
11355 * Fires after a new set of Records has been loaded, before they are added to the store.
11356 * @param {Store} this
11357 * @param {Roo.data.Record[]} records The Records that were loaded
11358 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11359 * @params {Object} return from reader
11363 * @event loadexception
11364 * Fires if an exception occurs in the Proxy during loading.
11365 * Called with the signature of the Proxy's "loadexception" event.
11366 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11369 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11370 * @param {Object} load options
11371 * @param {Object} jsonData from your request (normally this contains the Exception)
11373 loadexception : true
11377 this.proxy = Roo.factory(this.proxy, Roo.data);
11378 this.proxy.xmodule = this.xmodule || false;
11379 this.relayEvents(this.proxy, ["loadexception"]);
11381 this.sortToggle = {};
11382 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11384 Roo.data.Store.superclass.constructor.call(this);
11386 if(this.inlineData){
11387 this.loadData(this.inlineData);
11388 delete this.inlineData;
11392 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11394 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11395 * without a remote query - used by combo/forms at present.
11399 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11402 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11405 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11406 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11409 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11410 * on any HTTP request
11413 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11416 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11420 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11421 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11423 remoteSort : false,
11426 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11427 * loaded or when a record is removed. (defaults to false).
11429 pruneModifiedRecords : false,
11432 lastOptions : null,
11435 * Add Records to the Store and fires the add event.
11436 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11438 add : function(records){
11439 records = [].concat(records);
11440 for(var i = 0, len = records.length; i < len; i++){
11441 records[i].join(this);
11443 var index = this.data.length;
11444 this.data.addAll(records);
11445 this.fireEvent("add", this, records, index);
11449 * Remove a Record from the Store and fires the remove event.
11450 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11452 remove : function(record){
11453 var index = this.data.indexOf(record);
11454 this.data.removeAt(index);
11456 if(this.pruneModifiedRecords){
11457 this.modified.remove(record);
11459 this.fireEvent("remove", this, record, index);
11463 * Remove all Records from the Store and fires the clear event.
11465 removeAll : function(){
11467 if(this.pruneModifiedRecords){
11468 this.modified = [];
11470 this.fireEvent("clear", this);
11474 * Inserts Records to the Store at the given index and fires the add event.
11475 * @param {Number} index The start index at which to insert the passed Records.
11476 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11478 insert : function(index, records){
11479 records = [].concat(records);
11480 for(var i = 0, len = records.length; i < len; i++){
11481 this.data.insert(index, records[i]);
11482 records[i].join(this);
11484 this.fireEvent("add", this, records, index);
11488 * Get the index within the cache of the passed Record.
11489 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11490 * @return {Number} The index of the passed Record. Returns -1 if not found.
11492 indexOf : function(record){
11493 return this.data.indexOf(record);
11497 * Get the index within the cache of the Record with the passed id.
11498 * @param {String} id The id of the Record to find.
11499 * @return {Number} The index of the Record. Returns -1 if not found.
11501 indexOfId : function(id){
11502 return this.data.indexOfKey(id);
11506 * Get the Record with the specified id.
11507 * @param {String} id The id of the Record to find.
11508 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11510 getById : function(id){
11511 return this.data.key(id);
11515 * Get the Record at the specified index.
11516 * @param {Number} index The index of the Record to find.
11517 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11519 getAt : function(index){
11520 return this.data.itemAt(index);
11524 * Returns a range of Records between specified indices.
11525 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11526 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11527 * @return {Roo.data.Record[]} An array of Records
11529 getRange : function(start, end){
11530 return this.data.getRange(start, end);
11534 storeOptions : function(o){
11535 o = Roo.apply({}, o);
11538 this.lastOptions = o;
11542 * Loads the Record cache from the configured Proxy using the configured Reader.
11544 * If using remote paging, then the first load call must specify the <em>start</em>
11545 * and <em>limit</em> properties in the options.params property to establish the initial
11546 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11548 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11549 * and this call will return before the new data has been loaded. Perform any post-processing
11550 * in a callback function, or in a "load" event handler.</strong>
11552 * @param {Object} options An object containing properties which control loading options:<ul>
11553 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11554 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11555 * passed the following arguments:<ul>
11556 * <li>r : Roo.data.Record[]</li>
11557 * <li>options: Options object from the load call</li>
11558 * <li>success: Boolean success indicator</li></ul></li>
11559 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11560 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11563 load : function(options){
11564 options = options || {};
11565 if(this.fireEvent("beforeload", this, options) !== false){
11566 this.storeOptions(options);
11567 var p = Roo.apply(options.params || {}, this.baseParams);
11568 // if meta was not loaded from remote source.. try requesting it.
11569 if (!this.reader.metaFromRemote) {
11570 p._requestMeta = 1;
11572 if(this.sortInfo && this.remoteSort){
11573 var pn = this.paramNames;
11574 p[pn["sort"]] = this.sortInfo.field;
11575 p[pn["dir"]] = this.sortInfo.direction;
11577 if (this.multiSort) {
11578 var pn = this.paramNames;
11579 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11582 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11587 * Reloads the Record cache from the configured Proxy using the configured Reader and
11588 * the options from the last load operation performed.
11589 * @param {Object} options (optional) An object containing properties which may override the options
11590 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11591 * the most recently used options are reused).
11593 reload : function(options){
11594 this.load(Roo.applyIf(options||{}, this.lastOptions));
11598 // Called as a callback by the Reader during a load operation.
11599 loadRecords : function(o, options, success){
11600 if(!o || success === false){
11601 if(success !== false){
11602 this.fireEvent("load", this, [], options, o);
11604 if(options.callback){
11605 options.callback.call(options.scope || this, [], options, false);
11609 // if data returned failure - throw an exception.
11610 if (o.success === false) {
11611 // show a message if no listener is registered.
11612 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11613 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11615 // loadmask wil be hooked into this..
11616 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11619 var r = o.records, t = o.totalRecords || r.length;
11621 this.fireEvent("beforeloadadd", this, r, options, o);
11623 if(!options || options.add !== true){
11624 if(this.pruneModifiedRecords){
11625 this.modified = [];
11627 for(var i = 0, len = r.length; i < len; i++){
11631 this.data = this.snapshot;
11632 delete this.snapshot;
11635 this.data.addAll(r);
11636 this.totalLength = t;
11638 this.fireEvent("datachanged", this);
11640 this.totalLength = Math.max(t, this.data.length+r.length);
11644 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11646 var e = new Roo.data.Record({});
11648 e.set(this.parent.displayField, this.parent.emptyTitle);
11649 e.set(this.parent.valueField, '');
11654 this.fireEvent("load", this, r, options, o);
11655 if(options.callback){
11656 options.callback.call(options.scope || this, r, options, true);
11662 * Loads data from a passed data block. A Reader which understands the format of the data
11663 * must have been configured in the constructor.
11664 * @param {Object} data The data block from which to read the Records. The format of the data expected
11665 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11666 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11668 loadData : function(o, append){
11669 var r = this.reader.readRecords(o);
11670 this.loadRecords(r, {add: append}, true);
11674 * Gets the number of cached records.
11676 * <em>If using paging, this may not be the total size of the dataset. If the data object
11677 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11678 * the data set size</em>
11680 getCount : function(){
11681 return this.data.length || 0;
11685 * Gets the total number of records in the dataset as returned by the server.
11687 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11688 * the dataset size</em>
11690 getTotalCount : function(){
11691 return this.totalLength || 0;
11695 * Returns the sort state of the Store as an object with two properties:
11697 field {String} The name of the field by which the Records are sorted
11698 direction {String} The sort order, "ASC" or "DESC"
11701 getSortState : function(){
11702 return this.sortInfo;
11706 applySort : function(){
11707 if(this.sortInfo && !this.remoteSort){
11708 var s = this.sortInfo, f = s.field;
11709 var st = this.fields.get(f).sortType;
11710 var fn = function(r1, r2){
11711 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11712 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11714 this.data.sort(s.direction, fn);
11715 if(this.snapshot && this.snapshot != this.data){
11716 this.snapshot.sort(s.direction, fn);
11722 * Sets the default sort column and order to be used by the next load operation.
11723 * @param {String} fieldName The name of the field to sort by.
11724 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11726 setDefaultSort : function(field, dir){
11727 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11731 * Sort the Records.
11732 * If remote sorting is used, the sort is performed on the server, and the cache is
11733 * reloaded. If local sorting is used, the cache is sorted internally.
11734 * @param {String} fieldName The name of the field to sort by.
11735 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11737 sort : function(fieldName, dir){
11738 var f = this.fields.get(fieldName);
11740 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11742 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11743 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11748 this.sortToggle[f.name] = dir;
11749 this.sortInfo = {field: f.name, direction: dir};
11750 if(!this.remoteSort){
11752 this.fireEvent("datachanged", this);
11754 this.load(this.lastOptions);
11759 * Calls the specified function for each of the Records in the cache.
11760 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11761 * Returning <em>false</em> aborts and exits the iteration.
11762 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11764 each : function(fn, scope){
11765 this.data.each(fn, scope);
11769 * Gets all records modified since the last commit. Modified records are persisted across load operations
11770 * (e.g., during paging).
11771 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11773 getModifiedRecords : function(){
11774 return this.modified;
11778 createFilterFn : function(property, value, anyMatch){
11779 if(!value.exec){ // not a regex
11780 value = String(value);
11781 if(value.length == 0){
11784 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11786 return function(r){
11787 return value.test(r.data[property]);
11792 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11793 * @param {String} property A field on your records
11794 * @param {Number} start The record index to start at (defaults to 0)
11795 * @param {Number} end The last record index to include (defaults to length - 1)
11796 * @return {Number} The sum
11798 sum : function(property, start, end){
11799 var rs = this.data.items, v = 0;
11800 start = start || 0;
11801 end = (end || end === 0) ? end : rs.length-1;
11803 for(var i = start; i <= end; i++){
11804 v += (rs[i].data[property] || 0);
11810 * Filter the records by a specified property.
11811 * @param {String} field A field on your records
11812 * @param {String/RegExp} value Either a string that the field
11813 * should start with or a RegExp to test against the field
11814 * @param {Boolean} anyMatch True to match any part not just the beginning
11816 filter : function(property, value, anyMatch){
11817 var fn = this.createFilterFn(property, value, anyMatch);
11818 return fn ? this.filterBy(fn) : this.clearFilter();
11822 * Filter by a function. The specified function will be called with each
11823 * record in this data source. If the function returns true the record is included,
11824 * otherwise it is filtered.
11825 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11826 * @param {Object} scope (optional) The scope of the function (defaults to this)
11828 filterBy : function(fn, scope){
11829 this.snapshot = this.snapshot || this.data;
11830 this.data = this.queryBy(fn, scope||this);
11831 this.fireEvent("datachanged", this);
11835 * Query the records by a specified property.
11836 * @param {String} field A field on your records
11837 * @param {String/RegExp} value Either a string that the field
11838 * should start with or a RegExp to test against the field
11839 * @param {Boolean} anyMatch True to match any part not just the beginning
11840 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11842 query : function(property, value, anyMatch){
11843 var fn = this.createFilterFn(property, value, anyMatch);
11844 return fn ? this.queryBy(fn) : this.data.clone();
11848 * Query by a function. The specified function will be called with each
11849 * record in this data source. If the function returns true the record is included
11851 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11852 * @param {Object} scope (optional) The scope of the function (defaults to this)
11853 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11855 queryBy : function(fn, scope){
11856 var data = this.snapshot || this.data;
11857 return data.filterBy(fn, scope||this);
11861 * Collects unique values for a particular dataIndex from this store.
11862 * @param {String} dataIndex The property to collect
11863 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11864 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11865 * @return {Array} An array of the unique values
11867 collect : function(dataIndex, allowNull, bypassFilter){
11868 var d = (bypassFilter === true && this.snapshot) ?
11869 this.snapshot.items : this.data.items;
11870 var v, sv, r = [], l = {};
11871 for(var i = 0, len = d.length; i < len; i++){
11872 v = d[i].data[dataIndex];
11874 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11883 * Revert to a view of the Record cache with no filtering applied.
11884 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11886 clearFilter : function(suppressEvent){
11887 if(this.snapshot && this.snapshot != this.data){
11888 this.data = this.snapshot;
11889 delete this.snapshot;
11890 if(suppressEvent !== true){
11891 this.fireEvent("datachanged", this);
11897 afterEdit : function(record){
11898 if(this.modified.indexOf(record) == -1){
11899 this.modified.push(record);
11901 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11905 afterReject : function(record){
11906 this.modified.remove(record);
11907 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11911 afterCommit : function(record){
11912 this.modified.remove(record);
11913 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11917 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11918 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11920 commitChanges : function(){
11921 var m = this.modified.slice(0);
11922 this.modified = [];
11923 for(var i = 0, len = m.length; i < len; i++){
11929 * Cancel outstanding changes on all changed records.
11931 rejectChanges : function(){
11932 var m = this.modified.slice(0);
11933 this.modified = [];
11934 for(var i = 0, len = m.length; i < len; i++){
11939 onMetaChange : function(meta, rtype, o){
11940 this.recordType = rtype;
11941 this.fields = rtype.prototype.fields;
11942 delete this.snapshot;
11943 this.sortInfo = meta.sortInfo || this.sortInfo;
11944 this.modified = [];
11945 this.fireEvent('metachange', this, this.reader.meta);
11948 moveIndex : function(data, type)
11950 var index = this.indexOf(data);
11952 var newIndex = index + type;
11956 this.insert(newIndex, data);
11961 * Ext JS Library 1.1.1
11962 * Copyright(c) 2006-2007, Ext JS, LLC.
11964 * Originally Released Under LGPL - original licence link has changed is not relivant.
11967 * <script type="text/javascript">
11971 * @class Roo.data.SimpleStore
11972 * @extends Roo.data.Store
11973 * Small helper class to make creating Stores from Array data easier.
11974 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11975 * @cfg {Array} fields An array of field definition objects, or field name strings.
11976 * @cfg {Array} data The multi-dimensional array of data
11978 * @param {Object} config
11980 Roo.data.SimpleStore = function(config){
11981 Roo.data.SimpleStore.superclass.constructor.call(this, {
11983 reader: new Roo.data.ArrayReader({
11986 Roo.data.Record.create(config.fields)
11988 proxy : new Roo.data.MemoryProxy(config.data)
11992 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11994 * Ext JS Library 1.1.1
11995 * Copyright(c) 2006-2007, Ext JS, LLC.
11997 * Originally Released Under LGPL - original licence link has changed is not relivant.
12000 * <script type="text/javascript">
12005 * @extends Roo.data.Store
12006 * @class Roo.data.JsonStore
12007 * Small helper class to make creating Stores for JSON data easier. <br/>
12009 var store = new Roo.data.JsonStore({
12010 url: 'get-images.php',
12012 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12015 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12016 * JsonReader and HttpProxy (unless inline data is provided).</b>
12017 * @cfg {Array} fields An array of field definition objects, or field name strings.
12019 * @param {Object} config
12021 Roo.data.JsonStore = function(c){
12022 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12023 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12024 reader: new Roo.data.JsonReader(c, c.fields)
12027 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12029 * Ext JS Library 1.1.1
12030 * Copyright(c) 2006-2007, Ext JS, LLC.
12032 * Originally Released Under LGPL - original licence link has changed is not relivant.
12035 * <script type="text/javascript">
12039 Roo.data.Field = function(config){
12040 if(typeof config == "string"){
12041 config = {name: config};
12043 Roo.apply(this, config);
12046 this.type = "auto";
12049 var st = Roo.data.SortTypes;
12050 // named sortTypes are supported, here we look them up
12051 if(typeof this.sortType == "string"){
12052 this.sortType = st[this.sortType];
12055 // set default sortType for strings and dates
12056 if(!this.sortType){
12059 this.sortType = st.asUCString;
12062 this.sortType = st.asDate;
12065 this.sortType = st.none;
12070 var stripRe = /[\$,%]/g;
12072 // prebuilt conversion function for this field, instead of
12073 // switching every time we're reading a value
12075 var cv, dateFormat = this.dateFormat;
12080 cv = function(v){ return v; };
12083 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12087 return v !== undefined && v !== null && v !== '' ?
12088 parseInt(String(v).replace(stripRe, ""), 10) : '';
12093 return v !== undefined && v !== null && v !== '' ?
12094 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12099 cv = function(v){ return v === true || v === "true" || v == 1; };
12106 if(v instanceof Date){
12110 if(dateFormat == "timestamp"){
12111 return new Date(v*1000);
12113 return Date.parseDate(v, dateFormat);
12115 var parsed = Date.parse(v);
12116 return parsed ? new Date(parsed) : null;
12125 Roo.data.Field.prototype = {
12133 * Ext JS Library 1.1.1
12134 * Copyright(c) 2006-2007, Ext JS, LLC.
12136 * Originally Released Under LGPL - original licence link has changed is not relivant.
12139 * <script type="text/javascript">
12142 // Base class for reading structured data from a data source. This class is intended to be
12143 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12146 * @class Roo.data.DataReader
12147 * Base class for reading structured data from a data source. This class is intended to be
12148 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12151 Roo.data.DataReader = function(meta, recordType){
12155 this.recordType = recordType instanceof Array ?
12156 Roo.data.Record.create(recordType) : recordType;
12159 Roo.data.DataReader.prototype = {
12161 * Create an empty record
12162 * @param {Object} data (optional) - overlay some values
12163 * @return {Roo.data.Record} record created.
12165 newRow : function(d) {
12167 this.recordType.prototype.fields.each(function(c) {
12169 case 'int' : da[c.name] = 0; break;
12170 case 'date' : da[c.name] = new Date(); break;
12171 case 'float' : da[c.name] = 0.0; break;
12172 case 'boolean' : da[c.name] = false; break;
12173 default : da[c.name] = ""; break;
12177 return new this.recordType(Roo.apply(da, d));
12182 * Ext JS Library 1.1.1
12183 * Copyright(c) 2006-2007, Ext JS, LLC.
12185 * Originally Released Under LGPL - original licence link has changed is not relivant.
12188 * <script type="text/javascript">
12192 * @class Roo.data.DataProxy
12193 * @extends Roo.data.Observable
12194 * This class is an abstract base class for implementations which provide retrieval of
12195 * unformatted data objects.<br>
12197 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12198 * (of the appropriate type which knows how to parse the data object) to provide a block of
12199 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12201 * Custom implementations must implement the load method as described in
12202 * {@link Roo.data.HttpProxy#load}.
12204 Roo.data.DataProxy = function(){
12207 * @event beforeload
12208 * Fires before a network request is made to retrieve a data object.
12209 * @param {Object} This DataProxy object.
12210 * @param {Object} params The params parameter to the load function.
12215 * Fires before the load method's callback is called.
12216 * @param {Object} This DataProxy object.
12217 * @param {Object} o The data object.
12218 * @param {Object} arg The callback argument object passed to the load function.
12222 * @event loadexception
12223 * Fires if an Exception occurs during data retrieval.
12224 * @param {Object} This DataProxy object.
12225 * @param {Object} o The data object.
12226 * @param {Object} arg The callback argument object passed to the load function.
12227 * @param {Object} e The Exception.
12229 loadexception : true
12231 Roo.data.DataProxy.superclass.constructor.call(this);
12234 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12237 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12241 * Ext JS Library 1.1.1
12242 * Copyright(c) 2006-2007, Ext JS, LLC.
12244 * Originally Released Under LGPL - original licence link has changed is not relivant.
12247 * <script type="text/javascript">
12250 * @class Roo.data.MemoryProxy
12251 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12252 * to the Reader when its load method is called.
12254 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12256 Roo.data.MemoryProxy = function(data){
12260 Roo.data.MemoryProxy.superclass.constructor.call(this);
12264 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12267 * Load data from the requested source (in this case an in-memory
12268 * data object passed to the constructor), read the data object into
12269 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12270 * process that block using the passed callback.
12271 * @param {Object} params This parameter is not used by the MemoryProxy class.
12272 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12273 * object into a block of Roo.data.Records.
12274 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12275 * The function must be passed <ul>
12276 * <li>The Record block object</li>
12277 * <li>The "arg" argument from the load function</li>
12278 * <li>A boolean success indicator</li>
12280 * @param {Object} scope The scope in which to call the callback
12281 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12283 load : function(params, reader, callback, scope, arg){
12284 params = params || {};
12287 result = reader.readRecords(this.data);
12289 this.fireEvent("loadexception", this, arg, null, e);
12290 callback.call(scope, null, arg, false);
12293 callback.call(scope, result, arg, true);
12297 update : function(params, records){
12302 * Ext JS Library 1.1.1
12303 * Copyright(c) 2006-2007, Ext JS, LLC.
12305 * Originally Released Under LGPL - original licence link has changed is not relivant.
12308 * <script type="text/javascript">
12311 * @class Roo.data.HttpProxy
12312 * @extends Roo.data.DataProxy
12313 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12314 * configured to reference a certain URL.<br><br>
12316 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12317 * from which the running page was served.<br><br>
12319 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12321 * Be aware that to enable the browser to parse an XML document, the server must set
12322 * the Content-Type header in the HTTP response to "text/xml".
12324 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12325 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12326 * will be used to make the request.
12328 Roo.data.HttpProxy = function(conn){
12329 Roo.data.HttpProxy.superclass.constructor.call(this);
12330 // is conn a conn config or a real conn?
12332 this.useAjax = !conn || !conn.events;
12336 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12337 // thse are take from connection...
12340 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12343 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12344 * extra parameters to each request made by this object. (defaults to undefined)
12347 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12348 * to each request made by this object. (defaults to undefined)
12351 * @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)
12354 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12357 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12363 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12367 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12368 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12369 * a finer-grained basis than the DataProxy events.
12371 getConnection : function(){
12372 return this.useAjax ? Roo.Ajax : this.conn;
12376 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12377 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12378 * process that block using the passed callback.
12379 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12380 * for the request to the remote server.
12381 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12382 * object into a block of Roo.data.Records.
12383 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12384 * The function must be passed <ul>
12385 * <li>The Record block object</li>
12386 * <li>The "arg" argument from the load function</li>
12387 * <li>A boolean success indicator</li>
12389 * @param {Object} scope The scope in which to call the callback
12390 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12392 load : function(params, reader, callback, scope, arg){
12393 if(this.fireEvent("beforeload", this, params) !== false){
12395 params : params || {},
12397 callback : callback,
12402 callback : this.loadResponse,
12406 Roo.applyIf(o, this.conn);
12407 if(this.activeRequest){
12408 Roo.Ajax.abort(this.activeRequest);
12410 this.activeRequest = Roo.Ajax.request(o);
12412 this.conn.request(o);
12415 callback.call(scope||this, null, arg, false);
12420 loadResponse : function(o, success, response){
12421 delete this.activeRequest;
12423 this.fireEvent("loadexception", this, o, response);
12424 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12429 result = o.reader.read(response);
12431 this.fireEvent("loadexception", this, o, response, e);
12432 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12436 this.fireEvent("load", this, o, o.request.arg);
12437 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12441 update : function(dataSet){
12446 updateResponse : function(dataSet){
12451 * Ext JS Library 1.1.1
12452 * Copyright(c) 2006-2007, Ext JS, LLC.
12454 * Originally Released Under LGPL - original licence link has changed is not relivant.
12457 * <script type="text/javascript">
12461 * @class Roo.data.ScriptTagProxy
12462 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12463 * other than the originating domain of the running page.<br><br>
12465 * <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
12466 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12468 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12469 * source code that is used as the source inside a <script> tag.<br><br>
12471 * In order for the browser to process the returned data, the server must wrap the data object
12472 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12473 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12474 * depending on whether the callback name was passed:
12477 boolean scriptTag = false;
12478 String cb = request.getParameter("callback");
12481 response.setContentType("text/javascript");
12483 response.setContentType("application/x-json");
12485 Writer out = response.getWriter();
12487 out.write(cb + "(");
12489 out.print(dataBlock.toJsonString());
12496 * @param {Object} config A configuration object.
12498 Roo.data.ScriptTagProxy = function(config){
12499 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12500 Roo.apply(this, config);
12501 this.head = document.getElementsByTagName("head")[0];
12504 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12506 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12508 * @cfg {String} url The URL from which to request the data object.
12511 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12515 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12516 * the server the name of the callback function set up by the load call to process the returned data object.
12517 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12518 * javascript output which calls this named function passing the data object as its only parameter.
12520 callbackParam : "callback",
12522 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12523 * name to the request.
12528 * Load data from the configured URL, read the data object into
12529 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12530 * process that block using the passed callback.
12531 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12532 * for the request to the remote server.
12533 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12534 * object into a block of Roo.data.Records.
12535 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12536 * The function must be passed <ul>
12537 * <li>The Record block object</li>
12538 * <li>The "arg" argument from the load function</li>
12539 * <li>A boolean success indicator</li>
12541 * @param {Object} scope The scope in which to call the callback
12542 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12544 load : function(params, reader, callback, scope, arg){
12545 if(this.fireEvent("beforeload", this, params) !== false){
12547 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12549 var url = this.url;
12550 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12552 url += "&_dc=" + (new Date().getTime());
12554 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12557 cb : "stcCallback"+transId,
12558 scriptId : "stcScript"+transId,
12562 callback : callback,
12568 window[trans.cb] = function(o){
12569 conn.handleResponse(o, trans);
12572 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12574 if(this.autoAbort !== false){
12578 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12580 var script = document.createElement("script");
12581 script.setAttribute("src", url);
12582 script.setAttribute("type", "text/javascript");
12583 script.setAttribute("id", trans.scriptId);
12584 this.head.appendChild(script);
12586 this.trans = trans;
12588 callback.call(scope||this, null, arg, false);
12593 isLoading : function(){
12594 return this.trans ? true : false;
12598 * Abort the current server request.
12600 abort : function(){
12601 if(this.isLoading()){
12602 this.destroyTrans(this.trans);
12607 destroyTrans : function(trans, isLoaded){
12608 this.head.removeChild(document.getElementById(trans.scriptId));
12609 clearTimeout(trans.timeoutId);
12611 window[trans.cb] = undefined;
12613 delete window[trans.cb];
12616 // if hasn't been loaded, wait for load to remove it to prevent script error
12617 window[trans.cb] = function(){
12618 window[trans.cb] = undefined;
12620 delete window[trans.cb];
12627 handleResponse : function(o, trans){
12628 this.trans = false;
12629 this.destroyTrans(trans, true);
12632 result = trans.reader.readRecords(o);
12634 this.fireEvent("loadexception", this, o, trans.arg, e);
12635 trans.callback.call(trans.scope||window, null, trans.arg, false);
12638 this.fireEvent("load", this, o, trans.arg);
12639 trans.callback.call(trans.scope||window, result, trans.arg, true);
12643 handleFailure : function(trans){
12644 this.trans = false;
12645 this.destroyTrans(trans, false);
12646 this.fireEvent("loadexception", this, null, trans.arg);
12647 trans.callback.call(trans.scope||window, null, trans.arg, false);
12651 * Ext JS Library 1.1.1
12652 * Copyright(c) 2006-2007, Ext JS, LLC.
12654 * Originally Released Under LGPL - original licence link has changed is not relivant.
12657 * <script type="text/javascript">
12661 * @class Roo.data.JsonReader
12662 * @extends Roo.data.DataReader
12663 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12664 * based on mappings in a provided Roo.data.Record constructor.
12666 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12667 * in the reply previously.
12672 var RecordDef = Roo.data.Record.create([
12673 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12674 {name: 'occupation'} // This field will use "occupation" as the mapping.
12676 var myReader = new Roo.data.JsonReader({
12677 totalProperty: "results", // The property which contains the total dataset size (optional)
12678 root: "rows", // The property which contains an Array of row objects
12679 id: "id" // The property within each row object that provides an ID for the record (optional)
12683 * This would consume a JSON file like this:
12685 { 'results': 2, 'rows': [
12686 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12687 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12690 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12691 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12692 * paged from the remote server.
12693 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12694 * @cfg {String} root name of the property which contains the Array of row objects.
12695 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12696 * @cfg {Array} fields Array of field definition objects
12698 * Create a new JsonReader
12699 * @param {Object} meta Metadata configuration options
12700 * @param {Object} recordType Either an Array of field definition objects,
12701 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12703 Roo.data.JsonReader = function(meta, recordType){
12706 // set some defaults:
12707 Roo.applyIf(meta, {
12708 totalProperty: 'total',
12709 successProperty : 'success',
12714 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12716 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12719 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12720 * Used by Store query builder to append _requestMeta to params.
12723 metaFromRemote : false,
12725 * This method is only used by a DataProxy which has retrieved data from a remote server.
12726 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12727 * @return {Object} data A data block which is used by an Roo.data.Store object as
12728 * a cache of Roo.data.Records.
12730 read : function(response){
12731 var json = response.responseText;
12733 var o = /* eval:var:o */ eval("("+json+")");
12735 throw {message: "JsonReader.read: Json object not found"};
12741 this.metaFromRemote = true;
12742 this.meta = o.metaData;
12743 this.recordType = Roo.data.Record.create(o.metaData.fields);
12744 this.onMetaChange(this.meta, this.recordType, o);
12746 return this.readRecords(o);
12749 // private function a store will implement
12750 onMetaChange : function(meta, recordType, o){
12757 simpleAccess: function(obj, subsc) {
12764 getJsonAccessor: function(){
12766 return function(expr) {
12768 return(re.test(expr))
12769 ? new Function("obj", "return obj." + expr)
12774 return Roo.emptyFn;
12779 * Create a data block containing Roo.data.Records from an XML document.
12780 * @param {Object} o An object which contains an Array of row objects in the property specified
12781 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12782 * which contains the total size of the dataset.
12783 * @return {Object} data A data block which is used by an Roo.data.Store object as
12784 * a cache of Roo.data.Records.
12786 readRecords : function(o){
12788 * After any data loads, the raw JSON data is available for further custom processing.
12792 var s = this.meta, Record = this.recordType,
12793 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12795 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12797 if(s.totalProperty) {
12798 this.getTotal = this.getJsonAccessor(s.totalProperty);
12800 if(s.successProperty) {
12801 this.getSuccess = this.getJsonAccessor(s.successProperty);
12803 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12805 var g = this.getJsonAccessor(s.id);
12806 this.getId = function(rec) {
12808 return (r === undefined || r === "") ? null : r;
12811 this.getId = function(){return null;};
12814 for(var jj = 0; jj < fl; jj++){
12816 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12817 this.ef[jj] = this.getJsonAccessor(map);
12821 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12822 if(s.totalProperty){
12823 var vt = parseInt(this.getTotal(o), 10);
12828 if(s.successProperty){
12829 var vs = this.getSuccess(o);
12830 if(vs === false || vs === 'false'){
12835 for(var i = 0; i < c; i++){
12838 var id = this.getId(n);
12839 for(var j = 0; j < fl; j++){
12841 var v = this.ef[j](n);
12843 Roo.log('missing convert for ' + f.name);
12847 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12849 var record = new Record(values, id);
12851 records[i] = record;
12857 totalRecords : totalRecords
12862 * Ext JS Library 1.1.1
12863 * Copyright(c) 2006-2007, Ext JS, LLC.
12865 * Originally Released Under LGPL - original licence link has changed is not relivant.
12868 * <script type="text/javascript">
12872 * @class Roo.data.ArrayReader
12873 * @extends Roo.data.DataReader
12874 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12875 * Each element of that Array represents a row of data fields. The
12876 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12877 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12881 var RecordDef = Roo.data.Record.create([
12882 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12883 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12885 var myReader = new Roo.data.ArrayReader({
12886 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12890 * This would consume an Array like this:
12892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12894 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12896 * Create a new JsonReader
12897 * @param {Object} meta Metadata configuration options.
12898 * @param {Object} recordType Either an Array of field definition objects
12899 * as specified to {@link Roo.data.Record#create},
12900 * or an {@link Roo.data.Record} object
12901 * created using {@link Roo.data.Record#create}.
12903 Roo.data.ArrayReader = function(meta, recordType){
12904 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12907 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12909 * Create a data block containing Roo.data.Records from an XML document.
12910 * @param {Object} o An Array of row objects which represents the dataset.
12911 * @return {Object} data A data block which is used by an Roo.data.Store object as
12912 * a cache of Roo.data.Records.
12914 readRecords : function(o){
12915 var sid = this.meta ? this.meta.id : null;
12916 var recordType = this.recordType, fields = recordType.prototype.fields;
12919 for(var i = 0; i < root.length; i++){
12922 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12923 for(var j = 0, jlen = fields.length; j < jlen; j++){
12924 var f = fields.items[j];
12925 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12926 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12928 values[f.name] = v;
12930 var record = new recordType(values, id);
12932 records[records.length] = record;
12936 totalRecords : records.length
12945 * @class Roo.bootstrap.ComboBox
12946 * @extends Roo.bootstrap.TriggerField
12947 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12948 * @cfg {Boolean} append (true|false) default false
12949 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12950 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12951 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12952 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12953 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12954 * @cfg {Boolean} animate default true
12955 * @cfg {Boolean} emptyResultText only for touch device
12956 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12957 * @cfg {String} emptyTitle default ''
12959 * Create a new ComboBox.
12960 * @param {Object} config Configuration options
12962 Roo.bootstrap.ComboBox = function(config){
12963 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12967 * Fires when the dropdown list is expanded
12968 * @param {Roo.bootstrap.ComboBox} combo This combo box
12973 * Fires when the dropdown list is collapsed
12974 * @param {Roo.bootstrap.ComboBox} combo This combo box
12978 * @event beforeselect
12979 * Fires before a list item is selected. Return false to cancel the selection.
12980 * @param {Roo.bootstrap.ComboBox} combo This combo box
12981 * @param {Roo.data.Record} record The data record returned from the underlying store
12982 * @param {Number} index The index of the selected item in the dropdown list
12984 'beforeselect' : true,
12987 * Fires when a list item is selected
12988 * @param {Roo.bootstrap.ComboBox} combo This combo box
12989 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12990 * @param {Number} index The index of the selected item in the dropdown list
12994 * @event beforequery
12995 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12996 * The event object passed has these properties:
12997 * @param {Roo.bootstrap.ComboBox} combo This combo box
12998 * @param {String} query The query
12999 * @param {Boolean} forceAll true to force "all" query
13000 * @param {Boolean} cancel true to cancel the query
13001 * @param {Object} e The query event object
13003 'beforequery': true,
13006 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13007 * @param {Roo.bootstrap.ComboBox} combo This combo box
13012 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13013 * @param {Roo.bootstrap.ComboBox} combo This combo box
13014 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13019 * Fires when the remove value from the combobox array
13020 * @param {Roo.bootstrap.ComboBox} combo This combo box
13024 * @event afterremove
13025 * Fires when the remove value from the combobox array
13026 * @param {Roo.bootstrap.ComboBox} combo This combo box
13028 'afterremove' : true,
13030 * @event specialfilter
13031 * Fires when specialfilter
13032 * @param {Roo.bootstrap.ComboBox} combo This combo box
13034 'specialfilter' : true,
13037 * Fires when tick the element
13038 * @param {Roo.bootstrap.ComboBox} combo This combo box
13042 * @event touchviewdisplay
13043 * Fires when touch view require special display (default is using displayField)
13044 * @param {Roo.bootstrap.ComboBox} combo This combo box
13045 * @param {Object} cfg set html .
13047 'touchviewdisplay' : true
13052 this.tickItems = [];
13054 this.selectedIndex = -1;
13055 if(this.mode == 'local'){
13056 if(config.queryDelay === undefined){
13057 this.queryDelay = 10;
13059 if(config.minChars === undefined){
13065 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13068 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13069 * rendering into an Roo.Editor, defaults to false)
13072 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13073 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13076 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13079 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13080 * the dropdown list (defaults to undefined, with no header element)
13084 * @cfg {String/Roo.Template} tpl The template to use to render the output
13088 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13090 listWidth: undefined,
13092 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13093 * mode = 'remote' or 'text' if mode = 'local')
13095 displayField: undefined,
13098 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13099 * mode = 'remote' or 'value' if mode = 'local').
13100 * Note: use of a valueField requires the user make a selection
13101 * in order for a value to be mapped.
13103 valueField: undefined,
13105 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13110 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13111 * field's data value (defaults to the underlying DOM element's name)
13113 hiddenName: undefined,
13115 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13119 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13121 selectedClass: 'active',
13124 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13128 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13129 * anchor positions (defaults to 'tl-bl')
13131 listAlign: 'tl-bl?',
13133 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13137 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13138 * query specified by the allQuery config option (defaults to 'query')
13140 triggerAction: 'query',
13142 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13143 * (defaults to 4, does not apply if editable = false)
13147 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13148 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13152 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13153 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13157 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13158 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13162 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13163 * when editable = true (defaults to false)
13165 selectOnFocus:false,
13167 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13169 queryParam: 'query',
13171 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13172 * when mode = 'remote' (defaults to 'Loading...')
13174 loadingText: 'Loading...',
13176 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13180 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13184 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13185 * traditional select (defaults to true)
13189 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13193 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13197 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13198 * listWidth has a higher value)
13202 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13203 * allow the user to set arbitrary text into the field (defaults to false)
13205 forceSelection:false,
13207 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13208 * if typeAhead = true (defaults to 250)
13210 typeAheadDelay : 250,
13212 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13213 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13215 valueNotFoundText : undefined,
13217 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13219 blockFocus : false,
13222 * @cfg {Boolean} disableClear Disable showing of clear button.
13224 disableClear : false,
13226 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13228 alwaysQuery : false,
13231 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13236 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13238 invalidClass : "has-warning",
13241 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13243 validClass : "has-success",
13246 * @cfg {Boolean} specialFilter (true|false) special filter default false
13248 specialFilter : false,
13251 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13253 mobileTouchView : true,
13256 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13258 useNativeIOS : false,
13261 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13263 mobile_restrict_height : false,
13265 ios_options : false,
13277 btnPosition : 'right',
13278 triggerList : true,
13279 showToggleBtn : true,
13281 emptyResultText: 'Empty',
13282 triggerText : 'Select',
13285 // element that contains real text value.. (when hidden is used..)
13287 getAutoCreate : function()
13292 * Render classic select for iso
13295 if(Roo.isIOS && this.useNativeIOS){
13296 cfg = this.getAutoCreateNativeIOS();
13304 if(Roo.isTouch && this.mobileTouchView){
13305 cfg = this.getAutoCreateTouchView();
13312 if(!this.tickable){
13313 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13318 * ComboBox with tickable selections
13321 var align = this.labelAlign || this.parentLabelAlign();
13324 cls : 'form-group roo-combobox-tickable' //input-group
13327 var btn_text_select = '';
13328 var btn_text_done = '';
13329 var btn_text_cancel = '';
13331 if (this.btn_text_show) {
13332 btn_text_select = 'Select';
13333 btn_text_done = 'Done';
13334 btn_text_cancel = 'Cancel';
13339 cls : 'tickable-buttons',
13344 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13345 //html : this.triggerText
13346 html: btn_text_select
13352 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13354 html: btn_text_done
13360 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13362 html: btn_text_cancel
13368 buttons.cn.unshift({
13370 cls: 'roo-select2-search-field-input'
13376 Roo.each(buttons.cn, function(c){
13378 c.cls += ' btn-' + _this.size;
13381 if (_this.disabled) {
13388 style : 'display: contents',
13393 cls: 'form-hidden-field'
13397 cls: 'roo-select2-choices',
13401 cls: 'roo-select2-search-field',
13412 cls: 'roo-select2-container input-group roo-select2-container-multi',
13418 // cls: 'typeahead typeahead-long dropdown-menu',
13419 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13424 if(this.hasFeedback && !this.allowBlank){
13428 cls: 'glyphicon form-control-feedback'
13431 combobox.cn.push(feedback);
13436 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13437 tooltip : 'This field is required'
13439 if (Roo.bootstrap.version == 4) {
13442 style : 'display:none'
13445 if (align ==='left' && this.fieldLabel.length) {
13447 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13454 cls : 'control-label col-form-label',
13455 html : this.fieldLabel
13467 var labelCfg = cfg.cn[1];
13468 var contentCfg = cfg.cn[2];
13471 if(this.indicatorpos == 'right'){
13477 cls : 'control-label col-form-label',
13481 html : this.fieldLabel
13497 labelCfg = cfg.cn[0];
13498 contentCfg = cfg.cn[1];
13502 if(this.labelWidth > 12){
13503 labelCfg.style = "width: " + this.labelWidth + 'px';
13506 if(this.labelWidth < 13 && this.labelmd == 0){
13507 this.labelmd = this.labelWidth;
13510 if(this.labellg > 0){
13511 labelCfg.cls += ' col-lg-' + this.labellg;
13512 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13515 if(this.labelmd > 0){
13516 labelCfg.cls += ' col-md-' + this.labelmd;
13517 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13520 if(this.labelsm > 0){
13521 labelCfg.cls += ' col-sm-' + this.labelsm;
13522 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13525 if(this.labelxs > 0){
13526 labelCfg.cls += ' col-xs-' + this.labelxs;
13527 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13531 } else if ( this.fieldLabel.length) {
13532 // Roo.log(" label");
13537 //cls : 'input-group-addon',
13538 html : this.fieldLabel
13543 if(this.indicatorpos == 'right'){
13547 //cls : 'input-group-addon',
13548 html : this.fieldLabel
13558 // Roo.log(" no label && no align");
13565 ['xs','sm','md','lg'].map(function(size){
13566 if (settings[size]) {
13567 cfg.cls += ' col-' + size + '-' + settings[size];
13575 _initEventsCalled : false,
13578 initEvents: function()
13580 if (this._initEventsCalled) { // as we call render... prevent looping...
13583 this._initEventsCalled = true;
13586 throw "can not find store for combo";
13589 this.indicator = this.indicatorEl();
13591 this.store = Roo.factory(this.store, Roo.data);
13592 this.store.parent = this;
13594 // if we are building from html. then this element is so complex, that we can not really
13595 // use the rendered HTML.
13596 // so we have to trash and replace the previous code.
13597 if (Roo.XComponent.build_from_html) {
13598 // remove this element....
13599 var e = this.el.dom, k=0;
13600 while (e ) { e = e.previousSibling; ++k;}
13605 this.rendered = false;
13607 this.render(this.parent().getChildContainer(true), k);
13610 if(Roo.isIOS && this.useNativeIOS){
13611 this.initIOSView();
13619 if(Roo.isTouch && this.mobileTouchView){
13620 this.initTouchView();
13625 this.initTickableEvents();
13629 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13631 if(this.hiddenName){
13633 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13635 this.hiddenField.dom.value =
13636 this.hiddenValue !== undefined ? this.hiddenValue :
13637 this.value !== undefined ? this.value : '';
13639 // prevent input submission
13640 this.el.dom.removeAttribute('name');
13641 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13646 // this.el.dom.setAttribute('autocomplete', 'off');
13649 var cls = 'x-combo-list';
13651 //this.list = new Roo.Layer({
13652 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13658 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13659 _this.list.setWidth(lw);
13662 this.list.on('mouseover', this.onViewOver, this);
13663 this.list.on('mousemove', this.onViewMove, this);
13664 this.list.on('scroll', this.onViewScroll, this);
13667 this.list.swallowEvent('mousewheel');
13668 this.assetHeight = 0;
13671 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13672 this.assetHeight += this.header.getHeight();
13675 this.innerList = this.list.createChild({cls:cls+'-inner'});
13676 this.innerList.on('mouseover', this.onViewOver, this);
13677 this.innerList.on('mousemove', this.onViewMove, this);
13678 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13680 if(this.allowBlank && !this.pageSize && !this.disableClear){
13681 this.footer = this.list.createChild({cls:cls+'-ft'});
13682 this.pageTb = new Roo.Toolbar(this.footer);
13686 this.footer = this.list.createChild({cls:cls+'-ft'});
13687 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13688 {pageSize: this.pageSize});
13692 if (this.pageTb && this.allowBlank && !this.disableClear) {
13694 this.pageTb.add(new Roo.Toolbar.Fill(), {
13695 cls: 'x-btn-icon x-btn-clear',
13697 handler: function()
13700 _this.clearValue();
13701 _this.onSelect(false, -1);
13706 this.assetHeight += this.footer.getHeight();
13711 this.tpl = Roo.bootstrap.version == 4 ?
13712 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13713 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13716 this.view = new Roo.View(this.list, this.tpl, {
13717 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13719 //this.view.wrapEl.setDisplayed(false);
13720 this.view.on('click', this.onViewClick, this);
13723 this.store.on('beforeload', this.onBeforeLoad, this);
13724 this.store.on('load', this.onLoad, this);
13725 this.store.on('loadexception', this.onLoadException, this);
13727 if(this.resizable){
13728 this.resizer = new Roo.Resizable(this.list, {
13729 pinned:true, handles:'se'
13731 this.resizer.on('resize', function(r, w, h){
13732 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13733 this.listWidth = w;
13734 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13735 this.restrictHeight();
13737 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13740 if(!this.editable){
13741 this.editable = true;
13742 this.setEditable(false);
13747 if (typeof(this.events.add.listeners) != 'undefined') {
13749 this.addicon = this.wrap.createChild(
13750 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13752 this.addicon.on('click', function(e) {
13753 this.fireEvent('add', this);
13756 if (typeof(this.events.edit.listeners) != 'undefined') {
13758 this.editicon = this.wrap.createChild(
13759 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13760 if (this.addicon) {
13761 this.editicon.setStyle('margin-left', '40px');
13763 this.editicon.on('click', function(e) {
13765 // we fire even if inothing is selected..
13766 this.fireEvent('edit', this, this.lastData );
13772 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13773 "up" : function(e){
13774 this.inKeyMode = true;
13778 "down" : function(e){
13779 if(!this.isExpanded()){
13780 this.onTriggerClick();
13782 this.inKeyMode = true;
13787 "enter" : function(e){
13788 // this.onViewClick();
13792 if(this.fireEvent("specialkey", this, e)){
13793 this.onViewClick(false);
13799 "esc" : function(e){
13803 "tab" : function(e){
13806 if(this.fireEvent("specialkey", this, e)){
13807 this.onViewClick(false);
13815 doRelay : function(foo, bar, hname){
13816 if(hname == 'down' || this.scope.isExpanded()){
13817 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13826 this.queryDelay = Math.max(this.queryDelay || 10,
13827 this.mode == 'local' ? 10 : 250);
13830 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13832 if(this.typeAhead){
13833 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13835 if(this.editable !== false){
13836 this.inputEl().on("keyup", this.onKeyUp, this);
13838 if(this.forceSelection){
13839 this.inputEl().on('blur', this.doForce, this);
13843 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13844 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13848 initTickableEvents: function()
13852 if(this.hiddenName){
13854 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13856 this.hiddenField.dom.value =
13857 this.hiddenValue !== undefined ? this.hiddenValue :
13858 this.value !== undefined ? this.value : '';
13860 // prevent input submission
13861 this.el.dom.removeAttribute('name');
13862 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13867 // this.list = this.el.select('ul.dropdown-menu',true).first();
13869 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13870 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13871 if(this.triggerList){
13872 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13875 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13876 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13878 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13879 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13881 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13882 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13884 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13885 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13886 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13889 this.cancelBtn.hide();
13894 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13895 _this.list.setWidth(lw);
13898 this.list.on('mouseover', this.onViewOver, this);
13899 this.list.on('mousemove', this.onViewMove, this);
13901 this.list.on('scroll', this.onViewScroll, this);
13904 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13905 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13908 this.view = new Roo.View(this.list, this.tpl, {
13913 selectedClass: this.selectedClass
13916 //this.view.wrapEl.setDisplayed(false);
13917 this.view.on('click', this.onViewClick, this);
13921 this.store.on('beforeload', this.onBeforeLoad, this);
13922 this.store.on('load', this.onLoad, this);
13923 this.store.on('loadexception', this.onLoadException, this);
13926 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13927 "up" : function(e){
13928 this.inKeyMode = true;
13932 "down" : function(e){
13933 this.inKeyMode = true;
13937 "enter" : function(e){
13938 if(this.fireEvent("specialkey", this, e)){
13939 this.onViewClick(false);
13945 "esc" : function(e){
13946 this.onTickableFooterButtonClick(e, false, false);
13949 "tab" : function(e){
13950 this.fireEvent("specialkey", this, e);
13952 this.onTickableFooterButtonClick(e, false, false);
13959 doRelay : function(e, fn, key){
13960 if(this.scope.isExpanded()){
13961 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13970 this.queryDelay = Math.max(this.queryDelay || 10,
13971 this.mode == 'local' ? 10 : 250);
13974 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13976 if(this.typeAhead){
13977 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13980 if(this.editable !== false){
13981 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13984 this.indicator = this.indicatorEl();
13986 if(this.indicator){
13987 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13988 this.indicator.hide();
13993 onDestroy : function(){
13995 this.view.setStore(null);
13996 this.view.el.removeAllListeners();
13997 this.view.el.remove();
13998 this.view.purgeListeners();
14001 this.list.dom.innerHTML = '';
14005 this.store.un('beforeload', this.onBeforeLoad, this);
14006 this.store.un('load', this.onLoad, this);
14007 this.store.un('loadexception', this.onLoadException, this);
14009 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14013 fireKey : function(e){
14014 if(e.isNavKeyPress() && !this.list.isVisible()){
14015 this.fireEvent("specialkey", this, e);
14020 onResize: function(w, h){
14021 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14023 // if(typeof w != 'number'){
14024 // // we do not handle it!?!?
14027 // var tw = this.trigger.getWidth();
14028 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14029 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14031 // this.inputEl().setWidth( this.adjustWidth('input', x));
14033 // //this.trigger.setStyle('left', x+'px');
14035 // if(this.list && this.listWidth === undefined){
14036 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14037 // this.list.setWidth(lw);
14038 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14046 * Allow or prevent the user from directly editing the field text. If false is passed,
14047 * the user will only be able to select from the items defined in the dropdown list. This method
14048 * is the runtime equivalent of setting the 'editable' config option at config time.
14049 * @param {Boolean} value True to allow the user to directly edit the field text
14051 setEditable : function(value){
14052 if(value == this.editable){
14055 this.editable = value;
14057 this.inputEl().dom.setAttribute('readOnly', true);
14058 this.inputEl().on('mousedown', this.onTriggerClick, this);
14059 this.inputEl().addClass('x-combo-noedit');
14061 this.inputEl().dom.setAttribute('readOnly', false);
14062 this.inputEl().un('mousedown', this.onTriggerClick, this);
14063 this.inputEl().removeClass('x-combo-noedit');
14069 onBeforeLoad : function(combo,opts){
14070 if(!this.hasFocus){
14074 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14076 this.restrictHeight();
14077 this.selectedIndex = -1;
14081 onLoad : function(){
14083 this.hasQuery = false;
14085 if(!this.hasFocus){
14089 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14090 this.loading.hide();
14093 if(this.store.getCount() > 0){
14096 this.restrictHeight();
14097 if(this.lastQuery == this.allQuery){
14098 if(this.editable && !this.tickable){
14099 this.inputEl().dom.select();
14103 !this.selectByValue(this.value, true) &&
14106 !this.store.lastOptions ||
14107 typeof(this.store.lastOptions.add) == 'undefined' ||
14108 this.store.lastOptions.add != true
14111 this.select(0, true);
14114 if(this.autoFocus){
14117 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14118 this.taTask.delay(this.typeAheadDelay);
14122 this.onEmptyResults();
14128 onLoadException : function()
14130 this.hasQuery = false;
14132 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14133 this.loading.hide();
14136 if(this.tickable && this.editable){
14141 // only causes errors at present
14142 //Roo.log(this.store.reader.jsonData);
14143 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14145 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14151 onTypeAhead : function(){
14152 if(this.store.getCount() > 0){
14153 var r = this.store.getAt(0);
14154 var newValue = r.data[this.displayField];
14155 var len = newValue.length;
14156 var selStart = this.getRawValue().length;
14158 if(selStart != len){
14159 this.setRawValue(newValue);
14160 this.selectText(selStart, newValue.length);
14166 onSelect : function(record, index){
14168 if(this.fireEvent('beforeselect', this, record, index) !== false){
14170 this.setFromData(index > -1 ? record.data : false);
14173 this.fireEvent('select', this, record, index);
14178 * Returns the currently selected field value or empty string if no value is set.
14179 * @return {String} value The selected value
14181 getValue : function()
14183 if(Roo.isIOS && this.useNativeIOS){
14184 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14188 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14191 if(this.valueField){
14192 return typeof this.value != 'undefined' ? this.value : '';
14194 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14198 getRawValue : function()
14200 if(Roo.isIOS && this.useNativeIOS){
14201 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14204 var v = this.inputEl().getValue();
14210 * Clears any text/value currently set in the field
14212 clearValue : function(){
14214 if(this.hiddenField){
14215 this.hiddenField.dom.value = '';
14218 this.setRawValue('');
14219 this.lastSelectionText = '';
14220 this.lastData = false;
14222 var close = this.closeTriggerEl();
14233 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14234 * will be displayed in the field. If the value does not match the data value of an existing item,
14235 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14236 * Otherwise the field will be blank (although the value will still be set).
14237 * @param {String} value The value to match
14239 setValue : function(v)
14241 if(Roo.isIOS && this.useNativeIOS){
14242 this.setIOSValue(v);
14252 if(this.valueField){
14253 var r = this.findRecord(this.valueField, v);
14255 text = r.data[this.displayField];
14256 }else if(this.valueNotFoundText !== undefined){
14257 text = this.valueNotFoundText;
14260 this.lastSelectionText = text;
14261 if(this.hiddenField){
14262 this.hiddenField.dom.value = v;
14264 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14267 var close = this.closeTriggerEl();
14270 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14276 * @property {Object} the last set data for the element
14281 * Sets the value of the field based on a object which is related to the record format for the store.
14282 * @param {Object} value the value to set as. or false on reset?
14284 setFromData : function(o){
14291 var dv = ''; // display value
14292 var vv = ''; // value value..
14294 if (this.displayField) {
14295 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14297 // this is an error condition!!!
14298 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14301 if(this.valueField){
14302 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14305 var close = this.closeTriggerEl();
14308 if(dv.length || vv * 1 > 0){
14310 this.blockFocus=true;
14316 if(this.hiddenField){
14317 this.hiddenField.dom.value = vv;
14319 this.lastSelectionText = dv;
14320 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14324 // no hidden field.. - we store the value in 'value', but still display
14325 // display field!!!!
14326 this.lastSelectionText = dv;
14327 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14334 reset : function(){
14335 // overridden so that last data is reset..
14342 this.setValue(this.originalValue);
14343 //this.clearInvalid();
14344 this.lastData = false;
14346 this.view.clearSelections();
14352 findRecord : function(prop, value){
14354 if(this.store.getCount() > 0){
14355 this.store.each(function(r){
14356 if(r.data[prop] == value){
14366 getName: function()
14368 // returns hidden if it's set..
14369 if (!this.rendered) {return ''};
14370 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14374 onViewMove : function(e, t){
14375 this.inKeyMode = false;
14379 onViewOver : function(e, t){
14380 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14383 var item = this.view.findItemFromChild(t);
14386 var index = this.view.indexOf(item);
14387 this.select(index, false);
14392 onViewClick : function(view, doFocus, el, e)
14394 var index = this.view.getSelectedIndexes()[0];
14396 var r = this.store.getAt(index);
14400 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14407 Roo.each(this.tickItems, function(v,k){
14409 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14411 _this.tickItems.splice(k, 1);
14413 if(typeof(e) == 'undefined' && view == false){
14414 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14426 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14427 this.tickItems.push(r.data);
14430 if(typeof(e) == 'undefined' && view == false){
14431 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14438 this.onSelect(r, index);
14440 if(doFocus !== false && !this.blockFocus){
14441 this.inputEl().focus();
14446 restrictHeight : function(){
14447 //this.innerList.dom.style.height = '';
14448 //var inner = this.innerList.dom;
14449 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14450 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14451 //this.list.beginUpdate();
14452 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14453 this.list.alignTo(this.inputEl(), this.listAlign);
14454 this.list.alignTo(this.inputEl(), this.listAlign);
14455 //this.list.endUpdate();
14459 onEmptyResults : function(){
14461 if(this.tickable && this.editable){
14462 this.hasFocus = false;
14463 this.restrictHeight();
14471 * Returns true if the dropdown list is expanded, else false.
14473 isExpanded : function(){
14474 return this.list.isVisible();
14478 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14479 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14480 * @param {String} value The data value of the item to select
14481 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14482 * selected item if it is not currently in view (defaults to true)
14483 * @return {Boolean} True if the value matched an item in the list, else false
14485 selectByValue : function(v, scrollIntoView){
14486 if(v !== undefined && v !== null){
14487 var r = this.findRecord(this.valueField || this.displayField, v);
14489 this.select(this.store.indexOf(r), scrollIntoView);
14497 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14498 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14499 * @param {Number} index The zero-based index of the list item to select
14500 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14501 * selected item if it is not currently in view (defaults to true)
14503 select : function(index, scrollIntoView){
14504 this.selectedIndex = index;
14505 this.view.select(index);
14506 if(scrollIntoView !== false){
14507 var el = this.view.getNode(index);
14509 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14512 this.list.scrollChildIntoView(el, false);
14518 selectNext : function(){
14519 var ct = this.store.getCount();
14521 if(this.selectedIndex == -1){
14523 }else if(this.selectedIndex < ct-1){
14524 this.select(this.selectedIndex+1);
14530 selectPrev : function(){
14531 var ct = this.store.getCount();
14533 if(this.selectedIndex == -1){
14535 }else if(this.selectedIndex != 0){
14536 this.select(this.selectedIndex-1);
14542 onKeyUp : function(e){
14543 if(this.editable !== false && !e.isSpecialKey()){
14544 this.lastKey = e.getKey();
14545 this.dqTask.delay(this.queryDelay);
14550 validateBlur : function(){
14551 return !this.list || !this.list.isVisible();
14555 initQuery : function(){
14557 var v = this.getRawValue();
14559 if(this.tickable && this.editable){
14560 v = this.tickableInputEl().getValue();
14567 doForce : function(){
14568 if(this.inputEl().dom.value.length > 0){
14569 this.inputEl().dom.value =
14570 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14576 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14577 * query allowing the query action to be canceled if needed.
14578 * @param {String} query The SQL query to execute
14579 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14580 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14581 * saved in the current store (defaults to false)
14583 doQuery : function(q, forceAll){
14585 if(q === undefined || q === null){
14590 forceAll: forceAll,
14594 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14599 forceAll = qe.forceAll;
14600 if(forceAll === true || (q.length >= this.minChars)){
14602 this.hasQuery = true;
14604 if(this.lastQuery != q || this.alwaysQuery){
14605 this.lastQuery = q;
14606 if(this.mode == 'local'){
14607 this.selectedIndex = -1;
14609 this.store.clearFilter();
14612 if(this.specialFilter){
14613 this.fireEvent('specialfilter', this);
14618 this.store.filter(this.displayField, q);
14621 this.store.fireEvent("datachanged", this.store);
14628 this.store.baseParams[this.queryParam] = q;
14630 var options = {params : this.getParams(q)};
14633 options.add = true;
14634 options.params.start = this.page * this.pageSize;
14637 this.store.load(options);
14640 * this code will make the page width larger, at the beginning, the list not align correctly,
14641 * we should expand the list on onLoad
14642 * so command out it
14647 this.selectedIndex = -1;
14652 this.loadNext = false;
14656 getParams : function(q){
14658 //p[this.queryParam] = q;
14662 p.limit = this.pageSize;
14668 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14670 collapse : function(){
14671 if(!this.isExpanded()){
14677 this.hasFocus = false;
14681 this.cancelBtn.hide();
14682 this.trigger.show();
14685 this.tickableInputEl().dom.value = '';
14686 this.tickableInputEl().blur();
14691 Roo.get(document).un('mousedown', this.collapseIf, this);
14692 Roo.get(document).un('mousewheel', this.collapseIf, this);
14693 if (!this.editable) {
14694 Roo.get(document).un('keydown', this.listKeyPress, this);
14696 this.fireEvent('collapse', this);
14702 collapseIf : function(e){
14703 var in_combo = e.within(this.el);
14704 var in_list = e.within(this.list);
14705 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14707 if (in_combo || in_list || is_list) {
14708 //e.stopPropagation();
14713 this.onTickableFooterButtonClick(e, false, false);
14721 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14723 expand : function(){
14725 if(this.isExpanded() || !this.hasFocus){
14729 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14730 this.list.setWidth(lw);
14736 this.restrictHeight();
14740 this.tickItems = Roo.apply([], this.item);
14743 this.cancelBtn.show();
14744 this.trigger.hide();
14747 this.tickableInputEl().focus();
14752 Roo.get(document).on('mousedown', this.collapseIf, this);
14753 Roo.get(document).on('mousewheel', this.collapseIf, this);
14754 if (!this.editable) {
14755 Roo.get(document).on('keydown', this.listKeyPress, this);
14758 this.fireEvent('expand', this);
14762 // Implements the default empty TriggerField.onTriggerClick function
14763 onTriggerClick : function(e)
14765 Roo.log('trigger click');
14767 if(this.disabled || !this.triggerList){
14772 this.loadNext = false;
14774 if(this.isExpanded()){
14776 if (!this.blockFocus) {
14777 this.inputEl().focus();
14781 this.hasFocus = true;
14782 if(this.triggerAction == 'all') {
14783 this.doQuery(this.allQuery, true);
14785 this.doQuery(this.getRawValue());
14787 if (!this.blockFocus) {
14788 this.inputEl().focus();
14793 onTickableTriggerClick : function(e)
14800 this.loadNext = false;
14801 this.hasFocus = true;
14803 if(this.triggerAction == 'all') {
14804 this.doQuery(this.allQuery, true);
14806 this.doQuery(this.getRawValue());
14810 onSearchFieldClick : function(e)
14812 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14813 this.onTickableFooterButtonClick(e, false, false);
14817 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14822 this.loadNext = false;
14823 this.hasFocus = true;
14825 if(this.triggerAction == 'all') {
14826 this.doQuery(this.allQuery, true);
14828 this.doQuery(this.getRawValue());
14832 listKeyPress : function(e)
14834 //Roo.log('listkeypress');
14835 // scroll to first matching element based on key pres..
14836 if (e.isSpecialKey()) {
14839 var k = String.fromCharCode(e.getKey()).toUpperCase();
14842 var csel = this.view.getSelectedNodes();
14843 var cselitem = false;
14845 var ix = this.view.indexOf(csel[0]);
14846 cselitem = this.store.getAt(ix);
14847 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14853 this.store.each(function(v) {
14855 // start at existing selection.
14856 if (cselitem.id == v.id) {
14862 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14863 match = this.store.indexOf(v);
14869 if (match === false) {
14870 return true; // no more action?
14873 this.view.select(match);
14874 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14875 sn.scrollIntoView(sn.dom.parentNode, false);
14878 onViewScroll : function(e, t){
14880 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){
14884 this.hasQuery = true;
14886 this.loading = this.list.select('.loading', true).first();
14888 if(this.loading === null){
14889 this.list.createChild({
14891 cls: 'loading roo-select2-more-results roo-select2-active',
14892 html: 'Loading more results...'
14895 this.loading = this.list.select('.loading', true).first();
14897 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14899 this.loading.hide();
14902 this.loading.show();
14907 this.loadNext = true;
14909 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14914 addItem : function(o)
14916 var dv = ''; // display value
14918 if (this.displayField) {
14919 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14921 // this is an error condition!!!
14922 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14929 var choice = this.choices.createChild({
14931 cls: 'roo-select2-search-choice',
14940 cls: 'roo-select2-search-choice-close fa fa-times',
14945 }, this.searchField);
14947 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14949 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14957 this.inputEl().dom.value = '';
14962 onRemoveItem : function(e, _self, o)
14964 e.preventDefault();
14966 this.lastItem = Roo.apply([], this.item);
14968 var index = this.item.indexOf(o.data) * 1;
14971 Roo.log('not this item?!');
14975 this.item.splice(index, 1);
14980 this.fireEvent('remove', this, e);
14986 syncValue : function()
14988 if(!this.item.length){
14995 Roo.each(this.item, function(i){
14996 if(_this.valueField){
14997 value.push(i[_this.valueField]);
15004 this.value = value.join(',');
15006 if(this.hiddenField){
15007 this.hiddenField.dom.value = this.value;
15010 this.store.fireEvent("datachanged", this.store);
15015 clearItem : function()
15017 if(!this.multiple){
15023 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15031 if(this.tickable && !Roo.isTouch){
15032 this.view.refresh();
15036 inputEl: function ()
15038 if(Roo.isIOS && this.useNativeIOS){
15039 return this.el.select('select.roo-ios-select', true).first();
15042 if(Roo.isTouch && this.mobileTouchView){
15043 return this.el.select('input.form-control',true).first();
15047 return this.searchField;
15050 return this.el.select('input.form-control',true).first();
15053 onTickableFooterButtonClick : function(e, btn, el)
15055 e.preventDefault();
15057 this.lastItem = Roo.apply([], this.item);
15059 if(btn && btn.name == 'cancel'){
15060 this.tickItems = Roo.apply([], this.item);
15069 Roo.each(this.tickItems, function(o){
15077 validate : function()
15079 if(this.getVisibilityEl().hasClass('hidden')){
15083 var v = this.getRawValue();
15086 v = this.getValue();
15089 if(this.disabled || this.allowBlank || v.length){
15094 this.markInvalid();
15098 tickableInputEl : function()
15100 if(!this.tickable || !this.editable){
15101 return this.inputEl();
15104 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15108 getAutoCreateTouchView : function()
15113 cls: 'form-group' //input-group
15119 type : this.inputType,
15120 cls : 'form-control x-combo-noedit',
15121 autocomplete: 'new-password',
15122 placeholder : this.placeholder || '',
15127 input.name = this.name;
15131 input.cls += ' input-' + this.size;
15134 if (this.disabled) {
15135 input.disabled = true;
15146 inputblock.cls += ' input-group';
15148 inputblock.cn.unshift({
15150 cls : 'input-group-addon input-group-prepend input-group-text',
15155 if(this.removable && !this.multiple){
15156 inputblock.cls += ' roo-removable';
15158 inputblock.cn.push({
15161 cls : 'roo-combo-removable-btn close'
15165 if(this.hasFeedback && !this.allowBlank){
15167 inputblock.cls += ' has-feedback';
15169 inputblock.cn.push({
15171 cls: 'glyphicon form-control-feedback'
15178 inputblock.cls += (this.before) ? '' : ' input-group';
15180 inputblock.cn.push({
15182 cls : 'input-group-addon input-group-append input-group-text',
15188 var ibwrap = inputblock;
15193 cls: 'roo-select2-choices',
15197 cls: 'roo-select2-search-field',
15210 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15215 cls: 'form-hidden-field'
15221 if(!this.multiple && this.showToggleBtn){
15228 if (this.caret != false) {
15231 cls: 'fa fa-' + this.caret
15238 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15243 cls: 'combobox-clear',
15257 combobox.cls += ' roo-select2-container-multi';
15260 var align = this.labelAlign || this.parentLabelAlign();
15262 if (align ==='left' && this.fieldLabel.length) {
15267 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15268 tooltip : 'This field is required'
15272 cls : 'control-label col-form-label',
15273 html : this.fieldLabel
15284 var labelCfg = cfg.cn[1];
15285 var contentCfg = cfg.cn[2];
15288 if(this.indicatorpos == 'right'){
15293 cls : 'control-label col-form-label',
15297 html : this.fieldLabel
15301 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15302 tooltip : 'This field is required'
15315 labelCfg = cfg.cn[0];
15316 contentCfg = cfg.cn[1];
15321 if(this.labelWidth > 12){
15322 labelCfg.style = "width: " + this.labelWidth + 'px';
15325 if(this.labelWidth < 13 && this.labelmd == 0){
15326 this.labelmd = this.labelWidth;
15329 if(this.labellg > 0){
15330 labelCfg.cls += ' col-lg-' + this.labellg;
15331 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15334 if(this.labelmd > 0){
15335 labelCfg.cls += ' col-md-' + this.labelmd;
15336 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15339 if(this.labelsm > 0){
15340 labelCfg.cls += ' col-sm-' + this.labelsm;
15341 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15344 if(this.labelxs > 0){
15345 labelCfg.cls += ' col-xs-' + this.labelxs;
15346 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15350 } else if ( this.fieldLabel.length) {
15354 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15355 tooltip : 'This field is required'
15359 cls : 'control-label',
15360 html : this.fieldLabel
15371 if(this.indicatorpos == 'right'){
15375 cls : 'control-label',
15376 html : this.fieldLabel,
15380 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15381 tooltip : 'This field is required'
15398 var settings = this;
15400 ['xs','sm','md','lg'].map(function(size){
15401 if (settings[size]) {
15402 cfg.cls += ' col-' + size + '-' + settings[size];
15409 initTouchView : function()
15411 this.renderTouchView();
15413 this.touchViewEl.on('scroll', function(){
15414 this.el.dom.scrollTop = 0;
15417 this.originalValue = this.getValue();
15419 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15421 this.inputEl().on("click", this.showTouchView, this);
15422 if (this.triggerEl) {
15423 this.triggerEl.on("click", this.showTouchView, this);
15427 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15428 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15430 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15432 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15433 this.store.on('load', this.onTouchViewLoad, this);
15434 this.store.on('loadexception', this.onTouchViewLoadException, this);
15436 if(this.hiddenName){
15438 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15440 this.hiddenField.dom.value =
15441 this.hiddenValue !== undefined ? this.hiddenValue :
15442 this.value !== undefined ? this.value : '';
15444 this.el.dom.removeAttribute('name');
15445 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15449 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15450 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15453 if(this.removable && !this.multiple){
15454 var close = this.closeTriggerEl();
15456 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15457 close.on('click', this.removeBtnClick, this, close);
15461 * fix the bug in Safari iOS8
15463 this.inputEl().on("focus", function(e){
15464 document.activeElement.blur();
15467 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15474 renderTouchView : function()
15476 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15477 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15479 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15480 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15482 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15483 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15484 this.touchViewBodyEl.setStyle('overflow', 'auto');
15486 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15487 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15489 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15490 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15494 showTouchView : function()
15500 this.touchViewHeaderEl.hide();
15502 if(this.modalTitle.length){
15503 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15504 this.touchViewHeaderEl.show();
15507 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15508 this.touchViewEl.show();
15510 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15512 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15513 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15515 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15517 if(this.modalTitle.length){
15518 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15521 this.touchViewBodyEl.setHeight(bodyHeight);
15525 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15527 this.touchViewEl.addClass('in');
15530 if(this._touchViewMask){
15531 Roo.get(document.body).addClass("x-body-masked");
15532 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15533 this._touchViewMask.setStyle('z-index', 10000);
15534 this._touchViewMask.addClass('show');
15537 this.doTouchViewQuery();
15541 hideTouchView : function()
15543 this.touchViewEl.removeClass('in');
15547 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15549 this.touchViewEl.setStyle('display', 'none');
15552 if(this._touchViewMask){
15553 this._touchViewMask.removeClass('show');
15554 Roo.get(document.body).removeClass("x-body-masked");
15558 setTouchViewValue : function()
15565 Roo.each(this.tickItems, function(o){
15570 this.hideTouchView();
15573 doTouchViewQuery : function()
15582 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15586 if(!this.alwaysQuery || this.mode == 'local'){
15587 this.onTouchViewLoad();
15594 onTouchViewBeforeLoad : function(combo,opts)
15600 onTouchViewLoad : function()
15602 if(this.store.getCount() < 1){
15603 this.onTouchViewEmptyResults();
15607 this.clearTouchView();
15609 var rawValue = this.getRawValue();
15611 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15613 this.tickItems = [];
15615 this.store.data.each(function(d, rowIndex){
15616 var row = this.touchViewListGroup.createChild(template);
15618 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15619 row.addClass(d.data.cls);
15622 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15625 html : d.data[this.displayField]
15628 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15629 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15632 row.removeClass('selected');
15633 if(!this.multiple && this.valueField &&
15634 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15637 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15638 row.addClass('selected');
15641 if(this.multiple && this.valueField &&
15642 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15646 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15647 this.tickItems.push(d.data);
15650 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15654 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15656 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15658 if(this.modalTitle.length){
15659 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15662 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15664 if(this.mobile_restrict_height && listHeight < bodyHeight){
15665 this.touchViewBodyEl.setHeight(listHeight);
15670 if(firstChecked && listHeight > bodyHeight){
15671 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15676 onTouchViewLoadException : function()
15678 this.hideTouchView();
15681 onTouchViewEmptyResults : function()
15683 this.clearTouchView();
15685 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15687 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15691 clearTouchView : function()
15693 this.touchViewListGroup.dom.innerHTML = '';
15696 onTouchViewClick : function(e, el, o)
15698 e.preventDefault();
15701 var rowIndex = o.rowIndex;
15703 var r = this.store.getAt(rowIndex);
15705 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15707 if(!this.multiple){
15708 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15709 c.dom.removeAttribute('checked');
15712 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15714 this.setFromData(r.data);
15716 var close = this.closeTriggerEl();
15722 this.hideTouchView();
15724 this.fireEvent('select', this, r, rowIndex);
15729 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15730 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15731 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15735 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15736 this.addItem(r.data);
15737 this.tickItems.push(r.data);
15741 getAutoCreateNativeIOS : function()
15744 cls: 'form-group' //input-group,
15749 cls : 'roo-ios-select'
15753 combobox.name = this.name;
15756 if (this.disabled) {
15757 combobox.disabled = true;
15760 var settings = this;
15762 ['xs','sm','md','lg'].map(function(size){
15763 if (settings[size]) {
15764 cfg.cls += ' col-' + size + '-' + settings[size];
15774 initIOSView : function()
15776 this.store.on('load', this.onIOSViewLoad, this);
15781 onIOSViewLoad : function()
15783 if(this.store.getCount() < 1){
15787 this.clearIOSView();
15789 if(this.allowBlank) {
15791 var default_text = '-- SELECT --';
15793 if(this.placeholder.length){
15794 default_text = this.placeholder;
15797 if(this.emptyTitle.length){
15798 default_text += ' - ' + this.emptyTitle + ' -';
15801 var opt = this.inputEl().createChild({
15804 html : default_text
15808 o[this.valueField] = 0;
15809 o[this.displayField] = default_text;
15811 this.ios_options.push({
15818 this.store.data.each(function(d, rowIndex){
15822 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15823 html = d.data[this.displayField];
15828 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15829 value = d.data[this.valueField];
15838 if(this.value == d.data[this.valueField]){
15839 option['selected'] = true;
15842 var opt = this.inputEl().createChild(option);
15844 this.ios_options.push({
15851 this.inputEl().on('change', function(){
15852 this.fireEvent('select', this);
15857 clearIOSView: function()
15859 this.inputEl().dom.innerHTML = '';
15861 this.ios_options = [];
15864 setIOSValue: function(v)
15868 if(!this.ios_options){
15872 Roo.each(this.ios_options, function(opts){
15874 opts.el.dom.removeAttribute('selected');
15876 if(opts.data[this.valueField] != v){
15880 opts.el.dom.setAttribute('selected', true);
15886 * @cfg {Boolean} grow
15890 * @cfg {Number} growMin
15894 * @cfg {Number} growMax
15903 Roo.apply(Roo.bootstrap.ComboBox, {
15907 cls: 'modal-header',
15929 cls: 'list-group-item',
15933 cls: 'roo-combobox-list-group-item-value'
15937 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15951 listItemCheckbox : {
15953 cls: 'list-group-item',
15957 cls: 'roo-combobox-list-group-item-value'
15961 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15977 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15982 cls: 'modal-footer',
15990 cls: 'col-xs-6 text-left',
15993 cls: 'btn btn-danger roo-touch-view-cancel',
15999 cls: 'col-xs-6 text-right',
16002 cls: 'btn btn-success roo-touch-view-ok',
16013 Roo.apply(Roo.bootstrap.ComboBox, {
16015 touchViewTemplate : {
16017 cls: 'modal fade roo-combobox-touch-view',
16021 cls: 'modal-dialog',
16022 style : 'position:fixed', // we have to fix position....
16026 cls: 'modal-content',
16028 Roo.bootstrap.ComboBox.header,
16029 Roo.bootstrap.ComboBox.body,
16030 Roo.bootstrap.ComboBox.footer
16039 * Ext JS Library 1.1.1
16040 * Copyright(c) 2006-2007, Ext JS, LLC.
16042 * Originally Released Under LGPL - original licence link has changed is not relivant.
16045 * <script type="text/javascript">
16050 * @extends Roo.util.Observable
16051 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16052 * This class also supports single and multi selection modes. <br>
16053 * Create a data model bound view:
16055 var store = new Roo.data.Store(...);
16057 var view = new Roo.View({
16059 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16061 singleSelect: true,
16062 selectedClass: "ydataview-selected",
16066 // listen for node click?
16067 view.on("click", function(vw, index, node, e){
16068 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16072 dataModel.load("foobar.xml");
16074 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16076 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16077 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16079 * Note: old style constructor is still suported (container, template, config)
16082 * Create a new View
16083 * @param {Object} config The config object
16086 Roo.View = function(config, depreciated_tpl, depreciated_config){
16088 this.parent = false;
16090 if (typeof(depreciated_tpl) == 'undefined') {
16091 // new way.. - universal constructor.
16092 Roo.apply(this, config);
16093 this.el = Roo.get(this.el);
16096 this.el = Roo.get(config);
16097 this.tpl = depreciated_tpl;
16098 Roo.apply(this, depreciated_config);
16100 this.wrapEl = this.el.wrap().wrap();
16101 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16104 if(typeof(this.tpl) == "string"){
16105 this.tpl = new Roo.Template(this.tpl);
16107 // support xtype ctors..
16108 this.tpl = new Roo.factory(this.tpl, Roo);
16112 this.tpl.compile();
16117 * @event beforeclick
16118 * Fires before a click is processed. Returns false to cancel the default action.
16119 * @param {Roo.View} this
16120 * @param {Number} index The index of the target node
16121 * @param {HTMLElement} node The target node
16122 * @param {Roo.EventObject} e The raw event object
16124 "beforeclick" : true,
16127 * Fires when a template node is clicked.
16128 * @param {Roo.View} this
16129 * @param {Number} index The index of the target node
16130 * @param {HTMLElement} node The target node
16131 * @param {Roo.EventObject} e The raw event object
16136 * Fires when a template node is double clicked.
16137 * @param {Roo.View} this
16138 * @param {Number} index The index of the target node
16139 * @param {HTMLElement} node The target node
16140 * @param {Roo.EventObject} e The raw event object
16144 * @event contextmenu
16145 * Fires when a template node is right clicked.
16146 * @param {Roo.View} this
16147 * @param {Number} index The index of the target node
16148 * @param {HTMLElement} node The target node
16149 * @param {Roo.EventObject} e The raw event object
16151 "contextmenu" : true,
16153 * @event selectionchange
16154 * Fires when the selected nodes change.
16155 * @param {Roo.View} this
16156 * @param {Array} selections Array of the selected nodes
16158 "selectionchange" : true,
16161 * @event beforeselect
16162 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16163 * @param {Roo.View} this
16164 * @param {HTMLElement} node The node to be selected
16165 * @param {Array} selections Array of currently selected nodes
16167 "beforeselect" : true,
16169 * @event preparedata
16170 * Fires on every row to render, to allow you to change the data.
16171 * @param {Roo.View} this
16172 * @param {Object} data to be rendered (change this)
16174 "preparedata" : true
16182 "click": this.onClick,
16183 "dblclick": this.onDblClick,
16184 "contextmenu": this.onContextMenu,
16188 this.selections = [];
16190 this.cmp = new Roo.CompositeElementLite([]);
16192 this.store = Roo.factory(this.store, Roo.data);
16193 this.setStore(this.store, true);
16196 if ( this.footer && this.footer.xtype) {
16198 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16200 this.footer.dataSource = this.store;
16201 this.footer.container = fctr;
16202 this.footer = Roo.factory(this.footer, Roo);
16203 fctr.insertFirst(this.el);
16205 // this is a bit insane - as the paging toolbar seems to detach the el..
16206 // dom.parentNode.parentNode.parentNode
16207 // they get detached?
16211 Roo.View.superclass.constructor.call(this);
16216 Roo.extend(Roo.View, Roo.util.Observable, {
16219 * @cfg {Roo.data.Store} store Data store to load data from.
16224 * @cfg {String|Roo.Element} el The container element.
16229 * @cfg {String|Roo.Template} tpl The template used by this View
16233 * @cfg {String} dataName the named area of the template to use as the data area
16234 * Works with domtemplates roo-name="name"
16238 * @cfg {String} selectedClass The css class to add to selected nodes
16240 selectedClass : "x-view-selected",
16242 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16247 * @cfg {String} text to display on mask (default Loading)
16251 * @cfg {Boolean} multiSelect Allow multiple selection
16253 multiSelect : false,
16255 * @cfg {Boolean} singleSelect Allow single selection
16257 singleSelect: false,
16260 * @cfg {Boolean} toggleSelect - selecting
16262 toggleSelect : false,
16265 * @cfg {Boolean} tickable - selecting
16270 * Returns the element this view is bound to.
16271 * @return {Roo.Element}
16273 getEl : function(){
16274 return this.wrapEl;
16280 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16282 refresh : function(){
16283 //Roo.log('refresh');
16286 // if we are using something like 'domtemplate', then
16287 // the what gets used is:
16288 // t.applySubtemplate(NAME, data, wrapping data..)
16289 // the outer template then get' applied with
16290 // the store 'extra data'
16291 // and the body get's added to the
16292 // roo-name="data" node?
16293 // <span class='roo-tpl-{name}'></span> ?????
16297 this.clearSelections();
16298 this.el.update("");
16300 var records = this.store.getRange();
16301 if(records.length < 1) {
16303 // is this valid?? = should it render a template??
16305 this.el.update(this.emptyText);
16309 if (this.dataName) {
16310 this.el.update(t.apply(this.store.meta)); //????
16311 el = this.el.child('.roo-tpl-' + this.dataName);
16314 for(var i = 0, len = records.length; i < len; i++){
16315 var data = this.prepareData(records[i].data, i, records[i]);
16316 this.fireEvent("preparedata", this, data, i, records[i]);
16318 var d = Roo.apply({}, data);
16321 Roo.apply(d, {'roo-id' : Roo.id()});
16325 Roo.each(this.parent.item, function(item){
16326 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16329 Roo.apply(d, {'roo-data-checked' : 'checked'});
16333 html[html.length] = Roo.util.Format.trim(
16335 t.applySubtemplate(this.dataName, d, this.store.meta) :
16342 el.update(html.join(""));
16343 this.nodes = el.dom.childNodes;
16344 this.updateIndexes(0);
16349 * Function to override to reformat the data that is sent to
16350 * the template for each node.
16351 * DEPRICATED - use the preparedata event handler.
16352 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16353 * a JSON object for an UpdateManager bound view).
16355 prepareData : function(data, index, record)
16357 this.fireEvent("preparedata", this, data, index, record);
16361 onUpdate : function(ds, record){
16362 // Roo.log('on update');
16363 this.clearSelections();
16364 var index = this.store.indexOf(record);
16365 var n = this.nodes[index];
16366 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16367 n.parentNode.removeChild(n);
16368 this.updateIndexes(index, index);
16374 onAdd : function(ds, records, index)
16376 //Roo.log(['on Add', ds, records, index] );
16377 this.clearSelections();
16378 if(this.nodes.length == 0){
16382 var n = this.nodes[index];
16383 for(var i = 0, len = records.length; i < len; i++){
16384 var d = this.prepareData(records[i].data, i, records[i]);
16386 this.tpl.insertBefore(n, d);
16389 this.tpl.append(this.el, d);
16392 this.updateIndexes(index);
16395 onRemove : function(ds, record, index){
16396 // Roo.log('onRemove');
16397 this.clearSelections();
16398 var el = this.dataName ?
16399 this.el.child('.roo-tpl-' + this.dataName) :
16402 el.dom.removeChild(this.nodes[index]);
16403 this.updateIndexes(index);
16407 * Refresh an individual node.
16408 * @param {Number} index
16410 refreshNode : function(index){
16411 this.onUpdate(this.store, this.store.getAt(index));
16414 updateIndexes : function(startIndex, endIndex){
16415 var ns = this.nodes;
16416 startIndex = startIndex || 0;
16417 endIndex = endIndex || ns.length - 1;
16418 for(var i = startIndex; i <= endIndex; i++){
16419 ns[i].nodeIndex = i;
16424 * Changes the data store this view uses and refresh the view.
16425 * @param {Store} store
16427 setStore : function(store, initial){
16428 if(!initial && this.store){
16429 this.store.un("datachanged", this.refresh);
16430 this.store.un("add", this.onAdd);
16431 this.store.un("remove", this.onRemove);
16432 this.store.un("update", this.onUpdate);
16433 this.store.un("clear", this.refresh);
16434 this.store.un("beforeload", this.onBeforeLoad);
16435 this.store.un("load", this.onLoad);
16436 this.store.un("loadexception", this.onLoad);
16440 store.on("datachanged", this.refresh, this);
16441 store.on("add", this.onAdd, this);
16442 store.on("remove", this.onRemove, this);
16443 store.on("update", this.onUpdate, this);
16444 store.on("clear", this.refresh, this);
16445 store.on("beforeload", this.onBeforeLoad, this);
16446 store.on("load", this.onLoad, this);
16447 store.on("loadexception", this.onLoad, this);
16455 * onbeforeLoad - masks the loading area.
16458 onBeforeLoad : function(store,opts)
16460 //Roo.log('onBeforeLoad');
16462 this.el.update("");
16464 this.el.mask(this.mask ? this.mask : "Loading" );
16466 onLoad : function ()
16473 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16474 * @param {HTMLElement} node
16475 * @return {HTMLElement} The template node
16477 findItemFromChild : function(node){
16478 var el = this.dataName ?
16479 this.el.child('.roo-tpl-' + this.dataName,true) :
16482 if(!node || node.parentNode == el){
16485 var p = node.parentNode;
16486 while(p && p != el){
16487 if(p.parentNode == el){
16496 onClick : function(e){
16497 var item = this.findItemFromChild(e.getTarget());
16499 var index = this.indexOf(item);
16500 if(this.onItemClick(item, index, e) !== false){
16501 this.fireEvent("click", this, index, item, e);
16504 this.clearSelections();
16509 onContextMenu : function(e){
16510 var item = this.findItemFromChild(e.getTarget());
16512 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16517 onDblClick : function(e){
16518 var item = this.findItemFromChild(e.getTarget());
16520 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16524 onItemClick : function(item, index, e)
16526 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16529 if (this.toggleSelect) {
16530 var m = this.isSelected(item) ? 'unselect' : 'select';
16533 _t[m](item, true, false);
16536 if(this.multiSelect || this.singleSelect){
16537 if(this.multiSelect && e.shiftKey && this.lastSelection){
16538 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16540 this.select(item, this.multiSelect && e.ctrlKey);
16541 this.lastSelection = item;
16544 if(!this.tickable){
16545 e.preventDefault();
16553 * Get the number of selected nodes.
16556 getSelectionCount : function(){
16557 return this.selections.length;
16561 * Get the currently selected nodes.
16562 * @return {Array} An array of HTMLElements
16564 getSelectedNodes : function(){
16565 return this.selections;
16569 * Get the indexes of the selected nodes.
16572 getSelectedIndexes : function(){
16573 var indexes = [], s = this.selections;
16574 for(var i = 0, len = s.length; i < len; i++){
16575 indexes.push(s[i].nodeIndex);
16581 * Clear all selections
16582 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16584 clearSelections : function(suppressEvent){
16585 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16586 this.cmp.elements = this.selections;
16587 this.cmp.removeClass(this.selectedClass);
16588 this.selections = [];
16589 if(!suppressEvent){
16590 this.fireEvent("selectionchange", this, this.selections);
16596 * Returns true if the passed node is selected
16597 * @param {HTMLElement/Number} node The node or node index
16598 * @return {Boolean}
16600 isSelected : function(node){
16601 var s = this.selections;
16605 node = this.getNode(node);
16606 return s.indexOf(node) !== -1;
16611 * @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
16612 * @param {Boolean} keepExisting (optional) true to keep existing selections
16613 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16615 select : function(nodeInfo, keepExisting, suppressEvent){
16616 if(nodeInfo instanceof Array){
16618 this.clearSelections(true);
16620 for(var i = 0, len = nodeInfo.length; i < len; i++){
16621 this.select(nodeInfo[i], true, true);
16625 var node = this.getNode(nodeInfo);
16626 if(!node || this.isSelected(node)){
16627 return; // already selected.
16630 this.clearSelections(true);
16633 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16634 Roo.fly(node).addClass(this.selectedClass);
16635 this.selections.push(node);
16636 if(!suppressEvent){
16637 this.fireEvent("selectionchange", this, this.selections);
16645 * @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
16646 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16647 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16649 unselect : function(nodeInfo, keepExisting, suppressEvent)
16651 if(nodeInfo instanceof Array){
16652 Roo.each(this.selections, function(s) {
16653 this.unselect(s, nodeInfo);
16657 var node = this.getNode(nodeInfo);
16658 if(!node || !this.isSelected(node)){
16659 //Roo.log("not selected");
16660 return; // not selected.
16664 Roo.each(this.selections, function(s) {
16666 Roo.fly(node).removeClass(this.selectedClass);
16673 this.selections= ns;
16674 this.fireEvent("selectionchange", this, this.selections);
16678 * Gets a template node.
16679 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16680 * @return {HTMLElement} The node or null if it wasn't found
16682 getNode : function(nodeInfo){
16683 if(typeof nodeInfo == "string"){
16684 return document.getElementById(nodeInfo);
16685 }else if(typeof nodeInfo == "number"){
16686 return this.nodes[nodeInfo];
16692 * Gets a range template nodes.
16693 * @param {Number} startIndex
16694 * @param {Number} endIndex
16695 * @return {Array} An array of nodes
16697 getNodes : function(start, end){
16698 var ns = this.nodes;
16699 start = start || 0;
16700 end = typeof end == "undefined" ? ns.length - 1 : end;
16703 for(var i = start; i <= end; i++){
16707 for(var i = start; i >= end; i--){
16715 * Finds the index of the passed node
16716 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16717 * @return {Number} The index of the node or -1
16719 indexOf : function(node){
16720 node = this.getNode(node);
16721 if(typeof node.nodeIndex == "number"){
16722 return node.nodeIndex;
16724 var ns = this.nodes;
16725 for(var i = 0, len = ns.length; i < len; i++){
16736 * based on jquery fullcalendar
16740 Roo.bootstrap = Roo.bootstrap || {};
16742 * @class Roo.bootstrap.Calendar
16743 * @extends Roo.bootstrap.Component
16744 * Bootstrap Calendar class
16745 * @cfg {Boolean} loadMask (true|false) default false
16746 * @cfg {Object} header generate the user specific header of the calendar, default false
16749 * Create a new Container
16750 * @param {Object} config The config object
16755 Roo.bootstrap.Calendar = function(config){
16756 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16760 * Fires when a date is selected
16761 * @param {DatePicker} this
16762 * @param {Date} date The selected date
16766 * @event monthchange
16767 * Fires when the displayed month changes
16768 * @param {DatePicker} this
16769 * @param {Date} date The selected month
16771 'monthchange': true,
16773 * @event evententer
16774 * Fires when mouse over an event
16775 * @param {Calendar} this
16776 * @param {event} Event
16778 'evententer': true,
16780 * @event eventleave
16781 * Fires when the mouse leaves an
16782 * @param {Calendar} this
16785 'eventleave': true,
16787 * @event eventclick
16788 * Fires when the mouse click an
16789 * @param {Calendar} this
16798 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16801 * @cfg {Number} startDay
16802 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16810 getAutoCreate : function(){
16813 var fc_button = function(name, corner, style, content ) {
16814 return Roo.apply({},{
16816 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16818 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16821 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16832 style : 'width:100%',
16839 cls : 'fc-header-left',
16841 fc_button('prev', 'left', 'arrow', '‹' ),
16842 fc_button('next', 'right', 'arrow', '›' ),
16843 { tag: 'span', cls: 'fc-header-space' },
16844 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16852 cls : 'fc-header-center',
16856 cls: 'fc-header-title',
16859 html : 'month / year'
16867 cls : 'fc-header-right',
16869 /* fc_button('month', 'left', '', 'month' ),
16870 fc_button('week', '', '', 'week' ),
16871 fc_button('day', 'right', '', 'day' )
16883 header = this.header;
16886 var cal_heads = function() {
16888 // fixme - handle this.
16890 for (var i =0; i < Date.dayNames.length; i++) {
16891 var d = Date.dayNames[i];
16894 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16895 html : d.substring(0,3)
16899 ret[0].cls += ' fc-first';
16900 ret[6].cls += ' fc-last';
16903 var cal_cell = function(n) {
16906 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16911 cls: 'fc-day-number',
16915 cls: 'fc-day-content',
16919 style: 'position: relative;' // height: 17px;
16931 var cal_rows = function() {
16934 for (var r = 0; r < 6; r++) {
16941 for (var i =0; i < Date.dayNames.length; i++) {
16942 var d = Date.dayNames[i];
16943 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16946 row.cn[0].cls+=' fc-first';
16947 row.cn[0].cn[0].style = 'min-height:90px';
16948 row.cn[6].cls+=' fc-last';
16952 ret[0].cls += ' fc-first';
16953 ret[4].cls += ' fc-prev-last';
16954 ret[5].cls += ' fc-last';
16961 cls: 'fc-border-separate',
16962 style : 'width:100%',
16970 cls : 'fc-first fc-last',
16988 cls : 'fc-content',
16989 style : "position: relative;",
16992 cls : 'fc-view fc-view-month fc-grid',
16993 style : 'position: relative',
16994 unselectable : 'on',
16997 cls : 'fc-event-container',
16998 style : 'position:absolute;z-index:8;top:0;left:0;'
17016 initEvents : function()
17019 throw "can not find store for calendar";
17025 style: "text-align:center",
17029 style: "background-color:white;width:50%;margin:250 auto",
17033 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17044 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17046 var size = this.el.select('.fc-content', true).first().getSize();
17047 this.maskEl.setSize(size.width, size.height);
17048 this.maskEl.enableDisplayMode("block");
17049 if(!this.loadMask){
17050 this.maskEl.hide();
17053 this.store = Roo.factory(this.store, Roo.data);
17054 this.store.on('load', this.onLoad, this);
17055 this.store.on('beforeload', this.onBeforeLoad, this);
17059 this.cells = this.el.select('.fc-day',true);
17060 //Roo.log(this.cells);
17061 this.textNodes = this.el.query('.fc-day-number');
17062 this.cells.addClassOnOver('fc-state-hover');
17064 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17065 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17066 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17067 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17069 this.on('monthchange', this.onMonthChange, this);
17071 this.update(new Date().clearTime());
17074 resize : function() {
17075 var sz = this.el.getSize();
17077 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17078 this.el.select('.fc-day-content div',true).setHeight(34);
17083 showPrevMonth : function(e){
17084 this.update(this.activeDate.add("mo", -1));
17086 showToday : function(e){
17087 this.update(new Date().clearTime());
17090 showNextMonth : function(e){
17091 this.update(this.activeDate.add("mo", 1));
17095 showPrevYear : function(){
17096 this.update(this.activeDate.add("y", -1));
17100 showNextYear : function(){
17101 this.update(this.activeDate.add("y", 1));
17106 update : function(date)
17108 var vd = this.activeDate;
17109 this.activeDate = date;
17110 // if(vd && this.el){
17111 // var t = date.getTime();
17112 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17113 // Roo.log('using add remove');
17115 // this.fireEvent('monthchange', this, date);
17117 // this.cells.removeClass("fc-state-highlight");
17118 // this.cells.each(function(c){
17119 // if(c.dateValue == t){
17120 // c.addClass("fc-state-highlight");
17121 // setTimeout(function(){
17122 // try{c.dom.firstChild.focus();}catch(e){}
17132 var days = date.getDaysInMonth();
17134 var firstOfMonth = date.getFirstDateOfMonth();
17135 var startingPos = firstOfMonth.getDay()-this.startDay;
17137 if(startingPos < this.startDay){
17141 var pm = date.add(Date.MONTH, -1);
17142 var prevStart = pm.getDaysInMonth()-startingPos;
17144 this.cells = this.el.select('.fc-day',true);
17145 this.textNodes = this.el.query('.fc-day-number');
17146 this.cells.addClassOnOver('fc-state-hover');
17148 var cells = this.cells.elements;
17149 var textEls = this.textNodes;
17151 Roo.each(cells, function(cell){
17152 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17155 days += startingPos;
17157 // convert everything to numbers so it's fast
17158 var day = 86400000;
17159 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17162 //Roo.log(prevStart);
17164 var today = new Date().clearTime().getTime();
17165 var sel = date.clearTime().getTime();
17166 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17167 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17168 var ddMatch = this.disabledDatesRE;
17169 var ddText = this.disabledDatesText;
17170 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17171 var ddaysText = this.disabledDaysText;
17172 var format = this.format;
17174 var setCellClass = function(cal, cell){
17178 //Roo.log('set Cell Class');
17180 var t = d.getTime();
17184 cell.dateValue = t;
17186 cell.className += " fc-today";
17187 cell.className += " fc-state-highlight";
17188 cell.title = cal.todayText;
17191 // disable highlight in other month..
17192 //cell.className += " fc-state-highlight";
17197 cell.className = " fc-state-disabled";
17198 cell.title = cal.minText;
17202 cell.className = " fc-state-disabled";
17203 cell.title = cal.maxText;
17207 if(ddays.indexOf(d.getDay()) != -1){
17208 cell.title = ddaysText;
17209 cell.className = " fc-state-disabled";
17212 if(ddMatch && format){
17213 var fvalue = d.dateFormat(format);
17214 if(ddMatch.test(fvalue)){
17215 cell.title = ddText.replace("%0", fvalue);
17216 cell.className = " fc-state-disabled";
17220 if (!cell.initialClassName) {
17221 cell.initialClassName = cell.dom.className;
17224 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17229 for(; i < startingPos; i++) {
17230 textEls[i].innerHTML = (++prevStart);
17231 d.setDate(d.getDate()+1);
17233 cells[i].className = "fc-past fc-other-month";
17234 setCellClass(this, cells[i]);
17239 for(; i < days; i++){
17240 intDay = i - startingPos + 1;
17241 textEls[i].innerHTML = (intDay);
17242 d.setDate(d.getDate()+1);
17244 cells[i].className = ''; // "x-date-active";
17245 setCellClass(this, cells[i]);
17249 for(; i < 42; i++) {
17250 textEls[i].innerHTML = (++extraDays);
17251 d.setDate(d.getDate()+1);
17253 cells[i].className = "fc-future fc-other-month";
17254 setCellClass(this, cells[i]);
17257 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17259 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17261 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17262 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17264 if(totalRows != 6){
17265 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17266 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17269 this.fireEvent('monthchange', this, date);
17273 if(!this.internalRender){
17274 var main = this.el.dom.firstChild;
17275 var w = main.offsetWidth;
17276 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17277 Roo.fly(main).setWidth(w);
17278 this.internalRender = true;
17279 // opera does not respect the auto grow header center column
17280 // then, after it gets a width opera refuses to recalculate
17281 // without a second pass
17282 if(Roo.isOpera && !this.secondPass){
17283 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17284 this.secondPass = true;
17285 this.update.defer(10, this, [date]);
17292 findCell : function(dt) {
17293 dt = dt.clearTime().getTime();
17295 this.cells.each(function(c){
17296 //Roo.log("check " +c.dateValue + '?=' + dt);
17297 if(c.dateValue == dt){
17307 findCells : function(ev) {
17308 var s = ev.start.clone().clearTime().getTime();
17310 var e= ev.end.clone().clearTime().getTime();
17313 this.cells.each(function(c){
17314 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17316 if(c.dateValue > e){
17319 if(c.dateValue < s){
17328 // findBestRow: function(cells)
17332 // for (var i =0 ; i < cells.length;i++) {
17333 // ret = Math.max(cells[i].rows || 0,ret);
17340 addItem : function(ev)
17342 // look for vertical location slot in
17343 var cells = this.findCells(ev);
17345 // ev.row = this.findBestRow(cells);
17347 // work out the location.
17351 for(var i =0; i < cells.length; i++) {
17353 cells[i].row = cells[0].row;
17356 cells[i].row = cells[i].row + 1;
17366 if (crow.start.getY() == cells[i].getY()) {
17368 crow.end = cells[i];
17385 cells[0].events.push(ev);
17387 this.calevents.push(ev);
17390 clearEvents: function() {
17392 if(!this.calevents){
17396 Roo.each(this.cells.elements, function(c){
17402 Roo.each(this.calevents, function(e) {
17403 Roo.each(e.els, function(el) {
17404 el.un('mouseenter' ,this.onEventEnter, this);
17405 el.un('mouseleave' ,this.onEventLeave, this);
17410 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17416 renderEvents: function()
17420 this.cells.each(function(c) {
17429 if(c.row != c.events.length){
17430 r = 4 - (4 - (c.row - c.events.length));
17433 c.events = ev.slice(0, r);
17434 c.more = ev.slice(r);
17436 if(c.more.length && c.more.length == 1){
17437 c.events.push(c.more.pop());
17440 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17444 this.cells.each(function(c) {
17446 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17449 for (var e = 0; e < c.events.length; e++){
17450 var ev = c.events[e];
17451 var rows = ev.rows;
17453 for(var i = 0; i < rows.length; i++) {
17455 // how many rows should it span..
17458 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17459 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17461 unselectable : "on",
17464 cls: 'fc-event-inner',
17468 // cls: 'fc-event-time',
17469 // html : cells.length > 1 ? '' : ev.time
17473 cls: 'fc-event-title',
17474 html : String.format('{0}', ev.title)
17481 cls: 'ui-resizable-handle ui-resizable-e',
17482 html : '  '
17489 cfg.cls += ' fc-event-start';
17491 if ((i+1) == rows.length) {
17492 cfg.cls += ' fc-event-end';
17495 var ctr = _this.el.select('.fc-event-container',true).first();
17496 var cg = ctr.createChild(cfg);
17498 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17499 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17501 var r = (c.more.length) ? 1 : 0;
17502 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17503 cg.setWidth(ebox.right - sbox.x -2);
17505 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17506 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17507 cg.on('click', _this.onEventClick, _this, ev);
17518 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17519 style : 'position: absolute',
17520 unselectable : "on",
17523 cls: 'fc-event-inner',
17527 cls: 'fc-event-title',
17535 cls: 'ui-resizable-handle ui-resizable-e',
17536 html : '  '
17542 var ctr = _this.el.select('.fc-event-container',true).first();
17543 var cg = ctr.createChild(cfg);
17545 var sbox = c.select('.fc-day-content',true).first().getBox();
17546 var ebox = c.select('.fc-day-content',true).first().getBox();
17548 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17549 cg.setWidth(ebox.right - sbox.x -2);
17551 cg.on('click', _this.onMoreEventClick, _this, c.more);
17561 onEventEnter: function (e, el,event,d) {
17562 this.fireEvent('evententer', this, el, event);
17565 onEventLeave: function (e, el,event,d) {
17566 this.fireEvent('eventleave', this, el, event);
17569 onEventClick: function (e, el,event,d) {
17570 this.fireEvent('eventclick', this, el, event);
17573 onMonthChange: function () {
17577 onMoreEventClick: function(e, el, more)
17581 this.calpopover.placement = 'right';
17582 this.calpopover.setTitle('More');
17584 this.calpopover.setContent('');
17586 var ctr = this.calpopover.el.select('.popover-content', true).first();
17588 Roo.each(more, function(m){
17590 cls : 'fc-event-hori fc-event-draggable',
17593 var cg = ctr.createChild(cfg);
17595 cg.on('click', _this.onEventClick, _this, m);
17598 this.calpopover.show(el);
17603 onLoad: function ()
17605 this.calevents = [];
17608 if(this.store.getCount() > 0){
17609 this.store.data.each(function(d){
17612 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17613 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17614 time : d.data.start_time,
17615 title : d.data.title,
17616 description : d.data.description,
17617 venue : d.data.venue
17622 this.renderEvents();
17624 if(this.calevents.length && this.loadMask){
17625 this.maskEl.hide();
17629 onBeforeLoad: function()
17631 this.clearEvents();
17633 this.maskEl.show();
17647 * @class Roo.bootstrap.Popover
17648 * @extends Roo.bootstrap.Component
17649 * Bootstrap Popover class
17650 * @cfg {String} html contents of the popover (or false to use children..)
17651 * @cfg {String} title of popover (or false to hide)
17652 * @cfg {String} placement how it is placed
17653 * @cfg {String} trigger click || hover (or false to trigger manually)
17654 * @cfg {String} over what (parent or false to trigger manually.)
17655 * @cfg {Number} delay - delay before showing
17658 * Create a new Popover
17659 * @param {Object} config The config object
17662 Roo.bootstrap.Popover = function(config){
17663 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17669 * After the popover show
17671 * @param {Roo.bootstrap.Popover} this
17676 * After the popover hide
17678 * @param {Roo.bootstrap.Popover} this
17684 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17686 title: 'Fill in a title',
17689 placement : 'right',
17690 trigger : 'hover', // hover
17696 can_build_overlaid : false,
17698 getChildContainer : function()
17700 return this.el.select('.popover-content',true).first();
17703 getAutoCreate : function(){
17706 cls : 'popover roo-dynamic',
17707 style: 'display:block',
17713 cls : 'popover-inner',
17717 cls: 'popover-title popover-header',
17721 cls : 'popover-content popover-body',
17732 setTitle: function(str)
17735 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17737 setContent: function(str)
17740 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17742 // as it get's added to the bottom of the page.
17743 onRender : function(ct, position)
17745 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17747 var cfg = Roo.apply({}, this.getAutoCreate());
17751 cfg.cls += ' ' + this.cls;
17754 cfg.style = this.style;
17756 //Roo.log("adding to ");
17757 this.el = Roo.get(document.body).createChild(cfg, position);
17758 // Roo.log(this.el);
17763 initEvents : function()
17765 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17766 this.el.enableDisplayMode('block');
17768 if (this.over === false) {
17771 if (this.triggers === false) {
17774 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17775 var triggers = this.trigger ? this.trigger.split(' ') : [];
17776 Roo.each(triggers, function(trigger) {
17778 if (trigger == 'click') {
17779 on_el.on('click', this.toggle, this);
17780 } else if (trigger != 'manual') {
17781 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17782 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17784 on_el.on(eventIn ,this.enter, this);
17785 on_el.on(eventOut, this.leave, this);
17796 toggle : function () {
17797 this.hoverState == 'in' ? this.leave() : this.enter();
17800 enter : function () {
17802 clearTimeout(this.timeout);
17804 this.hoverState = 'in';
17806 if (!this.delay || !this.delay.show) {
17811 this.timeout = setTimeout(function () {
17812 if (_t.hoverState == 'in') {
17815 }, this.delay.show)
17818 leave : function() {
17819 clearTimeout(this.timeout);
17821 this.hoverState = 'out';
17823 if (!this.delay || !this.delay.hide) {
17828 this.timeout = setTimeout(function () {
17829 if (_t.hoverState == 'out') {
17832 }, this.delay.hide)
17835 show : function (on_el)
17838 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17842 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17843 if (this.html !== false) {
17844 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17846 this.el.removeClass([
17847 'fade','top','bottom', 'left', 'right','in',
17848 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17850 if (!this.title.length) {
17851 this.el.select('.popover-title',true).hide();
17854 var placement = typeof this.placement == 'function' ?
17855 this.placement.call(this, this.el, on_el) :
17858 var autoToken = /\s?auto?\s?/i;
17859 var autoPlace = autoToken.test(placement);
17861 placement = placement.replace(autoToken, '') || 'top';
17865 //this.el.setXY([0,0]);
17867 this.el.dom.style.display='block';
17868 this.el.addClass(placement);
17870 //this.el.appendTo(on_el);
17872 var p = this.getPosition();
17873 var box = this.el.getBox();
17878 var align = Roo.bootstrap.Popover.alignment[placement];
17881 this.el.alignTo(on_el, align[0],align[1]);
17882 //var arrow = this.el.select('.arrow',true).first();
17883 //arrow.set(align[2],
17885 this.el.addClass('in');
17888 if (this.el.hasClass('fade')) {
17892 this.hoverState = 'in';
17894 this.fireEvent('show', this);
17899 this.el.setXY([0,0]);
17900 this.el.removeClass('in');
17902 this.hoverState = null;
17904 this.fireEvent('hide', this);
17909 Roo.bootstrap.Popover.alignment = {
17910 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17911 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17912 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17913 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17924 * @class Roo.bootstrap.Progress
17925 * @extends Roo.bootstrap.Component
17926 * Bootstrap Progress class
17927 * @cfg {Boolean} striped striped of the progress bar
17928 * @cfg {Boolean} active animated of the progress bar
17932 * Create a new Progress
17933 * @param {Object} config The config object
17936 Roo.bootstrap.Progress = function(config){
17937 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17940 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17945 getAutoCreate : function(){
17953 cfg.cls += ' progress-striped';
17957 cfg.cls += ' active';
17976 * @class Roo.bootstrap.ProgressBar
17977 * @extends Roo.bootstrap.Component
17978 * Bootstrap ProgressBar class
17979 * @cfg {Number} aria_valuenow aria-value now
17980 * @cfg {Number} aria_valuemin aria-value min
17981 * @cfg {Number} aria_valuemax aria-value max
17982 * @cfg {String} label label for the progress bar
17983 * @cfg {String} panel (success | info | warning | danger )
17984 * @cfg {String} role role of the progress bar
17985 * @cfg {String} sr_only text
17989 * Create a new ProgressBar
17990 * @param {Object} config The config object
17993 Roo.bootstrap.ProgressBar = function(config){
17994 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17997 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18001 aria_valuemax : 100,
18007 getAutoCreate : function()
18012 cls: 'progress-bar',
18013 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18025 cfg.role = this.role;
18028 if(this.aria_valuenow){
18029 cfg['aria-valuenow'] = this.aria_valuenow;
18032 if(this.aria_valuemin){
18033 cfg['aria-valuemin'] = this.aria_valuemin;
18036 if(this.aria_valuemax){
18037 cfg['aria-valuemax'] = this.aria_valuemax;
18040 if(this.label && !this.sr_only){
18041 cfg.html = this.label;
18045 cfg.cls += ' progress-bar-' + this.panel;
18051 update : function(aria_valuenow)
18053 this.aria_valuenow = aria_valuenow;
18055 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18070 * @class Roo.bootstrap.TabGroup
18071 * @extends Roo.bootstrap.Column
18072 * Bootstrap Column class
18073 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18074 * @cfg {Boolean} carousel true to make the group behave like a carousel
18075 * @cfg {Boolean} bullets show bullets for the panels
18076 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18077 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18078 * @cfg {Boolean} showarrow (true|false) show arrow default true
18081 * Create a new TabGroup
18082 * @param {Object} config The config object
18085 Roo.bootstrap.TabGroup = function(config){
18086 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18088 this.navId = Roo.id();
18091 Roo.bootstrap.TabGroup.register(this);
18095 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18098 transition : false,
18103 slideOnTouch : false,
18106 getAutoCreate : function()
18108 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18110 cfg.cls += ' tab-content';
18112 if (this.carousel) {
18113 cfg.cls += ' carousel slide';
18116 cls : 'carousel-inner',
18120 if(this.bullets && !Roo.isTouch){
18123 cls : 'carousel-bullets',
18127 if(this.bullets_cls){
18128 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18135 cfg.cn[0].cn.push(bullets);
18138 if(this.showarrow){
18139 cfg.cn[0].cn.push({
18141 class : 'carousel-arrow',
18145 class : 'carousel-prev',
18149 class : 'fa fa-chevron-left'
18155 class : 'carousel-next',
18159 class : 'fa fa-chevron-right'
18172 initEvents: function()
18174 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18175 // this.el.on("touchstart", this.onTouchStart, this);
18178 if(this.autoslide){
18181 this.slideFn = window.setInterval(function() {
18182 _this.showPanelNext();
18186 if(this.showarrow){
18187 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18188 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18194 // onTouchStart : function(e, el, o)
18196 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18200 // this.showPanelNext();
18204 getChildContainer : function()
18206 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18210 * register a Navigation item
18211 * @param {Roo.bootstrap.NavItem} the navitem to add
18213 register : function(item)
18215 this.tabs.push( item);
18216 item.navId = this.navId; // not really needed..
18221 getActivePanel : function()
18224 Roo.each(this.tabs, function(t) {
18234 getPanelByName : function(n)
18237 Roo.each(this.tabs, function(t) {
18238 if (t.tabId == n) {
18246 indexOfPanel : function(p)
18249 Roo.each(this.tabs, function(t,i) {
18250 if (t.tabId == p.tabId) {
18259 * show a specific panel
18260 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18261 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18263 showPanel : function (pan)
18265 if(this.transition || typeof(pan) == 'undefined'){
18266 Roo.log("waiting for the transitionend");
18270 if (typeof(pan) == 'number') {
18271 pan = this.tabs[pan];
18274 if (typeof(pan) == 'string') {
18275 pan = this.getPanelByName(pan);
18278 var cur = this.getActivePanel();
18281 Roo.log('pan or acitve pan is undefined');
18285 if (pan.tabId == this.getActivePanel().tabId) {
18289 if (false === cur.fireEvent('beforedeactivate')) {
18293 if(this.bullets > 0 && !Roo.isTouch){
18294 this.setActiveBullet(this.indexOfPanel(pan));
18297 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18299 this.transition = true;
18300 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18301 var lr = dir == 'next' ? 'left' : 'right';
18302 pan.el.addClass(dir); // or prev
18303 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18304 cur.el.addClass(lr); // or right
18305 pan.el.addClass(lr);
18308 cur.el.on('transitionend', function() {
18309 Roo.log("trans end?");
18311 pan.el.removeClass([lr,dir]);
18312 pan.setActive(true);
18314 cur.el.removeClass([lr]);
18315 cur.setActive(false);
18317 _this.transition = false;
18319 }, this, { single: true } );
18324 cur.setActive(false);
18325 pan.setActive(true);
18330 showPanelNext : function()
18332 var i = this.indexOfPanel(this.getActivePanel());
18334 if (i >= this.tabs.length - 1 && !this.autoslide) {
18338 if (i >= this.tabs.length - 1 && this.autoslide) {
18342 this.showPanel(this.tabs[i+1]);
18345 showPanelPrev : function()
18347 var i = this.indexOfPanel(this.getActivePanel());
18349 if (i < 1 && !this.autoslide) {
18353 if (i < 1 && this.autoslide) {
18354 i = this.tabs.length;
18357 this.showPanel(this.tabs[i-1]);
18361 addBullet: function()
18363 if(!this.bullets || Roo.isTouch){
18366 var ctr = this.el.select('.carousel-bullets',true).first();
18367 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18368 var bullet = ctr.createChild({
18369 cls : 'bullet bullet-' + i
18370 },ctr.dom.lastChild);
18375 bullet.on('click', (function(e, el, o, ii, t){
18377 e.preventDefault();
18379 this.showPanel(ii);
18381 if(this.autoslide && this.slideFn){
18382 clearInterval(this.slideFn);
18383 this.slideFn = window.setInterval(function() {
18384 _this.showPanelNext();
18388 }).createDelegate(this, [i, bullet], true));
18393 setActiveBullet : function(i)
18399 Roo.each(this.el.select('.bullet', true).elements, function(el){
18400 el.removeClass('selected');
18403 var bullet = this.el.select('.bullet-' + i, true).first();
18409 bullet.addClass('selected');
18420 Roo.apply(Roo.bootstrap.TabGroup, {
18424 * register a Navigation Group
18425 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18427 register : function(navgrp)
18429 this.groups[navgrp.navId] = navgrp;
18433 * fetch a Navigation Group based on the navigation ID
18434 * if one does not exist , it will get created.
18435 * @param {string} the navgroup to add
18436 * @returns {Roo.bootstrap.NavGroup} the navgroup
18438 get: function(navId) {
18439 if (typeof(this.groups[navId]) == 'undefined') {
18440 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18442 return this.groups[navId] ;
18457 * @class Roo.bootstrap.TabPanel
18458 * @extends Roo.bootstrap.Component
18459 * Bootstrap TabPanel class
18460 * @cfg {Boolean} active panel active
18461 * @cfg {String} html panel content
18462 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18463 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18464 * @cfg {String} href click to link..
18468 * Create a new TabPanel
18469 * @param {Object} config The config object
18472 Roo.bootstrap.TabPanel = function(config){
18473 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18477 * Fires when the active status changes
18478 * @param {Roo.bootstrap.TabPanel} this
18479 * @param {Boolean} state the new state
18484 * @event beforedeactivate
18485 * Fires before a tab is de-activated - can be used to do validation on a form.
18486 * @param {Roo.bootstrap.TabPanel} this
18487 * @return {Boolean} false if there is an error
18490 'beforedeactivate': true
18493 this.tabId = this.tabId || Roo.id();
18497 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18505 getAutoCreate : function(){
18508 // item is needed for carousel - not sure if it has any effect otherwise
18509 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18510 html: this.html || ''
18514 cfg.cls += ' active';
18518 cfg.tabId = this.tabId;
18525 initEvents: function()
18527 var p = this.parent();
18529 this.navId = this.navId || p.navId;
18531 if (typeof(this.navId) != 'undefined') {
18532 // not really needed.. but just in case.. parent should be a NavGroup.
18533 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18537 var i = tg.tabs.length - 1;
18539 if(this.active && tg.bullets > 0 && i < tg.bullets){
18540 tg.setActiveBullet(i);
18544 this.el.on('click', this.onClick, this);
18547 this.el.on("touchstart", this.onTouchStart, this);
18548 this.el.on("touchmove", this.onTouchMove, this);
18549 this.el.on("touchend", this.onTouchEnd, this);
18554 onRender : function(ct, position)
18556 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18559 setActive : function(state)
18561 Roo.log("panel - set active " + this.tabId + "=" + state);
18563 this.active = state;
18565 this.el.removeClass('active');
18567 } else if (!this.el.hasClass('active')) {
18568 this.el.addClass('active');
18571 this.fireEvent('changed', this, state);
18574 onClick : function(e)
18576 e.preventDefault();
18578 if(!this.href.length){
18582 window.location.href = this.href;
18591 onTouchStart : function(e)
18593 this.swiping = false;
18595 this.startX = e.browserEvent.touches[0].clientX;
18596 this.startY = e.browserEvent.touches[0].clientY;
18599 onTouchMove : function(e)
18601 this.swiping = true;
18603 this.endX = e.browserEvent.touches[0].clientX;
18604 this.endY = e.browserEvent.touches[0].clientY;
18607 onTouchEnd : function(e)
18614 var tabGroup = this.parent();
18616 if(this.endX > this.startX){ // swiping right
18617 tabGroup.showPanelPrev();
18621 if(this.startX > this.endX){ // swiping left
18622 tabGroup.showPanelNext();
18641 * @class Roo.bootstrap.DateField
18642 * @extends Roo.bootstrap.Input
18643 * Bootstrap DateField class
18644 * @cfg {Number} weekStart default 0
18645 * @cfg {String} viewMode default empty, (months|years)
18646 * @cfg {String} minViewMode default empty, (months|years)
18647 * @cfg {Number} startDate default -Infinity
18648 * @cfg {Number} endDate default Infinity
18649 * @cfg {Boolean} todayHighlight default false
18650 * @cfg {Boolean} todayBtn default false
18651 * @cfg {Boolean} calendarWeeks default false
18652 * @cfg {Object} daysOfWeekDisabled default empty
18653 * @cfg {Boolean} singleMode default false (true | false)
18655 * @cfg {Boolean} keyboardNavigation default true
18656 * @cfg {String} language default en
18659 * Create a new DateField
18660 * @param {Object} config The config object
18663 Roo.bootstrap.DateField = function(config){
18664 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18668 * Fires when this field show.
18669 * @param {Roo.bootstrap.DateField} this
18670 * @param {Mixed} date The date value
18675 * Fires when this field hide.
18676 * @param {Roo.bootstrap.DateField} this
18677 * @param {Mixed} date The date value
18682 * Fires when select a date.
18683 * @param {Roo.bootstrap.DateField} this
18684 * @param {Mixed} date The date value
18688 * @event beforeselect
18689 * Fires when before select a date.
18690 * @param {Roo.bootstrap.DateField} this
18691 * @param {Mixed} date The date value
18693 beforeselect : true
18697 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18700 * @cfg {String} format
18701 * The default date format string which can be overriden for localization support. The format must be
18702 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18706 * @cfg {String} altFormats
18707 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18708 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18710 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18718 todayHighlight : false,
18724 keyboardNavigation: true,
18726 calendarWeeks: false,
18728 startDate: -Infinity,
18732 daysOfWeekDisabled: [],
18736 singleMode : false,
18738 UTCDate: function()
18740 return new Date(Date.UTC.apply(Date, arguments));
18743 UTCToday: function()
18745 var today = new Date();
18746 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18749 getDate: function() {
18750 var d = this.getUTCDate();
18751 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18754 getUTCDate: function() {
18758 setDate: function(d) {
18759 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18762 setUTCDate: function(d) {
18764 this.setValue(this.formatDate(this.date));
18767 onRender: function(ct, position)
18770 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18772 this.language = this.language || 'en';
18773 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18774 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18776 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18777 this.format = this.format || 'm/d/y';
18778 this.isInline = false;
18779 this.isInput = true;
18780 this.component = this.el.select('.add-on', true).first() || false;
18781 this.component = (this.component && this.component.length === 0) ? false : this.component;
18782 this.hasInput = this.component && this.inputEl().length;
18784 if (typeof(this.minViewMode === 'string')) {
18785 switch (this.minViewMode) {
18787 this.minViewMode = 1;
18790 this.minViewMode = 2;
18793 this.minViewMode = 0;
18798 if (typeof(this.viewMode === 'string')) {
18799 switch (this.viewMode) {
18812 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18814 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18816 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18818 this.picker().on('mousedown', this.onMousedown, this);
18819 this.picker().on('click', this.onClick, this);
18821 this.picker().addClass('datepicker-dropdown');
18823 this.startViewMode = this.viewMode;
18825 if(this.singleMode){
18826 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18827 v.setVisibilityMode(Roo.Element.DISPLAY);
18831 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18832 v.setStyle('width', '189px');
18836 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18837 if(!this.calendarWeeks){
18842 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18843 v.attr('colspan', function(i, val){
18844 return parseInt(val) + 1;
18849 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18851 this.setStartDate(this.startDate);
18852 this.setEndDate(this.endDate);
18854 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18861 if(this.isInline) {
18866 picker : function()
18868 return this.pickerEl;
18869 // return this.el.select('.datepicker', true).first();
18872 fillDow: function()
18874 var dowCnt = this.weekStart;
18883 if(this.calendarWeeks){
18891 while (dowCnt < this.weekStart + 7) {
18895 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18899 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18902 fillMonths: function()
18905 var months = this.picker().select('>.datepicker-months td', true).first();
18907 months.dom.innerHTML = '';
18913 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18916 months.createChild(month);
18923 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;
18925 if (this.date < this.startDate) {
18926 this.viewDate = new Date(this.startDate);
18927 } else if (this.date > this.endDate) {
18928 this.viewDate = new Date(this.endDate);
18930 this.viewDate = new Date(this.date);
18938 var d = new Date(this.viewDate),
18939 year = d.getUTCFullYear(),
18940 month = d.getUTCMonth(),
18941 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18942 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18943 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18944 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18945 currentDate = this.date && this.date.valueOf(),
18946 today = this.UTCToday();
18948 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18950 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18952 // this.picker.select('>tfoot th.today').
18953 // .text(dates[this.language].today)
18954 // .toggle(this.todayBtn !== false);
18956 this.updateNavArrows();
18959 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18961 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18963 prevMonth.setUTCDate(day);
18965 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18967 var nextMonth = new Date(prevMonth);
18969 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18971 nextMonth = nextMonth.valueOf();
18973 var fillMonths = false;
18975 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18977 while(prevMonth.valueOf() <= nextMonth) {
18980 if (prevMonth.getUTCDay() === this.weekStart) {
18982 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18990 if(this.calendarWeeks){
18991 // ISO 8601: First week contains first thursday.
18992 // ISO also states week starts on Monday, but we can be more abstract here.
18994 // Start of current week: based on weekstart/current date
18995 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18996 // Thursday of this week
18997 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18998 // First Thursday of year, year from thursday
18999 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19000 // Calendar week: ms between thursdays, div ms per day, div 7 days
19001 calWeek = (th - yth) / 864e5 / 7 + 1;
19003 fillMonths.cn.push({
19011 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19013 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19016 if (this.todayHighlight &&
19017 prevMonth.getUTCFullYear() == today.getFullYear() &&
19018 prevMonth.getUTCMonth() == today.getMonth() &&
19019 prevMonth.getUTCDate() == today.getDate()) {
19020 clsName += ' today';
19023 if (currentDate && prevMonth.valueOf() === currentDate) {
19024 clsName += ' active';
19027 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19028 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19029 clsName += ' disabled';
19032 fillMonths.cn.push({
19034 cls: 'day ' + clsName,
19035 html: prevMonth.getDate()
19038 prevMonth.setDate(prevMonth.getDate()+1);
19041 var currentYear = this.date && this.date.getUTCFullYear();
19042 var currentMonth = this.date && this.date.getUTCMonth();
19044 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19046 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19047 v.removeClass('active');
19049 if(currentYear === year && k === currentMonth){
19050 v.addClass('active');
19053 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19054 v.addClass('disabled');
19060 year = parseInt(year/10, 10) * 10;
19062 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19064 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19067 for (var i = -1; i < 11; i++) {
19068 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19070 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19078 showMode: function(dir)
19081 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19084 Roo.each(this.picker().select('>div',true).elements, function(v){
19085 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19088 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19093 if(this.isInline) {
19097 this.picker().removeClass(['bottom', 'top']);
19099 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19101 * place to the top of element!
19105 this.picker().addClass('top');
19106 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19111 this.picker().addClass('bottom');
19113 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19116 parseDate : function(value)
19118 if(!value || value instanceof Date){
19121 var v = Date.parseDate(value, this.format);
19122 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19123 v = Date.parseDate(value, 'Y-m-d');
19125 if(!v && this.altFormats){
19126 if(!this.altFormatsArray){
19127 this.altFormatsArray = this.altFormats.split("|");
19129 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19130 v = Date.parseDate(value, this.altFormatsArray[i]);
19136 formatDate : function(date, fmt)
19138 return (!date || !(date instanceof Date)) ?
19139 date : date.dateFormat(fmt || this.format);
19142 onFocus : function()
19144 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19148 onBlur : function()
19150 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19152 var d = this.inputEl().getValue();
19159 showPopup : function()
19161 this.picker().show();
19165 this.fireEvent('showpopup', this, this.date);
19168 hidePopup : function()
19170 if(this.isInline) {
19173 this.picker().hide();
19174 this.viewMode = this.startViewMode;
19177 this.fireEvent('hidepopup', this, this.date);
19181 onMousedown: function(e)
19183 e.stopPropagation();
19184 e.preventDefault();
19189 Roo.bootstrap.DateField.superclass.keyup.call(this);
19193 setValue: function(v)
19195 if(this.fireEvent('beforeselect', this, v) !== false){
19196 var d = new Date(this.parseDate(v) ).clearTime();
19198 if(isNaN(d.getTime())){
19199 this.date = this.viewDate = '';
19200 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19204 v = this.formatDate(d);
19206 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19208 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19212 this.fireEvent('select', this, this.date);
19216 getValue: function()
19218 return this.formatDate(this.date);
19221 fireKey: function(e)
19223 if (!this.picker().isVisible()){
19224 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19230 var dateChanged = false,
19232 newDate, newViewDate;
19237 e.preventDefault();
19241 if (!this.keyboardNavigation) {
19244 dir = e.keyCode == 37 ? -1 : 1;
19247 newDate = this.moveYear(this.date, dir);
19248 newViewDate = this.moveYear(this.viewDate, dir);
19249 } else if (e.shiftKey){
19250 newDate = this.moveMonth(this.date, dir);
19251 newViewDate = this.moveMonth(this.viewDate, dir);
19253 newDate = new Date(this.date);
19254 newDate.setUTCDate(this.date.getUTCDate() + dir);
19255 newViewDate = new Date(this.viewDate);
19256 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19258 if (this.dateWithinRange(newDate)){
19259 this.date = newDate;
19260 this.viewDate = newViewDate;
19261 this.setValue(this.formatDate(this.date));
19263 e.preventDefault();
19264 dateChanged = true;
19269 if (!this.keyboardNavigation) {
19272 dir = e.keyCode == 38 ? -1 : 1;
19274 newDate = this.moveYear(this.date, dir);
19275 newViewDate = this.moveYear(this.viewDate, dir);
19276 } else if (e.shiftKey){
19277 newDate = this.moveMonth(this.date, dir);
19278 newViewDate = this.moveMonth(this.viewDate, dir);
19280 newDate = new Date(this.date);
19281 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19282 newViewDate = new Date(this.viewDate);
19283 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19285 if (this.dateWithinRange(newDate)){
19286 this.date = newDate;
19287 this.viewDate = newViewDate;
19288 this.setValue(this.formatDate(this.date));
19290 e.preventDefault();
19291 dateChanged = true;
19295 this.setValue(this.formatDate(this.date));
19297 e.preventDefault();
19300 this.setValue(this.formatDate(this.date));
19314 onClick: function(e)
19316 e.stopPropagation();
19317 e.preventDefault();
19319 var target = e.getTarget();
19321 if(target.nodeName.toLowerCase() === 'i'){
19322 target = Roo.get(target).dom.parentNode;
19325 var nodeName = target.nodeName;
19326 var className = target.className;
19327 var html = target.innerHTML;
19328 //Roo.log(nodeName);
19330 switch(nodeName.toLowerCase()) {
19332 switch(className) {
19338 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19339 switch(this.viewMode){
19341 this.viewDate = this.moveMonth(this.viewDate, dir);
19345 this.viewDate = this.moveYear(this.viewDate, dir);
19351 var date = new Date();
19352 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19354 this.setValue(this.formatDate(this.date));
19361 if (className.indexOf('disabled') < 0) {
19362 this.viewDate.setUTCDate(1);
19363 if (className.indexOf('month') > -1) {
19364 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19366 var year = parseInt(html, 10) || 0;
19367 this.viewDate.setUTCFullYear(year);
19371 if(this.singleMode){
19372 this.setValue(this.formatDate(this.viewDate));
19383 //Roo.log(className);
19384 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19385 var day = parseInt(html, 10) || 1;
19386 var year = this.viewDate.getUTCFullYear(),
19387 month = this.viewDate.getUTCMonth();
19389 if (className.indexOf('old') > -1) {
19396 } else if (className.indexOf('new') > -1) {
19404 //Roo.log([year,month,day]);
19405 this.date = this.UTCDate(year, month, day,0,0,0,0);
19406 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19408 //Roo.log(this.formatDate(this.date));
19409 this.setValue(this.formatDate(this.date));
19416 setStartDate: function(startDate)
19418 this.startDate = startDate || -Infinity;
19419 if (this.startDate !== -Infinity) {
19420 this.startDate = this.parseDate(this.startDate);
19423 this.updateNavArrows();
19426 setEndDate: function(endDate)
19428 this.endDate = endDate || Infinity;
19429 if (this.endDate !== Infinity) {
19430 this.endDate = this.parseDate(this.endDate);
19433 this.updateNavArrows();
19436 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19438 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19439 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19440 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19442 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19443 return parseInt(d, 10);
19446 this.updateNavArrows();
19449 updateNavArrows: function()
19451 if(this.singleMode){
19455 var d = new Date(this.viewDate),
19456 year = d.getUTCFullYear(),
19457 month = d.getUTCMonth();
19459 Roo.each(this.picker().select('.prev', true).elements, function(v){
19461 switch (this.viewMode) {
19464 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19470 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19477 Roo.each(this.picker().select('.next', true).elements, function(v){
19479 switch (this.viewMode) {
19482 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19488 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19496 moveMonth: function(date, dir)
19501 var new_date = new Date(date.valueOf()),
19502 day = new_date.getUTCDate(),
19503 month = new_date.getUTCMonth(),
19504 mag = Math.abs(dir),
19506 dir = dir > 0 ? 1 : -1;
19509 // If going back one month, make sure month is not current month
19510 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19512 return new_date.getUTCMonth() == month;
19514 // If going forward one month, make sure month is as expected
19515 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19517 return new_date.getUTCMonth() != new_month;
19519 new_month = month + dir;
19520 new_date.setUTCMonth(new_month);
19521 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19522 if (new_month < 0 || new_month > 11) {
19523 new_month = (new_month + 12) % 12;
19526 // For magnitudes >1, move one month at a time...
19527 for (var i=0; i<mag; i++) {
19528 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19529 new_date = this.moveMonth(new_date, dir);
19531 // ...then reset the day, keeping it in the new month
19532 new_month = new_date.getUTCMonth();
19533 new_date.setUTCDate(day);
19535 return new_month != new_date.getUTCMonth();
19538 // Common date-resetting loop -- if date is beyond end of month, make it
19541 new_date.setUTCDate(--day);
19542 new_date.setUTCMonth(new_month);
19547 moveYear: function(date, dir)
19549 return this.moveMonth(date, dir*12);
19552 dateWithinRange: function(date)
19554 return date >= this.startDate && date <= this.endDate;
19560 this.picker().remove();
19563 validateValue : function(value)
19565 if(this.getVisibilityEl().hasClass('hidden')){
19569 if(value.length < 1) {
19570 if(this.allowBlank){
19576 if(value.length < this.minLength){
19579 if(value.length > this.maxLength){
19583 var vt = Roo.form.VTypes;
19584 if(!vt[this.vtype](value, this)){
19588 if(typeof this.validator == "function"){
19589 var msg = this.validator(value);
19595 if(this.regex && !this.regex.test(value)){
19599 if(typeof(this.parseDate(value)) == 'undefined'){
19603 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19607 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19617 this.date = this.viewDate = '';
19619 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19624 Roo.apply(Roo.bootstrap.DateField, {
19635 html: '<i class="fa fa-arrow-left"/>'
19645 html: '<i class="fa fa-arrow-right"/>'
19687 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19688 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19689 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19690 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19691 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19704 navFnc: 'FullYear',
19709 navFnc: 'FullYear',
19714 Roo.apply(Roo.bootstrap.DateField, {
19718 cls: 'datepicker dropdown-menu roo-dynamic',
19722 cls: 'datepicker-days',
19726 cls: 'table-condensed',
19728 Roo.bootstrap.DateField.head,
19732 Roo.bootstrap.DateField.footer
19739 cls: 'datepicker-months',
19743 cls: 'table-condensed',
19745 Roo.bootstrap.DateField.head,
19746 Roo.bootstrap.DateField.content,
19747 Roo.bootstrap.DateField.footer
19754 cls: 'datepicker-years',
19758 cls: 'table-condensed',
19760 Roo.bootstrap.DateField.head,
19761 Roo.bootstrap.DateField.content,
19762 Roo.bootstrap.DateField.footer
19781 * @class Roo.bootstrap.TimeField
19782 * @extends Roo.bootstrap.Input
19783 * Bootstrap DateField class
19787 * Create a new TimeField
19788 * @param {Object} config The config object
19791 Roo.bootstrap.TimeField = function(config){
19792 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19796 * Fires when this field show.
19797 * @param {Roo.bootstrap.DateField} thisthis
19798 * @param {Mixed} date The date value
19803 * Fires when this field hide.
19804 * @param {Roo.bootstrap.DateField} this
19805 * @param {Mixed} date The date value
19810 * Fires when select a date.
19811 * @param {Roo.bootstrap.DateField} this
19812 * @param {Mixed} date The date value
19818 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19821 * @cfg {String} format
19822 * The default time format string which can be overriden for localization support. The format must be
19823 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19827 onRender: function(ct, position)
19830 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19832 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19834 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19836 this.pop = this.picker().select('>.datepicker-time',true).first();
19837 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19839 this.picker().on('mousedown', this.onMousedown, this);
19840 this.picker().on('click', this.onClick, this);
19842 this.picker().addClass('datepicker-dropdown');
19847 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19848 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19849 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19850 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19851 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19852 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19856 fireKey: function(e){
19857 if (!this.picker().isVisible()){
19858 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19864 e.preventDefault();
19872 this.onTogglePeriod();
19875 this.onIncrementMinutes();
19878 this.onDecrementMinutes();
19887 onClick: function(e) {
19888 e.stopPropagation();
19889 e.preventDefault();
19892 picker : function()
19894 return this.el.select('.datepicker', true).first();
19897 fillTime: function()
19899 var time = this.pop.select('tbody', true).first();
19901 time.dom.innerHTML = '';
19916 cls: 'hours-up glyphicon glyphicon-chevron-up'
19936 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19957 cls: 'timepicker-hour',
19972 cls: 'timepicker-minute',
19987 cls: 'btn btn-primary period',
20009 cls: 'hours-down glyphicon glyphicon-chevron-down'
20029 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20047 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20054 var hours = this.time.getHours();
20055 var minutes = this.time.getMinutes();
20068 hours = hours - 12;
20072 hours = '0' + hours;
20076 minutes = '0' + minutes;
20079 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20080 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20081 this.pop.select('button', true).first().dom.innerHTML = period;
20087 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20089 var cls = ['bottom'];
20091 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20098 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20103 this.picker().addClass(cls.join('-'));
20107 Roo.each(cls, function(c){
20109 _this.picker().setTop(_this.inputEl().getHeight());
20113 _this.picker().setTop(0 - _this.picker().getHeight());
20118 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20122 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20129 onFocus : function()
20131 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20135 onBlur : function()
20137 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20143 this.picker().show();
20148 this.fireEvent('show', this, this.date);
20153 this.picker().hide();
20156 this.fireEvent('hide', this, this.date);
20159 setTime : function()
20162 this.setValue(this.time.format(this.format));
20164 this.fireEvent('select', this, this.date);
20169 onMousedown: function(e){
20170 e.stopPropagation();
20171 e.preventDefault();
20174 onIncrementHours: function()
20176 Roo.log('onIncrementHours');
20177 this.time = this.time.add(Date.HOUR, 1);
20182 onDecrementHours: function()
20184 Roo.log('onDecrementHours');
20185 this.time = this.time.add(Date.HOUR, -1);
20189 onIncrementMinutes: function()
20191 Roo.log('onIncrementMinutes');
20192 this.time = this.time.add(Date.MINUTE, 1);
20196 onDecrementMinutes: function()
20198 Roo.log('onDecrementMinutes');
20199 this.time = this.time.add(Date.MINUTE, -1);
20203 onTogglePeriod: function()
20205 Roo.log('onTogglePeriod');
20206 this.time = this.time.add(Date.HOUR, 12);
20213 Roo.apply(Roo.bootstrap.TimeField, {
20243 cls: 'btn btn-info ok',
20255 Roo.apply(Roo.bootstrap.TimeField, {
20259 cls: 'datepicker dropdown-menu',
20263 cls: 'datepicker-time',
20267 cls: 'table-condensed',
20269 Roo.bootstrap.TimeField.content,
20270 Roo.bootstrap.TimeField.footer
20289 * @class Roo.bootstrap.MonthField
20290 * @extends Roo.bootstrap.Input
20291 * Bootstrap MonthField class
20293 * @cfg {String} language default en
20296 * Create a new MonthField
20297 * @param {Object} config The config object
20300 Roo.bootstrap.MonthField = function(config){
20301 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20306 * Fires when this field show.
20307 * @param {Roo.bootstrap.MonthField} this
20308 * @param {Mixed} date The date value
20313 * Fires when this field hide.
20314 * @param {Roo.bootstrap.MonthField} this
20315 * @param {Mixed} date The date value
20320 * Fires when select a date.
20321 * @param {Roo.bootstrap.MonthField} this
20322 * @param {String} oldvalue The old value
20323 * @param {String} newvalue The new value
20329 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20331 onRender: function(ct, position)
20334 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20336 this.language = this.language || 'en';
20337 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20338 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20340 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20341 this.isInline = false;
20342 this.isInput = true;
20343 this.component = this.el.select('.add-on', true).first() || false;
20344 this.component = (this.component && this.component.length === 0) ? false : this.component;
20345 this.hasInput = this.component && this.inputEL().length;
20347 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20349 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20351 this.picker().on('mousedown', this.onMousedown, this);
20352 this.picker().on('click', this.onClick, this);
20354 this.picker().addClass('datepicker-dropdown');
20356 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20357 v.setStyle('width', '189px');
20364 if(this.isInline) {
20370 setValue: function(v, suppressEvent)
20372 var o = this.getValue();
20374 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20378 if(suppressEvent !== true){
20379 this.fireEvent('select', this, o, v);
20384 getValue: function()
20389 onClick: function(e)
20391 e.stopPropagation();
20392 e.preventDefault();
20394 var target = e.getTarget();
20396 if(target.nodeName.toLowerCase() === 'i'){
20397 target = Roo.get(target).dom.parentNode;
20400 var nodeName = target.nodeName;
20401 var className = target.className;
20402 var html = target.innerHTML;
20404 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20408 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20410 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20416 picker : function()
20418 return this.pickerEl;
20421 fillMonths: function()
20424 var months = this.picker().select('>.datepicker-months td', true).first();
20426 months.dom.innerHTML = '';
20432 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20435 months.createChild(month);
20444 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20445 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20448 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20449 e.removeClass('active');
20451 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20452 e.addClass('active');
20459 if(this.isInline) {
20463 this.picker().removeClass(['bottom', 'top']);
20465 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20467 * place to the top of element!
20471 this.picker().addClass('top');
20472 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20477 this.picker().addClass('bottom');
20479 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20482 onFocus : function()
20484 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20488 onBlur : function()
20490 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20492 var d = this.inputEl().getValue();
20501 this.picker().show();
20502 this.picker().select('>.datepicker-months', true).first().show();
20506 this.fireEvent('show', this, this.date);
20511 if(this.isInline) {
20514 this.picker().hide();
20515 this.fireEvent('hide', this, this.date);
20519 onMousedown: function(e)
20521 e.stopPropagation();
20522 e.preventDefault();
20527 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20531 fireKey: function(e)
20533 if (!this.picker().isVisible()){
20534 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20545 e.preventDefault();
20549 dir = e.keyCode == 37 ? -1 : 1;
20551 this.vIndex = this.vIndex + dir;
20553 if(this.vIndex < 0){
20557 if(this.vIndex > 11){
20561 if(isNaN(this.vIndex)){
20565 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20571 dir = e.keyCode == 38 ? -1 : 1;
20573 this.vIndex = this.vIndex + dir * 4;
20575 if(this.vIndex < 0){
20579 if(this.vIndex > 11){
20583 if(isNaN(this.vIndex)){
20587 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20592 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20593 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20597 e.preventDefault();
20600 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20601 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20617 this.picker().remove();
20622 Roo.apply(Roo.bootstrap.MonthField, {
20641 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20642 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20647 Roo.apply(Roo.bootstrap.MonthField, {
20651 cls: 'datepicker dropdown-menu roo-dynamic',
20655 cls: 'datepicker-months',
20659 cls: 'table-condensed',
20661 Roo.bootstrap.DateField.content
20681 * @class Roo.bootstrap.CheckBox
20682 * @extends Roo.bootstrap.Input
20683 * Bootstrap CheckBox class
20685 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20686 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20687 * @cfg {String} boxLabel The text that appears beside the checkbox
20688 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20689 * @cfg {Boolean} checked initnal the element
20690 * @cfg {Boolean} inline inline the element (default false)
20691 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20692 * @cfg {String} tooltip label tooltip
20695 * Create a new CheckBox
20696 * @param {Object} config The config object
20699 Roo.bootstrap.CheckBox = function(config){
20700 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20705 * Fires when the element is checked or unchecked.
20706 * @param {Roo.bootstrap.CheckBox} this This input
20707 * @param {Boolean} checked The new checked value
20712 * Fires when the element is click.
20713 * @param {Roo.bootstrap.CheckBox} this This input
20720 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20722 inputType: 'checkbox',
20731 getAutoCreate : function()
20733 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20739 cfg.cls = 'form-group ' + this.inputType; //input-group
20742 cfg.cls += ' ' + this.inputType + '-inline';
20748 type : this.inputType,
20749 value : this.inputValue,
20750 cls : 'roo-' + this.inputType, //'form-box',
20751 placeholder : this.placeholder || ''
20755 if(this.inputType != 'radio'){
20759 cls : 'roo-hidden-value',
20760 value : this.checked ? this.inputValue : this.valueOff
20765 if (this.weight) { // Validity check?
20766 cfg.cls += " " + this.inputType + "-" + this.weight;
20769 if (this.disabled) {
20770 input.disabled=true;
20774 input.checked = this.checked;
20779 input.name = this.name;
20781 if(this.inputType != 'radio'){
20782 hidden.name = this.name;
20783 input.name = '_hidden_' + this.name;
20788 input.cls += ' input-' + this.size;
20793 ['xs','sm','md','lg'].map(function(size){
20794 if (settings[size]) {
20795 cfg.cls += ' col-' + size + '-' + settings[size];
20799 var inputblock = input;
20801 if (this.before || this.after) {
20804 cls : 'input-group',
20809 inputblock.cn.push({
20811 cls : 'input-group-addon',
20816 inputblock.cn.push(input);
20818 if(this.inputType != 'radio'){
20819 inputblock.cn.push(hidden);
20823 inputblock.cn.push({
20825 cls : 'input-group-addon',
20832 if (align ==='left' && this.fieldLabel.length) {
20833 // Roo.log("left and has label");
20838 cls : 'control-label',
20839 html : this.fieldLabel
20849 if(this.labelWidth > 12){
20850 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20853 if(this.labelWidth < 13 && this.labelmd == 0){
20854 this.labelmd = this.labelWidth;
20857 if(this.labellg > 0){
20858 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20859 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20862 if(this.labelmd > 0){
20863 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20864 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20867 if(this.labelsm > 0){
20868 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20869 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20872 if(this.labelxs > 0){
20873 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20874 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20877 } else if ( this.fieldLabel.length) {
20878 // Roo.log(" label");
20882 tag: this.boxLabel ? 'span' : 'label',
20884 cls: 'control-label box-input-label',
20885 //cls : 'input-group-addon',
20886 html : this.fieldLabel
20895 // Roo.log(" no label && no align");
20896 cfg.cn = [ inputblock ] ;
20902 var boxLabelCfg = {
20904 //'for': id, // box label is handled by onclick - so no for...
20906 html: this.boxLabel
20910 boxLabelCfg.tooltip = this.tooltip;
20913 cfg.cn.push(boxLabelCfg);
20916 if(this.inputType != 'radio'){
20917 cfg.cn.push(hidden);
20925 * return the real input element.
20927 inputEl: function ()
20929 return this.el.select('input.roo-' + this.inputType,true).first();
20931 hiddenEl: function ()
20933 return this.el.select('input.roo-hidden-value',true).first();
20936 labelEl: function()
20938 return this.el.select('label.control-label',true).first();
20940 /* depricated... */
20944 return this.labelEl();
20947 boxLabelEl: function()
20949 return this.el.select('label.box-label',true).first();
20952 initEvents : function()
20954 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20956 this.inputEl().on('click', this.onClick, this);
20958 if (this.boxLabel) {
20959 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20962 this.startValue = this.getValue();
20965 Roo.bootstrap.CheckBox.register(this);
20969 onClick : function(e)
20971 if(this.fireEvent('click', this, e) !== false){
20972 this.setChecked(!this.checked);
20977 setChecked : function(state,suppressEvent)
20979 this.startValue = this.getValue();
20981 if(this.inputType == 'radio'){
20983 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20984 e.dom.checked = false;
20987 this.inputEl().dom.checked = true;
20989 this.inputEl().dom.value = this.inputValue;
20991 if(suppressEvent !== true){
20992 this.fireEvent('check', this, true);
21000 this.checked = state;
21002 this.inputEl().dom.checked = state;
21005 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21007 if(suppressEvent !== true){
21008 this.fireEvent('check', this, state);
21014 getValue : function()
21016 if(this.inputType == 'radio'){
21017 return this.getGroupValue();
21020 return this.hiddenEl().dom.value;
21024 getGroupValue : function()
21026 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21030 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21033 setValue : function(v,suppressEvent)
21035 if(this.inputType == 'radio'){
21036 this.setGroupValue(v, suppressEvent);
21040 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21045 setGroupValue : function(v, suppressEvent)
21047 this.startValue = this.getValue();
21049 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21050 e.dom.checked = false;
21052 if(e.dom.value == v){
21053 e.dom.checked = true;
21057 if(suppressEvent !== true){
21058 this.fireEvent('check', this, true);
21066 validate : function()
21068 if(this.getVisibilityEl().hasClass('hidden')){
21074 (this.inputType == 'radio' && this.validateRadio()) ||
21075 (this.inputType == 'checkbox' && this.validateCheckbox())
21081 this.markInvalid();
21085 validateRadio : function()
21087 if(this.getVisibilityEl().hasClass('hidden')){
21091 if(this.allowBlank){
21097 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21098 if(!e.dom.checked){
21110 validateCheckbox : function()
21113 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21114 //return (this.getValue() == this.inputValue) ? true : false;
21117 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21125 for(var i in group){
21126 if(group[i].el.isVisible(true)){
21134 for(var i in group){
21139 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21146 * Mark this field as valid
21148 markValid : function()
21152 this.fireEvent('valid', this);
21154 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21157 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21164 if(this.inputType == 'radio'){
21165 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21166 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21167 e.findParent('.form-group', false, true).addClass(_this.validClass);
21174 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21175 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21179 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21185 for(var i in group){
21186 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21187 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21192 * Mark this field as invalid
21193 * @param {String} msg The validation message
21195 markInvalid : function(msg)
21197 if(this.allowBlank){
21203 this.fireEvent('invalid', this, msg);
21205 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21208 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21212 label.markInvalid();
21215 if(this.inputType == 'radio'){
21216 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21217 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21218 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21225 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21226 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21230 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21236 for(var i in group){
21237 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21238 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21243 clearInvalid : function()
21245 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21247 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21249 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21251 if (label && label.iconEl) {
21252 label.iconEl.removeClass(label.validClass);
21253 label.iconEl.removeClass(label.invalidClass);
21257 disable : function()
21259 if(this.inputType != 'radio'){
21260 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21267 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21268 _this.getActionEl().addClass(this.disabledClass);
21269 e.dom.disabled = true;
21273 this.disabled = true;
21274 this.fireEvent("disable", this);
21278 enable : function()
21280 if(this.inputType != 'radio'){
21281 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21288 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21289 _this.getActionEl().removeClass(this.disabledClass);
21290 e.dom.disabled = false;
21294 this.disabled = false;
21295 this.fireEvent("enable", this);
21299 setBoxLabel : function(v)
21304 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21310 Roo.apply(Roo.bootstrap.CheckBox, {
21315 * register a CheckBox Group
21316 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21318 register : function(checkbox)
21320 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21321 this.groups[checkbox.groupId] = {};
21324 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21328 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21332 * fetch a CheckBox Group based on the group ID
21333 * @param {string} the group ID
21334 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21336 get: function(groupId) {
21337 if (typeof(this.groups[groupId]) == 'undefined') {
21341 return this.groups[groupId] ;
21354 * @class Roo.bootstrap.Radio
21355 * @extends Roo.bootstrap.Component
21356 * Bootstrap Radio class
21357 * @cfg {String} boxLabel - the label associated
21358 * @cfg {String} value - the value of radio
21361 * Create a new Radio
21362 * @param {Object} config The config object
21364 Roo.bootstrap.Radio = function(config){
21365 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21369 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21375 getAutoCreate : function()
21379 cls : 'form-group radio',
21384 html : this.boxLabel
21392 initEvents : function()
21394 this.parent().register(this);
21396 this.el.on('click', this.onClick, this);
21400 onClick : function(e)
21402 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21403 this.setChecked(true);
21407 setChecked : function(state, suppressEvent)
21409 this.parent().setValue(this.value, suppressEvent);
21413 setBoxLabel : function(v)
21418 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21433 * @class Roo.bootstrap.SecurePass
21434 * @extends Roo.bootstrap.Input
21435 * Bootstrap SecurePass class
21439 * Create a new SecurePass
21440 * @param {Object} config The config object
21443 Roo.bootstrap.SecurePass = function (config) {
21444 // these go here, so the translation tool can replace them..
21446 PwdEmpty: "Please type a password, and then retype it to confirm.",
21447 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21448 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21449 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21450 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21451 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21452 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21453 TooWeak: "Your password is Too Weak."
21455 this.meterLabel = "Password strength:";
21456 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21457 this.meterClass = [
21458 "roo-password-meter-tooweak",
21459 "roo-password-meter-weak",
21460 "roo-password-meter-medium",
21461 "roo-password-meter-strong",
21462 "roo-password-meter-grey"
21467 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21470 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21472 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21474 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21475 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21476 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21477 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21478 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21479 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21480 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21490 * @cfg {String/Object} Label for the strength meter (defaults to
21491 * 'Password strength:')
21496 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21497 * ['Weak', 'Medium', 'Strong'])
21500 pwdStrengths: false,
21513 initEvents: function ()
21515 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21517 if (this.el.is('input[type=password]') && Roo.isSafari) {
21518 this.el.on('keydown', this.SafariOnKeyDown, this);
21521 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21524 onRender: function (ct, position)
21526 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21527 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21528 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21530 this.trigger.createChild({
21535 cls: 'roo-password-meter-grey col-xs-12',
21538 //width: this.meterWidth + 'px'
21542 cls: 'roo-password-meter-text'
21548 if (this.hideTrigger) {
21549 this.trigger.setDisplayed(false);
21551 this.setSize(this.width || '', this.height || '');
21554 onDestroy: function ()
21556 if (this.trigger) {
21557 this.trigger.removeAllListeners();
21558 this.trigger.remove();
21561 this.wrap.remove();
21563 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21566 checkStrength: function ()
21568 var pwd = this.inputEl().getValue();
21569 if (pwd == this._lastPwd) {
21574 if (this.ClientSideStrongPassword(pwd)) {
21576 } else if (this.ClientSideMediumPassword(pwd)) {
21578 } else if (this.ClientSideWeakPassword(pwd)) {
21584 Roo.log('strength1: ' + strength);
21586 //var pm = this.trigger.child('div/div/div').dom;
21587 var pm = this.trigger.child('div/div');
21588 pm.removeClass(this.meterClass);
21589 pm.addClass(this.meterClass[strength]);
21592 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21594 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21596 this._lastPwd = pwd;
21600 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21602 this._lastPwd = '';
21604 var pm = this.trigger.child('div/div');
21605 pm.removeClass(this.meterClass);
21606 pm.addClass('roo-password-meter-grey');
21609 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21612 this.inputEl().dom.type='password';
21615 validateValue: function (value)
21618 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21621 if (value.length == 0) {
21622 if (this.allowBlank) {
21623 this.clearInvalid();
21627 this.markInvalid(this.errors.PwdEmpty);
21628 this.errorMsg = this.errors.PwdEmpty;
21636 if ('[\x21-\x7e]*'.match(value)) {
21637 this.markInvalid(this.errors.PwdBadChar);
21638 this.errorMsg = this.errors.PwdBadChar;
21641 if (value.length < 6) {
21642 this.markInvalid(this.errors.PwdShort);
21643 this.errorMsg = this.errors.PwdShort;
21646 if (value.length > 16) {
21647 this.markInvalid(this.errors.PwdLong);
21648 this.errorMsg = this.errors.PwdLong;
21652 if (this.ClientSideStrongPassword(value)) {
21654 } else if (this.ClientSideMediumPassword(value)) {
21656 } else if (this.ClientSideWeakPassword(value)) {
21663 if (strength < 2) {
21664 //this.markInvalid(this.errors.TooWeak);
21665 this.errorMsg = this.errors.TooWeak;
21670 console.log('strength2: ' + strength);
21672 //var pm = this.trigger.child('div/div/div').dom;
21674 var pm = this.trigger.child('div/div');
21675 pm.removeClass(this.meterClass);
21676 pm.addClass(this.meterClass[strength]);
21678 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21680 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21682 this.errorMsg = '';
21686 CharacterSetChecks: function (type)
21689 this.fResult = false;
21692 isctype: function (character, type)
21695 case this.kCapitalLetter:
21696 if (character >= 'A' && character <= 'Z') {
21701 case this.kSmallLetter:
21702 if (character >= 'a' && character <= 'z') {
21708 if (character >= '0' && character <= '9') {
21713 case this.kPunctuation:
21714 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21725 IsLongEnough: function (pwd, size)
21727 return !(pwd == null || isNaN(size) || pwd.length < size);
21730 SpansEnoughCharacterSets: function (word, nb)
21732 if (!this.IsLongEnough(word, nb))
21737 var characterSetChecks = new Array(
21738 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21739 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21742 for (var index = 0; index < word.length; ++index) {
21743 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21744 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21745 characterSetChecks[nCharSet].fResult = true;
21752 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21753 if (characterSetChecks[nCharSet].fResult) {
21758 if (nCharSets < nb) {
21764 ClientSideStrongPassword: function (pwd)
21766 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21769 ClientSideMediumPassword: function (pwd)
21771 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21774 ClientSideWeakPassword: function (pwd)
21776 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21779 })//<script type="text/javascript">
21782 * Based Ext JS Library 1.1.1
21783 * Copyright(c) 2006-2007, Ext JS, LLC.
21789 * @class Roo.HtmlEditorCore
21790 * @extends Roo.Component
21791 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21793 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21796 Roo.HtmlEditorCore = function(config){
21799 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21804 * @event initialize
21805 * Fires when the editor is fully initialized (including the iframe)
21806 * @param {Roo.HtmlEditorCore} this
21811 * Fires when the editor is first receives the focus. Any insertion must wait
21812 * until after this event.
21813 * @param {Roo.HtmlEditorCore} this
21817 * @event beforesync
21818 * Fires before the textarea is updated with content from the editor iframe. Return false
21819 * to cancel the sync.
21820 * @param {Roo.HtmlEditorCore} this
21821 * @param {String} html
21825 * @event beforepush
21826 * Fires before the iframe editor is updated with content from the textarea. Return false
21827 * to cancel the push.
21828 * @param {Roo.HtmlEditorCore} this
21829 * @param {String} html
21834 * Fires when the textarea is updated with content from the editor iframe.
21835 * @param {Roo.HtmlEditorCore} this
21836 * @param {String} html
21841 * Fires when the iframe editor is updated with content from the textarea.
21842 * @param {Roo.HtmlEditorCore} this
21843 * @param {String} html
21848 * @event editorevent
21849 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21850 * @param {Roo.HtmlEditorCore} this
21856 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21858 // defaults : white / black...
21859 this.applyBlacklists();
21866 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21870 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21876 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21881 * @cfg {Number} height (in pixels)
21885 * @cfg {Number} width (in pixels)
21890 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21893 stylesheets: false,
21898 // private properties
21899 validationEvent : false,
21901 initialized : false,
21903 sourceEditMode : false,
21904 onFocus : Roo.emptyFn,
21906 hideMode:'offsets',
21910 // blacklist + whitelisted elements..
21917 * Protected method that will not generally be called directly. It
21918 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21919 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21921 getDocMarkup : function(){
21925 // inherit styels from page...??
21926 if (this.stylesheets === false) {
21928 Roo.get(document.head).select('style').each(function(node) {
21929 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21932 Roo.get(document.head).select('link').each(function(node) {
21933 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21936 } else if (!this.stylesheets.length) {
21938 st = '<style type="text/css">' +
21939 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21942 st = '<style type="text/css">' +
21947 st += '<style type="text/css">' +
21948 'IMG { cursor: pointer } ' +
21951 var cls = 'roo-htmleditor-body';
21953 if(this.bodyCls.length){
21954 cls += ' ' + this.bodyCls;
21957 return '<html><head>' + st +
21958 //<style type="text/css">' +
21959 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21961 ' </head><body class="' + cls + '"></body></html>';
21965 onRender : function(ct, position)
21968 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21969 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21972 this.el.dom.style.border = '0 none';
21973 this.el.dom.setAttribute('tabIndex', -1);
21974 this.el.addClass('x-hidden hide');
21978 if(Roo.isIE){ // fix IE 1px bogus margin
21979 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21983 this.frameId = Roo.id();
21987 var iframe = this.owner.wrap.createChild({
21989 cls: 'form-control', // bootstrap..
21991 name: this.frameId,
21992 frameBorder : 'no',
21993 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21998 this.iframe = iframe.dom;
22000 this.assignDocWin();
22002 this.doc.designMode = 'on';
22005 this.doc.write(this.getDocMarkup());
22009 var task = { // must defer to wait for browser to be ready
22011 //console.log("run task?" + this.doc.readyState);
22012 this.assignDocWin();
22013 if(this.doc.body || this.doc.readyState == 'complete'){
22015 this.doc.designMode="on";
22019 Roo.TaskMgr.stop(task);
22020 this.initEditor.defer(10, this);
22027 Roo.TaskMgr.start(task);
22032 onResize : function(w, h)
22034 Roo.log('resize: ' +w + ',' + h );
22035 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22039 if(typeof w == 'number'){
22041 this.iframe.style.width = w + 'px';
22043 if(typeof h == 'number'){
22045 this.iframe.style.height = h + 'px';
22047 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22054 * Toggles the editor between standard and source edit mode.
22055 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22057 toggleSourceEdit : function(sourceEditMode){
22059 this.sourceEditMode = sourceEditMode === true;
22061 if(this.sourceEditMode){
22063 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22066 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22067 //this.iframe.className = '';
22070 //this.setSize(this.owner.wrap.getSize());
22071 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22078 * Protected method that will not generally be called directly. If you need/want
22079 * custom HTML cleanup, this is the method you should override.
22080 * @param {String} html The HTML to be cleaned
22081 * return {String} The cleaned HTML
22083 cleanHtml : function(html){
22084 html = String(html);
22085 if(html.length > 5){
22086 if(Roo.isSafari){ // strip safari nonsense
22087 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22090 if(html == ' '){
22097 * HTML Editor -> Textarea
22098 * Protected method that will not generally be called directly. Syncs the contents
22099 * of the editor iframe with the textarea.
22101 syncValue : function(){
22102 if(this.initialized){
22103 var bd = (this.doc.body || this.doc.documentElement);
22104 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22105 var html = bd.innerHTML;
22107 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22108 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22110 html = '<div style="'+m[0]+'">' + html + '</div>';
22113 html = this.cleanHtml(html);
22114 // fix up the special chars.. normaly like back quotes in word...
22115 // however we do not want to do this with chinese..
22116 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22117 var cc = b.charCodeAt();
22119 (cc >= 0x4E00 && cc < 0xA000 ) ||
22120 (cc >= 0x3400 && cc < 0x4E00 ) ||
22121 (cc >= 0xf900 && cc < 0xfb00 )
22127 if(this.owner.fireEvent('beforesync', this, html) !== false){
22128 this.el.dom.value = html;
22129 this.owner.fireEvent('sync', this, html);
22135 * Protected method that will not generally be called directly. Pushes the value of the textarea
22136 * into the iframe editor.
22138 pushValue : function(){
22139 if(this.initialized){
22140 var v = this.el.dom.value.trim();
22142 // if(v.length < 1){
22146 if(this.owner.fireEvent('beforepush', this, v) !== false){
22147 var d = (this.doc.body || this.doc.documentElement);
22149 this.cleanUpPaste();
22150 this.el.dom.value = d.innerHTML;
22151 this.owner.fireEvent('push', this, v);
22157 deferFocus : function(){
22158 this.focus.defer(10, this);
22162 focus : function(){
22163 if(this.win && !this.sourceEditMode){
22170 assignDocWin: function()
22172 var iframe = this.iframe;
22175 this.doc = iframe.contentWindow.document;
22176 this.win = iframe.contentWindow;
22178 // if (!Roo.get(this.frameId)) {
22181 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22182 // this.win = Roo.get(this.frameId).dom.contentWindow;
22184 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22188 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22189 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22194 initEditor : function(){
22195 //console.log("INIT EDITOR");
22196 this.assignDocWin();
22200 this.doc.designMode="on";
22202 this.doc.write(this.getDocMarkup());
22205 var dbody = (this.doc.body || this.doc.documentElement);
22206 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22207 // this copies styles from the containing element into thsi one..
22208 // not sure why we need all of this..
22209 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22211 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22212 //ss['background-attachment'] = 'fixed'; // w3c
22213 dbody.bgProperties = 'fixed'; // ie
22214 //Roo.DomHelper.applyStyles(dbody, ss);
22215 Roo.EventManager.on(this.doc, {
22216 //'mousedown': this.onEditorEvent,
22217 'mouseup': this.onEditorEvent,
22218 'dblclick': this.onEditorEvent,
22219 'click': this.onEditorEvent,
22220 'keyup': this.onEditorEvent,
22225 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22227 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22228 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22230 this.initialized = true;
22232 this.owner.fireEvent('initialize', this);
22237 onDestroy : function(){
22243 //for (var i =0; i < this.toolbars.length;i++) {
22244 // // fixme - ask toolbars for heights?
22245 // this.toolbars[i].onDestroy();
22248 //this.wrap.dom.innerHTML = '';
22249 //this.wrap.remove();
22254 onFirstFocus : function(){
22256 this.assignDocWin();
22259 this.activated = true;
22262 if(Roo.isGecko){ // prevent silly gecko errors
22264 var s = this.win.getSelection();
22265 if(!s.focusNode || s.focusNode.nodeType != 3){
22266 var r = s.getRangeAt(0);
22267 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22272 this.execCmd('useCSS', true);
22273 this.execCmd('styleWithCSS', false);
22276 this.owner.fireEvent('activate', this);
22280 adjustFont: function(btn){
22281 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22282 //if(Roo.isSafari){ // safari
22285 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22286 if(Roo.isSafari){ // safari
22287 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22288 v = (v < 10) ? 10 : v;
22289 v = (v > 48) ? 48 : v;
22290 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22295 v = Math.max(1, v+adjust);
22297 this.execCmd('FontSize', v );
22300 onEditorEvent : function(e)
22302 this.owner.fireEvent('editorevent', this, e);
22303 // this.updateToolbar();
22304 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22307 insertTag : function(tg)
22309 // could be a bit smarter... -> wrap the current selected tRoo..
22310 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22312 range = this.createRange(this.getSelection());
22313 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22314 wrappingNode.appendChild(range.extractContents());
22315 range.insertNode(wrappingNode);
22322 this.execCmd("formatblock", tg);
22326 insertText : function(txt)
22330 var range = this.createRange();
22331 range.deleteContents();
22332 //alert(Sender.getAttribute('label'));
22334 range.insertNode(this.doc.createTextNode(txt));
22340 * Executes a Midas editor command on the editor document and performs necessary focus and
22341 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22342 * @param {String} cmd The Midas command
22343 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22345 relayCmd : function(cmd, value){
22347 this.execCmd(cmd, value);
22348 this.owner.fireEvent('editorevent', this);
22349 //this.updateToolbar();
22350 this.owner.deferFocus();
22354 * Executes a Midas editor command directly on the editor document.
22355 * For visual commands, you should use {@link #relayCmd} instead.
22356 * <b>This should only be called after the editor is initialized.</b>
22357 * @param {String} cmd The Midas command
22358 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22360 execCmd : function(cmd, value){
22361 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22368 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22370 * @param {String} text | dom node..
22372 insertAtCursor : function(text)
22375 if(!this.activated){
22381 var r = this.doc.selection.createRange();
22392 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22396 // from jquery ui (MIT licenced)
22398 var win = this.win;
22400 if (win.getSelection && win.getSelection().getRangeAt) {
22401 range = win.getSelection().getRangeAt(0);
22402 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22403 range.insertNode(node);
22404 } else if (win.document.selection && win.document.selection.createRange) {
22405 // no firefox support
22406 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22407 win.document.selection.createRange().pasteHTML(txt);
22409 // no firefox support
22410 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22411 this.execCmd('InsertHTML', txt);
22420 mozKeyPress : function(e){
22422 var c = e.getCharCode(), cmd;
22425 c = String.fromCharCode(c).toLowerCase();
22439 this.cleanUpPaste.defer(100, this);
22447 e.preventDefault();
22455 fixKeys : function(){ // load time branching for fastest keydown performance
22457 return function(e){
22458 var k = e.getKey(), r;
22461 r = this.doc.selection.createRange();
22464 r.pasteHTML('    ');
22471 r = this.doc.selection.createRange();
22473 var target = r.parentElement();
22474 if(!target || target.tagName.toLowerCase() != 'li'){
22476 r.pasteHTML('<br />');
22482 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22483 this.cleanUpPaste.defer(100, this);
22489 }else if(Roo.isOpera){
22490 return function(e){
22491 var k = e.getKey();
22495 this.execCmd('InsertHTML','    ');
22498 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22499 this.cleanUpPaste.defer(100, this);
22504 }else if(Roo.isSafari){
22505 return function(e){
22506 var k = e.getKey();
22510 this.execCmd('InsertText','\t');
22514 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22515 this.cleanUpPaste.defer(100, this);
22523 getAllAncestors: function()
22525 var p = this.getSelectedNode();
22528 a.push(p); // push blank onto stack..
22529 p = this.getParentElement();
22533 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22537 a.push(this.doc.body);
22541 lastSelNode : false,
22544 getSelection : function()
22546 this.assignDocWin();
22547 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22550 getSelectedNode: function()
22552 // this may only work on Gecko!!!
22554 // should we cache this!!!!
22559 var range = this.createRange(this.getSelection()).cloneRange();
22562 var parent = range.parentElement();
22564 var testRange = range.duplicate();
22565 testRange.moveToElementText(parent);
22566 if (testRange.inRange(range)) {
22569 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22572 parent = parent.parentElement;
22577 // is ancestor a text element.
22578 var ac = range.commonAncestorContainer;
22579 if (ac.nodeType == 3) {
22580 ac = ac.parentNode;
22583 var ar = ac.childNodes;
22586 var other_nodes = [];
22587 var has_other_nodes = false;
22588 for (var i=0;i<ar.length;i++) {
22589 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22592 // fullly contained node.
22594 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22599 // probably selected..
22600 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22601 other_nodes.push(ar[i]);
22605 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22610 has_other_nodes = true;
22612 if (!nodes.length && other_nodes.length) {
22613 nodes= other_nodes;
22615 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22621 createRange: function(sel)
22623 // this has strange effects when using with
22624 // top toolbar - not sure if it's a great idea.
22625 //this.editor.contentWindow.focus();
22626 if (typeof sel != "undefined") {
22628 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22630 return this.doc.createRange();
22633 return this.doc.createRange();
22636 getParentElement: function()
22639 this.assignDocWin();
22640 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22642 var range = this.createRange(sel);
22645 var p = range.commonAncestorContainer;
22646 while (p.nodeType == 3) { // text node
22657 * Range intersection.. the hard stuff...
22661 * [ -- selected range --- ]
22665 * if end is before start or hits it. fail.
22666 * if start is after end or hits it fail.
22668 * if either hits (but other is outside. - then it's not
22674 // @see http://www.thismuchiknow.co.uk/?p=64.
22675 rangeIntersectsNode : function(range, node)
22677 var nodeRange = node.ownerDocument.createRange();
22679 nodeRange.selectNode(node);
22681 nodeRange.selectNodeContents(node);
22684 var rangeStartRange = range.cloneRange();
22685 rangeStartRange.collapse(true);
22687 var rangeEndRange = range.cloneRange();
22688 rangeEndRange.collapse(false);
22690 var nodeStartRange = nodeRange.cloneRange();
22691 nodeStartRange.collapse(true);
22693 var nodeEndRange = nodeRange.cloneRange();
22694 nodeEndRange.collapse(false);
22696 return rangeStartRange.compareBoundaryPoints(
22697 Range.START_TO_START, nodeEndRange) == -1 &&
22698 rangeEndRange.compareBoundaryPoints(
22699 Range.START_TO_START, nodeStartRange) == 1;
22703 rangeCompareNode : function(range, node)
22705 var nodeRange = node.ownerDocument.createRange();
22707 nodeRange.selectNode(node);
22709 nodeRange.selectNodeContents(node);
22713 range.collapse(true);
22715 nodeRange.collapse(true);
22717 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22718 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22720 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22722 var nodeIsBefore = ss == 1;
22723 var nodeIsAfter = ee == -1;
22725 if (nodeIsBefore && nodeIsAfter) {
22728 if (!nodeIsBefore && nodeIsAfter) {
22729 return 1; //right trailed.
22732 if (nodeIsBefore && !nodeIsAfter) {
22733 return 2; // left trailed.
22739 // private? - in a new class?
22740 cleanUpPaste : function()
22742 // cleans up the whole document..
22743 Roo.log('cleanuppaste');
22745 this.cleanUpChildren(this.doc.body);
22746 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22747 if (clean != this.doc.body.innerHTML) {
22748 this.doc.body.innerHTML = clean;
22753 cleanWordChars : function(input) {// change the chars to hex code
22754 var he = Roo.HtmlEditorCore;
22756 var output = input;
22757 Roo.each(he.swapCodes, function(sw) {
22758 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22760 output = output.replace(swapper, sw[1]);
22767 cleanUpChildren : function (n)
22769 if (!n.childNodes.length) {
22772 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22773 this.cleanUpChild(n.childNodes[i]);
22780 cleanUpChild : function (node)
22783 //console.log(node);
22784 if (node.nodeName == "#text") {
22785 // clean up silly Windows -- stuff?
22788 if (node.nodeName == "#comment") {
22789 node.parentNode.removeChild(node);
22790 // clean up silly Windows -- stuff?
22793 var lcname = node.tagName.toLowerCase();
22794 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22795 // whitelist of tags..
22797 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22799 node.parentNode.removeChild(node);
22804 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22806 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22807 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22809 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22810 // remove_keep_children = true;
22813 if (remove_keep_children) {
22814 this.cleanUpChildren(node);
22815 // inserts everything just before this node...
22816 while (node.childNodes.length) {
22817 var cn = node.childNodes[0];
22818 node.removeChild(cn);
22819 node.parentNode.insertBefore(cn, node);
22821 node.parentNode.removeChild(node);
22825 if (!node.attributes || !node.attributes.length) {
22826 this.cleanUpChildren(node);
22830 function cleanAttr(n,v)
22833 if (v.match(/^\./) || v.match(/^\//)) {
22836 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22839 if (v.match(/^#/)) {
22842 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22843 node.removeAttribute(n);
22847 var cwhite = this.cwhite;
22848 var cblack = this.cblack;
22850 function cleanStyle(n,v)
22852 if (v.match(/expression/)) { //XSS?? should we even bother..
22853 node.removeAttribute(n);
22857 var parts = v.split(/;/);
22860 Roo.each(parts, function(p) {
22861 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22865 var l = p.split(':').shift().replace(/\s+/g,'');
22866 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22868 if ( cwhite.length && cblack.indexOf(l) > -1) {
22869 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22870 //node.removeAttribute(n);
22874 // only allow 'c whitelisted system attributes'
22875 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22876 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22877 //node.removeAttribute(n);
22887 if (clean.length) {
22888 node.setAttribute(n, clean.join(';'));
22890 node.removeAttribute(n);
22896 for (var i = node.attributes.length-1; i > -1 ; i--) {
22897 var a = node.attributes[i];
22900 if (a.name.toLowerCase().substr(0,2)=='on') {
22901 node.removeAttribute(a.name);
22904 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22905 node.removeAttribute(a.name);
22908 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22909 cleanAttr(a.name,a.value); // fixme..
22912 if (a.name == 'style') {
22913 cleanStyle(a.name,a.value);
22916 /// clean up MS crap..
22917 // tecnically this should be a list of valid class'es..
22920 if (a.name == 'class') {
22921 if (a.value.match(/^Mso/)) {
22922 node.className = '';
22925 if (a.value.match(/^body$/)) {
22926 node.className = '';
22937 this.cleanUpChildren(node);
22943 * Clean up MS wordisms...
22945 cleanWord : function(node)
22950 this.cleanWord(this.doc.body);
22953 if (node.nodeName == "#text") {
22954 // clean up silly Windows -- stuff?
22957 if (node.nodeName == "#comment") {
22958 node.parentNode.removeChild(node);
22959 // clean up silly Windows -- stuff?
22963 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22964 node.parentNode.removeChild(node);
22968 // remove - but keep children..
22969 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22970 while (node.childNodes.length) {
22971 var cn = node.childNodes[0];
22972 node.removeChild(cn);
22973 node.parentNode.insertBefore(cn, node);
22975 node.parentNode.removeChild(node);
22976 this.iterateChildren(node, this.cleanWord);
22980 if (node.className.length) {
22982 var cn = node.className.split(/\W+/);
22984 Roo.each(cn, function(cls) {
22985 if (cls.match(/Mso[a-zA-Z]+/)) {
22990 node.className = cna.length ? cna.join(' ') : '';
22992 node.removeAttribute("class");
22996 if (node.hasAttribute("lang")) {
22997 node.removeAttribute("lang");
23000 if (node.hasAttribute("style")) {
23002 var styles = node.getAttribute("style").split(";");
23004 Roo.each(styles, function(s) {
23005 if (!s.match(/:/)) {
23008 var kv = s.split(":");
23009 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23012 // what ever is left... we allow.
23015 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23016 if (!nstyle.length) {
23017 node.removeAttribute('style');
23020 this.iterateChildren(node, this.cleanWord);
23026 * iterateChildren of a Node, calling fn each time, using this as the scole..
23027 * @param {DomNode} node node to iterate children of.
23028 * @param {Function} fn method of this class to call on each item.
23030 iterateChildren : function(node, fn)
23032 if (!node.childNodes.length) {
23035 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23036 fn.call(this, node.childNodes[i])
23042 * cleanTableWidths.
23044 * Quite often pasting from word etc.. results in tables with column and widths.
23045 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23048 cleanTableWidths : function(node)
23053 this.cleanTableWidths(this.doc.body);
23058 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23061 Roo.log(node.tagName);
23062 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23063 this.iterateChildren(node, this.cleanTableWidths);
23066 if (node.hasAttribute('width')) {
23067 node.removeAttribute('width');
23071 if (node.hasAttribute("style")) {
23074 var styles = node.getAttribute("style").split(";");
23076 Roo.each(styles, function(s) {
23077 if (!s.match(/:/)) {
23080 var kv = s.split(":");
23081 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23084 // what ever is left... we allow.
23087 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23088 if (!nstyle.length) {
23089 node.removeAttribute('style');
23093 this.iterateChildren(node, this.cleanTableWidths);
23101 domToHTML : function(currentElement, depth, nopadtext) {
23103 depth = depth || 0;
23104 nopadtext = nopadtext || false;
23106 if (!currentElement) {
23107 return this.domToHTML(this.doc.body);
23110 //Roo.log(currentElement);
23112 var allText = false;
23113 var nodeName = currentElement.nodeName;
23114 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23116 if (nodeName == '#text') {
23118 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23123 if (nodeName != 'BODY') {
23126 // Prints the node tagName, such as <A>, <IMG>, etc
23129 for(i = 0; i < currentElement.attributes.length;i++) {
23131 var aname = currentElement.attributes.item(i).name;
23132 if (!currentElement.attributes.item(i).value.length) {
23135 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23138 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23147 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23150 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23155 // Traverse the tree
23157 var currentElementChild = currentElement.childNodes.item(i);
23158 var allText = true;
23159 var innerHTML = '';
23161 while (currentElementChild) {
23162 // Formatting code (indent the tree so it looks nice on the screen)
23163 var nopad = nopadtext;
23164 if (lastnode == 'SPAN') {
23168 if (currentElementChild.nodeName == '#text') {
23169 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23170 toadd = nopadtext ? toadd : toadd.trim();
23171 if (!nopad && toadd.length > 80) {
23172 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23174 innerHTML += toadd;
23177 currentElementChild = currentElement.childNodes.item(i);
23183 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23185 // Recursively traverse the tree structure of the child node
23186 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23187 lastnode = currentElementChild.nodeName;
23189 currentElementChild=currentElement.childNodes.item(i);
23195 // The remaining code is mostly for formatting the tree
23196 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23201 ret+= "</"+tagName+">";
23207 applyBlacklists : function()
23209 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23210 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23214 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23215 if (b.indexOf(tag) > -1) {
23218 this.white.push(tag);
23222 Roo.each(w, function(tag) {
23223 if (b.indexOf(tag) > -1) {
23226 if (this.white.indexOf(tag) > -1) {
23229 this.white.push(tag);
23234 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23235 if (w.indexOf(tag) > -1) {
23238 this.black.push(tag);
23242 Roo.each(b, function(tag) {
23243 if (w.indexOf(tag) > -1) {
23246 if (this.black.indexOf(tag) > -1) {
23249 this.black.push(tag);
23254 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23255 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23259 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23260 if (b.indexOf(tag) > -1) {
23263 this.cwhite.push(tag);
23267 Roo.each(w, function(tag) {
23268 if (b.indexOf(tag) > -1) {
23271 if (this.cwhite.indexOf(tag) > -1) {
23274 this.cwhite.push(tag);
23279 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23280 if (w.indexOf(tag) > -1) {
23283 this.cblack.push(tag);
23287 Roo.each(b, function(tag) {
23288 if (w.indexOf(tag) > -1) {
23291 if (this.cblack.indexOf(tag) > -1) {
23294 this.cblack.push(tag);
23299 setStylesheets : function(stylesheets)
23301 if(typeof(stylesheets) == 'string'){
23302 Roo.get(this.iframe.contentDocument.head).createChild({
23304 rel : 'stylesheet',
23313 Roo.each(stylesheets, function(s) {
23318 Roo.get(_this.iframe.contentDocument.head).createChild({
23320 rel : 'stylesheet',
23329 removeStylesheets : function()
23333 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23338 setStyle : function(style)
23340 Roo.get(this.iframe.contentDocument.head).createChild({
23349 // hide stuff that is not compatible
23363 * @event specialkey
23367 * @cfg {String} fieldClass @hide
23370 * @cfg {String} focusClass @hide
23373 * @cfg {String} autoCreate @hide
23376 * @cfg {String} inputType @hide
23379 * @cfg {String} invalidClass @hide
23382 * @cfg {String} invalidText @hide
23385 * @cfg {String} msgFx @hide
23388 * @cfg {String} validateOnBlur @hide
23392 Roo.HtmlEditorCore.white = [
23393 'area', 'br', 'img', 'input', 'hr', 'wbr',
23395 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23396 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23397 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23398 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23399 'table', 'ul', 'xmp',
23401 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23404 'dir', 'menu', 'ol', 'ul', 'dl',
23410 Roo.HtmlEditorCore.black = [
23411 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23413 'base', 'basefont', 'bgsound', 'blink', 'body',
23414 'frame', 'frameset', 'head', 'html', 'ilayer',
23415 'iframe', 'layer', 'link', 'meta', 'object',
23416 'script', 'style' ,'title', 'xml' // clean later..
23418 Roo.HtmlEditorCore.clean = [
23419 'script', 'style', 'title', 'xml'
23421 Roo.HtmlEditorCore.remove = [
23426 Roo.HtmlEditorCore.ablack = [
23430 Roo.HtmlEditorCore.aclean = [
23431 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23435 Roo.HtmlEditorCore.pwhite= [
23436 'http', 'https', 'mailto'
23439 // white listed style attributes.
23440 Roo.HtmlEditorCore.cwhite= [
23441 // 'text-align', /// default is to allow most things..
23447 // black listed style attributes.
23448 Roo.HtmlEditorCore.cblack= [
23449 // 'font-size' -- this can be set by the project
23453 Roo.HtmlEditorCore.swapCodes =[
23472 * @class Roo.bootstrap.HtmlEditor
23473 * @extends Roo.bootstrap.TextArea
23474 * Bootstrap HtmlEditor class
23477 * Create a new HtmlEditor
23478 * @param {Object} config The config object
23481 Roo.bootstrap.HtmlEditor = function(config){
23482 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23483 if (!this.toolbars) {
23484 this.toolbars = [];
23487 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23490 * @event initialize
23491 * Fires when the editor is fully initialized (including the iframe)
23492 * @param {HtmlEditor} this
23497 * Fires when the editor is first receives the focus. Any insertion must wait
23498 * until after this event.
23499 * @param {HtmlEditor} this
23503 * @event beforesync
23504 * Fires before the textarea is updated with content from the editor iframe. Return false
23505 * to cancel the sync.
23506 * @param {HtmlEditor} this
23507 * @param {String} html
23511 * @event beforepush
23512 * Fires before the iframe editor is updated with content from the textarea. Return false
23513 * to cancel the push.
23514 * @param {HtmlEditor} this
23515 * @param {String} html
23520 * Fires when the textarea is updated with content from the editor iframe.
23521 * @param {HtmlEditor} this
23522 * @param {String} html
23527 * Fires when the iframe editor is updated with content from the textarea.
23528 * @param {HtmlEditor} this
23529 * @param {String} html
23533 * @event editmodechange
23534 * Fires when the editor switches edit modes
23535 * @param {HtmlEditor} this
23536 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23538 editmodechange: true,
23540 * @event editorevent
23541 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23542 * @param {HtmlEditor} this
23546 * @event firstfocus
23547 * Fires when on first focus - needed by toolbars..
23548 * @param {HtmlEditor} this
23553 * Auto save the htmlEditor value as a file into Events
23554 * @param {HtmlEditor} this
23558 * @event savedpreview
23559 * preview the saved version of htmlEditor
23560 * @param {HtmlEditor} this
23567 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23571 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23576 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23581 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23586 * @cfg {Number} height (in pixels)
23590 * @cfg {Number} width (in pixels)
23595 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23598 stylesheets: false,
23603 // private properties
23604 validationEvent : false,
23606 initialized : false,
23609 onFocus : Roo.emptyFn,
23611 hideMode:'offsets',
23613 tbContainer : false,
23617 toolbarContainer :function() {
23618 return this.wrap.select('.x-html-editor-tb',true).first();
23622 * Protected method that will not generally be called directly. It
23623 * is called when the editor creates its toolbar. Override this method if you need to
23624 * add custom toolbar buttons.
23625 * @param {HtmlEditor} editor
23627 createToolbar : function(){
23628 Roo.log('renewing');
23629 Roo.log("create toolbars");
23631 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23632 this.toolbars[0].render(this.toolbarContainer());
23636 // if (!editor.toolbars || !editor.toolbars.length) {
23637 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23640 // for (var i =0 ; i < editor.toolbars.length;i++) {
23641 // editor.toolbars[i] = Roo.factory(
23642 // typeof(editor.toolbars[i]) == 'string' ?
23643 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23644 // Roo.bootstrap.HtmlEditor);
23645 // editor.toolbars[i].init(editor);
23651 onRender : function(ct, position)
23653 // Roo.log("Call onRender: " + this.xtype);
23655 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23657 this.wrap = this.inputEl().wrap({
23658 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23661 this.editorcore.onRender(ct, position);
23663 if (this.resizable) {
23664 this.resizeEl = new Roo.Resizable(this.wrap, {
23668 minHeight : this.height,
23669 height: this.height,
23670 handles : this.resizable,
23673 resize : function(r, w, h) {
23674 _t.onResize(w,h); // -something
23680 this.createToolbar(this);
23683 if(!this.width && this.resizable){
23684 this.setSize(this.wrap.getSize());
23686 if (this.resizeEl) {
23687 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23688 // should trigger onReize..
23694 onResize : function(w, h)
23696 Roo.log('resize: ' +w + ',' + h );
23697 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23701 if(this.inputEl() ){
23702 if(typeof w == 'number'){
23703 var aw = w - this.wrap.getFrameWidth('lr');
23704 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23707 if(typeof h == 'number'){
23708 var tbh = -11; // fixme it needs to tool bar size!
23709 for (var i =0; i < this.toolbars.length;i++) {
23710 // fixme - ask toolbars for heights?
23711 tbh += this.toolbars[i].el.getHeight();
23712 //if (this.toolbars[i].footer) {
23713 // tbh += this.toolbars[i].footer.el.getHeight();
23721 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23722 ah -= 5; // knock a few pixes off for look..
23723 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23727 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23728 this.editorcore.onResize(ew,eh);
23733 * Toggles the editor between standard and source edit mode.
23734 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23736 toggleSourceEdit : function(sourceEditMode)
23738 this.editorcore.toggleSourceEdit(sourceEditMode);
23740 if(this.editorcore.sourceEditMode){
23741 Roo.log('editor - showing textarea');
23744 // Roo.log(this.syncValue());
23746 this.inputEl().removeClass(['hide', 'x-hidden']);
23747 this.inputEl().dom.removeAttribute('tabIndex');
23748 this.inputEl().focus();
23750 Roo.log('editor - hiding textarea');
23752 // Roo.log(this.pushValue());
23755 this.inputEl().addClass(['hide', 'x-hidden']);
23756 this.inputEl().dom.setAttribute('tabIndex', -1);
23757 //this.deferFocus();
23760 if(this.resizable){
23761 this.setSize(this.wrap.getSize());
23764 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23767 // private (for BoxComponent)
23768 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23770 // private (for BoxComponent)
23771 getResizeEl : function(){
23775 // private (for BoxComponent)
23776 getPositionEl : function(){
23781 initEvents : function(){
23782 this.originalValue = this.getValue();
23786 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23789 // markInvalid : Roo.emptyFn,
23791 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23794 // clearInvalid : Roo.emptyFn,
23796 setValue : function(v){
23797 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23798 this.editorcore.pushValue();
23803 deferFocus : function(){
23804 this.focus.defer(10, this);
23808 focus : function(){
23809 this.editorcore.focus();
23815 onDestroy : function(){
23821 for (var i =0; i < this.toolbars.length;i++) {
23822 // fixme - ask toolbars for heights?
23823 this.toolbars[i].onDestroy();
23826 this.wrap.dom.innerHTML = '';
23827 this.wrap.remove();
23832 onFirstFocus : function(){
23833 //Roo.log("onFirstFocus");
23834 this.editorcore.onFirstFocus();
23835 for (var i =0; i < this.toolbars.length;i++) {
23836 this.toolbars[i].onFirstFocus();
23842 syncValue : function()
23844 this.editorcore.syncValue();
23847 pushValue : function()
23849 this.editorcore.pushValue();
23853 // hide stuff that is not compatible
23867 * @event specialkey
23871 * @cfg {String} fieldClass @hide
23874 * @cfg {String} focusClass @hide
23877 * @cfg {String} autoCreate @hide
23880 * @cfg {String} inputType @hide
23883 * @cfg {String} invalidClass @hide
23886 * @cfg {String} invalidText @hide
23889 * @cfg {String} msgFx @hide
23892 * @cfg {String} validateOnBlur @hide
23901 Roo.namespace('Roo.bootstrap.htmleditor');
23903 * @class Roo.bootstrap.HtmlEditorToolbar1
23908 new Roo.bootstrap.HtmlEditor({
23911 new Roo.bootstrap.HtmlEditorToolbar1({
23912 disable : { fonts: 1 , format: 1, ..., ... , ...],
23918 * @cfg {Object} disable List of elements to disable..
23919 * @cfg {Array} btns List of additional buttons.
23923 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23926 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23929 Roo.apply(this, config);
23931 // default disabled, based on 'good practice'..
23932 this.disable = this.disable || {};
23933 Roo.applyIf(this.disable, {
23936 specialElements : true
23938 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23940 this.editor = config.editor;
23941 this.editorcore = config.editor.editorcore;
23943 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23945 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23946 // dont call parent... till later.
23948 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23953 editorcore : false,
23958 "h1","h2","h3","h4","h5","h6",
23960 "abbr", "acronym", "address", "cite", "samp", "var",
23964 onRender : function(ct, position)
23966 // Roo.log("Call onRender: " + this.xtype);
23968 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23970 this.el.dom.style.marginBottom = '0';
23972 var editorcore = this.editorcore;
23973 var editor= this.editor;
23976 var btn = function(id,cmd , toggle, handler, html){
23978 var event = toggle ? 'toggle' : 'click';
23983 xns: Roo.bootstrap,
23987 enableToggle:toggle !== false,
23989 pressed : toggle ? false : null,
23992 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23993 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23999 // var cb_box = function...
24004 xns: Roo.bootstrap,
24009 xns: Roo.bootstrap,
24013 Roo.each(this.formats, function(f) {
24014 style.menu.items.push({
24016 xns: Roo.bootstrap,
24017 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24022 editorcore.insertTag(this.tagname);
24029 children.push(style);
24031 btn('bold',false,true);
24032 btn('italic',false,true);
24033 btn('align-left', 'justifyleft',true);
24034 btn('align-center', 'justifycenter',true);
24035 btn('align-right' , 'justifyright',true);
24036 btn('link', false, false, function(btn) {
24037 //Roo.log("create link?");
24038 var url = prompt(this.createLinkText, this.defaultLinkValue);
24039 if(url && url != 'http:/'+'/'){
24040 this.editorcore.relayCmd('createlink', url);
24043 btn('list','insertunorderedlist',true);
24044 btn('pencil', false,true, function(btn){
24046 this.toggleSourceEdit(btn.pressed);
24049 if (this.editor.btns.length > 0) {
24050 for (var i = 0; i<this.editor.btns.length; i++) {
24051 children.push(this.editor.btns[i]);
24059 xns: Roo.bootstrap,
24064 xns: Roo.bootstrap,
24069 cog.menu.items.push({
24071 xns: Roo.bootstrap,
24072 html : Clean styles,
24077 editorcore.insertTag(this.tagname);
24086 this.xtype = 'NavSimplebar';
24088 for(var i=0;i< children.length;i++) {
24090 this.buttons.add(this.addxtypeChild(children[i]));
24094 editor.on('editorevent', this.updateToolbar, this);
24096 onBtnClick : function(id)
24098 this.editorcore.relayCmd(id);
24099 this.editorcore.focus();
24103 * Protected method that will not generally be called directly. It triggers
24104 * a toolbar update by reading the markup state of the current selection in the editor.
24106 updateToolbar: function(){
24108 if(!this.editorcore.activated){
24109 this.editor.onFirstFocus(); // is this neeed?
24113 var btns = this.buttons;
24114 var doc = this.editorcore.doc;
24115 btns.get('bold').setActive(doc.queryCommandState('bold'));
24116 btns.get('italic').setActive(doc.queryCommandState('italic'));
24117 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24119 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24120 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24121 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24123 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24124 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24127 var ans = this.editorcore.getAllAncestors();
24128 if (this.formatCombo) {
24131 var store = this.formatCombo.store;
24132 this.formatCombo.setValue("");
24133 for (var i =0; i < ans.length;i++) {
24134 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24136 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24144 // hides menus... - so this cant be on a menu...
24145 Roo.bootstrap.MenuMgr.hideAll();
24147 Roo.bootstrap.MenuMgr.hideAll();
24148 //this.editorsyncValue();
24150 onFirstFocus: function() {
24151 this.buttons.each(function(item){
24155 toggleSourceEdit : function(sourceEditMode){
24158 if(sourceEditMode){
24159 Roo.log("disabling buttons");
24160 this.buttons.each( function(item){
24161 if(item.cmd != 'pencil'){
24167 Roo.log("enabling buttons");
24168 if(this.editorcore.initialized){
24169 this.buttons.each( function(item){
24175 Roo.log("calling toggole on editor");
24176 // tell the editor that it's been pressed..
24177 this.editor.toggleSourceEdit(sourceEditMode);
24187 * @class Roo.bootstrap.Table.AbstractSelectionModel
24188 * @extends Roo.util.Observable
24189 * Abstract base class for grid SelectionModels. It provides the interface that should be
24190 * implemented by descendant classes. This class should not be directly instantiated.
24193 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24194 this.locked = false;
24195 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24199 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24200 /** @ignore Called by the grid automatically. Do not call directly. */
24201 init : function(grid){
24207 * Locks the selections.
24210 this.locked = true;
24214 * Unlocks the selections.
24216 unlock : function(){
24217 this.locked = false;
24221 * Returns true if the selections are locked.
24222 * @return {Boolean}
24224 isLocked : function(){
24225 return this.locked;
24229 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24230 * @class Roo.bootstrap.Table.RowSelectionModel
24231 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24232 * It supports multiple selections and keyboard selection/navigation.
24234 * @param {Object} config
24237 Roo.bootstrap.Table.RowSelectionModel = function(config){
24238 Roo.apply(this, config);
24239 this.selections = new Roo.util.MixedCollection(false, function(o){
24244 this.lastActive = false;
24248 * @event selectionchange
24249 * Fires when the selection changes
24250 * @param {SelectionModel} this
24252 "selectionchange" : true,
24254 * @event afterselectionchange
24255 * Fires after the selection changes (eg. by key press or clicking)
24256 * @param {SelectionModel} this
24258 "afterselectionchange" : true,
24260 * @event beforerowselect
24261 * Fires when a row is selected being selected, return false to cancel.
24262 * @param {SelectionModel} this
24263 * @param {Number} rowIndex The selected index
24264 * @param {Boolean} keepExisting False if other selections will be cleared
24266 "beforerowselect" : true,
24269 * Fires when a row is selected.
24270 * @param {SelectionModel} this
24271 * @param {Number} rowIndex The selected index
24272 * @param {Roo.data.Record} r The record
24274 "rowselect" : true,
24276 * @event rowdeselect
24277 * Fires when a row is deselected.
24278 * @param {SelectionModel} this
24279 * @param {Number} rowIndex The selected index
24281 "rowdeselect" : true
24283 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24284 this.locked = false;
24287 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24289 * @cfg {Boolean} singleSelect
24290 * True to allow selection of only one row at a time (defaults to false)
24292 singleSelect : false,
24295 initEvents : function()
24298 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24299 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24300 //}else{ // allow click to work like normal
24301 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24303 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24304 this.grid.on("rowclick", this.handleMouseDown, this);
24306 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24307 "up" : function(e){
24309 this.selectPrevious(e.shiftKey);
24310 }else if(this.last !== false && this.lastActive !== false){
24311 var last = this.last;
24312 this.selectRange(this.last, this.lastActive-1);
24313 this.grid.getView().focusRow(this.lastActive);
24314 if(last !== false){
24318 this.selectFirstRow();
24320 this.fireEvent("afterselectionchange", this);
24322 "down" : function(e){
24324 this.selectNext(e.shiftKey);
24325 }else if(this.last !== false && this.lastActive !== false){
24326 var last = this.last;
24327 this.selectRange(this.last, this.lastActive+1);
24328 this.grid.getView().focusRow(this.lastActive);
24329 if(last !== false){
24333 this.selectFirstRow();
24335 this.fireEvent("afterselectionchange", this);
24339 this.grid.store.on('load', function(){
24340 this.selections.clear();
24343 var view = this.grid.view;
24344 view.on("refresh", this.onRefresh, this);
24345 view.on("rowupdated", this.onRowUpdated, this);
24346 view.on("rowremoved", this.onRemove, this);
24351 onRefresh : function()
24353 var ds = this.grid.store, i, v = this.grid.view;
24354 var s = this.selections;
24355 s.each(function(r){
24356 if((i = ds.indexOfId(r.id)) != -1){
24365 onRemove : function(v, index, r){
24366 this.selections.remove(r);
24370 onRowUpdated : function(v, index, r){
24371 if(this.isSelected(r)){
24372 v.onRowSelect(index);
24378 * @param {Array} records The records to select
24379 * @param {Boolean} keepExisting (optional) True to keep existing selections
24381 selectRecords : function(records, keepExisting)
24384 this.clearSelections();
24386 var ds = this.grid.store;
24387 for(var i = 0, len = records.length; i < len; i++){
24388 this.selectRow(ds.indexOf(records[i]), true);
24393 * Gets the number of selected rows.
24396 getCount : function(){
24397 return this.selections.length;
24401 * Selects the first row in the grid.
24403 selectFirstRow : function(){
24408 * Select the last row.
24409 * @param {Boolean} keepExisting (optional) True to keep existing selections
24411 selectLastRow : function(keepExisting){
24412 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24413 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24417 * Selects the row immediately following the last selected row.
24418 * @param {Boolean} keepExisting (optional) True to keep existing selections
24420 selectNext : function(keepExisting)
24422 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24423 this.selectRow(this.last+1, keepExisting);
24424 this.grid.getView().focusRow(this.last);
24429 * Selects the row that precedes the last selected row.
24430 * @param {Boolean} keepExisting (optional) True to keep existing selections
24432 selectPrevious : function(keepExisting){
24434 this.selectRow(this.last-1, keepExisting);
24435 this.grid.getView().focusRow(this.last);
24440 * Returns the selected records
24441 * @return {Array} Array of selected records
24443 getSelections : function(){
24444 return [].concat(this.selections.items);
24448 * Returns the first selected record.
24451 getSelected : function(){
24452 return this.selections.itemAt(0);
24457 * Clears all selections.
24459 clearSelections : function(fast)
24465 var ds = this.grid.store;
24466 var s = this.selections;
24467 s.each(function(r){
24468 this.deselectRow(ds.indexOfId(r.id));
24472 this.selections.clear();
24479 * Selects all rows.
24481 selectAll : function(){
24485 this.selections.clear();
24486 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24487 this.selectRow(i, true);
24492 * Returns True if there is a selection.
24493 * @return {Boolean}
24495 hasSelection : function(){
24496 return this.selections.length > 0;
24500 * Returns True if the specified row is selected.
24501 * @param {Number/Record} record The record or index of the record to check
24502 * @return {Boolean}
24504 isSelected : function(index){
24505 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24506 return (r && this.selections.key(r.id) ? true : false);
24510 * Returns True if the specified record id is selected.
24511 * @param {String} id The id of record to check
24512 * @return {Boolean}
24514 isIdSelected : function(id){
24515 return (this.selections.key(id) ? true : false);
24520 handleMouseDBClick : function(e, t){
24524 handleMouseDown : function(e, t)
24526 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24527 if(this.isLocked() || rowIndex < 0 ){
24530 if(e.shiftKey && this.last !== false){
24531 var last = this.last;
24532 this.selectRange(last, rowIndex, e.ctrlKey);
24533 this.last = last; // reset the last
24537 var isSelected = this.isSelected(rowIndex);
24538 //Roo.log("select row:" + rowIndex);
24540 this.deselectRow(rowIndex);
24542 this.selectRow(rowIndex, true);
24546 if(e.button !== 0 && isSelected){
24547 alert('rowIndex 2: ' + rowIndex);
24548 view.focusRow(rowIndex);
24549 }else if(e.ctrlKey && isSelected){
24550 this.deselectRow(rowIndex);
24551 }else if(!isSelected){
24552 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24553 view.focusRow(rowIndex);
24557 this.fireEvent("afterselectionchange", this);
24560 handleDragableRowClick : function(grid, rowIndex, e)
24562 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24563 this.selectRow(rowIndex, false);
24564 grid.view.focusRow(rowIndex);
24565 this.fireEvent("afterselectionchange", this);
24570 * Selects multiple rows.
24571 * @param {Array} rows Array of the indexes of the row to select
24572 * @param {Boolean} keepExisting (optional) True to keep existing selections
24574 selectRows : function(rows, keepExisting){
24576 this.clearSelections();
24578 for(var i = 0, len = rows.length; i < len; i++){
24579 this.selectRow(rows[i], true);
24584 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24585 * @param {Number} startRow The index of the first row in the range
24586 * @param {Number} endRow The index of the last row in the range
24587 * @param {Boolean} keepExisting (optional) True to retain existing selections
24589 selectRange : function(startRow, endRow, keepExisting){
24594 this.clearSelections();
24596 if(startRow <= endRow){
24597 for(var i = startRow; i <= endRow; i++){
24598 this.selectRow(i, true);
24601 for(var i = startRow; i >= endRow; i--){
24602 this.selectRow(i, true);
24608 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24609 * @param {Number} startRow The index of the first row in the range
24610 * @param {Number} endRow The index of the last row in the range
24612 deselectRange : function(startRow, endRow, preventViewNotify){
24616 for(var i = startRow; i <= endRow; i++){
24617 this.deselectRow(i, preventViewNotify);
24623 * @param {Number} row The index of the row to select
24624 * @param {Boolean} keepExisting (optional) True to keep existing selections
24626 selectRow : function(index, keepExisting, preventViewNotify)
24628 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24631 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24632 if(!keepExisting || this.singleSelect){
24633 this.clearSelections();
24636 var r = this.grid.store.getAt(index);
24637 //console.log('selectRow - record id :' + r.id);
24639 this.selections.add(r);
24640 this.last = this.lastActive = index;
24641 if(!preventViewNotify){
24642 var proxy = new Roo.Element(
24643 this.grid.getRowDom(index)
24645 proxy.addClass('bg-info info');
24647 this.fireEvent("rowselect", this, index, r);
24648 this.fireEvent("selectionchange", this);
24654 * @param {Number} row The index of the row to deselect
24656 deselectRow : function(index, preventViewNotify)
24661 if(this.last == index){
24664 if(this.lastActive == index){
24665 this.lastActive = false;
24668 var r = this.grid.store.getAt(index);
24673 this.selections.remove(r);
24674 //.console.log('deselectRow - record id :' + r.id);
24675 if(!preventViewNotify){
24677 var proxy = new Roo.Element(
24678 this.grid.getRowDom(index)
24680 proxy.removeClass('bg-info info');
24682 this.fireEvent("rowdeselect", this, index);
24683 this.fireEvent("selectionchange", this);
24687 restoreLast : function(){
24689 this.last = this._last;
24694 acceptsNav : function(row, col, cm){
24695 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24699 onEditorKey : function(field, e){
24700 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24705 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24707 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24709 }else if(k == e.ENTER && !e.ctrlKey){
24713 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24715 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24717 }else if(k == e.ESC){
24721 g.startEditing(newCell[0], newCell[1]);
24727 * Ext JS Library 1.1.1
24728 * Copyright(c) 2006-2007, Ext JS, LLC.
24730 * Originally Released Under LGPL - original licence link has changed is not relivant.
24733 * <script type="text/javascript">
24737 * @class Roo.bootstrap.PagingToolbar
24738 * @extends Roo.bootstrap.NavSimplebar
24739 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24741 * Create a new PagingToolbar
24742 * @param {Object} config The config object
24743 * @param {Roo.data.Store} store
24745 Roo.bootstrap.PagingToolbar = function(config)
24747 // old args format still supported... - xtype is prefered..
24748 // created from xtype...
24750 this.ds = config.dataSource;
24752 if (config.store && !this.ds) {
24753 this.store= Roo.factory(config.store, Roo.data);
24754 this.ds = this.store;
24755 this.ds.xmodule = this.xmodule || false;
24758 this.toolbarItems = [];
24759 if (config.items) {
24760 this.toolbarItems = config.items;
24763 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24768 this.bind(this.ds);
24771 if (Roo.bootstrap.version == 4) {
24772 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24774 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24779 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24781 * @cfg {Roo.data.Store} dataSource
24782 * The underlying data store providing the paged data
24785 * @cfg {String/HTMLElement/Element} container
24786 * container The id or element that will contain the toolbar
24789 * @cfg {Boolean} displayInfo
24790 * True to display the displayMsg (defaults to false)
24793 * @cfg {Number} pageSize
24794 * The number of records to display per page (defaults to 20)
24798 * @cfg {String} displayMsg
24799 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24801 displayMsg : 'Displaying {0} - {1} of {2}',
24803 * @cfg {String} emptyMsg
24804 * The message to display when no records are found (defaults to "No data to display")
24806 emptyMsg : 'No data to display',
24808 * Customizable piece of the default paging text (defaults to "Page")
24811 beforePageText : "Page",
24813 * Customizable piece of the default paging text (defaults to "of %0")
24816 afterPageText : "of {0}",
24818 * Customizable piece of the default paging text (defaults to "First Page")
24821 firstText : "First Page",
24823 * Customizable piece of the default paging text (defaults to "Previous Page")
24826 prevText : "Previous Page",
24828 * Customizable piece of the default paging text (defaults to "Next Page")
24831 nextText : "Next Page",
24833 * Customizable piece of the default paging text (defaults to "Last Page")
24836 lastText : "Last Page",
24838 * Customizable piece of the default paging text (defaults to "Refresh")
24841 refreshText : "Refresh",
24845 onRender : function(ct, position)
24847 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24848 this.navgroup.parentId = this.id;
24849 this.navgroup.onRender(this.el, null);
24850 // add the buttons to the navgroup
24852 if(this.displayInfo){
24853 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24854 this.displayEl = this.el.select('.x-paging-info', true).first();
24855 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24856 // this.displayEl = navel.el.select('span',true).first();
24862 Roo.each(_this.buttons, function(e){ // this might need to use render????
24863 Roo.factory(e).render(_this.el);
24867 Roo.each(_this.toolbarItems, function(e) {
24868 _this.navgroup.addItem(e);
24872 this.first = this.navgroup.addItem({
24873 tooltip: this.firstText,
24874 cls: "prev btn-outline-secondary",
24875 html : ' <i class="fa fa-step-backward"></i>',
24877 preventDefault: true,
24878 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24881 this.prev = this.navgroup.addItem({
24882 tooltip: this.prevText,
24883 cls: "prev btn-outline-secondary",
24884 html : ' <i class="fa fa-backward"></i>',
24886 preventDefault: true,
24887 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24889 //this.addSeparator();
24892 var field = this.navgroup.addItem( {
24894 cls : 'x-paging-position btn-outline-secondary',
24896 html : this.beforePageText +
24897 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24898 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24901 this.field = field.el.select('input', true).first();
24902 this.field.on("keydown", this.onPagingKeydown, this);
24903 this.field.on("focus", function(){this.dom.select();});
24906 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24907 //this.field.setHeight(18);
24908 //this.addSeparator();
24909 this.next = this.navgroup.addItem({
24910 tooltip: this.nextText,
24911 cls: "next btn-outline-secondary",
24912 html : ' <i class="fa fa-forward"></i>',
24914 preventDefault: true,
24915 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24917 this.last = this.navgroup.addItem({
24918 tooltip: this.lastText,
24919 html : ' <i class="fa fa-step-forward"></i>',
24920 cls: "next btn-outline-secondary",
24922 preventDefault: true,
24923 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24925 //this.addSeparator();
24926 this.loading = this.navgroup.addItem({
24927 tooltip: this.refreshText,
24928 cls: "btn-outline-secondary",
24929 html : ' <i class="fa fa-refresh"></i>',
24930 preventDefault: true,
24931 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24937 updateInfo : function(){
24938 if(this.displayEl){
24939 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24940 var msg = count == 0 ?
24944 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24946 this.displayEl.update(msg);
24951 onLoad : function(ds, r, o)
24953 this.cursor = o.params.start ? o.params.start : 0;
24955 var d = this.getPageData(),
24960 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24961 this.field.dom.value = ap;
24962 this.first.setDisabled(ap == 1);
24963 this.prev.setDisabled(ap == 1);
24964 this.next.setDisabled(ap == ps);
24965 this.last.setDisabled(ap == ps);
24966 this.loading.enable();
24971 getPageData : function(){
24972 var total = this.ds.getTotalCount();
24975 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24976 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24981 onLoadError : function(){
24982 this.loading.enable();
24986 onPagingKeydown : function(e){
24987 var k = e.getKey();
24988 var d = this.getPageData();
24990 var v = this.field.dom.value, pageNum;
24991 if(!v || isNaN(pageNum = parseInt(v, 10))){
24992 this.field.dom.value = d.activePage;
24995 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24996 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24999 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))
25001 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25002 this.field.dom.value = pageNum;
25003 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25006 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25008 var v = this.field.dom.value, pageNum;
25009 var increment = (e.shiftKey) ? 10 : 1;
25010 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25013 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25014 this.field.dom.value = d.activePage;
25017 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25019 this.field.dom.value = parseInt(v, 10) + increment;
25020 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25021 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25028 beforeLoad : function(){
25030 this.loading.disable();
25035 onClick : function(which){
25044 ds.load({params:{start: 0, limit: this.pageSize}});
25047 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25050 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25053 var total = ds.getTotalCount();
25054 var extra = total % this.pageSize;
25055 var lastStart = extra ? (total - extra) : total-this.pageSize;
25056 ds.load({params:{start: lastStart, limit: this.pageSize}});
25059 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25065 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25066 * @param {Roo.data.Store} store The data store to unbind
25068 unbind : function(ds){
25069 ds.un("beforeload", this.beforeLoad, this);
25070 ds.un("load", this.onLoad, this);
25071 ds.un("loadexception", this.onLoadError, this);
25072 ds.un("remove", this.updateInfo, this);
25073 ds.un("add", this.updateInfo, this);
25074 this.ds = undefined;
25078 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25079 * @param {Roo.data.Store} store The data store to bind
25081 bind : function(ds){
25082 ds.on("beforeload", this.beforeLoad, this);
25083 ds.on("load", this.onLoad, this);
25084 ds.on("loadexception", this.onLoadError, this);
25085 ds.on("remove", this.updateInfo, this);
25086 ds.on("add", this.updateInfo, this);
25097 * @class Roo.bootstrap.MessageBar
25098 * @extends Roo.bootstrap.Component
25099 * Bootstrap MessageBar class
25100 * @cfg {String} html contents of the MessageBar
25101 * @cfg {String} weight (info | success | warning | danger) default info
25102 * @cfg {String} beforeClass insert the bar before the given class
25103 * @cfg {Boolean} closable (true | false) default false
25104 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25107 * Create a new Element
25108 * @param {Object} config The config object
25111 Roo.bootstrap.MessageBar = function(config){
25112 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25115 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25121 beforeClass: 'bootstrap-sticky-wrap',
25123 getAutoCreate : function(){
25127 cls: 'alert alert-dismissable alert-' + this.weight,
25132 html: this.html || ''
25138 cfg.cls += ' alert-messages-fixed';
25152 onRender : function(ct, position)
25154 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25157 var cfg = Roo.apply({}, this.getAutoCreate());
25161 cfg.cls += ' ' + this.cls;
25164 cfg.style = this.style;
25166 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25168 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25171 this.el.select('>button.close').on('click', this.hide, this);
25177 if (!this.rendered) {
25183 this.fireEvent('show', this);
25189 if (!this.rendered) {
25195 this.fireEvent('hide', this);
25198 update : function()
25200 // var e = this.el.dom.firstChild;
25202 // if(this.closable){
25203 // e = e.nextSibling;
25206 // e.data = this.html || '';
25208 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25224 * @class Roo.bootstrap.Graph
25225 * @extends Roo.bootstrap.Component
25226 * Bootstrap Graph class
25230 @cfg {String} graphtype bar | vbar | pie
25231 @cfg {number} g_x coodinator | centre x (pie)
25232 @cfg {number} g_y coodinator | centre y (pie)
25233 @cfg {number} g_r radius (pie)
25234 @cfg {number} g_height height of the chart (respected by all elements in the set)
25235 @cfg {number} g_width width of the chart (respected by all elements in the set)
25236 @cfg {Object} title The title of the chart
25239 -opts (object) options for the chart
25241 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25242 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25244 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.
25245 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25247 o stretch (boolean)
25249 -opts (object) options for the pie
25252 o startAngle (number)
25253 o endAngle (number)
25257 * Create a new Input
25258 * @param {Object} config The config object
25261 Roo.bootstrap.Graph = function(config){
25262 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25268 * The img click event for the img.
25269 * @param {Roo.EventObject} e
25275 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25286 //g_colors: this.colors,
25293 getAutoCreate : function(){
25304 onRender : function(ct,position){
25307 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25309 if (typeof(Raphael) == 'undefined') {
25310 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25314 this.raphael = Raphael(this.el.dom);
25316 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25317 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25318 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25319 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25321 r.text(160, 10, "Single Series Chart").attr(txtattr);
25322 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25323 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25324 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25326 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25327 r.barchart(330, 10, 300, 220, data1);
25328 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25329 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25332 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25333 // r.barchart(30, 30, 560, 250, xdata, {
25334 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25335 // axis : "0 0 1 1",
25336 // axisxlabels : xdata
25337 // //yvalues : cols,
25340 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25342 // this.load(null,xdata,{
25343 // axis : "0 0 1 1",
25344 // axisxlabels : xdata
25349 load : function(graphtype,xdata,opts)
25351 this.raphael.clear();
25353 graphtype = this.graphtype;
25358 var r = this.raphael,
25359 fin = function () {
25360 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25362 fout = function () {
25363 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25365 pfin = function() {
25366 this.sector.stop();
25367 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25370 this.label[0].stop();
25371 this.label[0].attr({ r: 7.5 });
25372 this.label[1].attr({ "font-weight": 800 });
25375 pfout = function() {
25376 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25379 this.label[0].animate({ r: 5 }, 500, "bounce");
25380 this.label[1].attr({ "font-weight": 400 });
25386 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25389 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25392 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25393 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25395 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25402 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25407 setTitle: function(o)
25412 initEvents: function() {
25415 this.el.on('click', this.onClick, this);
25419 onClick : function(e)
25421 Roo.log('img onclick');
25422 this.fireEvent('click', this, e);
25434 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25437 * @class Roo.bootstrap.dash.NumberBox
25438 * @extends Roo.bootstrap.Component
25439 * Bootstrap NumberBox class
25440 * @cfg {String} headline Box headline
25441 * @cfg {String} content Box content
25442 * @cfg {String} icon Box icon
25443 * @cfg {String} footer Footer text
25444 * @cfg {String} fhref Footer href
25447 * Create a new NumberBox
25448 * @param {Object} config The config object
25452 Roo.bootstrap.dash.NumberBox = function(config){
25453 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25457 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25466 getAutoCreate : function(){
25470 cls : 'small-box ',
25478 cls : 'roo-headline',
25479 html : this.headline
25483 cls : 'roo-content',
25484 html : this.content
25498 cls : 'ion ' + this.icon
25507 cls : 'small-box-footer',
25508 href : this.fhref || '#',
25512 cfg.cn.push(footer);
25519 onRender : function(ct,position){
25520 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25527 setHeadline: function (value)
25529 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25532 setFooter: function (value, href)
25534 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25537 this.el.select('a.small-box-footer',true).first().attr('href', href);
25542 setContent: function (value)
25544 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25547 initEvents: function()
25561 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25564 * @class Roo.bootstrap.dash.TabBox
25565 * @extends Roo.bootstrap.Component
25566 * Bootstrap TabBox class
25567 * @cfg {String} title Title of the TabBox
25568 * @cfg {String} icon Icon of the TabBox
25569 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25570 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25573 * Create a new TabBox
25574 * @param {Object} config The config object
25578 Roo.bootstrap.dash.TabBox = function(config){
25579 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25584 * When a pane is added
25585 * @param {Roo.bootstrap.dash.TabPane} pane
25589 * @event activatepane
25590 * When a pane is activated
25591 * @param {Roo.bootstrap.dash.TabPane} pane
25593 "activatepane" : true
25601 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25606 tabScrollable : false,
25608 getChildContainer : function()
25610 return this.el.select('.tab-content', true).first();
25613 getAutoCreate : function(){
25617 cls: 'pull-left header',
25625 cls: 'fa ' + this.icon
25631 cls: 'nav nav-tabs pull-right',
25637 if(this.tabScrollable){
25644 cls: 'nav nav-tabs pull-right',
25655 cls: 'nav-tabs-custom',
25660 cls: 'tab-content no-padding',
25668 initEvents : function()
25670 //Roo.log('add add pane handler');
25671 this.on('addpane', this.onAddPane, this);
25674 * Updates the box title
25675 * @param {String} html to set the title to.
25677 setTitle : function(value)
25679 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25681 onAddPane : function(pane)
25683 this.panes.push(pane);
25684 //Roo.log('addpane');
25686 // tabs are rendere left to right..
25687 if(!this.showtabs){
25691 var ctr = this.el.select('.nav-tabs', true).first();
25694 var existing = ctr.select('.nav-tab',true);
25695 var qty = existing.getCount();;
25698 var tab = ctr.createChild({
25700 cls : 'nav-tab' + (qty ? '' : ' active'),
25708 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25711 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25713 pane.el.addClass('active');
25718 onTabClick : function(ev,un,ob,pane)
25720 //Roo.log('tab - prev default');
25721 ev.preventDefault();
25724 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25725 pane.tab.addClass('active');
25726 //Roo.log(pane.title);
25727 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25728 // technically we should have a deactivate event.. but maybe add later.
25729 // and it should not de-activate the selected tab...
25730 this.fireEvent('activatepane', pane);
25731 pane.el.addClass('active');
25732 pane.fireEvent('activate');
25737 getActivePane : function()
25740 Roo.each(this.panes, function(p) {
25741 if(p.el.hasClass('active')){
25762 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25764 * @class Roo.bootstrap.TabPane
25765 * @extends Roo.bootstrap.Component
25766 * Bootstrap TabPane class
25767 * @cfg {Boolean} active (false | true) Default false
25768 * @cfg {String} title title of panel
25772 * Create a new TabPane
25773 * @param {Object} config The config object
25776 Roo.bootstrap.dash.TabPane = function(config){
25777 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25783 * When a pane is activated
25784 * @param {Roo.bootstrap.dash.TabPane} pane
25791 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25796 // the tabBox that this is attached to.
25799 getAutoCreate : function()
25807 cfg.cls += ' active';
25812 initEvents : function()
25814 //Roo.log('trigger add pane handler');
25815 this.parent().fireEvent('addpane', this)
25819 * Updates the tab title
25820 * @param {String} html to set the title to.
25822 setTitle: function(str)
25828 this.tab.select('a', true).first().dom.innerHTML = str;
25845 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25848 * @class Roo.bootstrap.menu.Menu
25849 * @extends Roo.bootstrap.Component
25850 * Bootstrap Menu class - container for Menu
25851 * @cfg {String} html Text of the menu
25852 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25853 * @cfg {String} icon Font awesome icon
25854 * @cfg {String} pos Menu align to (top | bottom) default bottom
25858 * Create a new Menu
25859 * @param {Object} config The config object
25863 Roo.bootstrap.menu.Menu = function(config){
25864 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25868 * @event beforeshow
25869 * Fires before this menu is displayed
25870 * @param {Roo.bootstrap.menu.Menu} this
25874 * @event beforehide
25875 * Fires before this menu is hidden
25876 * @param {Roo.bootstrap.menu.Menu} this
25881 * Fires after this menu is displayed
25882 * @param {Roo.bootstrap.menu.Menu} this
25887 * Fires after this menu is hidden
25888 * @param {Roo.bootstrap.menu.Menu} this
25893 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25894 * @param {Roo.bootstrap.menu.Menu} this
25895 * @param {Roo.EventObject} e
25902 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25906 weight : 'default',
25911 getChildContainer : function() {
25912 if(this.isSubMenu){
25916 return this.el.select('ul.dropdown-menu', true).first();
25919 getAutoCreate : function()
25924 cls : 'roo-menu-text',
25932 cls : 'fa ' + this.icon
25943 cls : 'dropdown-button btn btn-' + this.weight,
25948 cls : 'dropdown-toggle btn btn-' + this.weight,
25958 cls : 'dropdown-menu'
25964 if(this.pos == 'top'){
25965 cfg.cls += ' dropup';
25968 if(this.isSubMenu){
25971 cls : 'dropdown-menu'
25978 onRender : function(ct, position)
25980 this.isSubMenu = ct.hasClass('dropdown-submenu');
25982 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25985 initEvents : function()
25987 if(this.isSubMenu){
25991 this.hidden = true;
25993 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25994 this.triggerEl.on('click', this.onTriggerPress, this);
25996 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25997 this.buttonEl.on('click', this.onClick, this);
26003 if(this.isSubMenu){
26007 return this.el.select('ul.dropdown-menu', true).first();
26010 onClick : function(e)
26012 this.fireEvent("click", this, e);
26015 onTriggerPress : function(e)
26017 if (this.isVisible()) {
26024 isVisible : function(){
26025 return !this.hidden;
26030 this.fireEvent("beforeshow", this);
26032 this.hidden = false;
26033 this.el.addClass('open');
26035 Roo.get(document).on("mouseup", this.onMouseUp, this);
26037 this.fireEvent("show", this);
26044 this.fireEvent("beforehide", this);
26046 this.hidden = true;
26047 this.el.removeClass('open');
26049 Roo.get(document).un("mouseup", this.onMouseUp);
26051 this.fireEvent("hide", this);
26054 onMouseUp : function()
26068 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26071 * @class Roo.bootstrap.menu.Item
26072 * @extends Roo.bootstrap.Component
26073 * Bootstrap MenuItem class
26074 * @cfg {Boolean} submenu (true | false) default false
26075 * @cfg {String} html text of the item
26076 * @cfg {String} href the link
26077 * @cfg {Boolean} disable (true | false) default false
26078 * @cfg {Boolean} preventDefault (true | false) default true
26079 * @cfg {String} icon Font awesome icon
26080 * @cfg {String} pos Submenu align to (left | right) default right
26084 * Create a new Item
26085 * @param {Object} config The config object
26089 Roo.bootstrap.menu.Item = function(config){
26090 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26094 * Fires when the mouse is hovering over this menu
26095 * @param {Roo.bootstrap.menu.Item} this
26096 * @param {Roo.EventObject} e
26101 * Fires when the mouse exits this menu
26102 * @param {Roo.bootstrap.menu.Item} this
26103 * @param {Roo.EventObject} e
26109 * The raw click event for the entire grid.
26110 * @param {Roo.EventObject} e
26116 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26121 preventDefault: true,
26126 getAutoCreate : function()
26131 cls : 'roo-menu-item-text',
26139 cls : 'fa ' + this.icon
26148 href : this.href || '#',
26155 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26159 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26161 if(this.pos == 'left'){
26162 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26169 initEvents : function()
26171 this.el.on('mouseover', this.onMouseOver, this);
26172 this.el.on('mouseout', this.onMouseOut, this);
26174 this.el.select('a', true).first().on('click', this.onClick, this);
26178 onClick : function(e)
26180 if(this.preventDefault){
26181 e.preventDefault();
26184 this.fireEvent("click", this, e);
26187 onMouseOver : function(e)
26189 if(this.submenu && this.pos == 'left'){
26190 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26193 this.fireEvent("mouseover", this, e);
26196 onMouseOut : function(e)
26198 this.fireEvent("mouseout", this, e);
26210 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26213 * @class Roo.bootstrap.menu.Separator
26214 * @extends Roo.bootstrap.Component
26215 * Bootstrap Separator class
26218 * Create a new Separator
26219 * @param {Object} config The config object
26223 Roo.bootstrap.menu.Separator = function(config){
26224 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26227 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26229 getAutoCreate : function(){
26250 * @class Roo.bootstrap.Tooltip
26251 * Bootstrap Tooltip class
26252 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26253 * to determine which dom element triggers the tooltip.
26255 * It needs to add support for additional attributes like tooltip-position
26258 * Create a new Toolti
26259 * @param {Object} config The config object
26262 Roo.bootstrap.Tooltip = function(config){
26263 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26265 this.alignment = Roo.bootstrap.Tooltip.alignment;
26267 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26268 this.alignment = config.alignment;
26273 Roo.apply(Roo.bootstrap.Tooltip, {
26275 * @function init initialize tooltip monitoring.
26279 currentTip : false,
26280 currentRegion : false,
26286 Roo.get(document).on('mouseover', this.enter ,this);
26287 Roo.get(document).on('mouseout', this.leave, this);
26290 this.currentTip = new Roo.bootstrap.Tooltip();
26293 enter : function(ev)
26295 var dom = ev.getTarget();
26297 //Roo.log(['enter',dom]);
26298 var el = Roo.fly(dom);
26299 if (this.currentEl) {
26301 //Roo.log(this.currentEl);
26302 //Roo.log(this.currentEl.contains(dom));
26303 if (this.currentEl == el) {
26306 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26312 if (this.currentTip.el) {
26313 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26317 if(!el || el.dom == document){
26323 // you can not look for children, as if el is the body.. then everythign is the child..
26324 if (!el.attr('tooltip')) { //
26325 if (!el.select("[tooltip]").elements.length) {
26328 // is the mouse over this child...?
26329 bindEl = el.select("[tooltip]").first();
26330 var xy = ev.getXY();
26331 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26332 //Roo.log("not in region.");
26335 //Roo.log("child element over..");
26338 this.currentEl = bindEl;
26339 this.currentTip.bind(bindEl);
26340 this.currentRegion = Roo.lib.Region.getRegion(dom);
26341 this.currentTip.enter();
26344 leave : function(ev)
26346 var dom = ev.getTarget();
26347 //Roo.log(['leave',dom]);
26348 if (!this.currentEl) {
26353 if (dom != this.currentEl.dom) {
26356 var xy = ev.getXY();
26357 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26360 // only activate leave if mouse cursor is outside... bounding box..
26365 if (this.currentTip) {
26366 this.currentTip.leave();
26368 //Roo.log('clear currentEl');
26369 this.currentEl = false;
26374 'left' : ['r-l', [-2,0], 'right'],
26375 'right' : ['l-r', [2,0], 'left'],
26376 'bottom' : ['t-b', [0,2], 'top'],
26377 'top' : [ 'b-t', [0,-2], 'bottom']
26383 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26388 delay : null, // can be { show : 300 , hide: 500}
26392 hoverState : null, //???
26394 placement : 'bottom',
26398 getAutoCreate : function(){
26405 cls : 'tooltip-arrow'
26408 cls : 'tooltip-inner'
26415 bind : function(el)
26421 enter : function () {
26423 if (this.timeout != null) {
26424 clearTimeout(this.timeout);
26427 this.hoverState = 'in';
26428 //Roo.log("enter - show");
26429 if (!this.delay || !this.delay.show) {
26434 this.timeout = setTimeout(function () {
26435 if (_t.hoverState == 'in') {
26438 }, this.delay.show);
26442 clearTimeout(this.timeout);
26444 this.hoverState = 'out';
26445 if (!this.delay || !this.delay.hide) {
26451 this.timeout = setTimeout(function () {
26452 //Roo.log("leave - timeout");
26454 if (_t.hoverState == 'out') {
26456 Roo.bootstrap.Tooltip.currentEl = false;
26461 show : function (msg)
26464 this.render(document.body);
26467 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26469 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26471 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26473 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26475 var placement = typeof this.placement == 'function' ?
26476 this.placement.call(this, this.el, on_el) :
26479 var autoToken = /\s?auto?\s?/i;
26480 var autoPlace = autoToken.test(placement);
26482 placement = placement.replace(autoToken, '') || 'top';
26486 //this.el.setXY([0,0]);
26488 //this.el.dom.style.display='block';
26490 //this.el.appendTo(on_el);
26492 var p = this.getPosition();
26493 var box = this.el.getBox();
26499 var align = this.alignment[placement];
26501 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26503 if(placement == 'top' || placement == 'bottom'){
26505 placement = 'right';
26508 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26509 placement = 'left';
26512 var scroll = Roo.select('body', true).first().getScroll();
26514 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26518 align = this.alignment[placement];
26521 this.el.alignTo(this.bindEl, align[0],align[1]);
26522 //var arrow = this.el.select('.arrow',true).first();
26523 //arrow.set(align[2],
26525 this.el.addClass(placement);
26527 this.el.addClass('in fade');
26529 this.hoverState = null;
26531 if (this.el.hasClass('fade')) {
26542 //this.el.setXY([0,0]);
26543 this.el.removeClass('in');
26559 * @class Roo.bootstrap.LocationPicker
26560 * @extends Roo.bootstrap.Component
26561 * Bootstrap LocationPicker class
26562 * @cfg {Number} latitude Position when init default 0
26563 * @cfg {Number} longitude Position when init default 0
26564 * @cfg {Number} zoom default 15
26565 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26566 * @cfg {Boolean} mapTypeControl default false
26567 * @cfg {Boolean} disableDoubleClickZoom default false
26568 * @cfg {Boolean} scrollwheel default true
26569 * @cfg {Boolean} streetViewControl default false
26570 * @cfg {Number} radius default 0
26571 * @cfg {String} locationName
26572 * @cfg {Boolean} draggable default true
26573 * @cfg {Boolean} enableAutocomplete default false
26574 * @cfg {Boolean} enableReverseGeocode default true
26575 * @cfg {String} markerTitle
26578 * Create a new LocationPicker
26579 * @param {Object} config The config object
26583 Roo.bootstrap.LocationPicker = function(config){
26585 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26590 * Fires when the picker initialized.
26591 * @param {Roo.bootstrap.LocationPicker} this
26592 * @param {Google Location} location
26596 * @event positionchanged
26597 * Fires when the picker position changed.
26598 * @param {Roo.bootstrap.LocationPicker} this
26599 * @param {Google Location} location
26601 positionchanged : true,
26604 * Fires when the map resize.
26605 * @param {Roo.bootstrap.LocationPicker} this
26610 * Fires when the map show.
26611 * @param {Roo.bootstrap.LocationPicker} this
26616 * Fires when the map hide.
26617 * @param {Roo.bootstrap.LocationPicker} this
26622 * Fires when click the map.
26623 * @param {Roo.bootstrap.LocationPicker} this
26624 * @param {Map event} e
26628 * @event mapRightClick
26629 * Fires when right click the map.
26630 * @param {Roo.bootstrap.LocationPicker} this
26631 * @param {Map event} e
26633 mapRightClick : true,
26635 * @event markerClick
26636 * Fires when click the marker.
26637 * @param {Roo.bootstrap.LocationPicker} this
26638 * @param {Map event} e
26640 markerClick : true,
26642 * @event markerRightClick
26643 * Fires when right click the marker.
26644 * @param {Roo.bootstrap.LocationPicker} this
26645 * @param {Map event} e
26647 markerRightClick : true,
26649 * @event OverlayViewDraw
26650 * Fires when OverlayView Draw
26651 * @param {Roo.bootstrap.LocationPicker} this
26653 OverlayViewDraw : true,
26655 * @event OverlayViewOnAdd
26656 * Fires when OverlayView Draw
26657 * @param {Roo.bootstrap.LocationPicker} this
26659 OverlayViewOnAdd : true,
26661 * @event OverlayViewOnRemove
26662 * Fires when OverlayView Draw
26663 * @param {Roo.bootstrap.LocationPicker} this
26665 OverlayViewOnRemove : true,
26667 * @event OverlayViewShow
26668 * Fires when OverlayView Draw
26669 * @param {Roo.bootstrap.LocationPicker} this
26670 * @param {Pixel} cpx
26672 OverlayViewShow : true,
26674 * @event OverlayViewHide
26675 * Fires when OverlayView Draw
26676 * @param {Roo.bootstrap.LocationPicker} this
26678 OverlayViewHide : true,
26680 * @event loadexception
26681 * Fires when load google lib failed.
26682 * @param {Roo.bootstrap.LocationPicker} this
26684 loadexception : true
26689 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26691 gMapContext: false,
26697 mapTypeControl: false,
26698 disableDoubleClickZoom: false,
26700 streetViewControl: false,
26704 enableAutocomplete: false,
26705 enableReverseGeocode: true,
26708 getAutoCreate: function()
26713 cls: 'roo-location-picker'
26719 initEvents: function(ct, position)
26721 if(!this.el.getWidth() || this.isApplied()){
26725 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26730 initial: function()
26732 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26733 this.fireEvent('loadexception', this);
26737 if(!this.mapTypeId){
26738 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26741 this.gMapContext = this.GMapContext();
26743 this.initOverlayView();
26745 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26749 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26750 _this.setPosition(_this.gMapContext.marker.position);
26753 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26754 _this.fireEvent('mapClick', this, event);
26758 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26759 _this.fireEvent('mapRightClick', this, event);
26763 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26764 _this.fireEvent('markerClick', this, event);
26768 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26769 _this.fireEvent('markerRightClick', this, event);
26773 this.setPosition(this.gMapContext.location);
26775 this.fireEvent('initial', this, this.gMapContext.location);
26778 initOverlayView: function()
26782 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26786 _this.fireEvent('OverlayViewDraw', _this);
26791 _this.fireEvent('OverlayViewOnAdd', _this);
26794 onRemove: function()
26796 _this.fireEvent('OverlayViewOnRemove', _this);
26799 show: function(cpx)
26801 _this.fireEvent('OverlayViewShow', _this, cpx);
26806 _this.fireEvent('OverlayViewHide', _this);
26812 fromLatLngToContainerPixel: function(event)
26814 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26817 isApplied: function()
26819 return this.getGmapContext() == false ? false : true;
26822 getGmapContext: function()
26824 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26827 GMapContext: function()
26829 var position = new google.maps.LatLng(this.latitude, this.longitude);
26831 var _map = new google.maps.Map(this.el.dom, {
26834 mapTypeId: this.mapTypeId,
26835 mapTypeControl: this.mapTypeControl,
26836 disableDoubleClickZoom: this.disableDoubleClickZoom,
26837 scrollwheel: this.scrollwheel,
26838 streetViewControl: this.streetViewControl,
26839 locationName: this.locationName,
26840 draggable: this.draggable,
26841 enableAutocomplete: this.enableAutocomplete,
26842 enableReverseGeocode: this.enableReverseGeocode
26845 var _marker = new google.maps.Marker({
26846 position: position,
26848 title: this.markerTitle,
26849 draggable: this.draggable
26856 location: position,
26857 radius: this.radius,
26858 locationName: this.locationName,
26859 addressComponents: {
26860 formatted_address: null,
26861 addressLine1: null,
26862 addressLine2: null,
26864 streetNumber: null,
26868 stateOrProvince: null
26871 domContainer: this.el.dom,
26872 geodecoder: new google.maps.Geocoder()
26876 drawCircle: function(center, radius, options)
26878 if (this.gMapContext.circle != null) {
26879 this.gMapContext.circle.setMap(null);
26883 options = Roo.apply({}, options, {
26884 strokeColor: "#0000FF",
26885 strokeOpacity: .35,
26887 fillColor: "#0000FF",
26891 options.map = this.gMapContext.map;
26892 options.radius = radius;
26893 options.center = center;
26894 this.gMapContext.circle = new google.maps.Circle(options);
26895 return this.gMapContext.circle;
26901 setPosition: function(location)
26903 this.gMapContext.location = location;
26904 this.gMapContext.marker.setPosition(location);
26905 this.gMapContext.map.panTo(location);
26906 this.drawCircle(location, this.gMapContext.radius, {});
26910 if (this.gMapContext.settings.enableReverseGeocode) {
26911 this.gMapContext.geodecoder.geocode({
26912 latLng: this.gMapContext.location
26913 }, function(results, status) {
26915 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26916 _this.gMapContext.locationName = results[0].formatted_address;
26917 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26919 _this.fireEvent('positionchanged', this, location);
26926 this.fireEvent('positionchanged', this, location);
26931 google.maps.event.trigger(this.gMapContext.map, "resize");
26933 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26935 this.fireEvent('resize', this);
26938 setPositionByLatLng: function(latitude, longitude)
26940 this.setPosition(new google.maps.LatLng(latitude, longitude));
26943 getCurrentPosition: function()
26946 latitude: this.gMapContext.location.lat(),
26947 longitude: this.gMapContext.location.lng()
26951 getAddressName: function()
26953 return this.gMapContext.locationName;
26956 getAddressComponents: function()
26958 return this.gMapContext.addressComponents;
26961 address_component_from_google_geocode: function(address_components)
26965 for (var i = 0; i < address_components.length; i++) {
26966 var component = address_components[i];
26967 if (component.types.indexOf("postal_code") >= 0) {
26968 result.postalCode = component.short_name;
26969 } else if (component.types.indexOf("street_number") >= 0) {
26970 result.streetNumber = component.short_name;
26971 } else if (component.types.indexOf("route") >= 0) {
26972 result.streetName = component.short_name;
26973 } else if (component.types.indexOf("neighborhood") >= 0) {
26974 result.city = component.short_name;
26975 } else if (component.types.indexOf("locality") >= 0) {
26976 result.city = component.short_name;
26977 } else if (component.types.indexOf("sublocality") >= 0) {
26978 result.district = component.short_name;
26979 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26980 result.stateOrProvince = component.short_name;
26981 } else if (component.types.indexOf("country") >= 0) {
26982 result.country = component.short_name;
26986 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26987 result.addressLine2 = "";
26991 setZoomLevel: function(zoom)
26993 this.gMapContext.map.setZoom(zoom);
27006 this.fireEvent('show', this);
27017 this.fireEvent('hide', this);
27022 Roo.apply(Roo.bootstrap.LocationPicker, {
27024 OverlayView : function(map, options)
27026 options = options || {};
27040 * @class Roo.bootstrap.Alert
27041 * @extends Roo.bootstrap.Component
27042 * Bootstrap Alert class
27043 * @cfg {String} title The title of alert
27044 * @cfg {String} html The content of alert
27045 * @cfg {String} weight ( success | info | warning | danger )
27046 * @cfg {String} faicon font-awesomeicon
27049 * Create a new alert
27050 * @param {Object} config The config object
27054 Roo.bootstrap.Alert = function(config){
27055 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27059 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27066 getAutoCreate : function()
27075 cls : 'roo-alert-icon'
27080 cls : 'roo-alert-title',
27085 cls : 'roo-alert-text',
27092 cfg.cn[0].cls += ' fa ' + this.faicon;
27096 cfg.cls += ' alert-' + this.weight;
27102 initEvents: function()
27104 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27107 setTitle : function(str)
27109 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27112 setText : function(str)
27114 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27117 setWeight : function(weight)
27120 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27123 this.weight = weight;
27125 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27128 setIcon : function(icon)
27131 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27134 this.faicon = icon;
27136 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27157 * @class Roo.bootstrap.UploadCropbox
27158 * @extends Roo.bootstrap.Component
27159 * Bootstrap UploadCropbox class
27160 * @cfg {String} emptyText show when image has been loaded
27161 * @cfg {String} rotateNotify show when image too small to rotate
27162 * @cfg {Number} errorTimeout default 3000
27163 * @cfg {Number} minWidth default 300
27164 * @cfg {Number} minHeight default 300
27165 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27166 * @cfg {Boolean} isDocument (true|false) default false
27167 * @cfg {String} url action url
27168 * @cfg {String} paramName default 'imageUpload'
27169 * @cfg {String} method default POST
27170 * @cfg {Boolean} loadMask (true|false) default true
27171 * @cfg {Boolean} loadingText default 'Loading...'
27174 * Create a new UploadCropbox
27175 * @param {Object} config The config object
27178 Roo.bootstrap.UploadCropbox = function(config){
27179 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27183 * @event beforeselectfile
27184 * Fire before select file
27185 * @param {Roo.bootstrap.UploadCropbox} this
27187 "beforeselectfile" : true,
27190 * Fire after initEvent
27191 * @param {Roo.bootstrap.UploadCropbox} this
27196 * Fire after initEvent
27197 * @param {Roo.bootstrap.UploadCropbox} this
27198 * @param {String} data
27203 * Fire when preparing the file data
27204 * @param {Roo.bootstrap.UploadCropbox} this
27205 * @param {Object} file
27210 * Fire when get exception
27211 * @param {Roo.bootstrap.UploadCropbox} this
27212 * @param {XMLHttpRequest} xhr
27214 "exception" : true,
27216 * @event beforeloadcanvas
27217 * Fire before load the canvas
27218 * @param {Roo.bootstrap.UploadCropbox} this
27219 * @param {String} src
27221 "beforeloadcanvas" : true,
27224 * Fire when trash image
27225 * @param {Roo.bootstrap.UploadCropbox} this
27230 * Fire when download the image
27231 * @param {Roo.bootstrap.UploadCropbox} this
27235 * @event footerbuttonclick
27236 * Fire when footerbuttonclick
27237 * @param {Roo.bootstrap.UploadCropbox} this
27238 * @param {String} type
27240 "footerbuttonclick" : true,
27244 * @param {Roo.bootstrap.UploadCropbox} this
27249 * Fire when rotate the image
27250 * @param {Roo.bootstrap.UploadCropbox} this
27251 * @param {String} pos
27256 * Fire when inspect the file
27257 * @param {Roo.bootstrap.UploadCropbox} this
27258 * @param {Object} file
27263 * Fire when xhr upload the file
27264 * @param {Roo.bootstrap.UploadCropbox} this
27265 * @param {Object} data
27270 * Fire when arrange the file data
27271 * @param {Roo.bootstrap.UploadCropbox} this
27272 * @param {Object} formData
27277 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27280 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27282 emptyText : 'Click to upload image',
27283 rotateNotify : 'Image is too small to rotate',
27284 errorTimeout : 3000,
27298 cropType : 'image/jpeg',
27300 canvasLoaded : false,
27301 isDocument : false,
27303 paramName : 'imageUpload',
27305 loadingText : 'Loading...',
27308 getAutoCreate : function()
27312 cls : 'roo-upload-cropbox',
27316 cls : 'roo-upload-cropbox-selector',
27321 cls : 'roo-upload-cropbox-body',
27322 style : 'cursor:pointer',
27326 cls : 'roo-upload-cropbox-preview'
27330 cls : 'roo-upload-cropbox-thumb'
27334 cls : 'roo-upload-cropbox-empty-notify',
27335 html : this.emptyText
27339 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27340 html : this.rotateNotify
27346 cls : 'roo-upload-cropbox-footer',
27349 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27359 onRender : function(ct, position)
27361 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27363 if (this.buttons.length) {
27365 Roo.each(this.buttons, function(bb) {
27367 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27369 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27375 this.maskEl = this.el;
27379 initEvents : function()
27381 this.urlAPI = (window.createObjectURL && window) ||
27382 (window.URL && URL.revokeObjectURL && URL) ||
27383 (window.webkitURL && webkitURL);
27385 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27386 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27388 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27389 this.selectorEl.hide();
27391 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27392 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27394 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27395 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27396 this.thumbEl.hide();
27398 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27399 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27401 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27402 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27403 this.errorEl.hide();
27405 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27406 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27407 this.footerEl.hide();
27409 this.setThumbBoxSize();
27415 this.fireEvent('initial', this);
27422 window.addEventListener("resize", function() { _this.resize(); } );
27424 this.bodyEl.on('click', this.beforeSelectFile, this);
27427 this.bodyEl.on('touchstart', this.onTouchStart, this);
27428 this.bodyEl.on('touchmove', this.onTouchMove, this);
27429 this.bodyEl.on('touchend', this.onTouchEnd, this);
27433 this.bodyEl.on('mousedown', this.onMouseDown, this);
27434 this.bodyEl.on('mousemove', this.onMouseMove, this);
27435 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27436 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27437 Roo.get(document).on('mouseup', this.onMouseUp, this);
27440 this.selectorEl.on('change', this.onFileSelected, this);
27446 this.baseScale = 1;
27448 this.baseRotate = 1;
27449 this.dragable = false;
27450 this.pinching = false;
27453 this.cropData = false;
27454 this.notifyEl.dom.innerHTML = this.emptyText;
27456 this.selectorEl.dom.value = '';
27460 resize : function()
27462 if(this.fireEvent('resize', this) != false){
27463 this.setThumbBoxPosition();
27464 this.setCanvasPosition();
27468 onFooterButtonClick : function(e, el, o, type)
27471 case 'rotate-left' :
27472 this.onRotateLeft(e);
27474 case 'rotate-right' :
27475 this.onRotateRight(e);
27478 this.beforeSelectFile(e);
27493 this.fireEvent('footerbuttonclick', this, type);
27496 beforeSelectFile : function(e)
27498 e.preventDefault();
27500 if(this.fireEvent('beforeselectfile', this) != false){
27501 this.selectorEl.dom.click();
27505 onFileSelected : function(e)
27507 e.preventDefault();
27509 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27513 var file = this.selectorEl.dom.files[0];
27515 if(this.fireEvent('inspect', this, file) != false){
27516 this.prepare(file);
27521 trash : function(e)
27523 this.fireEvent('trash', this);
27526 download : function(e)
27528 this.fireEvent('download', this);
27531 loadCanvas : function(src)
27533 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27537 this.imageEl = document.createElement('img');
27541 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27543 this.imageEl.src = src;
27547 onLoadCanvas : function()
27549 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27550 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27552 this.bodyEl.un('click', this.beforeSelectFile, this);
27554 this.notifyEl.hide();
27555 this.thumbEl.show();
27556 this.footerEl.show();
27558 this.baseRotateLevel();
27560 if(this.isDocument){
27561 this.setThumbBoxSize();
27564 this.setThumbBoxPosition();
27566 this.baseScaleLevel();
27572 this.canvasLoaded = true;
27575 this.maskEl.unmask();
27580 setCanvasPosition : function()
27582 if(!this.canvasEl){
27586 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27587 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27589 this.previewEl.setLeft(pw);
27590 this.previewEl.setTop(ph);
27594 onMouseDown : function(e)
27598 this.dragable = true;
27599 this.pinching = false;
27601 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27602 this.dragable = false;
27606 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27607 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27611 onMouseMove : function(e)
27615 if(!this.canvasLoaded){
27619 if (!this.dragable){
27623 var minX = Math.ceil(this.thumbEl.getLeft(true));
27624 var minY = Math.ceil(this.thumbEl.getTop(true));
27626 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27627 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27629 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27630 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27632 x = x - this.mouseX;
27633 y = y - this.mouseY;
27635 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27636 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27638 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27639 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27641 this.previewEl.setLeft(bgX);
27642 this.previewEl.setTop(bgY);
27644 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27645 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27648 onMouseUp : function(e)
27652 this.dragable = false;
27655 onMouseWheel : function(e)
27659 this.startScale = this.scale;
27661 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27663 if(!this.zoomable()){
27664 this.scale = this.startScale;
27673 zoomable : function()
27675 var minScale = this.thumbEl.getWidth() / this.minWidth;
27677 if(this.minWidth < this.minHeight){
27678 minScale = this.thumbEl.getHeight() / this.minHeight;
27681 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27682 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27686 (this.rotate == 0 || this.rotate == 180) &&
27688 width > this.imageEl.OriginWidth ||
27689 height > this.imageEl.OriginHeight ||
27690 (width < this.minWidth && height < this.minHeight)
27698 (this.rotate == 90 || this.rotate == 270) &&
27700 width > this.imageEl.OriginWidth ||
27701 height > this.imageEl.OriginHeight ||
27702 (width < this.minHeight && height < this.minWidth)
27709 !this.isDocument &&
27710 (this.rotate == 0 || this.rotate == 180) &&
27712 width < this.minWidth ||
27713 width > this.imageEl.OriginWidth ||
27714 height < this.minHeight ||
27715 height > this.imageEl.OriginHeight
27722 !this.isDocument &&
27723 (this.rotate == 90 || this.rotate == 270) &&
27725 width < this.minHeight ||
27726 width > this.imageEl.OriginWidth ||
27727 height < this.minWidth ||
27728 height > this.imageEl.OriginHeight
27738 onRotateLeft : function(e)
27740 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27742 var minScale = this.thumbEl.getWidth() / this.minWidth;
27744 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27745 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27747 this.startScale = this.scale;
27749 while (this.getScaleLevel() < minScale){
27751 this.scale = this.scale + 1;
27753 if(!this.zoomable()){
27758 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27759 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27764 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27771 this.scale = this.startScale;
27773 this.onRotateFail();
27778 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27780 if(this.isDocument){
27781 this.setThumbBoxSize();
27782 this.setThumbBoxPosition();
27783 this.setCanvasPosition();
27788 this.fireEvent('rotate', this, 'left');
27792 onRotateRight : function(e)
27794 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27796 var minScale = this.thumbEl.getWidth() / this.minWidth;
27798 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27799 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27801 this.startScale = this.scale;
27803 while (this.getScaleLevel() < minScale){
27805 this.scale = this.scale + 1;
27807 if(!this.zoomable()){
27812 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27813 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27818 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27825 this.scale = this.startScale;
27827 this.onRotateFail();
27832 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27834 if(this.isDocument){
27835 this.setThumbBoxSize();
27836 this.setThumbBoxPosition();
27837 this.setCanvasPosition();
27842 this.fireEvent('rotate', this, 'right');
27845 onRotateFail : function()
27847 this.errorEl.show(true);
27851 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27856 this.previewEl.dom.innerHTML = '';
27858 var canvasEl = document.createElement("canvas");
27860 var contextEl = canvasEl.getContext("2d");
27862 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27863 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27864 var center = this.imageEl.OriginWidth / 2;
27866 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27867 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27868 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27869 center = this.imageEl.OriginHeight / 2;
27872 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27874 contextEl.translate(center, center);
27875 contextEl.rotate(this.rotate * Math.PI / 180);
27877 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27879 this.canvasEl = document.createElement("canvas");
27881 this.contextEl = this.canvasEl.getContext("2d");
27883 switch (this.rotate) {
27886 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27887 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27889 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27894 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27895 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27897 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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);
27902 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27907 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27908 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27910 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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);
27915 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);
27920 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27921 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27923 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27924 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27928 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);
27935 this.previewEl.appendChild(this.canvasEl);
27937 this.setCanvasPosition();
27942 if(!this.canvasLoaded){
27946 var imageCanvas = document.createElement("canvas");
27948 var imageContext = imageCanvas.getContext("2d");
27950 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27951 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27953 var center = imageCanvas.width / 2;
27955 imageContext.translate(center, center);
27957 imageContext.rotate(this.rotate * Math.PI / 180);
27959 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27961 var canvas = document.createElement("canvas");
27963 var context = canvas.getContext("2d");
27965 canvas.width = this.minWidth;
27966 canvas.height = this.minHeight;
27968 switch (this.rotate) {
27971 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27972 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27974 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27975 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27977 var targetWidth = this.minWidth - 2 * x;
27978 var targetHeight = this.minHeight - 2 * y;
27982 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27983 scale = targetWidth / width;
27986 if(x > 0 && y == 0){
27987 scale = targetHeight / height;
27990 if(x > 0 && y > 0){
27991 scale = targetWidth / width;
27993 if(width < height){
27994 scale = targetHeight / height;
27998 context.scale(scale, scale);
28000 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28001 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28003 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28004 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28006 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28011 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28012 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28014 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28015 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28017 var targetWidth = this.minWidth - 2 * x;
28018 var targetHeight = this.minHeight - 2 * y;
28022 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28023 scale = targetWidth / width;
28026 if(x > 0 && y == 0){
28027 scale = targetHeight / height;
28030 if(x > 0 && y > 0){
28031 scale = targetWidth / width;
28033 if(width < height){
28034 scale = targetHeight / height;
28038 context.scale(scale, scale);
28040 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28041 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28043 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28044 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28046 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28048 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28053 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28054 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28056 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28057 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28059 var targetWidth = this.minWidth - 2 * x;
28060 var targetHeight = this.minHeight - 2 * y;
28064 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28065 scale = targetWidth / width;
28068 if(x > 0 && y == 0){
28069 scale = targetHeight / height;
28072 if(x > 0 && y > 0){
28073 scale = targetWidth / width;
28075 if(width < height){
28076 scale = targetHeight / height;
28080 context.scale(scale, scale);
28082 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28083 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28085 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28086 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28088 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28089 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28091 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28096 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28097 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28099 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28100 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28102 var targetWidth = this.minWidth - 2 * x;
28103 var targetHeight = this.minHeight - 2 * y;
28107 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28108 scale = targetWidth / width;
28111 if(x > 0 && y == 0){
28112 scale = targetHeight / height;
28115 if(x > 0 && y > 0){
28116 scale = targetWidth / width;
28118 if(width < height){
28119 scale = targetHeight / height;
28123 context.scale(scale, scale);
28125 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28126 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28128 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28129 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28131 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28133 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28140 this.cropData = canvas.toDataURL(this.cropType);
28142 if(this.fireEvent('crop', this, this.cropData) !== false){
28143 this.process(this.file, this.cropData);
28150 setThumbBoxSize : function()
28154 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28155 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28156 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28158 this.minWidth = width;
28159 this.minHeight = height;
28161 if(this.rotate == 90 || this.rotate == 270){
28162 this.minWidth = height;
28163 this.minHeight = width;
28168 width = Math.ceil(this.minWidth * height / this.minHeight);
28170 if(this.minWidth > this.minHeight){
28172 height = Math.ceil(this.minHeight * width / this.minWidth);
28175 this.thumbEl.setStyle({
28176 width : width + 'px',
28177 height : height + 'px'
28184 setThumbBoxPosition : function()
28186 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28187 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28189 this.thumbEl.setLeft(x);
28190 this.thumbEl.setTop(y);
28194 baseRotateLevel : function()
28196 this.baseRotate = 1;
28199 typeof(this.exif) != 'undefined' &&
28200 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28201 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28203 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28206 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28210 baseScaleLevel : function()
28214 if(this.isDocument){
28216 if(this.baseRotate == 6 || this.baseRotate == 8){
28218 height = this.thumbEl.getHeight();
28219 this.baseScale = height / this.imageEl.OriginWidth;
28221 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28222 width = this.thumbEl.getWidth();
28223 this.baseScale = width / this.imageEl.OriginHeight;
28229 height = this.thumbEl.getHeight();
28230 this.baseScale = height / this.imageEl.OriginHeight;
28232 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28233 width = this.thumbEl.getWidth();
28234 this.baseScale = width / this.imageEl.OriginWidth;
28240 if(this.baseRotate == 6 || this.baseRotate == 8){
28242 width = this.thumbEl.getHeight();
28243 this.baseScale = width / this.imageEl.OriginHeight;
28245 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28246 height = this.thumbEl.getWidth();
28247 this.baseScale = height / this.imageEl.OriginHeight;
28250 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28251 height = this.thumbEl.getWidth();
28252 this.baseScale = height / this.imageEl.OriginHeight;
28254 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28255 width = this.thumbEl.getHeight();
28256 this.baseScale = width / this.imageEl.OriginWidth;
28263 width = this.thumbEl.getWidth();
28264 this.baseScale = width / this.imageEl.OriginWidth;
28266 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28267 height = this.thumbEl.getHeight();
28268 this.baseScale = height / this.imageEl.OriginHeight;
28271 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28273 height = this.thumbEl.getHeight();
28274 this.baseScale = height / this.imageEl.OriginHeight;
28276 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28277 width = this.thumbEl.getWidth();
28278 this.baseScale = width / this.imageEl.OriginWidth;
28286 getScaleLevel : function()
28288 return this.baseScale * Math.pow(1.1, this.scale);
28291 onTouchStart : function(e)
28293 if(!this.canvasLoaded){
28294 this.beforeSelectFile(e);
28298 var touches = e.browserEvent.touches;
28304 if(touches.length == 1){
28305 this.onMouseDown(e);
28309 if(touches.length != 2){
28315 for(var i = 0, finger; finger = touches[i]; i++){
28316 coords.push(finger.pageX, finger.pageY);
28319 var x = Math.pow(coords[0] - coords[2], 2);
28320 var y = Math.pow(coords[1] - coords[3], 2);
28322 this.startDistance = Math.sqrt(x + y);
28324 this.startScale = this.scale;
28326 this.pinching = true;
28327 this.dragable = false;
28331 onTouchMove : function(e)
28333 if(!this.pinching && !this.dragable){
28337 var touches = e.browserEvent.touches;
28344 this.onMouseMove(e);
28350 for(var i = 0, finger; finger = touches[i]; i++){
28351 coords.push(finger.pageX, finger.pageY);
28354 var x = Math.pow(coords[0] - coords[2], 2);
28355 var y = Math.pow(coords[1] - coords[3], 2);
28357 this.endDistance = Math.sqrt(x + y);
28359 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28361 if(!this.zoomable()){
28362 this.scale = this.startScale;
28370 onTouchEnd : function(e)
28372 this.pinching = false;
28373 this.dragable = false;
28377 process : function(file, crop)
28380 this.maskEl.mask(this.loadingText);
28383 this.xhr = new XMLHttpRequest();
28385 file.xhr = this.xhr;
28387 this.xhr.open(this.method, this.url, true);
28390 "Accept": "application/json",
28391 "Cache-Control": "no-cache",
28392 "X-Requested-With": "XMLHttpRequest"
28395 for (var headerName in headers) {
28396 var headerValue = headers[headerName];
28398 this.xhr.setRequestHeader(headerName, headerValue);
28404 this.xhr.onload = function()
28406 _this.xhrOnLoad(_this.xhr);
28409 this.xhr.onerror = function()
28411 _this.xhrOnError(_this.xhr);
28414 var formData = new FormData();
28416 formData.append('returnHTML', 'NO');
28419 formData.append('crop', crop);
28422 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28423 formData.append(this.paramName, file, file.name);
28426 if(typeof(file.filename) != 'undefined'){
28427 formData.append('filename', file.filename);
28430 if(typeof(file.mimetype) != 'undefined'){
28431 formData.append('mimetype', file.mimetype);
28434 if(this.fireEvent('arrange', this, formData) != false){
28435 this.xhr.send(formData);
28439 xhrOnLoad : function(xhr)
28442 this.maskEl.unmask();
28445 if (xhr.readyState !== 4) {
28446 this.fireEvent('exception', this, xhr);
28450 var response = Roo.decode(xhr.responseText);
28452 if(!response.success){
28453 this.fireEvent('exception', this, xhr);
28457 var response = Roo.decode(xhr.responseText);
28459 this.fireEvent('upload', this, response);
28463 xhrOnError : function()
28466 this.maskEl.unmask();
28469 Roo.log('xhr on error');
28471 var response = Roo.decode(xhr.responseText);
28477 prepare : function(file)
28480 this.maskEl.mask(this.loadingText);
28486 if(typeof(file) === 'string'){
28487 this.loadCanvas(file);
28491 if(!file || !this.urlAPI){
28496 this.cropType = file.type;
28500 if(this.fireEvent('prepare', this, this.file) != false){
28502 var reader = new FileReader();
28504 reader.onload = function (e) {
28505 if (e.target.error) {
28506 Roo.log(e.target.error);
28510 var buffer = e.target.result,
28511 dataView = new DataView(buffer),
28513 maxOffset = dataView.byteLength - 4,
28517 if (dataView.getUint16(0) === 0xffd8) {
28518 while (offset < maxOffset) {
28519 markerBytes = dataView.getUint16(offset);
28521 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28522 markerLength = dataView.getUint16(offset + 2) + 2;
28523 if (offset + markerLength > dataView.byteLength) {
28524 Roo.log('Invalid meta data: Invalid segment size.');
28528 if(markerBytes == 0xffe1){
28529 _this.parseExifData(
28536 offset += markerLength;
28546 var url = _this.urlAPI.createObjectURL(_this.file);
28548 _this.loadCanvas(url);
28553 reader.readAsArrayBuffer(this.file);
28559 parseExifData : function(dataView, offset, length)
28561 var tiffOffset = offset + 10,
28565 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28566 // No Exif data, might be XMP data instead
28570 // Check for the ASCII code for "Exif" (0x45786966):
28571 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28572 // No Exif data, might be XMP data instead
28575 if (tiffOffset + 8 > dataView.byteLength) {
28576 Roo.log('Invalid Exif data: Invalid segment size.');
28579 // Check for the two null bytes:
28580 if (dataView.getUint16(offset + 8) !== 0x0000) {
28581 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28584 // Check the byte alignment:
28585 switch (dataView.getUint16(tiffOffset)) {
28587 littleEndian = true;
28590 littleEndian = false;
28593 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28596 // Check for the TIFF tag marker (0x002A):
28597 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28598 Roo.log('Invalid Exif data: Missing TIFF marker.');
28601 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28602 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28604 this.parseExifTags(
28607 tiffOffset + dirOffset,
28612 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28617 if (dirOffset + 6 > dataView.byteLength) {
28618 Roo.log('Invalid Exif data: Invalid directory offset.');
28621 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28622 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28623 if (dirEndOffset + 4 > dataView.byteLength) {
28624 Roo.log('Invalid Exif data: Invalid directory size.');
28627 for (i = 0; i < tagsNumber; i += 1) {
28631 dirOffset + 2 + 12 * i, // tag offset
28635 // Return the offset to the next directory:
28636 return dataView.getUint32(dirEndOffset, littleEndian);
28639 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28641 var tag = dataView.getUint16(offset, littleEndian);
28643 this.exif[tag] = this.getExifValue(
28647 dataView.getUint16(offset + 2, littleEndian), // tag type
28648 dataView.getUint32(offset + 4, littleEndian), // tag length
28653 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28655 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28664 Roo.log('Invalid Exif data: Invalid tag type.');
28668 tagSize = tagType.size * length;
28669 // Determine if the value is contained in the dataOffset bytes,
28670 // or if the value at the dataOffset is a pointer to the actual data:
28671 dataOffset = tagSize > 4 ?
28672 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28673 if (dataOffset + tagSize > dataView.byteLength) {
28674 Roo.log('Invalid Exif data: Invalid data offset.');
28677 if (length === 1) {
28678 return tagType.getValue(dataView, dataOffset, littleEndian);
28681 for (i = 0; i < length; i += 1) {
28682 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28685 if (tagType.ascii) {
28687 // Concatenate the chars:
28688 for (i = 0; i < values.length; i += 1) {
28690 // Ignore the terminating NULL byte(s):
28691 if (c === '\u0000') {
28703 Roo.apply(Roo.bootstrap.UploadCropbox, {
28705 'Orientation': 0x0112
28709 1: 0, //'top-left',
28711 3: 180, //'bottom-right',
28712 // 4: 'bottom-left',
28714 6: 90, //'right-top',
28715 // 7: 'right-bottom',
28716 8: 270 //'left-bottom'
28720 // byte, 8-bit unsigned int:
28722 getValue: function (dataView, dataOffset) {
28723 return dataView.getUint8(dataOffset);
28727 // ascii, 8-bit byte:
28729 getValue: function (dataView, dataOffset) {
28730 return String.fromCharCode(dataView.getUint8(dataOffset));
28735 // short, 16 bit int:
28737 getValue: function (dataView, dataOffset, littleEndian) {
28738 return dataView.getUint16(dataOffset, littleEndian);
28742 // long, 32 bit int:
28744 getValue: function (dataView, dataOffset, littleEndian) {
28745 return dataView.getUint32(dataOffset, littleEndian);
28749 // rational = two long values, first is numerator, second is denominator:
28751 getValue: function (dataView, dataOffset, littleEndian) {
28752 return dataView.getUint32(dataOffset, littleEndian) /
28753 dataView.getUint32(dataOffset + 4, littleEndian);
28757 // slong, 32 bit signed int:
28759 getValue: function (dataView, dataOffset, littleEndian) {
28760 return dataView.getInt32(dataOffset, littleEndian);
28764 // srational, two slongs, first is numerator, second is denominator:
28766 getValue: function (dataView, dataOffset, littleEndian) {
28767 return dataView.getInt32(dataOffset, littleEndian) /
28768 dataView.getInt32(dataOffset + 4, littleEndian);
28778 cls : 'btn-group roo-upload-cropbox-rotate-left',
28779 action : 'rotate-left',
28783 cls : 'btn btn-default',
28784 html : '<i class="fa fa-undo"></i>'
28790 cls : 'btn-group roo-upload-cropbox-picture',
28791 action : 'picture',
28795 cls : 'btn btn-default',
28796 html : '<i class="fa fa-picture-o"></i>'
28802 cls : 'btn-group roo-upload-cropbox-rotate-right',
28803 action : 'rotate-right',
28807 cls : 'btn btn-default',
28808 html : '<i class="fa fa-repeat"></i>'
28816 cls : 'btn-group roo-upload-cropbox-rotate-left',
28817 action : 'rotate-left',
28821 cls : 'btn btn-default',
28822 html : '<i class="fa fa-undo"></i>'
28828 cls : 'btn-group roo-upload-cropbox-download',
28829 action : 'download',
28833 cls : 'btn btn-default',
28834 html : '<i class="fa fa-download"></i>'
28840 cls : 'btn-group roo-upload-cropbox-crop',
28845 cls : 'btn btn-default',
28846 html : '<i class="fa fa-crop"></i>'
28852 cls : 'btn-group roo-upload-cropbox-trash',
28857 cls : 'btn btn-default',
28858 html : '<i class="fa fa-trash"></i>'
28864 cls : 'btn-group roo-upload-cropbox-rotate-right',
28865 action : 'rotate-right',
28869 cls : 'btn btn-default',
28870 html : '<i class="fa fa-repeat"></i>'
28878 cls : 'btn-group roo-upload-cropbox-rotate-left',
28879 action : 'rotate-left',
28883 cls : 'btn btn-default',
28884 html : '<i class="fa fa-undo"></i>'
28890 cls : 'btn-group roo-upload-cropbox-rotate-right',
28891 action : 'rotate-right',
28895 cls : 'btn btn-default',
28896 html : '<i class="fa fa-repeat"></i>'
28909 * @class Roo.bootstrap.DocumentManager
28910 * @extends Roo.bootstrap.Component
28911 * Bootstrap DocumentManager class
28912 * @cfg {String} paramName default 'imageUpload'
28913 * @cfg {String} toolTipName default 'filename'
28914 * @cfg {String} method default POST
28915 * @cfg {String} url action url
28916 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28917 * @cfg {Boolean} multiple multiple upload default true
28918 * @cfg {Number} thumbSize default 300
28919 * @cfg {String} fieldLabel
28920 * @cfg {Number} labelWidth default 4
28921 * @cfg {String} labelAlign (left|top) default left
28922 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28923 * @cfg {Number} labellg set the width of label (1-12)
28924 * @cfg {Number} labelmd set the width of label (1-12)
28925 * @cfg {Number} labelsm set the width of label (1-12)
28926 * @cfg {Number} labelxs set the width of label (1-12)
28929 * Create a new DocumentManager
28930 * @param {Object} config The config object
28933 Roo.bootstrap.DocumentManager = function(config){
28934 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28937 this.delegates = [];
28942 * Fire when initial the DocumentManager
28943 * @param {Roo.bootstrap.DocumentManager} this
28948 * inspect selected file
28949 * @param {Roo.bootstrap.DocumentManager} this
28950 * @param {File} file
28955 * Fire when xhr load exception
28956 * @param {Roo.bootstrap.DocumentManager} this
28957 * @param {XMLHttpRequest} xhr
28959 "exception" : true,
28961 * @event afterupload
28962 * Fire when xhr load exception
28963 * @param {Roo.bootstrap.DocumentManager} this
28964 * @param {XMLHttpRequest} xhr
28966 "afterupload" : true,
28969 * prepare the form data
28970 * @param {Roo.bootstrap.DocumentManager} this
28971 * @param {Object} formData
28976 * Fire when remove the file
28977 * @param {Roo.bootstrap.DocumentManager} this
28978 * @param {Object} file
28983 * Fire after refresh the file
28984 * @param {Roo.bootstrap.DocumentManager} this
28989 * Fire after click the image
28990 * @param {Roo.bootstrap.DocumentManager} this
28991 * @param {Object} file
28996 * Fire when upload a image and editable set to true
28997 * @param {Roo.bootstrap.DocumentManager} this
28998 * @param {Object} file
29002 * @event beforeselectfile
29003 * Fire before select file
29004 * @param {Roo.bootstrap.DocumentManager} this
29006 "beforeselectfile" : true,
29009 * Fire before process file
29010 * @param {Roo.bootstrap.DocumentManager} this
29011 * @param {Object} file
29015 * @event previewrendered
29016 * Fire when preview rendered
29017 * @param {Roo.bootstrap.DocumentManager} this
29018 * @param {Object} file
29020 "previewrendered" : true,
29023 "previewResize" : true
29028 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29037 paramName : 'imageUpload',
29038 toolTipName : 'filename',
29041 labelAlign : 'left',
29051 getAutoCreate : function()
29053 var managerWidget = {
29055 cls : 'roo-document-manager',
29059 cls : 'roo-document-manager-selector',
29064 cls : 'roo-document-manager-uploader',
29068 cls : 'roo-document-manager-upload-btn',
29069 html : '<i class="fa fa-plus"></i>'
29080 cls : 'column col-md-12',
29085 if(this.fieldLabel.length){
29090 cls : 'column col-md-12',
29091 html : this.fieldLabel
29095 cls : 'column col-md-12',
29100 if(this.labelAlign == 'left'){
29105 html : this.fieldLabel
29114 if(this.labelWidth > 12){
29115 content[0].style = "width: " + this.labelWidth + 'px';
29118 if(this.labelWidth < 13 && this.labelmd == 0){
29119 this.labelmd = this.labelWidth;
29122 if(this.labellg > 0){
29123 content[0].cls += ' col-lg-' + this.labellg;
29124 content[1].cls += ' col-lg-' + (12 - this.labellg);
29127 if(this.labelmd > 0){
29128 content[0].cls += ' col-md-' + this.labelmd;
29129 content[1].cls += ' col-md-' + (12 - this.labelmd);
29132 if(this.labelsm > 0){
29133 content[0].cls += ' col-sm-' + this.labelsm;
29134 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29137 if(this.labelxs > 0){
29138 content[0].cls += ' col-xs-' + this.labelxs;
29139 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29147 cls : 'row clearfix',
29155 initEvents : function()
29157 this.managerEl = this.el.select('.roo-document-manager', true).first();
29158 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29160 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29161 this.selectorEl.hide();
29164 this.selectorEl.attr('multiple', 'multiple');
29167 this.selectorEl.on('change', this.onFileSelected, this);
29169 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29170 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29172 this.uploader.on('click', this.onUploaderClick, this);
29174 this.renderProgressDialog();
29178 window.addEventListener("resize", function() { _this.refresh(); } );
29180 this.fireEvent('initial', this);
29183 renderProgressDialog : function()
29187 this.progressDialog = new Roo.bootstrap.Modal({
29188 cls : 'roo-document-manager-progress-dialog',
29189 allow_close : false,
29200 btnclick : function() {
29201 _this.uploadCancel();
29207 this.progressDialog.render(Roo.get(document.body));
29209 this.progress = new Roo.bootstrap.Progress({
29210 cls : 'roo-document-manager-progress',
29215 this.progress.render(this.progressDialog.getChildContainer());
29217 this.progressBar = new Roo.bootstrap.ProgressBar({
29218 cls : 'roo-document-manager-progress-bar',
29221 aria_valuemax : 12,
29225 this.progressBar.render(this.progress.getChildContainer());
29228 onUploaderClick : function(e)
29230 e.preventDefault();
29232 if(this.fireEvent('beforeselectfile', this) != false){
29233 this.selectorEl.dom.click();
29238 onFileSelected : function(e)
29240 e.preventDefault();
29242 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29246 Roo.each(this.selectorEl.dom.files, function(file){
29247 if(this.fireEvent('inspect', this, file) != false){
29248 this.files.push(file);
29258 this.selectorEl.dom.value = '';
29260 if(!this.files || !this.files.length){
29264 if(this.boxes > 0 && this.files.length > this.boxes){
29265 this.files = this.files.slice(0, this.boxes);
29268 this.uploader.show();
29270 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29271 this.uploader.hide();
29280 Roo.each(this.files, function(file){
29282 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29283 var f = this.renderPreview(file);
29288 if(file.type.indexOf('image') != -1){
29289 this.delegates.push(
29291 _this.process(file);
29292 }).createDelegate(this)
29300 _this.process(file);
29301 }).createDelegate(this)
29306 this.files = files;
29308 this.delegates = this.delegates.concat(docs);
29310 if(!this.delegates.length){
29315 this.progressBar.aria_valuemax = this.delegates.length;
29322 arrange : function()
29324 if(!this.delegates.length){
29325 this.progressDialog.hide();
29330 var delegate = this.delegates.shift();
29332 this.progressDialog.show();
29334 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29336 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29341 refresh : function()
29343 this.uploader.show();
29345 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29346 this.uploader.hide();
29349 Roo.isTouch ? this.closable(false) : this.closable(true);
29351 this.fireEvent('refresh', this);
29354 onRemove : function(e, el, o)
29356 e.preventDefault();
29358 this.fireEvent('remove', this, o);
29362 remove : function(o)
29366 Roo.each(this.files, function(file){
29367 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29376 this.files = files;
29383 Roo.each(this.files, function(file){
29388 file.target.remove();
29397 onClick : function(e, el, o)
29399 e.preventDefault();
29401 this.fireEvent('click', this, o);
29405 closable : function(closable)
29407 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29409 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29421 xhrOnLoad : function(xhr)
29423 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29427 if (xhr.readyState !== 4) {
29429 this.fireEvent('exception', this, xhr);
29433 var response = Roo.decode(xhr.responseText);
29435 if(!response.success){
29437 this.fireEvent('exception', this, xhr);
29441 var file = this.renderPreview(response.data);
29443 this.files.push(file);
29447 this.fireEvent('afterupload', this, xhr);
29451 xhrOnError : function(xhr)
29453 Roo.log('xhr on error');
29455 var response = Roo.decode(xhr.responseText);
29462 process : function(file)
29464 if(this.fireEvent('process', this, file) !== false){
29465 if(this.editable && file.type.indexOf('image') != -1){
29466 this.fireEvent('edit', this, file);
29470 this.uploadStart(file, false);
29477 uploadStart : function(file, crop)
29479 this.xhr = new XMLHttpRequest();
29481 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29486 file.xhr = this.xhr;
29488 this.managerEl.createChild({
29490 cls : 'roo-document-manager-loading',
29494 tooltip : file.name,
29495 cls : 'roo-document-manager-thumb',
29496 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29502 this.xhr.open(this.method, this.url, true);
29505 "Accept": "application/json",
29506 "Cache-Control": "no-cache",
29507 "X-Requested-With": "XMLHttpRequest"
29510 for (var headerName in headers) {
29511 var headerValue = headers[headerName];
29513 this.xhr.setRequestHeader(headerName, headerValue);
29519 this.xhr.onload = function()
29521 _this.xhrOnLoad(_this.xhr);
29524 this.xhr.onerror = function()
29526 _this.xhrOnError(_this.xhr);
29529 var formData = new FormData();
29531 formData.append('returnHTML', 'NO');
29534 formData.append('crop', crop);
29537 formData.append(this.paramName, file, file.name);
29544 if(this.fireEvent('prepare', this, formData, options) != false){
29546 if(options.manually){
29550 this.xhr.send(formData);
29554 this.uploadCancel();
29557 uploadCancel : function()
29563 this.delegates = [];
29565 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29572 renderPreview : function(file)
29574 if(typeof(file.target) != 'undefined' && file.target){
29578 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29580 var previewEl = this.managerEl.createChild({
29582 cls : 'roo-document-manager-preview',
29586 tooltip : file[this.toolTipName],
29587 cls : 'roo-document-manager-thumb',
29588 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29593 html : '<i class="fa fa-times-circle"></i>'
29598 var close = previewEl.select('button.close', true).first();
29600 close.on('click', this.onRemove, this, file);
29602 file.target = previewEl;
29604 var image = previewEl.select('img', true).first();
29608 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29610 image.on('click', this.onClick, this, file);
29612 this.fireEvent('previewrendered', this, file);
29618 onPreviewLoad : function(file, image)
29620 if(typeof(file.target) == 'undefined' || !file.target){
29624 var width = image.dom.naturalWidth || image.dom.width;
29625 var height = image.dom.naturalHeight || image.dom.height;
29627 if(!this.previewResize) {
29631 if(width > height){
29632 file.target.addClass('wide');
29636 file.target.addClass('tall');
29641 uploadFromSource : function(file, crop)
29643 this.xhr = new XMLHttpRequest();
29645 this.managerEl.createChild({
29647 cls : 'roo-document-manager-loading',
29651 tooltip : file.name,
29652 cls : 'roo-document-manager-thumb',
29653 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29659 this.xhr.open(this.method, this.url, true);
29662 "Accept": "application/json",
29663 "Cache-Control": "no-cache",
29664 "X-Requested-With": "XMLHttpRequest"
29667 for (var headerName in headers) {
29668 var headerValue = headers[headerName];
29670 this.xhr.setRequestHeader(headerName, headerValue);
29676 this.xhr.onload = function()
29678 _this.xhrOnLoad(_this.xhr);
29681 this.xhr.onerror = function()
29683 _this.xhrOnError(_this.xhr);
29686 var formData = new FormData();
29688 formData.append('returnHTML', 'NO');
29690 formData.append('crop', crop);
29692 if(typeof(file.filename) != 'undefined'){
29693 formData.append('filename', file.filename);
29696 if(typeof(file.mimetype) != 'undefined'){
29697 formData.append('mimetype', file.mimetype);
29702 if(this.fireEvent('prepare', this, formData) != false){
29703 this.xhr.send(formData);
29713 * @class Roo.bootstrap.DocumentViewer
29714 * @extends Roo.bootstrap.Component
29715 * Bootstrap DocumentViewer class
29716 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29717 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29720 * Create a new DocumentViewer
29721 * @param {Object} config The config object
29724 Roo.bootstrap.DocumentViewer = function(config){
29725 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29730 * Fire after initEvent
29731 * @param {Roo.bootstrap.DocumentViewer} this
29737 * @param {Roo.bootstrap.DocumentViewer} this
29742 * Fire after download button
29743 * @param {Roo.bootstrap.DocumentViewer} this
29748 * Fire after trash button
29749 * @param {Roo.bootstrap.DocumentViewer} this
29756 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29758 showDownload : true,
29762 getAutoCreate : function()
29766 cls : 'roo-document-viewer',
29770 cls : 'roo-document-viewer-body',
29774 cls : 'roo-document-viewer-thumb',
29778 cls : 'roo-document-viewer-image'
29786 cls : 'roo-document-viewer-footer',
29789 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29793 cls : 'btn-group roo-document-viewer-download',
29797 cls : 'btn btn-default',
29798 html : '<i class="fa fa-download"></i>'
29804 cls : 'btn-group roo-document-viewer-trash',
29808 cls : 'btn btn-default',
29809 html : '<i class="fa fa-trash"></i>'
29822 initEvents : function()
29824 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29825 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29827 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29828 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29830 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29831 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29833 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29834 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29836 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29837 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29839 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29840 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29842 this.bodyEl.on('click', this.onClick, this);
29843 this.downloadBtn.on('click', this.onDownload, this);
29844 this.trashBtn.on('click', this.onTrash, this);
29846 this.downloadBtn.hide();
29847 this.trashBtn.hide();
29849 if(this.showDownload){
29850 this.downloadBtn.show();
29853 if(this.showTrash){
29854 this.trashBtn.show();
29857 if(!this.showDownload && !this.showTrash) {
29858 this.footerEl.hide();
29863 initial : function()
29865 this.fireEvent('initial', this);
29869 onClick : function(e)
29871 e.preventDefault();
29873 this.fireEvent('click', this);
29876 onDownload : function(e)
29878 e.preventDefault();
29880 this.fireEvent('download', this);
29883 onTrash : function(e)
29885 e.preventDefault();
29887 this.fireEvent('trash', this);
29899 * @class Roo.bootstrap.NavProgressBar
29900 * @extends Roo.bootstrap.Component
29901 * Bootstrap NavProgressBar class
29904 * Create a new nav progress bar
29905 * @param {Object} config The config object
29908 Roo.bootstrap.NavProgressBar = function(config){
29909 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29911 this.bullets = this.bullets || [];
29913 // Roo.bootstrap.NavProgressBar.register(this);
29917 * Fires when the active item changes
29918 * @param {Roo.bootstrap.NavProgressBar} this
29919 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29920 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29927 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29932 getAutoCreate : function()
29934 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29938 cls : 'roo-navigation-bar-group',
29942 cls : 'roo-navigation-top-bar'
29946 cls : 'roo-navigation-bullets-bar',
29950 cls : 'roo-navigation-bar'
29957 cls : 'roo-navigation-bottom-bar'
29967 initEvents: function()
29972 onRender : function(ct, position)
29974 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29976 if(this.bullets.length){
29977 Roo.each(this.bullets, function(b){
29986 addItem : function(cfg)
29988 var item = new Roo.bootstrap.NavProgressItem(cfg);
29990 item.parentId = this.id;
29991 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29994 var top = new Roo.bootstrap.Element({
29996 cls : 'roo-navigation-bar-text'
29999 var bottom = new Roo.bootstrap.Element({
30001 cls : 'roo-navigation-bar-text'
30004 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30005 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30007 var topText = new Roo.bootstrap.Element({
30009 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30012 var bottomText = new Roo.bootstrap.Element({
30014 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30017 topText.onRender(top.el, null);
30018 bottomText.onRender(bottom.el, null);
30021 item.bottomEl = bottom;
30024 this.barItems.push(item);
30029 getActive : function()
30031 var active = false;
30033 Roo.each(this.barItems, function(v){
30035 if (!v.isActive()) {
30047 setActiveItem : function(item)
30051 Roo.each(this.barItems, function(v){
30052 if (v.rid == item.rid) {
30056 if (v.isActive()) {
30057 v.setActive(false);
30062 item.setActive(true);
30064 this.fireEvent('changed', this, item, prev);
30067 getBarItem: function(rid)
30071 Roo.each(this.barItems, function(e) {
30072 if (e.rid != rid) {
30083 indexOfItem : function(item)
30087 Roo.each(this.barItems, function(v, i){
30089 if (v.rid != item.rid) {
30100 setActiveNext : function()
30102 var i = this.indexOfItem(this.getActive());
30104 if (i > this.barItems.length) {
30108 this.setActiveItem(this.barItems[i+1]);
30111 setActivePrev : function()
30113 var i = this.indexOfItem(this.getActive());
30119 this.setActiveItem(this.barItems[i-1]);
30122 format : function()
30124 if(!this.barItems.length){
30128 var width = 100 / this.barItems.length;
30130 Roo.each(this.barItems, function(i){
30131 i.el.setStyle('width', width + '%');
30132 i.topEl.el.setStyle('width', width + '%');
30133 i.bottomEl.el.setStyle('width', width + '%');
30142 * Nav Progress Item
30147 * @class Roo.bootstrap.NavProgressItem
30148 * @extends Roo.bootstrap.Component
30149 * Bootstrap NavProgressItem class
30150 * @cfg {String} rid the reference id
30151 * @cfg {Boolean} active (true|false) Is item active default false
30152 * @cfg {Boolean} disabled (true|false) Is item active default false
30153 * @cfg {String} html
30154 * @cfg {String} position (top|bottom) text position default bottom
30155 * @cfg {String} icon show icon instead of number
30158 * Create a new NavProgressItem
30159 * @param {Object} config The config object
30161 Roo.bootstrap.NavProgressItem = function(config){
30162 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30167 * The raw click event for the entire grid.
30168 * @param {Roo.bootstrap.NavProgressItem} this
30169 * @param {Roo.EventObject} e
30176 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30182 position : 'bottom',
30185 getAutoCreate : function()
30187 var iconCls = 'roo-navigation-bar-item-icon';
30189 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30193 cls: 'roo-navigation-bar-item',
30203 cfg.cls += ' active';
30206 cfg.cls += ' disabled';
30212 disable : function()
30214 this.setDisabled(true);
30217 enable : function()
30219 this.setDisabled(false);
30222 initEvents: function()
30224 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30226 this.iconEl.on('click', this.onClick, this);
30229 onClick : function(e)
30231 e.preventDefault();
30237 if(this.fireEvent('click', this, e) === false){
30241 this.parent().setActiveItem(this);
30244 isActive: function ()
30246 return this.active;
30249 setActive : function(state)
30251 if(this.active == state){
30255 this.active = state;
30258 this.el.addClass('active');
30262 this.el.removeClass('active');
30267 setDisabled : function(state)
30269 if(this.disabled == state){
30273 this.disabled = state;
30276 this.el.addClass('disabled');
30280 this.el.removeClass('disabled');
30283 tooltipEl : function()
30285 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30298 * @class Roo.bootstrap.FieldLabel
30299 * @extends Roo.bootstrap.Component
30300 * Bootstrap FieldLabel class
30301 * @cfg {String} html contents of the element
30302 * @cfg {String} tag tag of the element default label
30303 * @cfg {String} cls class of the element
30304 * @cfg {String} target label target
30305 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30306 * @cfg {String} invalidClass default "text-warning"
30307 * @cfg {String} validClass default "text-success"
30308 * @cfg {String} iconTooltip default "This field is required"
30309 * @cfg {String} indicatorpos (left|right) default left
30312 * Create a new FieldLabel
30313 * @param {Object} config The config object
30316 Roo.bootstrap.FieldLabel = function(config){
30317 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30322 * Fires after the field has been marked as invalid.
30323 * @param {Roo.form.FieldLabel} this
30324 * @param {String} msg The validation message
30329 * Fires after the field has been validated with no errors.
30330 * @param {Roo.form.FieldLabel} this
30336 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30343 invalidClass : 'has-warning',
30344 validClass : 'has-success',
30345 iconTooltip : 'This field is required',
30346 indicatorpos : 'left',
30348 getAutoCreate : function(){
30351 if (!this.allowBlank) {
30357 cls : 'roo-bootstrap-field-label ' + this.cls,
30362 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30363 tooltip : this.iconTooltip
30372 if(this.indicatorpos == 'right'){
30375 cls : 'roo-bootstrap-field-label ' + this.cls,
30384 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30385 tooltip : this.iconTooltip
30394 initEvents: function()
30396 Roo.bootstrap.Element.superclass.initEvents.call(this);
30398 this.indicator = this.indicatorEl();
30400 if(this.indicator){
30401 this.indicator.removeClass('visible');
30402 this.indicator.addClass('invisible');
30405 Roo.bootstrap.FieldLabel.register(this);
30408 indicatorEl : function()
30410 var indicator = this.el.select('i.roo-required-indicator',true).first();
30421 * Mark this field as valid
30423 markValid : function()
30425 if(this.indicator){
30426 this.indicator.removeClass('visible');
30427 this.indicator.addClass('invisible');
30430 this.el.removeClass(this.invalidClass);
30432 this.el.addClass(this.validClass);
30434 this.fireEvent('valid', this);
30438 * Mark this field as invalid
30439 * @param {String} msg The validation message
30441 markInvalid : function(msg)
30443 if(this.indicator){
30444 this.indicator.removeClass('invisible');
30445 this.indicator.addClass('visible');
30448 this.el.removeClass(this.validClass);
30450 this.el.addClass(this.invalidClass);
30452 this.fireEvent('invalid', this, msg);
30458 Roo.apply(Roo.bootstrap.FieldLabel, {
30463 * register a FieldLabel Group
30464 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30466 register : function(label)
30468 if(this.groups.hasOwnProperty(label.target)){
30472 this.groups[label.target] = label;
30476 * fetch a FieldLabel Group based on the target
30477 * @param {string} target
30478 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30480 get: function(target) {
30481 if (typeof(this.groups[target]) == 'undefined') {
30485 return this.groups[target] ;
30494 * page DateSplitField.
30500 * @class Roo.bootstrap.DateSplitField
30501 * @extends Roo.bootstrap.Component
30502 * Bootstrap DateSplitField class
30503 * @cfg {string} fieldLabel - the label associated
30504 * @cfg {Number} labelWidth set the width of label (0-12)
30505 * @cfg {String} labelAlign (top|left)
30506 * @cfg {Boolean} dayAllowBlank (true|false) default false
30507 * @cfg {Boolean} monthAllowBlank (true|false) default false
30508 * @cfg {Boolean} yearAllowBlank (true|false) default false
30509 * @cfg {string} dayPlaceholder
30510 * @cfg {string} monthPlaceholder
30511 * @cfg {string} yearPlaceholder
30512 * @cfg {string} dayFormat default 'd'
30513 * @cfg {string} monthFormat default 'm'
30514 * @cfg {string} yearFormat default 'Y'
30515 * @cfg {Number} labellg set the width of label (1-12)
30516 * @cfg {Number} labelmd set the width of label (1-12)
30517 * @cfg {Number} labelsm set the width of label (1-12)
30518 * @cfg {Number} labelxs set the width of label (1-12)
30522 * Create a new DateSplitField
30523 * @param {Object} config The config object
30526 Roo.bootstrap.DateSplitField = function(config){
30527 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30533 * getting the data of years
30534 * @param {Roo.bootstrap.DateSplitField} this
30535 * @param {Object} years
30540 * getting the data of days
30541 * @param {Roo.bootstrap.DateSplitField} this
30542 * @param {Object} days
30547 * Fires after the field has been marked as invalid.
30548 * @param {Roo.form.Field} this
30549 * @param {String} msg The validation message
30554 * Fires after the field has been validated with no errors.
30555 * @param {Roo.form.Field} this
30561 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30564 labelAlign : 'top',
30566 dayAllowBlank : false,
30567 monthAllowBlank : false,
30568 yearAllowBlank : false,
30569 dayPlaceholder : '',
30570 monthPlaceholder : '',
30571 yearPlaceholder : '',
30575 isFormField : true,
30581 getAutoCreate : function()
30585 cls : 'row roo-date-split-field-group',
30590 cls : 'form-hidden-field roo-date-split-field-group-value',
30596 var labelCls = 'col-md-12';
30597 var contentCls = 'col-md-4';
30599 if(this.fieldLabel){
30603 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30607 html : this.fieldLabel
30612 if(this.labelAlign == 'left'){
30614 if(this.labelWidth > 12){
30615 label.style = "width: " + this.labelWidth + 'px';
30618 if(this.labelWidth < 13 && this.labelmd == 0){
30619 this.labelmd = this.labelWidth;
30622 if(this.labellg > 0){
30623 labelCls = ' col-lg-' + this.labellg;
30624 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30627 if(this.labelmd > 0){
30628 labelCls = ' col-md-' + this.labelmd;
30629 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30632 if(this.labelsm > 0){
30633 labelCls = ' col-sm-' + this.labelsm;
30634 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30637 if(this.labelxs > 0){
30638 labelCls = ' col-xs-' + this.labelxs;
30639 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30643 label.cls += ' ' + labelCls;
30645 cfg.cn.push(label);
30648 Roo.each(['day', 'month', 'year'], function(t){
30651 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30658 inputEl: function ()
30660 return this.el.select('.roo-date-split-field-group-value', true).first();
30663 onRender : function(ct, position)
30667 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30669 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30671 this.dayField = new Roo.bootstrap.ComboBox({
30672 allowBlank : this.dayAllowBlank,
30673 alwaysQuery : true,
30674 displayField : 'value',
30677 forceSelection : true,
30679 placeholder : this.dayPlaceholder,
30680 selectOnFocus : true,
30681 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30682 triggerAction : 'all',
30684 valueField : 'value',
30685 store : new Roo.data.SimpleStore({
30686 data : (function() {
30688 _this.fireEvent('days', _this, days);
30691 fields : [ 'value' ]
30694 select : function (_self, record, index)
30696 _this.setValue(_this.getValue());
30701 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30703 this.monthField = new Roo.bootstrap.MonthField({
30704 after : '<i class=\"fa fa-calendar\"></i>',
30705 allowBlank : this.monthAllowBlank,
30706 placeholder : this.monthPlaceholder,
30709 render : function (_self)
30711 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30712 e.preventDefault();
30716 select : function (_self, oldvalue, newvalue)
30718 _this.setValue(_this.getValue());
30723 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30725 this.yearField = new Roo.bootstrap.ComboBox({
30726 allowBlank : this.yearAllowBlank,
30727 alwaysQuery : true,
30728 displayField : 'value',
30731 forceSelection : true,
30733 placeholder : this.yearPlaceholder,
30734 selectOnFocus : true,
30735 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30736 triggerAction : 'all',
30738 valueField : 'value',
30739 store : new Roo.data.SimpleStore({
30740 data : (function() {
30742 _this.fireEvent('years', _this, years);
30745 fields : [ 'value' ]
30748 select : function (_self, record, index)
30750 _this.setValue(_this.getValue());
30755 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30758 setValue : function(v, format)
30760 this.inputEl.dom.value = v;
30762 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30764 var d = Date.parseDate(v, f);
30771 this.setDay(d.format(this.dayFormat));
30772 this.setMonth(d.format(this.monthFormat));
30773 this.setYear(d.format(this.yearFormat));
30780 setDay : function(v)
30782 this.dayField.setValue(v);
30783 this.inputEl.dom.value = this.getValue();
30788 setMonth : function(v)
30790 this.monthField.setValue(v, true);
30791 this.inputEl.dom.value = this.getValue();
30796 setYear : function(v)
30798 this.yearField.setValue(v);
30799 this.inputEl.dom.value = this.getValue();
30804 getDay : function()
30806 return this.dayField.getValue();
30809 getMonth : function()
30811 return this.monthField.getValue();
30814 getYear : function()
30816 return this.yearField.getValue();
30819 getValue : function()
30821 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30823 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30833 this.inputEl.dom.value = '';
30838 validate : function()
30840 var d = this.dayField.validate();
30841 var m = this.monthField.validate();
30842 var y = this.yearField.validate();
30847 (!this.dayAllowBlank && !d) ||
30848 (!this.monthAllowBlank && !m) ||
30849 (!this.yearAllowBlank && !y)
30854 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30863 this.markInvalid();
30868 markValid : function()
30871 var label = this.el.select('label', true).first();
30872 var icon = this.el.select('i.fa-star', true).first();
30878 this.fireEvent('valid', this);
30882 * Mark this field as invalid
30883 * @param {String} msg The validation message
30885 markInvalid : function(msg)
30888 var label = this.el.select('label', true).first();
30889 var icon = this.el.select('i.fa-star', true).first();
30891 if(label && !icon){
30892 this.el.select('.roo-date-split-field-label', true).createChild({
30894 cls : 'text-danger fa fa-lg fa-star',
30895 tooltip : 'This field is required',
30896 style : 'margin-right:5px;'
30900 this.fireEvent('invalid', this, msg);
30903 clearInvalid : function()
30905 var label = this.el.select('label', true).first();
30906 var icon = this.el.select('i.fa-star', true).first();
30912 this.fireEvent('valid', this);
30915 getName: function()
30925 * http://masonry.desandro.com
30927 * The idea is to render all the bricks based on vertical width...
30929 * The original code extends 'outlayer' - we might need to use that....
30935 * @class Roo.bootstrap.LayoutMasonry
30936 * @extends Roo.bootstrap.Component
30937 * Bootstrap Layout Masonry class
30940 * Create a new Element
30941 * @param {Object} config The config object
30944 Roo.bootstrap.LayoutMasonry = function(config){
30946 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30950 Roo.bootstrap.LayoutMasonry.register(this);
30956 * Fire after layout the items
30957 * @param {Roo.bootstrap.LayoutMasonry} this
30958 * @param {Roo.EventObject} e
30965 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30968 * @cfg {Boolean} isLayoutInstant = no animation?
30970 isLayoutInstant : false, // needed?
30973 * @cfg {Number} boxWidth width of the columns
30978 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30983 * @cfg {Number} padWidth padding below box..
30988 * @cfg {Number} gutter gutter width..
30993 * @cfg {Number} maxCols maximum number of columns
30999 * @cfg {Boolean} isAutoInitial defalut true
31001 isAutoInitial : true,
31006 * @cfg {Boolean} isHorizontal defalut false
31008 isHorizontal : false,
31010 currentSize : null,
31016 bricks: null, //CompositeElement
31020 _isLayoutInited : false,
31022 // isAlternative : false, // only use for vertical layout...
31025 * @cfg {Number} alternativePadWidth padding below box..
31027 alternativePadWidth : 50,
31029 selectedBrick : [],
31031 getAutoCreate : function(){
31033 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31037 cls: 'blog-masonary-wrapper ' + this.cls,
31039 cls : 'mas-boxes masonary'
31046 getChildContainer: function( )
31048 if (this.boxesEl) {
31049 return this.boxesEl;
31052 this.boxesEl = this.el.select('.mas-boxes').first();
31054 return this.boxesEl;
31058 initEvents : function()
31062 if(this.isAutoInitial){
31063 Roo.log('hook children rendered');
31064 this.on('childrenrendered', function() {
31065 Roo.log('children rendered');
31071 initial : function()
31073 this.selectedBrick = [];
31075 this.currentSize = this.el.getBox(true);
31077 Roo.EventManager.onWindowResize(this.resize, this);
31079 if(!this.isAutoInitial){
31087 //this.layout.defer(500,this);
31091 resize : function()
31093 var cs = this.el.getBox(true);
31096 this.currentSize.width == cs.width &&
31097 this.currentSize.x == cs.x &&
31098 this.currentSize.height == cs.height &&
31099 this.currentSize.y == cs.y
31101 Roo.log("no change in with or X or Y");
31105 this.currentSize = cs;
31111 layout : function()
31113 this._resetLayout();
31115 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31117 this.layoutItems( isInstant );
31119 this._isLayoutInited = true;
31121 this.fireEvent('layout', this);
31125 _resetLayout : function()
31127 if(this.isHorizontal){
31128 this.horizontalMeasureColumns();
31132 this.verticalMeasureColumns();
31136 verticalMeasureColumns : function()
31138 this.getContainerWidth();
31140 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31141 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31145 var boxWidth = this.boxWidth + this.padWidth;
31147 if(this.containerWidth < this.boxWidth){
31148 boxWidth = this.containerWidth
31151 var containerWidth = this.containerWidth;
31153 var cols = Math.floor(containerWidth / boxWidth);
31155 this.cols = Math.max( cols, 1 );
31157 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31159 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31161 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31163 this.colWidth = boxWidth + avail - this.padWidth;
31165 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31166 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31169 horizontalMeasureColumns : function()
31171 this.getContainerWidth();
31173 var boxWidth = this.boxWidth;
31175 if(this.containerWidth < boxWidth){
31176 boxWidth = this.containerWidth;
31179 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31181 this.el.setHeight(boxWidth);
31185 getContainerWidth : function()
31187 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31190 layoutItems : function( isInstant )
31192 Roo.log(this.bricks);
31194 var items = Roo.apply([], this.bricks);
31196 if(this.isHorizontal){
31197 this._horizontalLayoutItems( items , isInstant );
31201 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31202 // this._verticalAlternativeLayoutItems( items , isInstant );
31206 this._verticalLayoutItems( items , isInstant );
31210 _verticalLayoutItems : function ( items , isInstant)
31212 if ( !items || !items.length ) {
31217 ['xs', 'xs', 'xs', 'tall'],
31218 ['xs', 'xs', 'tall'],
31219 ['xs', 'xs', 'sm'],
31220 ['xs', 'xs', 'xs'],
31226 ['sm', 'xs', 'xs'],
31230 ['tall', 'xs', 'xs', 'xs'],
31231 ['tall', 'xs', 'xs'],
31243 Roo.each(items, function(item, k){
31245 switch (item.size) {
31246 // these layouts take up a full box,
31257 boxes.push([item]);
31280 var filterPattern = function(box, length)
31288 var pattern = box.slice(0, length);
31292 Roo.each(pattern, function(i){
31293 format.push(i.size);
31296 Roo.each(standard, function(s){
31298 if(String(s) != String(format)){
31307 if(!match && length == 1){
31312 filterPattern(box, length - 1);
31316 queue.push(pattern);
31318 box = box.slice(length, box.length);
31320 filterPattern(box, 4);
31326 Roo.each(boxes, function(box, k){
31332 if(box.length == 1){
31337 filterPattern(box, 4);
31341 this._processVerticalLayoutQueue( queue, isInstant );
31345 // _verticalAlternativeLayoutItems : function( items , isInstant )
31347 // if ( !items || !items.length ) {
31351 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31355 _horizontalLayoutItems : function ( items , isInstant)
31357 if ( !items || !items.length || items.length < 3) {
31363 var eItems = items.slice(0, 3);
31365 items = items.slice(3, items.length);
31368 ['xs', 'xs', 'xs', 'wide'],
31369 ['xs', 'xs', 'wide'],
31370 ['xs', 'xs', 'sm'],
31371 ['xs', 'xs', 'xs'],
31377 ['sm', 'xs', 'xs'],
31381 ['wide', 'xs', 'xs', 'xs'],
31382 ['wide', 'xs', 'xs'],
31395 Roo.each(items, function(item, k){
31397 switch (item.size) {
31408 boxes.push([item]);
31432 var filterPattern = function(box, length)
31440 var pattern = box.slice(0, length);
31444 Roo.each(pattern, function(i){
31445 format.push(i.size);
31448 Roo.each(standard, function(s){
31450 if(String(s) != String(format)){
31459 if(!match && length == 1){
31464 filterPattern(box, length - 1);
31468 queue.push(pattern);
31470 box = box.slice(length, box.length);
31472 filterPattern(box, 4);
31478 Roo.each(boxes, function(box, k){
31484 if(box.length == 1){
31489 filterPattern(box, 4);
31496 var pos = this.el.getBox(true);
31500 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31502 var hit_end = false;
31504 Roo.each(queue, function(box){
31508 Roo.each(box, function(b){
31510 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31520 Roo.each(box, function(b){
31522 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31525 mx = Math.max(mx, b.x);
31529 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31533 Roo.each(box, function(b){
31535 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31549 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31552 /** Sets position of item in DOM
31553 * @param {Element} item
31554 * @param {Number} x - horizontal position
31555 * @param {Number} y - vertical position
31556 * @param {Boolean} isInstant - disables transitions
31558 _processVerticalLayoutQueue : function( queue, isInstant )
31560 var pos = this.el.getBox(true);
31565 for (var i = 0; i < this.cols; i++){
31569 Roo.each(queue, function(box, k){
31571 var col = k % this.cols;
31573 Roo.each(box, function(b,kk){
31575 b.el.position('absolute');
31577 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31578 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31580 if(b.size == 'md-left' || b.size == 'md-right'){
31581 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31582 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31585 b.el.setWidth(width);
31586 b.el.setHeight(height);
31588 b.el.select('iframe',true).setSize(width,height);
31592 for (var i = 0; i < this.cols; i++){
31594 if(maxY[i] < maxY[col]){
31599 col = Math.min(col, i);
31603 x = pos.x + col * (this.colWidth + this.padWidth);
31607 var positions = [];
31609 switch (box.length){
31611 positions = this.getVerticalOneBoxColPositions(x, y, box);
31614 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31617 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31620 positions = this.getVerticalFourBoxColPositions(x, y, box);
31626 Roo.each(box, function(b,kk){
31628 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31630 var sz = b.el.getSize();
31632 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31640 for (var i = 0; i < this.cols; i++){
31641 mY = Math.max(mY, maxY[i]);
31644 this.el.setHeight(mY - pos.y);
31648 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31650 // var pos = this.el.getBox(true);
31653 // var maxX = pos.right;
31655 // var maxHeight = 0;
31657 // Roo.each(items, function(item, k){
31661 // item.el.position('absolute');
31663 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31665 // item.el.setWidth(width);
31667 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31669 // item.el.setHeight(height);
31672 // item.el.setXY([x, y], isInstant ? false : true);
31674 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31677 // y = y + height + this.alternativePadWidth;
31679 // maxHeight = maxHeight + height + this.alternativePadWidth;
31683 // this.el.setHeight(maxHeight);
31687 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31689 var pos = this.el.getBox(true);
31694 var maxX = pos.right;
31696 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31698 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31700 Roo.each(queue, function(box, k){
31702 Roo.each(box, function(b, kk){
31704 b.el.position('absolute');
31706 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31707 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31709 if(b.size == 'md-left' || b.size == 'md-right'){
31710 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31711 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31714 b.el.setWidth(width);
31715 b.el.setHeight(height);
31723 var positions = [];
31725 switch (box.length){
31727 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31730 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31733 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31736 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31742 Roo.each(box, function(b,kk){
31744 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31746 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31754 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31756 Roo.each(eItems, function(b,k){
31758 b.size = (k == 0) ? 'sm' : 'xs';
31759 b.x = (k == 0) ? 2 : 1;
31760 b.y = (k == 0) ? 2 : 1;
31762 b.el.position('absolute');
31764 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31766 b.el.setWidth(width);
31768 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31770 b.el.setHeight(height);
31774 var positions = [];
31777 x : maxX - this.unitWidth * 2 - this.gutter,
31782 x : maxX - this.unitWidth,
31783 y : minY + (this.unitWidth + this.gutter) * 2
31787 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31791 Roo.each(eItems, function(b,k){
31793 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31799 getVerticalOneBoxColPositions : function(x, y, box)
31803 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31805 if(box[0].size == 'md-left'){
31809 if(box[0].size == 'md-right'){
31814 x : x + (this.unitWidth + this.gutter) * rand,
31821 getVerticalTwoBoxColPositions : function(x, y, box)
31825 if(box[0].size == 'xs'){
31829 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31833 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31847 x : x + (this.unitWidth + this.gutter) * 2,
31848 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31855 getVerticalThreeBoxColPositions : function(x, y, box)
31859 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31867 x : x + (this.unitWidth + this.gutter) * 1,
31872 x : x + (this.unitWidth + this.gutter) * 2,
31880 if(box[0].size == 'xs' && box[1].size == 'xs'){
31889 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31893 x : x + (this.unitWidth + this.gutter) * 1,
31907 x : x + (this.unitWidth + this.gutter) * 2,
31912 x : x + (this.unitWidth + this.gutter) * 2,
31913 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31920 getVerticalFourBoxColPositions : function(x, y, box)
31924 if(box[0].size == 'xs'){
31933 y : y + (this.unitHeight + this.gutter) * 1
31938 y : y + (this.unitHeight + this.gutter) * 2
31942 x : x + (this.unitWidth + this.gutter) * 1,
31956 x : x + (this.unitWidth + this.gutter) * 2,
31961 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31962 y : y + (this.unitHeight + this.gutter) * 1
31966 x : x + (this.unitWidth + this.gutter) * 2,
31967 y : y + (this.unitWidth + this.gutter) * 2
31974 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31978 if(box[0].size == 'md-left'){
31980 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31987 if(box[0].size == 'md-right'){
31989 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31990 y : minY + (this.unitWidth + this.gutter) * 1
31996 var rand = Math.floor(Math.random() * (4 - box[0].y));
31999 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000 y : minY + (this.unitWidth + this.gutter) * rand
32007 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32011 if(box[0].size == 'xs'){
32014 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32019 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32020 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32028 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32033 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32034 y : minY + (this.unitWidth + this.gutter) * 2
32041 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32045 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32048 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32053 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32054 y : minY + (this.unitWidth + this.gutter) * 1
32058 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32059 y : minY + (this.unitWidth + this.gutter) * 2
32066 if(box[0].size == 'xs' && box[1].size == 'xs'){
32069 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32074 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32079 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32080 y : minY + (this.unitWidth + this.gutter) * 1
32088 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32093 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32094 y : minY + (this.unitWidth + this.gutter) * 2
32098 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32099 y : minY + (this.unitWidth + this.gutter) * 2
32106 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32110 if(box[0].size == 'xs'){
32113 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32118 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32123 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),
32128 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32129 y : minY + (this.unitWidth + this.gutter) * 1
32137 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32142 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32143 y : minY + (this.unitWidth + this.gutter) * 2
32147 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32148 y : minY + (this.unitWidth + this.gutter) * 2
32152 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),
32153 y : minY + (this.unitWidth + this.gutter) * 2
32161 * remove a Masonry Brick
32162 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32164 removeBrick : function(brick_id)
32170 for (var i = 0; i<this.bricks.length; i++) {
32171 if (this.bricks[i].id == brick_id) {
32172 this.bricks.splice(i,1);
32173 this.el.dom.removeChild(Roo.get(brick_id).dom);
32180 * adds a Masonry Brick
32181 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32183 addBrick : function(cfg)
32185 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32186 //this.register(cn);
32187 cn.parentId = this.id;
32188 cn.render(this.el);
32193 * register a Masonry Brick
32194 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32197 register : function(brick)
32199 this.bricks.push(brick);
32200 brick.masonryId = this.id;
32204 * clear all the Masonry Brick
32206 clearAll : function()
32209 //this.getChildContainer().dom.innerHTML = "";
32210 this.el.dom.innerHTML = '';
32213 getSelected : function()
32215 if (!this.selectedBrick) {
32219 return this.selectedBrick;
32223 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32227 * register a Masonry Layout
32228 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32231 register : function(layout)
32233 this.groups[layout.id] = layout;
32236 * fetch a Masonry Layout based on the masonry layout ID
32237 * @param {string} the masonry layout to add
32238 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32241 get: function(layout_id) {
32242 if (typeof(this.groups[layout_id]) == 'undefined') {
32245 return this.groups[layout_id] ;
32257 * http://masonry.desandro.com
32259 * The idea is to render all the bricks based on vertical width...
32261 * The original code extends 'outlayer' - we might need to use that....
32267 * @class Roo.bootstrap.LayoutMasonryAuto
32268 * @extends Roo.bootstrap.Component
32269 * Bootstrap Layout Masonry class
32272 * Create a new Element
32273 * @param {Object} config The config object
32276 Roo.bootstrap.LayoutMasonryAuto = function(config){
32277 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32280 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32283 * @cfg {Boolean} isFitWidth - resize the width..
32285 isFitWidth : false, // options..
32287 * @cfg {Boolean} isOriginLeft = left align?
32289 isOriginLeft : true,
32291 * @cfg {Boolean} isOriginTop = top align?
32293 isOriginTop : false,
32295 * @cfg {Boolean} isLayoutInstant = no animation?
32297 isLayoutInstant : false, // needed?
32299 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32301 isResizingContainer : true,
32303 * @cfg {Number} columnWidth width of the columns
32309 * @cfg {Number} maxCols maximum number of columns
32314 * @cfg {Number} padHeight padding below box..
32320 * @cfg {Boolean} isAutoInitial defalut true
32323 isAutoInitial : true,
32329 initialColumnWidth : 0,
32330 currentSize : null,
32332 colYs : null, // array.
32339 bricks: null, //CompositeElement
32340 cols : 0, // array?
32341 // element : null, // wrapped now this.el
32342 _isLayoutInited : null,
32345 getAutoCreate : function(){
32349 cls: 'blog-masonary-wrapper ' + this.cls,
32351 cls : 'mas-boxes masonary'
32358 getChildContainer: function( )
32360 if (this.boxesEl) {
32361 return this.boxesEl;
32364 this.boxesEl = this.el.select('.mas-boxes').first();
32366 return this.boxesEl;
32370 initEvents : function()
32374 if(this.isAutoInitial){
32375 Roo.log('hook children rendered');
32376 this.on('childrenrendered', function() {
32377 Roo.log('children rendered');
32384 initial : function()
32386 this.reloadItems();
32388 this.currentSize = this.el.getBox(true);
32390 /// was window resize... - let's see if this works..
32391 Roo.EventManager.onWindowResize(this.resize, this);
32393 if(!this.isAutoInitial){
32398 this.layout.defer(500,this);
32401 reloadItems: function()
32403 this.bricks = this.el.select('.masonry-brick', true);
32405 this.bricks.each(function(b) {
32406 //Roo.log(b.getSize());
32407 if (!b.attr('originalwidth')) {
32408 b.attr('originalwidth', b.getSize().width);
32413 Roo.log(this.bricks.elements.length);
32416 resize : function()
32419 var cs = this.el.getBox(true);
32421 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32422 Roo.log("no change in with or X");
32425 this.currentSize = cs;
32429 layout : function()
32432 this._resetLayout();
32433 //this._manageStamps();
32435 // don't animate first layout
32436 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32437 this.layoutItems( isInstant );
32439 // flag for initalized
32440 this._isLayoutInited = true;
32443 layoutItems : function( isInstant )
32445 //var items = this._getItemsForLayout( this.items );
32446 // original code supports filtering layout items.. we just ignore it..
32448 this._layoutItems( this.bricks , isInstant );
32450 this._postLayout();
32452 _layoutItems : function ( items , isInstant)
32454 //this.fireEvent( 'layout', this, items );
32457 if ( !items || !items.elements.length ) {
32458 // no items, emit event with empty array
32463 items.each(function(item) {
32464 Roo.log("layout item");
32466 // get x/y object from method
32467 var position = this._getItemLayoutPosition( item );
32469 position.item = item;
32470 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32471 queue.push( position );
32474 this._processLayoutQueue( queue );
32476 /** Sets position of item in DOM
32477 * @param {Element} item
32478 * @param {Number} x - horizontal position
32479 * @param {Number} y - vertical position
32480 * @param {Boolean} isInstant - disables transitions
32482 _processLayoutQueue : function( queue )
32484 for ( var i=0, len = queue.length; i < len; i++ ) {
32485 var obj = queue[i];
32486 obj.item.position('absolute');
32487 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32493 * Any logic you want to do after each layout,
32494 * i.e. size the container
32496 _postLayout : function()
32498 this.resizeContainer();
32501 resizeContainer : function()
32503 if ( !this.isResizingContainer ) {
32506 var size = this._getContainerSize();
32508 this.el.setSize(size.width,size.height);
32509 this.boxesEl.setSize(size.width,size.height);
32515 _resetLayout : function()
32517 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32518 this.colWidth = this.el.getWidth();
32519 //this.gutter = this.el.getWidth();
32521 this.measureColumns();
32527 this.colYs.push( 0 );
32533 measureColumns : function()
32535 this.getContainerWidth();
32536 // if columnWidth is 0, default to outerWidth of first item
32537 if ( !this.columnWidth ) {
32538 var firstItem = this.bricks.first();
32539 Roo.log(firstItem);
32540 this.columnWidth = this.containerWidth;
32541 if (firstItem && firstItem.attr('originalwidth') ) {
32542 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32544 // columnWidth fall back to item of first element
32545 Roo.log("set column width?");
32546 this.initialColumnWidth = this.columnWidth ;
32548 // if first elem has no width, default to size of container
32553 if (this.initialColumnWidth) {
32554 this.columnWidth = this.initialColumnWidth;
32559 // column width is fixed at the top - however if container width get's smaller we should
32562 // this bit calcs how man columns..
32564 var columnWidth = this.columnWidth += this.gutter;
32566 // calculate columns
32567 var containerWidth = this.containerWidth + this.gutter;
32569 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32570 // fix rounding errors, typically with gutters
32571 var excess = columnWidth - containerWidth % columnWidth;
32574 // if overshoot is less than a pixel, round up, otherwise floor it
32575 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32576 cols = Math[ mathMethod ]( cols );
32577 this.cols = Math.max( cols, 1 );
32578 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32580 // padding positioning..
32581 var totalColWidth = this.cols * this.columnWidth;
32582 var padavail = this.containerWidth - totalColWidth;
32583 // so for 2 columns - we need 3 'pads'
32585 var padNeeded = (1+this.cols) * this.padWidth;
32587 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32589 this.columnWidth += padExtra
32590 //this.padWidth = Math.floor(padavail / ( this.cols));
32592 // adjust colum width so that padding is fixed??
32594 // we have 3 columns ... total = width * 3
32595 // we have X left over... that should be used by
32597 //if (this.expandC) {
32605 getContainerWidth : function()
32607 /* // container is parent if fit width
32608 var container = this.isFitWidth ? this.element.parentNode : this.element;
32609 // check that this.size and size are there
32610 // IE8 triggers resize on body size change, so they might not be
32612 var size = getSize( container ); //FIXME
32613 this.containerWidth = size && size.innerWidth; //FIXME
32616 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32620 _getItemLayoutPosition : function( item ) // what is item?
32622 // we resize the item to our columnWidth..
32624 item.setWidth(this.columnWidth);
32625 item.autoBoxAdjust = false;
32627 var sz = item.getSize();
32629 // how many columns does this brick span
32630 var remainder = this.containerWidth % this.columnWidth;
32632 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32633 // round if off by 1 pixel, otherwise use ceil
32634 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32635 colSpan = Math.min( colSpan, this.cols );
32637 // normally this should be '1' as we dont' currently allow multi width columns..
32639 var colGroup = this._getColGroup( colSpan );
32640 // get the minimum Y value from the columns
32641 var minimumY = Math.min.apply( Math, colGroup );
32642 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32644 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32646 // position the brick
32648 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32649 y: this.currentSize.y + minimumY + this.padHeight
32653 // apply setHeight to necessary columns
32654 var setHeight = minimumY + sz.height + this.padHeight;
32655 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32657 var setSpan = this.cols + 1 - colGroup.length;
32658 for ( var i = 0; i < setSpan; i++ ) {
32659 this.colYs[ shortColIndex + i ] = setHeight ;
32666 * @param {Number} colSpan - number of columns the element spans
32667 * @returns {Array} colGroup
32669 _getColGroup : function( colSpan )
32671 if ( colSpan < 2 ) {
32672 // if brick spans only one column, use all the column Ys
32677 // how many different places could this brick fit horizontally
32678 var groupCount = this.cols + 1 - colSpan;
32679 // for each group potential horizontal position
32680 for ( var i = 0; i < groupCount; i++ ) {
32681 // make an array of colY values for that one group
32682 var groupColYs = this.colYs.slice( i, i + colSpan );
32683 // and get the max value of the array
32684 colGroup[i] = Math.max.apply( Math, groupColYs );
32689 _manageStamp : function( stamp )
32691 var stampSize = stamp.getSize();
32692 var offset = stamp.getBox();
32693 // get the columns that this stamp affects
32694 var firstX = this.isOriginLeft ? offset.x : offset.right;
32695 var lastX = firstX + stampSize.width;
32696 var firstCol = Math.floor( firstX / this.columnWidth );
32697 firstCol = Math.max( 0, firstCol );
32699 var lastCol = Math.floor( lastX / this.columnWidth );
32700 // lastCol should not go over if multiple of columnWidth #425
32701 lastCol -= lastX % this.columnWidth ? 0 : 1;
32702 lastCol = Math.min( this.cols - 1, lastCol );
32704 // set colYs to bottom of the stamp
32705 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32708 for ( var i = firstCol; i <= lastCol; i++ ) {
32709 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32714 _getContainerSize : function()
32716 this.maxY = Math.max.apply( Math, this.colYs );
32721 if ( this.isFitWidth ) {
32722 size.width = this._getContainerFitWidth();
32728 _getContainerFitWidth : function()
32730 var unusedCols = 0;
32731 // count unused columns
32734 if ( this.colYs[i] !== 0 ) {
32739 // fit container to columns that have been used
32740 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32743 needsResizeLayout : function()
32745 var previousWidth = this.containerWidth;
32746 this.getContainerWidth();
32747 return previousWidth !== this.containerWidth;
32762 * @class Roo.bootstrap.MasonryBrick
32763 * @extends Roo.bootstrap.Component
32764 * Bootstrap MasonryBrick class
32767 * Create a new MasonryBrick
32768 * @param {Object} config The config object
32771 Roo.bootstrap.MasonryBrick = function(config){
32773 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32775 Roo.bootstrap.MasonryBrick.register(this);
32781 * When a MasonryBrick is clcik
32782 * @param {Roo.bootstrap.MasonryBrick} this
32783 * @param {Roo.EventObject} e
32789 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32792 * @cfg {String} title
32796 * @cfg {String} html
32800 * @cfg {String} bgimage
32804 * @cfg {String} videourl
32808 * @cfg {String} cls
32812 * @cfg {String} href
32816 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32821 * @cfg {String} placetitle (center|bottom)
32826 * @cfg {Boolean} isFitContainer defalut true
32828 isFitContainer : true,
32831 * @cfg {Boolean} preventDefault defalut false
32833 preventDefault : false,
32836 * @cfg {Boolean} inverse defalut false
32838 maskInverse : false,
32840 getAutoCreate : function()
32842 if(!this.isFitContainer){
32843 return this.getSplitAutoCreate();
32846 var cls = 'masonry-brick masonry-brick-full';
32848 if(this.href.length){
32849 cls += ' masonry-brick-link';
32852 if(this.bgimage.length){
32853 cls += ' masonry-brick-image';
32856 if(this.maskInverse){
32857 cls += ' mask-inverse';
32860 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32861 cls += ' enable-mask';
32865 cls += ' masonry-' + this.size + '-brick';
32868 if(this.placetitle.length){
32870 switch (this.placetitle) {
32872 cls += ' masonry-center-title';
32875 cls += ' masonry-bottom-title';
32882 if(!this.html.length && !this.bgimage.length){
32883 cls += ' masonry-center-title';
32886 if(!this.html.length && this.bgimage.length){
32887 cls += ' masonry-bottom-title';
32892 cls += ' ' + this.cls;
32896 tag: (this.href.length) ? 'a' : 'div',
32901 cls: 'masonry-brick-mask'
32905 cls: 'masonry-brick-paragraph',
32911 if(this.href.length){
32912 cfg.href = this.href;
32915 var cn = cfg.cn[1].cn;
32917 if(this.title.length){
32920 cls: 'masonry-brick-title',
32925 if(this.html.length){
32928 cls: 'masonry-brick-text',
32933 if (!this.title.length && !this.html.length) {
32934 cfg.cn[1].cls += ' hide';
32937 if(this.bgimage.length){
32940 cls: 'masonry-brick-image-view',
32945 if(this.videourl.length){
32946 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32947 // youtube support only?
32950 cls: 'masonry-brick-image-view',
32953 allowfullscreen : true
32961 getSplitAutoCreate : function()
32963 var cls = 'masonry-brick masonry-brick-split';
32965 if(this.href.length){
32966 cls += ' masonry-brick-link';
32969 if(this.bgimage.length){
32970 cls += ' masonry-brick-image';
32974 cls += ' masonry-' + this.size + '-brick';
32977 switch (this.placetitle) {
32979 cls += ' masonry-center-title';
32982 cls += ' masonry-bottom-title';
32985 if(!this.bgimage.length){
32986 cls += ' masonry-center-title';
32989 if(this.bgimage.length){
32990 cls += ' masonry-bottom-title';
32996 cls += ' ' + this.cls;
33000 tag: (this.href.length) ? 'a' : 'div',
33005 cls: 'masonry-brick-split-head',
33009 cls: 'masonry-brick-paragraph',
33016 cls: 'masonry-brick-split-body',
33022 if(this.href.length){
33023 cfg.href = this.href;
33026 if(this.title.length){
33027 cfg.cn[0].cn[0].cn.push({
33029 cls: 'masonry-brick-title',
33034 if(this.html.length){
33035 cfg.cn[1].cn.push({
33037 cls: 'masonry-brick-text',
33042 if(this.bgimage.length){
33043 cfg.cn[0].cn.push({
33045 cls: 'masonry-brick-image-view',
33050 if(this.videourl.length){
33051 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33052 // youtube support only?
33053 cfg.cn[0].cn.cn.push({
33055 cls: 'masonry-brick-image-view',
33058 allowfullscreen : true
33065 initEvents: function()
33067 switch (this.size) {
33100 this.el.on('touchstart', this.onTouchStart, this);
33101 this.el.on('touchmove', this.onTouchMove, this);
33102 this.el.on('touchend', this.onTouchEnd, this);
33103 this.el.on('contextmenu', this.onContextMenu, this);
33105 this.el.on('mouseenter' ,this.enter, this);
33106 this.el.on('mouseleave', this.leave, this);
33107 this.el.on('click', this.onClick, this);
33110 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33111 this.parent().bricks.push(this);
33116 onClick: function(e, el)
33118 var time = this.endTimer - this.startTimer;
33119 // Roo.log(e.preventDefault());
33122 e.preventDefault();
33127 if(!this.preventDefault){
33131 e.preventDefault();
33133 if (this.activeClass != '') {
33134 this.selectBrick();
33137 this.fireEvent('click', this, e);
33140 enter: function(e, el)
33142 e.preventDefault();
33144 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33148 if(this.bgimage.length && this.html.length){
33149 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33153 leave: function(e, el)
33155 e.preventDefault();
33157 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33161 if(this.bgimage.length && this.html.length){
33162 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33166 onTouchStart: function(e, el)
33168 // e.preventDefault();
33170 this.touchmoved = false;
33172 if(!this.isFitContainer){
33176 if(!this.bgimage.length || !this.html.length){
33180 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33182 this.timer = new Date().getTime();
33186 onTouchMove: function(e, el)
33188 this.touchmoved = true;
33191 onContextMenu : function(e,el)
33193 e.preventDefault();
33194 e.stopPropagation();
33198 onTouchEnd: function(e, el)
33200 // e.preventDefault();
33202 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33209 if(!this.bgimage.length || !this.html.length){
33211 if(this.href.length){
33212 window.location.href = this.href;
33218 if(!this.isFitContainer){
33222 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33224 window.location.href = this.href;
33227 //selection on single brick only
33228 selectBrick : function() {
33230 if (!this.parentId) {
33234 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33235 var index = m.selectedBrick.indexOf(this.id);
33238 m.selectedBrick.splice(index,1);
33239 this.el.removeClass(this.activeClass);
33243 for(var i = 0; i < m.selectedBrick.length; i++) {
33244 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33245 b.el.removeClass(b.activeClass);
33248 m.selectedBrick = [];
33250 m.selectedBrick.push(this.id);
33251 this.el.addClass(this.activeClass);
33255 isSelected : function(){
33256 return this.el.hasClass(this.activeClass);
33261 Roo.apply(Roo.bootstrap.MasonryBrick, {
33264 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33266 * register a Masonry Brick
33267 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33270 register : function(brick)
33272 //this.groups[brick.id] = brick;
33273 this.groups.add(brick.id, brick);
33276 * fetch a masonry brick based on the masonry brick ID
33277 * @param {string} the masonry brick to add
33278 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33281 get: function(brick_id)
33283 // if (typeof(this.groups[brick_id]) == 'undefined') {
33286 // return this.groups[brick_id] ;
33288 if(this.groups.key(brick_id)) {
33289 return this.groups.key(brick_id);
33307 * @class Roo.bootstrap.Brick
33308 * @extends Roo.bootstrap.Component
33309 * Bootstrap Brick class
33312 * Create a new Brick
33313 * @param {Object} config The config object
33316 Roo.bootstrap.Brick = function(config){
33317 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33323 * When a Brick is click
33324 * @param {Roo.bootstrap.Brick} this
33325 * @param {Roo.EventObject} e
33331 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33334 * @cfg {String} title
33338 * @cfg {String} html
33342 * @cfg {String} bgimage
33346 * @cfg {String} cls
33350 * @cfg {String} href
33354 * @cfg {String} video
33358 * @cfg {Boolean} square
33362 getAutoCreate : function()
33364 var cls = 'roo-brick';
33366 if(this.href.length){
33367 cls += ' roo-brick-link';
33370 if(this.bgimage.length){
33371 cls += ' roo-brick-image';
33374 if(!this.html.length && !this.bgimage.length){
33375 cls += ' roo-brick-center-title';
33378 if(!this.html.length && this.bgimage.length){
33379 cls += ' roo-brick-bottom-title';
33383 cls += ' ' + this.cls;
33387 tag: (this.href.length) ? 'a' : 'div',
33392 cls: 'roo-brick-paragraph',
33398 if(this.href.length){
33399 cfg.href = this.href;
33402 var cn = cfg.cn[0].cn;
33404 if(this.title.length){
33407 cls: 'roo-brick-title',
33412 if(this.html.length){
33415 cls: 'roo-brick-text',
33422 if(this.bgimage.length){
33425 cls: 'roo-brick-image-view',
33433 initEvents: function()
33435 if(this.title.length || this.html.length){
33436 this.el.on('mouseenter' ,this.enter, this);
33437 this.el.on('mouseleave', this.leave, this);
33440 Roo.EventManager.onWindowResize(this.resize, this);
33442 if(this.bgimage.length){
33443 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33444 this.imageEl.on('load', this.onImageLoad, this);
33451 onImageLoad : function()
33456 resize : function()
33458 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33460 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33462 if(this.bgimage.length){
33463 var image = this.el.select('.roo-brick-image-view', true).first();
33465 image.setWidth(paragraph.getWidth());
33468 image.setHeight(paragraph.getWidth());
33471 this.el.setHeight(image.getHeight());
33472 paragraph.setHeight(image.getHeight());
33478 enter: function(e, el)
33480 e.preventDefault();
33482 if(this.bgimage.length){
33483 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33484 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33488 leave: function(e, el)
33490 e.preventDefault();
33492 if(this.bgimage.length){
33493 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33494 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33509 * @class Roo.bootstrap.NumberField
33510 * @extends Roo.bootstrap.Input
33511 * Bootstrap NumberField class
33517 * Create a new NumberField
33518 * @param {Object} config The config object
33521 Roo.bootstrap.NumberField = function(config){
33522 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33525 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33528 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33530 allowDecimals : true,
33532 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33534 decimalSeparator : ".",
33536 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33538 decimalPrecision : 2,
33540 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33542 allowNegative : true,
33545 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33549 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33551 minValue : Number.NEGATIVE_INFINITY,
33553 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33555 maxValue : Number.MAX_VALUE,
33557 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33559 minText : "The minimum value for this field is {0}",
33561 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33563 maxText : "The maximum value for this field is {0}",
33565 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33566 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33568 nanText : "{0} is not a valid number",
33570 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33572 thousandsDelimiter : false,
33574 * @cfg {String} valueAlign alignment of value
33576 valueAlign : "left",
33578 getAutoCreate : function()
33580 var hiddenInput = {
33584 cls: 'hidden-number-input'
33588 hiddenInput.name = this.name;
33593 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33595 this.name = hiddenInput.name;
33597 if(cfg.cn.length > 0) {
33598 cfg.cn.push(hiddenInput);
33605 initEvents : function()
33607 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33609 var allowed = "0123456789";
33611 if(this.allowDecimals){
33612 allowed += this.decimalSeparator;
33615 if(this.allowNegative){
33619 if(this.thousandsDelimiter) {
33623 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33625 var keyPress = function(e){
33627 var k = e.getKey();
33629 var c = e.getCharCode();
33632 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33633 allowed.indexOf(String.fromCharCode(c)) === -1
33639 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33643 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33648 this.el.on("keypress", keyPress, this);
33651 validateValue : function(value)
33654 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33658 var num = this.parseValue(value);
33661 this.markInvalid(String.format(this.nanText, value));
33665 if(num < this.minValue){
33666 this.markInvalid(String.format(this.minText, this.minValue));
33670 if(num > this.maxValue){
33671 this.markInvalid(String.format(this.maxText, this.maxValue));
33678 getValue : function()
33680 var v = this.hiddenEl().getValue();
33682 return this.fixPrecision(this.parseValue(v));
33685 parseValue : function(value)
33687 if(this.thousandsDelimiter) {
33689 r = new RegExp(",", "g");
33690 value = value.replace(r, "");
33693 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33694 return isNaN(value) ? '' : value;
33697 fixPrecision : function(value)
33699 if(this.thousandsDelimiter) {
33701 r = new RegExp(",", "g");
33702 value = value.replace(r, "");
33705 var nan = isNaN(value);
33707 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33708 return nan ? '' : value;
33710 return parseFloat(value).toFixed(this.decimalPrecision);
33713 setValue : function(v)
33715 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33721 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33723 this.inputEl().dom.value = (v == '') ? '' :
33724 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33726 if(!this.allowZero && v === '0') {
33727 this.hiddenEl().dom.value = '';
33728 this.inputEl().dom.value = '';
33735 decimalPrecisionFcn : function(v)
33737 return Math.floor(v);
33740 beforeBlur : function()
33742 var v = this.parseValue(this.getRawValue());
33744 if(v || v === 0 || v === ''){
33749 hiddenEl : function()
33751 return this.el.select('input.hidden-number-input',true).first();
33763 * @class Roo.bootstrap.DocumentSlider
33764 * @extends Roo.bootstrap.Component
33765 * Bootstrap DocumentSlider class
33768 * Create a new DocumentViewer
33769 * @param {Object} config The config object
33772 Roo.bootstrap.DocumentSlider = function(config){
33773 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33780 * Fire after initEvent
33781 * @param {Roo.bootstrap.DocumentSlider} this
33786 * Fire after update
33787 * @param {Roo.bootstrap.DocumentSlider} this
33793 * @param {Roo.bootstrap.DocumentSlider} this
33799 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33805 getAutoCreate : function()
33809 cls : 'roo-document-slider',
33813 cls : 'roo-document-slider-header',
33817 cls : 'roo-document-slider-header-title'
33823 cls : 'roo-document-slider-body',
33827 cls : 'roo-document-slider-prev',
33831 cls : 'fa fa-chevron-left'
33837 cls : 'roo-document-slider-thumb',
33841 cls : 'roo-document-slider-image'
33847 cls : 'roo-document-slider-next',
33851 cls : 'fa fa-chevron-right'
33863 initEvents : function()
33865 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33866 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33868 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33869 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33871 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33872 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33874 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33875 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33877 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33878 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33880 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33881 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33883 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33884 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33886 this.thumbEl.on('click', this.onClick, this);
33888 this.prevIndicator.on('click', this.prev, this);
33890 this.nextIndicator.on('click', this.next, this);
33894 initial : function()
33896 if(this.files.length){
33897 this.indicator = 1;
33901 this.fireEvent('initial', this);
33904 update : function()
33906 this.imageEl.attr('src', this.files[this.indicator - 1]);
33908 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33910 this.prevIndicator.show();
33912 if(this.indicator == 1){
33913 this.prevIndicator.hide();
33916 this.nextIndicator.show();
33918 if(this.indicator == this.files.length){
33919 this.nextIndicator.hide();
33922 this.thumbEl.scrollTo('top');
33924 this.fireEvent('update', this);
33927 onClick : function(e)
33929 e.preventDefault();
33931 this.fireEvent('click', this);
33936 e.preventDefault();
33938 this.indicator = Math.max(1, this.indicator - 1);
33945 e.preventDefault();
33947 this.indicator = Math.min(this.files.length, this.indicator + 1);
33961 * @class Roo.bootstrap.RadioSet
33962 * @extends Roo.bootstrap.Input
33963 * Bootstrap RadioSet class
33964 * @cfg {String} indicatorpos (left|right) default left
33965 * @cfg {Boolean} inline (true|false) inline the element (default true)
33966 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33968 * Create a new RadioSet
33969 * @param {Object} config The config object
33972 Roo.bootstrap.RadioSet = function(config){
33974 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33978 Roo.bootstrap.RadioSet.register(this);
33983 * Fires when the element is checked or unchecked.
33984 * @param {Roo.bootstrap.RadioSet} this This radio
33985 * @param {Roo.bootstrap.Radio} item The checked item
33990 * Fires when the element is click.
33991 * @param {Roo.bootstrap.RadioSet} this This radio set
33992 * @param {Roo.bootstrap.Radio} item The checked item
33993 * @param {Roo.EventObject} e The event object
34000 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34008 indicatorpos : 'left',
34010 getAutoCreate : function()
34014 cls : 'roo-radio-set-label',
34018 html : this.fieldLabel
34023 if(this.indicatorpos == 'left'){
34026 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34027 tooltip : 'This field is required'
34032 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34033 tooltip : 'This field is required'
34039 cls : 'roo-radio-set-items'
34042 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34044 if (align === 'left' && this.fieldLabel.length) {
34047 cls : "roo-radio-set-right",
34053 if(this.labelWidth > 12){
34054 label.style = "width: " + this.labelWidth + 'px';
34057 if(this.labelWidth < 13 && this.labelmd == 0){
34058 this.labelmd = this.labelWidth;
34061 if(this.labellg > 0){
34062 label.cls += ' col-lg-' + this.labellg;
34063 items.cls += ' col-lg-' + (12 - this.labellg);
34066 if(this.labelmd > 0){
34067 label.cls += ' col-md-' + this.labelmd;
34068 items.cls += ' col-md-' + (12 - this.labelmd);
34071 if(this.labelsm > 0){
34072 label.cls += ' col-sm-' + this.labelsm;
34073 items.cls += ' col-sm-' + (12 - this.labelsm);
34076 if(this.labelxs > 0){
34077 label.cls += ' col-xs-' + this.labelxs;
34078 items.cls += ' col-xs-' + (12 - this.labelxs);
34084 cls : 'roo-radio-set',
34088 cls : 'roo-radio-set-input',
34091 value : this.value ? this.value : ''
34098 if(this.weight.length){
34099 cfg.cls += ' roo-radio-' + this.weight;
34103 cfg.cls += ' roo-radio-set-inline';
34107 ['xs','sm','md','lg'].map(function(size){
34108 if (settings[size]) {
34109 cfg.cls += ' col-' + size + '-' + settings[size];
34117 initEvents : function()
34119 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34120 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34122 if(!this.fieldLabel.length){
34123 this.labelEl.hide();
34126 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34127 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34129 this.indicator = this.indicatorEl();
34131 if(this.indicator){
34132 this.indicator.addClass('invisible');
34135 this.originalValue = this.getValue();
34139 inputEl: function ()
34141 return this.el.select('.roo-radio-set-input', true).first();
34144 getChildContainer : function()
34146 return this.itemsEl;
34149 register : function(item)
34151 this.radioes.push(item);
34155 validate : function()
34157 if(this.getVisibilityEl().hasClass('hidden')){
34163 Roo.each(this.radioes, function(i){
34172 if(this.allowBlank) {
34176 if(this.disabled || valid){
34181 this.markInvalid();
34186 markValid : function()
34188 if(this.labelEl.isVisible(true)){
34189 this.indicatorEl().removeClass('visible');
34190 this.indicatorEl().addClass('invisible');
34193 this.el.removeClass([this.invalidClass, this.validClass]);
34194 this.el.addClass(this.validClass);
34196 this.fireEvent('valid', this);
34199 markInvalid : function(msg)
34201 if(this.allowBlank || this.disabled){
34205 if(this.labelEl.isVisible(true)){
34206 this.indicatorEl().removeClass('invisible');
34207 this.indicatorEl().addClass('visible');
34210 this.el.removeClass([this.invalidClass, this.validClass]);
34211 this.el.addClass(this.invalidClass);
34213 this.fireEvent('invalid', this, msg);
34217 setValue : function(v, suppressEvent)
34219 if(this.value === v){
34226 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34229 Roo.each(this.radioes, function(i){
34231 i.el.removeClass('checked');
34234 Roo.each(this.radioes, function(i){
34236 if(i.value === v || i.value.toString() === v.toString()){
34238 i.el.addClass('checked');
34240 if(suppressEvent !== true){
34241 this.fireEvent('check', this, i);
34252 clearInvalid : function(){
34254 if(!this.el || this.preventMark){
34258 this.el.removeClass([this.invalidClass]);
34260 this.fireEvent('valid', this);
34265 Roo.apply(Roo.bootstrap.RadioSet, {
34269 register : function(set)
34271 this.groups[set.name] = set;
34274 get: function(name)
34276 if (typeof(this.groups[name]) == 'undefined') {
34280 return this.groups[name] ;
34286 * Ext JS Library 1.1.1
34287 * Copyright(c) 2006-2007, Ext JS, LLC.
34289 * Originally Released Under LGPL - original licence link has changed is not relivant.
34292 * <script type="text/javascript">
34297 * @class Roo.bootstrap.SplitBar
34298 * @extends Roo.util.Observable
34299 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34303 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34304 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34305 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34306 split.minSize = 100;
34307 split.maxSize = 600;
34308 split.animate = true;
34309 split.on('moved', splitterMoved);
34312 * Create a new SplitBar
34313 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34314 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34315 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34316 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34317 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34318 position of the SplitBar).
34320 Roo.bootstrap.SplitBar = function(cfg){
34325 // dragElement : elm
34326 // resizingElement: el,
34328 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34329 // placement : Roo.bootstrap.SplitBar.LEFT ,
34330 // existingProxy ???
34333 this.el = Roo.get(cfg.dragElement, true);
34334 this.el.dom.unselectable = "on";
34336 this.resizingEl = Roo.get(cfg.resizingElement, true);
34340 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34341 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34344 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34347 * The minimum size of the resizing element. (Defaults to 0)
34353 * The maximum size of the resizing element. (Defaults to 2000)
34356 this.maxSize = 2000;
34359 * Whether to animate the transition to the new size
34362 this.animate = false;
34365 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34368 this.useShim = false;
34373 if(!cfg.existingProxy){
34375 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34377 this.proxy = Roo.get(cfg.existingProxy).dom;
34380 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34383 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34386 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34389 this.dragSpecs = {};
34392 * @private The adapter to use to positon and resize elements
34394 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34395 this.adapter.init(this);
34397 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34399 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34400 this.el.addClass("roo-splitbar-h");
34403 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34404 this.el.addClass("roo-splitbar-v");
34410 * Fires when the splitter is moved (alias for {@link #event-moved})
34411 * @param {Roo.bootstrap.SplitBar} this
34412 * @param {Number} newSize the new width or height
34417 * Fires when the splitter is moved
34418 * @param {Roo.bootstrap.SplitBar} this
34419 * @param {Number} newSize the new width or height
34423 * @event beforeresize
34424 * Fires before the splitter is dragged
34425 * @param {Roo.bootstrap.SplitBar} this
34427 "beforeresize" : true,
34429 "beforeapply" : true
34432 Roo.util.Observable.call(this);
34435 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34436 onStartProxyDrag : function(x, y){
34437 this.fireEvent("beforeresize", this);
34439 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34441 o.enableDisplayMode("block");
34442 // all splitbars share the same overlay
34443 Roo.bootstrap.SplitBar.prototype.overlay = o;
34445 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34446 this.overlay.show();
34447 Roo.get(this.proxy).setDisplayed("block");
34448 var size = this.adapter.getElementSize(this);
34449 this.activeMinSize = this.getMinimumSize();;
34450 this.activeMaxSize = this.getMaximumSize();;
34451 var c1 = size - this.activeMinSize;
34452 var c2 = Math.max(this.activeMaxSize - size, 0);
34453 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34454 this.dd.resetConstraints();
34455 this.dd.setXConstraint(
34456 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34457 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34459 this.dd.setYConstraint(0, 0);
34461 this.dd.resetConstraints();
34462 this.dd.setXConstraint(0, 0);
34463 this.dd.setYConstraint(
34464 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34465 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34468 this.dragSpecs.startSize = size;
34469 this.dragSpecs.startPoint = [x, y];
34470 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34474 * @private Called after the drag operation by the DDProxy
34476 onEndProxyDrag : function(e){
34477 Roo.get(this.proxy).setDisplayed(false);
34478 var endPoint = Roo.lib.Event.getXY(e);
34480 this.overlay.hide();
34483 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34484 newSize = this.dragSpecs.startSize +
34485 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34486 endPoint[0] - this.dragSpecs.startPoint[0] :
34487 this.dragSpecs.startPoint[0] - endPoint[0]
34490 newSize = this.dragSpecs.startSize +
34491 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34492 endPoint[1] - this.dragSpecs.startPoint[1] :
34493 this.dragSpecs.startPoint[1] - endPoint[1]
34496 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34497 if(newSize != this.dragSpecs.startSize){
34498 if(this.fireEvent('beforeapply', this, newSize) !== false){
34499 this.adapter.setElementSize(this, newSize);
34500 this.fireEvent("moved", this, newSize);
34501 this.fireEvent("resize", this, newSize);
34507 * Get the adapter this SplitBar uses
34508 * @return The adapter object
34510 getAdapter : function(){
34511 return this.adapter;
34515 * Set the adapter this SplitBar uses
34516 * @param {Object} adapter A SplitBar adapter object
34518 setAdapter : function(adapter){
34519 this.adapter = adapter;
34520 this.adapter.init(this);
34524 * Gets the minimum size for the resizing element
34525 * @return {Number} The minimum size
34527 getMinimumSize : function(){
34528 return this.minSize;
34532 * Sets the minimum size for the resizing element
34533 * @param {Number} minSize The minimum size
34535 setMinimumSize : function(minSize){
34536 this.minSize = minSize;
34540 * Gets the maximum size for the resizing element
34541 * @return {Number} The maximum size
34543 getMaximumSize : function(){
34544 return this.maxSize;
34548 * Sets the maximum size for the resizing element
34549 * @param {Number} maxSize The maximum size
34551 setMaximumSize : function(maxSize){
34552 this.maxSize = maxSize;
34556 * Sets the initialize size for the resizing element
34557 * @param {Number} size The initial size
34559 setCurrentSize : function(size){
34560 var oldAnimate = this.animate;
34561 this.animate = false;
34562 this.adapter.setElementSize(this, size);
34563 this.animate = oldAnimate;
34567 * Destroy this splitbar.
34568 * @param {Boolean} removeEl True to remove the element
34570 destroy : function(removeEl){
34572 this.shim.remove();
34575 this.proxy.parentNode.removeChild(this.proxy);
34583 * @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.
34585 Roo.bootstrap.SplitBar.createProxy = function(dir){
34586 var proxy = new Roo.Element(document.createElement("div"));
34587 proxy.unselectable();
34588 var cls = 'roo-splitbar-proxy';
34589 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34590 document.body.appendChild(proxy.dom);
34595 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34596 * Default Adapter. It assumes the splitter and resizing element are not positioned
34597 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34599 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34602 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34603 // do nothing for now
34604 init : function(s){
34608 * Called before drag operations to get the current size of the resizing element.
34609 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34611 getElementSize : function(s){
34612 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34613 return s.resizingEl.getWidth();
34615 return s.resizingEl.getHeight();
34620 * Called after drag operations to set the size of the resizing element.
34621 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34622 * @param {Number} newSize The new size to set
34623 * @param {Function} onComplete A function to be invoked when resizing is complete
34625 setElementSize : function(s, newSize, onComplete){
34626 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34628 s.resizingEl.setWidth(newSize);
34630 onComplete(s, newSize);
34633 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34638 s.resizingEl.setHeight(newSize);
34640 onComplete(s, newSize);
34643 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34650 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34651 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34652 * Adapter that moves the splitter element to align with the resized sizing element.
34653 * Used with an absolute positioned SplitBar.
34654 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34655 * document.body, make sure you assign an id to the body element.
34657 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34658 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34659 this.container = Roo.get(container);
34662 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34663 init : function(s){
34664 this.basic.init(s);
34667 getElementSize : function(s){
34668 return this.basic.getElementSize(s);
34671 setElementSize : function(s, newSize, onComplete){
34672 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34675 moveSplitter : function(s){
34676 var yes = Roo.bootstrap.SplitBar;
34677 switch(s.placement){
34679 s.el.setX(s.resizingEl.getRight());
34682 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34685 s.el.setY(s.resizingEl.getBottom());
34688 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34695 * Orientation constant - Create a vertical SplitBar
34699 Roo.bootstrap.SplitBar.VERTICAL = 1;
34702 * Orientation constant - Create a horizontal SplitBar
34706 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34709 * Placement constant - The resizing element is to the left of the splitter element
34713 Roo.bootstrap.SplitBar.LEFT = 1;
34716 * Placement constant - The resizing element is to the right of the splitter element
34720 Roo.bootstrap.SplitBar.RIGHT = 2;
34723 * Placement constant - The resizing element is positioned above the splitter element
34727 Roo.bootstrap.SplitBar.TOP = 3;
34730 * Placement constant - The resizing element is positioned under splitter element
34734 Roo.bootstrap.SplitBar.BOTTOM = 4;
34735 Roo.namespace("Roo.bootstrap.layout");/*
34737 * Ext JS Library 1.1.1
34738 * Copyright(c) 2006-2007, Ext JS, LLC.
34740 * Originally Released Under LGPL - original licence link has changed is not relivant.
34743 * <script type="text/javascript">
34747 * @class Roo.bootstrap.layout.Manager
34748 * @extends Roo.bootstrap.Component
34749 * Base class for layout managers.
34751 Roo.bootstrap.layout.Manager = function(config)
34753 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34759 /** false to disable window resize monitoring @type Boolean */
34760 this.monitorWindowResize = true;
34765 * Fires when a layout is performed.
34766 * @param {Roo.LayoutManager} this
34770 * @event regionresized
34771 * Fires when the user resizes a region.
34772 * @param {Roo.LayoutRegion} region The resized region
34773 * @param {Number} newSize The new size (width for east/west, height for north/south)
34775 "regionresized" : true,
34777 * @event regioncollapsed
34778 * Fires when a region is collapsed.
34779 * @param {Roo.LayoutRegion} region The collapsed region
34781 "regioncollapsed" : true,
34783 * @event regionexpanded
34784 * Fires when a region is expanded.
34785 * @param {Roo.LayoutRegion} region The expanded region
34787 "regionexpanded" : true
34789 this.updating = false;
34792 this.el = Roo.get(config.el);
34798 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34803 monitorWindowResize : true,
34809 onRender : function(ct, position)
34812 this.el = Roo.get(ct);
34815 //this.fireEvent('render',this);
34819 initEvents: function()
34823 // ie scrollbar fix
34824 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34825 document.body.scroll = "no";
34826 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34827 this.el.position('relative');
34829 this.id = this.el.id;
34830 this.el.addClass("roo-layout-container");
34831 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34832 if(this.el.dom != document.body ) {
34833 this.el.on('resize', this.layout,this);
34834 this.el.on('show', this.layout,this);
34840 * Returns true if this layout is currently being updated
34841 * @return {Boolean}
34843 isUpdating : function(){
34844 return this.updating;
34848 * Suspend the LayoutManager from doing auto-layouts while
34849 * making multiple add or remove calls
34851 beginUpdate : function(){
34852 this.updating = true;
34856 * Restore auto-layouts and optionally disable the manager from performing a layout
34857 * @param {Boolean} noLayout true to disable a layout update
34859 endUpdate : function(noLayout){
34860 this.updating = false;
34866 layout: function(){
34870 onRegionResized : function(region, newSize){
34871 this.fireEvent("regionresized", region, newSize);
34875 onRegionCollapsed : function(region){
34876 this.fireEvent("regioncollapsed", region);
34879 onRegionExpanded : function(region){
34880 this.fireEvent("regionexpanded", region);
34884 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34885 * performs box-model adjustments.
34886 * @return {Object} The size as an object {width: (the width), height: (the height)}
34888 getViewSize : function()
34891 if(this.el.dom != document.body){
34892 size = this.el.getSize();
34894 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34896 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34897 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34902 * Returns the Element this layout is bound to.
34903 * @return {Roo.Element}
34905 getEl : function(){
34910 * Returns the specified region.
34911 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34912 * @return {Roo.LayoutRegion}
34914 getRegion : function(target){
34915 return this.regions[target.toLowerCase()];
34918 onWindowResize : function(){
34919 if(this.monitorWindowResize){
34926 * Ext JS Library 1.1.1
34927 * Copyright(c) 2006-2007, Ext JS, LLC.
34929 * Originally Released Under LGPL - original licence link has changed is not relivant.
34932 * <script type="text/javascript">
34935 * @class Roo.bootstrap.layout.Border
34936 * @extends Roo.bootstrap.layout.Manager
34937 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34938 * please see: examples/bootstrap/nested.html<br><br>
34940 <b>The container the layout is rendered into can be either the body element or any other element.
34941 If it is not the body element, the container needs to either be an absolute positioned element,
34942 or you will need to add "position:relative" to the css of the container. You will also need to specify
34943 the container size if it is not the body element.</b>
34946 * Create a new Border
34947 * @param {Object} config Configuration options
34949 Roo.bootstrap.layout.Border = function(config){
34950 config = config || {};
34951 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34955 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34956 if(config[region]){
34957 config[region].region = region;
34958 this.addRegion(config[region]);
34964 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34966 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34968 * Creates and adds a new region if it doesn't already exist.
34969 * @param {String} target The target region key (north, south, east, west or center).
34970 * @param {Object} config The regions config object
34971 * @return {BorderLayoutRegion} The new region
34973 addRegion : function(config)
34975 if(!this.regions[config.region]){
34976 var r = this.factory(config);
34977 this.bindRegion(r);
34979 return this.regions[config.region];
34983 bindRegion : function(r){
34984 this.regions[r.config.region] = r;
34986 r.on("visibilitychange", this.layout, this);
34987 r.on("paneladded", this.layout, this);
34988 r.on("panelremoved", this.layout, this);
34989 r.on("invalidated", this.layout, this);
34990 r.on("resized", this.onRegionResized, this);
34991 r.on("collapsed", this.onRegionCollapsed, this);
34992 r.on("expanded", this.onRegionExpanded, this);
34996 * Performs a layout update.
34998 layout : function()
35000 if(this.updating) {
35004 // render all the rebions if they have not been done alreayd?
35005 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35006 if(this.regions[region] && !this.regions[region].bodyEl){
35007 this.regions[region].onRender(this.el)
35011 var size = this.getViewSize();
35012 var w = size.width;
35013 var h = size.height;
35018 //var x = 0, y = 0;
35020 var rs = this.regions;
35021 var north = rs["north"];
35022 var south = rs["south"];
35023 var west = rs["west"];
35024 var east = rs["east"];
35025 var center = rs["center"];
35026 //if(this.hideOnLayout){ // not supported anymore
35027 //c.el.setStyle("display", "none");
35029 if(north && north.isVisible()){
35030 var b = north.getBox();
35031 var m = north.getMargins();
35032 b.width = w - (m.left+m.right);
35035 centerY = b.height + b.y + m.bottom;
35036 centerH -= centerY;
35037 north.updateBox(this.safeBox(b));
35039 if(south && south.isVisible()){
35040 var b = south.getBox();
35041 var m = south.getMargins();
35042 b.width = w - (m.left+m.right);
35044 var totalHeight = (b.height + m.top + m.bottom);
35045 b.y = h - totalHeight + m.top;
35046 centerH -= totalHeight;
35047 south.updateBox(this.safeBox(b));
35049 if(west && west.isVisible()){
35050 var b = west.getBox();
35051 var m = west.getMargins();
35052 b.height = centerH - (m.top+m.bottom);
35054 b.y = centerY + m.top;
35055 var totalWidth = (b.width + m.left + m.right);
35056 centerX += totalWidth;
35057 centerW -= totalWidth;
35058 west.updateBox(this.safeBox(b));
35060 if(east && east.isVisible()){
35061 var b = east.getBox();
35062 var m = east.getMargins();
35063 b.height = centerH - (m.top+m.bottom);
35064 var totalWidth = (b.width + m.left + m.right);
35065 b.x = w - totalWidth + m.left;
35066 b.y = centerY + m.top;
35067 centerW -= totalWidth;
35068 east.updateBox(this.safeBox(b));
35071 var m = center.getMargins();
35073 x: centerX + m.left,
35074 y: centerY + m.top,
35075 width: centerW - (m.left+m.right),
35076 height: centerH - (m.top+m.bottom)
35078 //if(this.hideOnLayout){
35079 //center.el.setStyle("display", "block");
35081 center.updateBox(this.safeBox(centerBox));
35084 this.fireEvent("layout", this);
35088 safeBox : function(box){
35089 box.width = Math.max(0, box.width);
35090 box.height = Math.max(0, box.height);
35095 * Adds a ContentPanel (or subclass) to this layout.
35096 * @param {String} target The target region key (north, south, east, west or center).
35097 * @param {Roo.ContentPanel} panel The panel to add
35098 * @return {Roo.ContentPanel} The added panel
35100 add : function(target, panel){
35102 target = target.toLowerCase();
35103 return this.regions[target].add(panel);
35107 * Remove a ContentPanel (or subclass) to this layout.
35108 * @param {String} target The target region key (north, south, east, west or center).
35109 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35110 * @return {Roo.ContentPanel} The removed panel
35112 remove : function(target, panel){
35113 target = target.toLowerCase();
35114 return this.regions[target].remove(panel);
35118 * Searches all regions for a panel with the specified id
35119 * @param {String} panelId
35120 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35122 findPanel : function(panelId){
35123 var rs = this.regions;
35124 for(var target in rs){
35125 if(typeof rs[target] != "function"){
35126 var p = rs[target].getPanel(panelId);
35136 * Searches all regions for a panel with the specified id and activates (shows) it.
35137 * @param {String/ContentPanel} panelId The panels id or the panel itself
35138 * @return {Roo.ContentPanel} The shown panel or null
35140 showPanel : function(panelId) {
35141 var rs = this.regions;
35142 for(var target in rs){
35143 var r = rs[target];
35144 if(typeof r != "function"){
35145 if(r.hasPanel(panelId)){
35146 return r.showPanel(panelId);
35154 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35155 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35158 restoreState : function(provider){
35160 provider = Roo.state.Manager;
35162 var sm = new Roo.LayoutStateManager();
35163 sm.init(this, provider);
35169 * Adds a xtype elements to the layout.
35173 xtype : 'ContentPanel',
35180 xtype : 'NestedLayoutPanel',
35186 items : [ ... list of content panels or nested layout panels.. ]
35190 * @param {Object} cfg Xtype definition of item to add.
35192 addxtype : function(cfg)
35194 // basically accepts a pannel...
35195 // can accept a layout region..!?!?
35196 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35199 // theory? children can only be panels??
35201 //if (!cfg.xtype.match(/Panel$/)) {
35206 if (typeof(cfg.region) == 'undefined') {
35207 Roo.log("Failed to add Panel, region was not set");
35211 var region = cfg.region;
35217 xitems = cfg.items;
35224 case 'Content': // ContentPanel (el, cfg)
35225 case 'Scroll': // ContentPanel (el, cfg)
35227 cfg.autoCreate = true;
35228 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35230 // var el = this.el.createChild();
35231 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35234 this.add(region, ret);
35238 case 'TreePanel': // our new panel!
35239 cfg.el = this.el.createChild();
35240 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35241 this.add(region, ret);
35246 // create a new Layout (which is a Border Layout...
35248 var clayout = cfg.layout;
35249 clayout.el = this.el.createChild();
35250 clayout.items = clayout.items || [];
35254 // replace this exitems with the clayout ones..
35255 xitems = clayout.items;
35257 // force background off if it's in center...
35258 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35259 cfg.background = false;
35261 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35264 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35265 //console.log('adding nested layout panel ' + cfg.toSource());
35266 this.add(region, ret);
35267 nb = {}; /// find first...
35272 // needs grid and region
35274 //var el = this.getRegion(region).el.createChild();
35276 *var el = this.el.createChild();
35277 // create the grid first...
35278 cfg.grid.container = el;
35279 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35282 if (region == 'center' && this.active ) {
35283 cfg.background = false;
35286 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35288 this.add(region, ret);
35290 if (cfg.background) {
35291 // render grid on panel activation (if panel background)
35292 ret.on('activate', function(gp) {
35293 if (!gp.grid.rendered) {
35294 // gp.grid.render(el);
35298 // cfg.grid.render(el);
35304 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35305 // it was the old xcomponent building that caused this before.
35306 // espeically if border is the top element in the tree.
35316 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35318 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35319 this.add(region, ret);
35323 throw "Can not add '" + cfg.xtype + "' to Border";
35329 this.beginUpdate();
35333 Roo.each(xitems, function(i) {
35334 region = nb && i.region ? i.region : false;
35336 var add = ret.addxtype(i);
35339 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35340 if (!i.background) {
35341 abn[region] = nb[region] ;
35348 // make the last non-background panel active..
35349 //if (nb) { Roo.log(abn); }
35352 for(var r in abn) {
35353 region = this.getRegion(r);
35355 // tried using nb[r], but it does not work..
35357 region.showPanel(abn[r]);
35368 factory : function(cfg)
35371 var validRegions = Roo.bootstrap.layout.Border.regions;
35373 var target = cfg.region;
35376 var r = Roo.bootstrap.layout;
35380 return new r.North(cfg);
35382 return new r.South(cfg);
35384 return new r.East(cfg);
35386 return new r.West(cfg);
35388 return new r.Center(cfg);
35390 throw 'Layout region "'+target+'" not supported.';
35397 * Ext JS Library 1.1.1
35398 * Copyright(c) 2006-2007, Ext JS, LLC.
35400 * Originally Released Under LGPL - original licence link has changed is not relivant.
35403 * <script type="text/javascript">
35407 * @class Roo.bootstrap.layout.Basic
35408 * @extends Roo.util.Observable
35409 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35410 * and does not have a titlebar, tabs or any other features. All it does is size and position
35411 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35412 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35413 * @cfg {string} region the region that it inhabits..
35414 * @cfg {bool} skipConfig skip config?
35418 Roo.bootstrap.layout.Basic = function(config){
35420 this.mgr = config.mgr;
35422 this.position = config.region;
35424 var skipConfig = config.skipConfig;
35428 * @scope Roo.BasicLayoutRegion
35432 * @event beforeremove
35433 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35434 * @param {Roo.LayoutRegion} this
35435 * @param {Roo.ContentPanel} panel The panel
35436 * @param {Object} e The cancel event object
35438 "beforeremove" : true,
35440 * @event invalidated
35441 * Fires when the layout for this region is changed.
35442 * @param {Roo.LayoutRegion} this
35444 "invalidated" : true,
35446 * @event visibilitychange
35447 * Fires when this region is shown or hidden
35448 * @param {Roo.LayoutRegion} this
35449 * @param {Boolean} visibility true or false
35451 "visibilitychange" : true,
35453 * @event paneladded
35454 * Fires when a panel is added.
35455 * @param {Roo.LayoutRegion} this
35456 * @param {Roo.ContentPanel} panel The panel
35458 "paneladded" : true,
35460 * @event panelremoved
35461 * Fires when a panel is removed.
35462 * @param {Roo.LayoutRegion} this
35463 * @param {Roo.ContentPanel} panel The panel
35465 "panelremoved" : true,
35467 * @event beforecollapse
35468 * Fires when this region before collapse.
35469 * @param {Roo.LayoutRegion} this
35471 "beforecollapse" : true,
35474 * Fires when this region is collapsed.
35475 * @param {Roo.LayoutRegion} this
35477 "collapsed" : true,
35480 * Fires when this region is expanded.
35481 * @param {Roo.LayoutRegion} this
35486 * Fires when this region is slid into view.
35487 * @param {Roo.LayoutRegion} this
35489 "slideshow" : true,
35492 * Fires when this region slides out of view.
35493 * @param {Roo.LayoutRegion} this
35495 "slidehide" : true,
35497 * @event panelactivated
35498 * Fires when a panel is activated.
35499 * @param {Roo.LayoutRegion} this
35500 * @param {Roo.ContentPanel} panel The activated panel
35502 "panelactivated" : true,
35505 * Fires when the user resizes this region.
35506 * @param {Roo.LayoutRegion} this
35507 * @param {Number} newSize The new size (width for east/west, height for north/south)
35511 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35512 this.panels = new Roo.util.MixedCollection();
35513 this.panels.getKey = this.getPanelId.createDelegate(this);
35515 this.activePanel = null;
35516 // ensure listeners are added...
35518 if (config.listeners || config.events) {
35519 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35520 listeners : config.listeners || {},
35521 events : config.events || {}
35525 if(skipConfig !== true){
35526 this.applyConfig(config);
35530 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35532 getPanelId : function(p){
35536 applyConfig : function(config){
35537 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35538 this.config = config;
35543 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35544 * the width, for horizontal (north, south) the height.
35545 * @param {Number} newSize The new width or height
35547 resizeTo : function(newSize){
35548 var el = this.el ? this.el :
35549 (this.activePanel ? this.activePanel.getEl() : null);
35551 switch(this.position){
35554 el.setWidth(newSize);
35555 this.fireEvent("resized", this, newSize);
35559 el.setHeight(newSize);
35560 this.fireEvent("resized", this, newSize);
35566 getBox : function(){
35567 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35570 getMargins : function(){
35571 return this.margins;
35574 updateBox : function(box){
35576 var el = this.activePanel.getEl();
35577 el.dom.style.left = box.x + "px";
35578 el.dom.style.top = box.y + "px";
35579 this.activePanel.setSize(box.width, box.height);
35583 * Returns the container element for this region.
35584 * @return {Roo.Element}
35586 getEl : function(){
35587 return this.activePanel;
35591 * Returns true if this region is currently visible.
35592 * @return {Boolean}
35594 isVisible : function(){
35595 return this.activePanel ? true : false;
35598 setActivePanel : function(panel){
35599 panel = this.getPanel(panel);
35600 if(this.activePanel && this.activePanel != panel){
35601 this.activePanel.setActiveState(false);
35602 this.activePanel.getEl().setLeftTop(-10000,-10000);
35604 this.activePanel = panel;
35605 panel.setActiveState(true);
35607 panel.setSize(this.box.width, this.box.height);
35609 this.fireEvent("panelactivated", this, panel);
35610 this.fireEvent("invalidated");
35614 * Show the specified panel.
35615 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35616 * @return {Roo.ContentPanel} The shown panel or null
35618 showPanel : function(panel){
35619 panel = this.getPanel(panel);
35621 this.setActivePanel(panel);
35627 * Get the active panel for this region.
35628 * @return {Roo.ContentPanel} The active panel or null
35630 getActivePanel : function(){
35631 return this.activePanel;
35635 * Add the passed ContentPanel(s)
35636 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35637 * @return {Roo.ContentPanel} The panel added (if only one was added)
35639 add : function(panel){
35640 if(arguments.length > 1){
35641 for(var i = 0, len = arguments.length; i < len; i++) {
35642 this.add(arguments[i]);
35646 if(this.hasPanel(panel)){
35647 this.showPanel(panel);
35650 var el = panel.getEl();
35651 if(el.dom.parentNode != this.mgr.el.dom){
35652 this.mgr.el.dom.appendChild(el.dom);
35654 if(panel.setRegion){
35655 panel.setRegion(this);
35657 this.panels.add(panel);
35658 el.setStyle("position", "absolute");
35659 if(!panel.background){
35660 this.setActivePanel(panel);
35661 if(this.config.initialSize && this.panels.getCount()==1){
35662 this.resizeTo(this.config.initialSize);
35665 this.fireEvent("paneladded", this, panel);
35670 * Returns true if the panel is in this region.
35671 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35672 * @return {Boolean}
35674 hasPanel : function(panel){
35675 if(typeof panel == "object"){ // must be panel obj
35676 panel = panel.getId();
35678 return this.getPanel(panel) ? true : false;
35682 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35683 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35684 * @param {Boolean} preservePanel Overrides the config preservePanel option
35685 * @return {Roo.ContentPanel} The panel that was removed
35687 remove : function(panel, preservePanel){
35688 panel = this.getPanel(panel);
35693 this.fireEvent("beforeremove", this, panel, e);
35694 if(e.cancel === true){
35697 var panelId = panel.getId();
35698 this.panels.removeKey(panelId);
35703 * Returns the panel specified or null if it's not in this region.
35704 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35705 * @return {Roo.ContentPanel}
35707 getPanel : function(id){
35708 if(typeof id == "object"){ // must be panel obj
35711 return this.panels.get(id);
35715 * Returns this regions position (north/south/east/west/center).
35718 getPosition: function(){
35719 return this.position;
35723 * Ext JS Library 1.1.1
35724 * Copyright(c) 2006-2007, Ext JS, LLC.
35726 * Originally Released Under LGPL - original licence link has changed is not relivant.
35729 * <script type="text/javascript">
35733 * @class Roo.bootstrap.layout.Region
35734 * @extends Roo.bootstrap.layout.Basic
35735 * This class represents a region in a layout manager.
35737 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35738 * @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})
35739 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35740 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35741 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35742 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35743 * @cfg {String} title The title for the region (overrides panel titles)
35744 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35745 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35746 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35747 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35748 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35749 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35750 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35751 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35752 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35753 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35755 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35756 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35757 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35758 * @cfg {Number} width For East/West panels
35759 * @cfg {Number} height For North/South panels
35760 * @cfg {Boolean} split To show the splitter
35761 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35763 * @cfg {string} cls Extra CSS classes to add to region
35765 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35766 * @cfg {string} region the region that it inhabits..
35769 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35770 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35772 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35773 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35774 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35776 Roo.bootstrap.layout.Region = function(config)
35778 this.applyConfig(config);
35780 var mgr = config.mgr;
35781 var pos = config.region;
35782 config.skipConfig = true;
35783 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35786 this.onRender(mgr.el);
35789 this.visible = true;
35790 this.collapsed = false;
35791 this.unrendered_panels = [];
35794 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35796 position: '', // set by wrapper (eg. north/south etc..)
35797 unrendered_panels : null, // unrendered panels.
35798 createBody : function(){
35799 /** This region's body element
35800 * @type Roo.Element */
35801 this.bodyEl = this.el.createChild({
35803 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35807 onRender: function(ctr, pos)
35809 var dh = Roo.DomHelper;
35810 /** This region's container element
35811 * @type Roo.Element */
35812 this.el = dh.append(ctr.dom, {
35814 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35816 /** This region's title element
35817 * @type Roo.Element */
35819 this.titleEl = dh.append(this.el.dom,
35822 unselectable: "on",
35823 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35825 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35826 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35829 this.titleEl.enableDisplayMode();
35830 /** This region's title text element
35831 * @type HTMLElement */
35832 this.titleTextEl = this.titleEl.dom.firstChild;
35833 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35835 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35836 this.closeBtn.enableDisplayMode();
35837 this.closeBtn.on("click", this.closeClicked, this);
35838 this.closeBtn.hide();
35840 this.createBody(this.config);
35841 if(this.config.hideWhenEmpty){
35843 this.on("paneladded", this.validateVisibility, this);
35844 this.on("panelremoved", this.validateVisibility, this);
35846 if(this.autoScroll){
35847 this.bodyEl.setStyle("overflow", "auto");
35849 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35851 //if(c.titlebar !== false){
35852 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35853 this.titleEl.hide();
35855 this.titleEl.show();
35856 if(this.config.title){
35857 this.titleTextEl.innerHTML = this.config.title;
35861 if(this.config.collapsed){
35862 this.collapse(true);
35864 if(this.config.hidden){
35868 if (this.unrendered_panels && this.unrendered_panels.length) {
35869 for (var i =0;i< this.unrendered_panels.length; i++) {
35870 this.add(this.unrendered_panels[i]);
35872 this.unrendered_panels = null;
35878 applyConfig : function(c)
35881 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35882 var dh = Roo.DomHelper;
35883 if(c.titlebar !== false){
35884 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35885 this.collapseBtn.on("click", this.collapse, this);
35886 this.collapseBtn.enableDisplayMode();
35888 if(c.showPin === true || this.showPin){
35889 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35890 this.stickBtn.enableDisplayMode();
35891 this.stickBtn.on("click", this.expand, this);
35892 this.stickBtn.hide();
35897 /** This region's collapsed element
35898 * @type Roo.Element */
35901 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35902 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35905 if(c.floatable !== false){
35906 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35907 this.collapsedEl.on("click", this.collapseClick, this);
35910 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35911 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35912 id: "message", unselectable: "on", style:{"float":"left"}});
35913 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35915 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35916 this.expandBtn.on("click", this.expand, this);
35920 if(this.collapseBtn){
35921 this.collapseBtn.setVisible(c.collapsible == true);
35924 this.cmargins = c.cmargins || this.cmargins ||
35925 (this.position == "west" || this.position == "east" ?
35926 {top: 0, left: 2, right:2, bottom: 0} :
35927 {top: 2, left: 0, right:0, bottom: 2});
35929 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35932 this.bottomTabs = c.tabPosition != "top";
35934 this.autoScroll = c.autoScroll || false;
35939 this.duration = c.duration || .30;
35940 this.slideDuration = c.slideDuration || .45;
35945 * Returns true if this region is currently visible.
35946 * @return {Boolean}
35948 isVisible : function(){
35949 return this.visible;
35953 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35954 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35956 //setCollapsedTitle : function(title){
35957 // title = title || " ";
35958 // if(this.collapsedTitleTextEl){
35959 // this.collapsedTitleTextEl.innerHTML = title;
35963 getBox : function(){
35965 // if(!this.collapsed){
35966 b = this.el.getBox(false, true);
35968 // b = this.collapsedEl.getBox(false, true);
35973 getMargins : function(){
35974 return this.margins;
35975 //return this.collapsed ? this.cmargins : this.margins;
35978 highlight : function(){
35979 this.el.addClass("x-layout-panel-dragover");
35982 unhighlight : function(){
35983 this.el.removeClass("x-layout-panel-dragover");
35986 updateBox : function(box)
35988 if (!this.bodyEl) {
35989 return; // not rendered yet..
35993 if(!this.collapsed){
35994 this.el.dom.style.left = box.x + "px";
35995 this.el.dom.style.top = box.y + "px";
35996 this.updateBody(box.width, box.height);
35998 this.collapsedEl.dom.style.left = box.x + "px";
35999 this.collapsedEl.dom.style.top = box.y + "px";
36000 this.collapsedEl.setSize(box.width, box.height);
36003 this.tabs.autoSizeTabs();
36007 updateBody : function(w, h)
36010 this.el.setWidth(w);
36011 w -= this.el.getBorderWidth("rl");
36012 if(this.config.adjustments){
36013 w += this.config.adjustments[0];
36016 if(h !== null && h > 0){
36017 this.el.setHeight(h);
36018 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36019 h -= this.el.getBorderWidth("tb");
36020 if(this.config.adjustments){
36021 h += this.config.adjustments[1];
36023 this.bodyEl.setHeight(h);
36025 h = this.tabs.syncHeight(h);
36028 if(this.panelSize){
36029 w = w !== null ? w : this.panelSize.width;
36030 h = h !== null ? h : this.panelSize.height;
36032 if(this.activePanel){
36033 var el = this.activePanel.getEl();
36034 w = w !== null ? w : el.getWidth();
36035 h = h !== null ? h : el.getHeight();
36036 this.panelSize = {width: w, height: h};
36037 this.activePanel.setSize(w, h);
36039 if(Roo.isIE && this.tabs){
36040 this.tabs.el.repaint();
36045 * Returns the container element for this region.
36046 * @return {Roo.Element}
36048 getEl : function(){
36053 * Hides this region.
36056 //if(!this.collapsed){
36057 this.el.dom.style.left = "-2000px";
36060 // this.collapsedEl.dom.style.left = "-2000px";
36061 // this.collapsedEl.hide();
36063 this.visible = false;
36064 this.fireEvent("visibilitychange", this, false);
36068 * Shows this region if it was previously hidden.
36071 //if(!this.collapsed){
36074 // this.collapsedEl.show();
36076 this.visible = true;
36077 this.fireEvent("visibilitychange", this, true);
36080 closeClicked : function(){
36081 if(this.activePanel){
36082 this.remove(this.activePanel);
36086 collapseClick : function(e){
36088 e.stopPropagation();
36091 e.stopPropagation();
36097 * Collapses this region.
36098 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36101 collapse : function(skipAnim, skipCheck = false){
36102 if(this.collapsed) {
36106 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36108 this.collapsed = true;
36110 this.split.el.hide();
36112 if(this.config.animate && skipAnim !== true){
36113 this.fireEvent("invalidated", this);
36114 this.animateCollapse();
36116 this.el.setLocation(-20000,-20000);
36118 this.collapsedEl.show();
36119 this.fireEvent("collapsed", this);
36120 this.fireEvent("invalidated", this);
36126 animateCollapse : function(){
36131 * Expands this region if it was previously collapsed.
36132 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36133 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36136 expand : function(e, skipAnim){
36138 e.stopPropagation();
36140 if(!this.collapsed || this.el.hasActiveFx()) {
36144 this.afterSlideIn();
36147 this.collapsed = false;
36148 if(this.config.animate && skipAnim !== true){
36149 this.animateExpand();
36153 this.split.el.show();
36155 this.collapsedEl.setLocation(-2000,-2000);
36156 this.collapsedEl.hide();
36157 this.fireEvent("invalidated", this);
36158 this.fireEvent("expanded", this);
36162 animateExpand : function(){
36166 initTabs : function()
36168 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36170 var ts = new Roo.bootstrap.panel.Tabs({
36171 el: this.bodyEl.dom,
36172 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36173 disableTooltips: this.config.disableTabTips,
36174 toolbar : this.config.toolbar
36177 if(this.config.hideTabs){
36178 ts.stripWrap.setDisplayed(false);
36181 ts.resizeTabs = this.config.resizeTabs === true;
36182 ts.minTabWidth = this.config.minTabWidth || 40;
36183 ts.maxTabWidth = this.config.maxTabWidth || 250;
36184 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36185 ts.monitorResize = false;
36186 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36187 ts.bodyEl.addClass('roo-layout-tabs-body');
36188 this.panels.each(this.initPanelAsTab, this);
36191 initPanelAsTab : function(panel){
36192 var ti = this.tabs.addTab(
36196 this.config.closeOnTab && panel.isClosable(),
36199 if(panel.tabTip !== undefined){
36200 ti.setTooltip(panel.tabTip);
36202 ti.on("activate", function(){
36203 this.setActivePanel(panel);
36206 if(this.config.closeOnTab){
36207 ti.on("beforeclose", function(t, e){
36209 this.remove(panel);
36213 panel.tabItem = ti;
36218 updatePanelTitle : function(panel, title)
36220 if(this.activePanel == panel){
36221 this.updateTitle(title);
36224 var ti = this.tabs.getTab(panel.getEl().id);
36226 if(panel.tabTip !== undefined){
36227 ti.setTooltip(panel.tabTip);
36232 updateTitle : function(title){
36233 if(this.titleTextEl && !this.config.title){
36234 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36238 setActivePanel : function(panel)
36240 panel = this.getPanel(panel);
36241 if(this.activePanel && this.activePanel != panel){
36242 if(this.activePanel.setActiveState(false) === false){
36246 this.activePanel = panel;
36247 panel.setActiveState(true);
36248 if(this.panelSize){
36249 panel.setSize(this.panelSize.width, this.panelSize.height);
36252 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36254 this.updateTitle(panel.getTitle());
36256 this.fireEvent("invalidated", this);
36258 this.fireEvent("panelactivated", this, panel);
36262 * Shows the specified panel.
36263 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36264 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36266 showPanel : function(panel)
36268 panel = this.getPanel(panel);
36271 var tab = this.tabs.getTab(panel.getEl().id);
36272 if(tab.isHidden()){
36273 this.tabs.unhideTab(tab.id);
36277 this.setActivePanel(panel);
36284 * Get the active panel for this region.
36285 * @return {Roo.ContentPanel} The active panel or null
36287 getActivePanel : function(){
36288 return this.activePanel;
36291 validateVisibility : function(){
36292 if(this.panels.getCount() < 1){
36293 this.updateTitle(" ");
36294 this.closeBtn.hide();
36297 if(!this.isVisible()){
36304 * Adds the passed ContentPanel(s) to this region.
36305 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36306 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36308 add : function(panel)
36310 if(arguments.length > 1){
36311 for(var i = 0, len = arguments.length; i < len; i++) {
36312 this.add(arguments[i]);
36317 // if we have not been rendered yet, then we can not really do much of this..
36318 if (!this.bodyEl) {
36319 this.unrendered_panels.push(panel);
36326 if(this.hasPanel(panel)){
36327 this.showPanel(panel);
36330 panel.setRegion(this);
36331 this.panels.add(panel);
36332 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36333 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36334 // and hide them... ???
36335 this.bodyEl.dom.appendChild(panel.getEl().dom);
36336 if(panel.background !== true){
36337 this.setActivePanel(panel);
36339 this.fireEvent("paneladded", this, panel);
36346 this.initPanelAsTab(panel);
36350 if(panel.background !== true){
36351 this.tabs.activate(panel.getEl().id);
36353 this.fireEvent("paneladded", this, panel);
36358 * Hides the tab for the specified panel.
36359 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36361 hidePanel : function(panel){
36362 if(this.tabs && (panel = this.getPanel(panel))){
36363 this.tabs.hideTab(panel.getEl().id);
36368 * Unhides the tab for a previously hidden panel.
36369 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36371 unhidePanel : function(panel){
36372 if(this.tabs && (panel = this.getPanel(panel))){
36373 this.tabs.unhideTab(panel.getEl().id);
36377 clearPanels : function(){
36378 while(this.panels.getCount() > 0){
36379 this.remove(this.panels.first());
36384 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36385 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36386 * @param {Boolean} preservePanel Overrides the config preservePanel option
36387 * @return {Roo.ContentPanel} The panel that was removed
36389 remove : function(panel, preservePanel)
36391 panel = this.getPanel(panel);
36396 this.fireEvent("beforeremove", this, panel, e);
36397 if(e.cancel === true){
36400 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36401 var panelId = panel.getId();
36402 this.panels.removeKey(panelId);
36404 document.body.appendChild(panel.getEl().dom);
36407 this.tabs.removeTab(panel.getEl().id);
36408 }else if (!preservePanel){
36409 this.bodyEl.dom.removeChild(panel.getEl().dom);
36411 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36412 var p = this.panels.first();
36413 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36414 tempEl.appendChild(p.getEl().dom);
36415 this.bodyEl.update("");
36416 this.bodyEl.dom.appendChild(p.getEl().dom);
36418 this.updateTitle(p.getTitle());
36420 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36421 this.setActivePanel(p);
36423 panel.setRegion(null);
36424 if(this.activePanel == panel){
36425 this.activePanel = null;
36427 if(this.config.autoDestroy !== false && preservePanel !== true){
36428 try{panel.destroy();}catch(e){}
36430 this.fireEvent("panelremoved", this, panel);
36435 * Returns the TabPanel component used by this region
36436 * @return {Roo.TabPanel}
36438 getTabs : function(){
36442 createTool : function(parentEl, className){
36443 var btn = Roo.DomHelper.append(parentEl, {
36445 cls: "x-layout-tools-button",
36448 cls: "roo-layout-tools-button-inner " + className,
36452 btn.addClassOnOver("roo-layout-tools-button-over");
36457 * Ext JS Library 1.1.1
36458 * Copyright(c) 2006-2007, Ext JS, LLC.
36460 * Originally Released Under LGPL - original licence link has changed is not relivant.
36463 * <script type="text/javascript">
36469 * @class Roo.SplitLayoutRegion
36470 * @extends Roo.LayoutRegion
36471 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36473 Roo.bootstrap.layout.Split = function(config){
36474 this.cursor = config.cursor;
36475 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36478 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36480 splitTip : "Drag to resize.",
36481 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36482 useSplitTips : false,
36484 applyConfig : function(config){
36485 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36488 onRender : function(ctr,pos) {
36490 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36491 if(!this.config.split){
36496 var splitEl = Roo.DomHelper.append(ctr.dom, {
36498 id: this.el.id + "-split",
36499 cls: "roo-layout-split roo-layout-split-"+this.position,
36502 /** The SplitBar for this region
36503 * @type Roo.SplitBar */
36504 // does not exist yet...
36505 Roo.log([this.position, this.orientation]);
36507 this.split = new Roo.bootstrap.SplitBar({
36508 dragElement : splitEl,
36509 resizingElement: this.el,
36510 orientation : this.orientation
36513 this.split.on("moved", this.onSplitMove, this);
36514 this.split.useShim = this.config.useShim === true;
36515 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36516 if(this.useSplitTips){
36517 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36519 //if(config.collapsible){
36520 // this.split.el.on("dblclick", this.collapse, this);
36523 if(typeof this.config.minSize != "undefined"){
36524 this.split.minSize = this.config.minSize;
36526 if(typeof this.config.maxSize != "undefined"){
36527 this.split.maxSize = this.config.maxSize;
36529 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36530 this.hideSplitter();
36535 getHMaxSize : function(){
36536 var cmax = this.config.maxSize || 10000;
36537 var center = this.mgr.getRegion("center");
36538 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36541 getVMaxSize : function(){
36542 var cmax = this.config.maxSize || 10000;
36543 var center = this.mgr.getRegion("center");
36544 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36547 onSplitMove : function(split, newSize){
36548 this.fireEvent("resized", this, newSize);
36552 * Returns the {@link Roo.SplitBar} for this region.
36553 * @return {Roo.SplitBar}
36555 getSplitBar : function(){
36560 this.hideSplitter();
36561 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36564 hideSplitter : function(){
36566 this.split.el.setLocation(-2000,-2000);
36567 this.split.el.hide();
36573 this.split.el.show();
36575 Roo.bootstrap.layout.Split.superclass.show.call(this);
36578 beforeSlide: function(){
36579 if(Roo.isGecko){// firefox overflow auto bug workaround
36580 this.bodyEl.clip();
36582 this.tabs.bodyEl.clip();
36584 if(this.activePanel){
36585 this.activePanel.getEl().clip();
36587 if(this.activePanel.beforeSlide){
36588 this.activePanel.beforeSlide();
36594 afterSlide : function(){
36595 if(Roo.isGecko){// firefox overflow auto bug workaround
36596 this.bodyEl.unclip();
36598 this.tabs.bodyEl.unclip();
36600 if(this.activePanel){
36601 this.activePanel.getEl().unclip();
36602 if(this.activePanel.afterSlide){
36603 this.activePanel.afterSlide();
36609 initAutoHide : function(){
36610 if(this.autoHide !== false){
36611 if(!this.autoHideHd){
36612 var st = new Roo.util.DelayedTask(this.slideIn, this);
36613 this.autoHideHd = {
36614 "mouseout": function(e){
36615 if(!e.within(this.el, true)){
36619 "mouseover" : function(e){
36625 this.el.on(this.autoHideHd);
36629 clearAutoHide : function(){
36630 if(this.autoHide !== false){
36631 this.el.un("mouseout", this.autoHideHd.mouseout);
36632 this.el.un("mouseover", this.autoHideHd.mouseover);
36636 clearMonitor : function(){
36637 Roo.get(document).un("click", this.slideInIf, this);
36640 // these names are backwards but not changed for compat
36641 slideOut : function(){
36642 if(this.isSlid || this.el.hasActiveFx()){
36645 this.isSlid = true;
36646 if(this.collapseBtn){
36647 this.collapseBtn.hide();
36649 this.closeBtnState = this.closeBtn.getStyle('display');
36650 this.closeBtn.hide();
36652 this.stickBtn.show();
36655 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36656 this.beforeSlide();
36657 this.el.setStyle("z-index", 10001);
36658 this.el.slideIn(this.getSlideAnchor(), {
36659 callback: function(){
36661 this.initAutoHide();
36662 Roo.get(document).on("click", this.slideInIf, this);
36663 this.fireEvent("slideshow", this);
36670 afterSlideIn : function(){
36671 this.clearAutoHide();
36672 this.isSlid = false;
36673 this.clearMonitor();
36674 this.el.setStyle("z-index", "");
36675 if(this.collapseBtn){
36676 this.collapseBtn.show();
36678 this.closeBtn.setStyle('display', this.closeBtnState);
36680 this.stickBtn.hide();
36682 this.fireEvent("slidehide", this);
36685 slideIn : function(cb){
36686 if(!this.isSlid || this.el.hasActiveFx()){
36690 this.isSlid = false;
36691 this.beforeSlide();
36692 this.el.slideOut(this.getSlideAnchor(), {
36693 callback: function(){
36694 this.el.setLeftTop(-10000, -10000);
36696 this.afterSlideIn();
36704 slideInIf : function(e){
36705 if(!e.within(this.el)){
36710 animateCollapse : function(){
36711 this.beforeSlide();
36712 this.el.setStyle("z-index", 20000);
36713 var anchor = this.getSlideAnchor();
36714 this.el.slideOut(anchor, {
36715 callback : function(){
36716 this.el.setStyle("z-index", "");
36717 this.collapsedEl.slideIn(anchor, {duration:.3});
36719 this.el.setLocation(-10000,-10000);
36721 this.fireEvent("collapsed", this);
36728 animateExpand : function(){
36729 this.beforeSlide();
36730 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36731 this.el.setStyle("z-index", 20000);
36732 this.collapsedEl.hide({
36735 this.el.slideIn(this.getSlideAnchor(), {
36736 callback : function(){
36737 this.el.setStyle("z-index", "");
36740 this.split.el.show();
36742 this.fireEvent("invalidated", this);
36743 this.fireEvent("expanded", this);
36771 getAnchor : function(){
36772 return this.anchors[this.position];
36775 getCollapseAnchor : function(){
36776 return this.canchors[this.position];
36779 getSlideAnchor : function(){
36780 return this.sanchors[this.position];
36783 getAlignAdj : function(){
36784 var cm = this.cmargins;
36785 switch(this.position){
36801 getExpandAdj : function(){
36802 var c = this.collapsedEl, cm = this.cmargins;
36803 switch(this.position){
36805 return [-(cm.right+c.getWidth()+cm.left), 0];
36808 return [cm.right+c.getWidth()+cm.left, 0];
36811 return [0, -(cm.top+cm.bottom+c.getHeight())];
36814 return [0, cm.top+cm.bottom+c.getHeight()];
36820 * Ext JS Library 1.1.1
36821 * Copyright(c) 2006-2007, Ext JS, LLC.
36823 * Originally Released Under LGPL - original licence link has changed is not relivant.
36826 * <script type="text/javascript">
36829 * These classes are private internal classes
36831 Roo.bootstrap.layout.Center = function(config){
36832 config.region = "center";
36833 Roo.bootstrap.layout.Region.call(this, config);
36834 this.visible = true;
36835 this.minWidth = config.minWidth || 20;
36836 this.minHeight = config.minHeight || 20;
36839 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36841 // center panel can't be hidden
36845 // center panel can't be hidden
36848 getMinWidth: function(){
36849 return this.minWidth;
36852 getMinHeight: function(){
36853 return this.minHeight;
36866 Roo.bootstrap.layout.North = function(config)
36868 config.region = 'north';
36869 config.cursor = 'n-resize';
36871 Roo.bootstrap.layout.Split.call(this, config);
36875 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36876 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36877 this.split.el.addClass("roo-layout-split-v");
36879 var size = config.initialSize || config.height;
36880 if(typeof size != "undefined"){
36881 this.el.setHeight(size);
36884 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36886 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36890 getBox : function(){
36891 if(this.collapsed){
36892 return this.collapsedEl.getBox();
36894 var box = this.el.getBox();
36896 box.height += this.split.el.getHeight();
36901 updateBox : function(box){
36902 if(this.split && !this.collapsed){
36903 box.height -= this.split.el.getHeight();
36904 this.split.el.setLeft(box.x);
36905 this.split.el.setTop(box.y+box.height);
36906 this.split.el.setWidth(box.width);
36908 if(this.collapsed){
36909 this.updateBody(box.width, null);
36911 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36919 Roo.bootstrap.layout.South = function(config){
36920 config.region = 'south';
36921 config.cursor = 's-resize';
36922 Roo.bootstrap.layout.Split.call(this, config);
36924 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36925 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36926 this.split.el.addClass("roo-layout-split-v");
36928 var size = config.initialSize || config.height;
36929 if(typeof size != "undefined"){
36930 this.el.setHeight(size);
36934 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36935 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36936 getBox : function(){
36937 if(this.collapsed){
36938 return this.collapsedEl.getBox();
36940 var box = this.el.getBox();
36942 var sh = this.split.el.getHeight();
36949 updateBox : function(box){
36950 if(this.split && !this.collapsed){
36951 var sh = this.split.el.getHeight();
36954 this.split.el.setLeft(box.x);
36955 this.split.el.setTop(box.y-sh);
36956 this.split.el.setWidth(box.width);
36958 if(this.collapsed){
36959 this.updateBody(box.width, null);
36961 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36965 Roo.bootstrap.layout.East = function(config){
36966 config.region = "east";
36967 config.cursor = "e-resize";
36968 Roo.bootstrap.layout.Split.call(this, config);
36970 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36971 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36972 this.split.el.addClass("roo-layout-split-h");
36974 var size = config.initialSize || config.width;
36975 if(typeof size != "undefined"){
36976 this.el.setWidth(size);
36979 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36980 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36981 getBox : function(){
36982 if(this.collapsed){
36983 return this.collapsedEl.getBox();
36985 var box = this.el.getBox();
36987 var sw = this.split.el.getWidth();
36994 updateBox : function(box){
36995 if(this.split && !this.collapsed){
36996 var sw = this.split.el.getWidth();
36998 this.split.el.setLeft(box.x);
36999 this.split.el.setTop(box.y);
37000 this.split.el.setHeight(box.height);
37003 if(this.collapsed){
37004 this.updateBody(null, box.height);
37006 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37010 Roo.bootstrap.layout.West = function(config){
37011 config.region = "west";
37012 config.cursor = "w-resize";
37014 Roo.bootstrap.layout.Split.call(this, config);
37016 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37017 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37018 this.split.el.addClass("roo-layout-split-h");
37022 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37023 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37025 onRender: function(ctr, pos)
37027 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37028 var size = this.config.initialSize || this.config.width;
37029 if(typeof size != "undefined"){
37030 this.el.setWidth(size);
37034 getBox : function(){
37035 if(this.collapsed){
37036 return this.collapsedEl.getBox();
37038 var box = this.el.getBox();
37040 box.width += this.split.el.getWidth();
37045 updateBox : function(box){
37046 if(this.split && !this.collapsed){
37047 var sw = this.split.el.getWidth();
37049 this.split.el.setLeft(box.x+box.width);
37050 this.split.el.setTop(box.y);
37051 this.split.el.setHeight(box.height);
37053 if(this.collapsed){
37054 this.updateBody(null, box.height);
37056 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37059 Roo.namespace("Roo.bootstrap.panel");/*
37061 * Ext JS Library 1.1.1
37062 * Copyright(c) 2006-2007, Ext JS, LLC.
37064 * Originally Released Under LGPL - original licence link has changed is not relivant.
37067 * <script type="text/javascript">
37070 * @class Roo.ContentPanel
37071 * @extends Roo.util.Observable
37072 * A basic ContentPanel element.
37073 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37074 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37075 * @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
37076 * @cfg {Boolean} closable True if the panel can be closed/removed
37077 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37078 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37079 * @cfg {Toolbar} toolbar A toolbar for this panel
37080 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37081 * @cfg {String} title The title for this panel
37082 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37083 * @cfg {String} url Calls {@link #setUrl} with this value
37084 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37085 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37086 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37087 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37088 * @cfg {Boolean} badges render the badges
37091 * Create a new ContentPanel.
37092 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37093 * @param {String/Object} config A string to set only the title or a config object
37094 * @param {String} content (optional) Set the HTML content for this panel
37095 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37097 Roo.bootstrap.panel.Content = function( config){
37099 this.tpl = config.tpl || false;
37101 var el = config.el;
37102 var content = config.content;
37104 if(config.autoCreate){ // xtype is available if this is called from factory
37107 this.el = Roo.get(el);
37108 if(!this.el && config && config.autoCreate){
37109 if(typeof config.autoCreate == "object"){
37110 if(!config.autoCreate.id){
37111 config.autoCreate.id = config.id||el;
37113 this.el = Roo.DomHelper.append(document.body,
37114 config.autoCreate, true);
37116 var elcfg = { tag: "div",
37117 cls: "roo-layout-inactive-content",
37121 elcfg.html = config.html;
37125 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37128 this.closable = false;
37129 this.loaded = false;
37130 this.active = false;
37133 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37135 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37137 this.wrapEl = this.el; //this.el.wrap();
37139 if (config.toolbar.items) {
37140 ti = config.toolbar.items ;
37141 delete config.toolbar.items ;
37145 this.toolbar.render(this.wrapEl, 'before');
37146 for(var i =0;i < ti.length;i++) {
37147 // Roo.log(['add child', items[i]]);
37148 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37150 this.toolbar.items = nitems;
37151 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37152 delete config.toolbar;
37156 // xtype created footer. - not sure if will work as we normally have to render first..
37157 if (this.footer && !this.footer.el && this.footer.xtype) {
37158 if (!this.wrapEl) {
37159 this.wrapEl = this.el.wrap();
37162 this.footer.container = this.wrapEl.createChild();
37164 this.footer = Roo.factory(this.footer, Roo);
37169 if(typeof config == "string"){
37170 this.title = config;
37172 Roo.apply(this, config);
37176 this.resizeEl = Roo.get(this.resizeEl, true);
37178 this.resizeEl = this.el;
37180 // handle view.xtype
37188 * Fires when this panel is activated.
37189 * @param {Roo.ContentPanel} this
37193 * @event deactivate
37194 * Fires when this panel is activated.
37195 * @param {Roo.ContentPanel} this
37197 "deactivate" : true,
37201 * Fires when this panel is resized if fitToFrame is true.
37202 * @param {Roo.ContentPanel} this
37203 * @param {Number} width The width after any component adjustments
37204 * @param {Number} height The height after any component adjustments
37210 * Fires when this tab is created
37211 * @param {Roo.ContentPanel} this
37222 if(this.autoScroll){
37223 this.resizeEl.setStyle("overflow", "auto");
37225 // fix randome scrolling
37226 //this.el.on('scroll', function() {
37227 // Roo.log('fix random scolling');
37228 // this.scrollTo('top',0);
37231 content = content || this.content;
37233 this.setContent(content);
37235 if(config && config.url){
37236 this.setUrl(this.url, this.params, this.loadOnce);
37241 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37243 if (this.view && typeof(this.view.xtype) != 'undefined') {
37244 this.view.el = this.el.appendChild(document.createElement("div"));
37245 this.view = Roo.factory(this.view);
37246 this.view.render && this.view.render(false, '');
37250 this.fireEvent('render', this);
37253 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37257 setRegion : function(region){
37258 this.region = region;
37259 this.setActiveClass(region && !this.background);
37263 setActiveClass: function(state)
37266 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37267 this.el.setStyle('position','relative');
37269 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37270 this.el.setStyle('position', 'absolute');
37275 * Returns the toolbar for this Panel if one was configured.
37276 * @return {Roo.Toolbar}
37278 getToolbar : function(){
37279 return this.toolbar;
37282 setActiveState : function(active)
37284 this.active = active;
37285 this.setActiveClass(active);
37287 if(this.fireEvent("deactivate", this) === false){
37292 this.fireEvent("activate", this);
37296 * Updates this panel's element
37297 * @param {String} content The new content
37298 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37300 setContent : function(content, loadScripts){
37301 this.el.update(content, loadScripts);
37304 ignoreResize : function(w, h){
37305 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37308 this.lastSize = {width: w, height: h};
37313 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37314 * @return {Roo.UpdateManager} The UpdateManager
37316 getUpdateManager : function(){
37317 return this.el.getUpdateManager();
37320 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37321 * @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:
37324 url: "your-url.php",
37325 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37326 callback: yourFunction,
37327 scope: yourObject, //(optional scope)
37330 text: "Loading...",
37335 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37336 * 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.
37337 * @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}
37338 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37339 * @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.
37340 * @return {Roo.ContentPanel} this
37343 var um = this.el.getUpdateManager();
37344 um.update.apply(um, arguments);
37350 * 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.
37351 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37352 * @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)
37353 * @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)
37354 * @return {Roo.UpdateManager} The UpdateManager
37356 setUrl : function(url, params, loadOnce){
37357 if(this.refreshDelegate){
37358 this.removeListener("activate", this.refreshDelegate);
37360 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37361 this.on("activate", this.refreshDelegate);
37362 return this.el.getUpdateManager();
37365 _handleRefresh : function(url, params, loadOnce){
37366 if(!loadOnce || !this.loaded){
37367 var updater = this.el.getUpdateManager();
37368 updater.update(url, params, this._setLoaded.createDelegate(this));
37372 _setLoaded : function(){
37373 this.loaded = true;
37377 * Returns this panel's id
37380 getId : function(){
37385 * Returns this panel's element - used by regiosn to add.
37386 * @return {Roo.Element}
37388 getEl : function(){
37389 return this.wrapEl || this.el;
37394 adjustForComponents : function(width, height)
37396 //Roo.log('adjustForComponents ');
37397 if(this.resizeEl != this.el){
37398 width -= this.el.getFrameWidth('lr');
37399 height -= this.el.getFrameWidth('tb');
37402 var te = this.toolbar.getEl();
37403 te.setWidth(width);
37404 height -= te.getHeight();
37407 var te = this.footer.getEl();
37408 te.setWidth(width);
37409 height -= te.getHeight();
37413 if(this.adjustments){
37414 width += this.adjustments[0];
37415 height += this.adjustments[1];
37417 return {"width": width, "height": height};
37420 setSize : function(width, height){
37421 if(this.fitToFrame && !this.ignoreResize(width, height)){
37422 if(this.fitContainer && this.resizeEl != this.el){
37423 this.el.setSize(width, height);
37425 var size = this.adjustForComponents(width, height);
37426 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37427 this.fireEvent('resize', this, size.width, size.height);
37432 * Returns this panel's title
37435 getTitle : function(){
37437 if (typeof(this.title) != 'object') {
37442 for (var k in this.title) {
37443 if (!this.title.hasOwnProperty(k)) {
37447 if (k.indexOf('-') >= 0) {
37448 var s = k.split('-');
37449 for (var i = 0; i<s.length; i++) {
37450 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37453 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37460 * Set this panel's title
37461 * @param {String} title
37463 setTitle : function(title){
37464 this.title = title;
37466 this.region.updatePanelTitle(this, title);
37471 * Returns true is this panel was configured to be closable
37472 * @return {Boolean}
37474 isClosable : function(){
37475 return this.closable;
37478 beforeSlide : function(){
37480 this.resizeEl.clip();
37483 afterSlide : function(){
37485 this.resizeEl.unclip();
37489 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37490 * Will fail silently if the {@link #setUrl} method has not been called.
37491 * This does not activate the panel, just updates its content.
37493 refresh : function(){
37494 if(this.refreshDelegate){
37495 this.loaded = false;
37496 this.refreshDelegate();
37501 * Destroys this panel
37503 destroy : function(){
37504 this.el.removeAllListeners();
37505 var tempEl = document.createElement("span");
37506 tempEl.appendChild(this.el.dom);
37507 tempEl.innerHTML = "";
37513 * form - if the content panel contains a form - this is a reference to it.
37514 * @type {Roo.form.Form}
37518 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37519 * This contains a reference to it.
37525 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37535 * @param {Object} cfg Xtype definition of item to add.
37539 getChildContainer: function () {
37540 return this.getEl();
37545 var ret = new Roo.factory(cfg);
37550 if (cfg.xtype.match(/^Form$/)) {
37553 //if (this.footer) {
37554 // el = this.footer.container.insertSibling(false, 'before');
37556 el = this.el.createChild();
37559 this.form = new Roo.form.Form(cfg);
37562 if ( this.form.allItems.length) {
37563 this.form.render(el.dom);
37567 // should only have one of theses..
37568 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37569 // views.. should not be just added - used named prop 'view''
37571 cfg.el = this.el.appendChild(document.createElement("div"));
37574 var ret = new Roo.factory(cfg);
37576 ret.render && ret.render(false, ''); // render blank..
37586 * @class Roo.bootstrap.panel.Grid
37587 * @extends Roo.bootstrap.panel.Content
37589 * Create a new GridPanel.
37590 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37591 * @param {Object} config A the config object
37597 Roo.bootstrap.panel.Grid = function(config)
37601 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37602 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37604 config.el = this.wrapper;
37605 //this.el = this.wrapper;
37607 if (config.container) {
37608 // ctor'ed from a Border/panel.grid
37611 this.wrapper.setStyle("overflow", "hidden");
37612 this.wrapper.addClass('roo-grid-container');
37617 if(config.toolbar){
37618 var tool_el = this.wrapper.createChild();
37619 this.toolbar = Roo.factory(config.toolbar);
37621 if (config.toolbar.items) {
37622 ti = config.toolbar.items ;
37623 delete config.toolbar.items ;
37627 this.toolbar.render(tool_el);
37628 for(var i =0;i < ti.length;i++) {
37629 // Roo.log(['add child', items[i]]);
37630 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37632 this.toolbar.items = nitems;
37634 delete config.toolbar;
37637 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37638 config.grid.scrollBody = true;;
37639 config.grid.monitorWindowResize = false; // turn off autosizing
37640 config.grid.autoHeight = false;
37641 config.grid.autoWidth = false;
37643 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37645 if (config.background) {
37646 // render grid on panel activation (if panel background)
37647 this.on('activate', function(gp) {
37648 if (!gp.grid.rendered) {
37649 gp.grid.render(this.wrapper);
37650 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37655 this.grid.render(this.wrapper);
37656 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37659 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37660 // ??? needed ??? config.el = this.wrapper;
37665 // xtype created footer. - not sure if will work as we normally have to render first..
37666 if (this.footer && !this.footer.el && this.footer.xtype) {
37668 var ctr = this.grid.getView().getFooterPanel(true);
37669 this.footer.dataSource = this.grid.dataSource;
37670 this.footer = Roo.factory(this.footer, Roo);
37671 this.footer.render(ctr);
37681 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37682 getId : function(){
37683 return this.grid.id;
37687 * Returns the grid for this panel
37688 * @return {Roo.bootstrap.Table}
37690 getGrid : function(){
37694 setSize : function(width, height){
37695 if(!this.ignoreResize(width, height)){
37696 var grid = this.grid;
37697 var size = this.adjustForComponents(width, height);
37698 var gridel = grid.getGridEl();
37699 gridel.setSize(size.width, size.height);
37701 var thd = grid.getGridEl().select('thead',true).first();
37702 var tbd = grid.getGridEl().select('tbody', true).first();
37704 tbd.setSize(width, height - thd.getHeight());
37713 beforeSlide : function(){
37714 this.grid.getView().scroller.clip();
37717 afterSlide : function(){
37718 this.grid.getView().scroller.unclip();
37721 destroy : function(){
37722 this.grid.destroy();
37724 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37729 * @class Roo.bootstrap.panel.Nest
37730 * @extends Roo.bootstrap.panel.Content
37732 * Create a new Panel, that can contain a layout.Border.
37735 * @param {Roo.BorderLayout} layout The layout for this panel
37736 * @param {String/Object} config A string to set only the title or a config object
37738 Roo.bootstrap.panel.Nest = function(config)
37740 // construct with only one argument..
37741 /* FIXME - implement nicer consturctors
37742 if (layout.layout) {
37744 layout = config.layout;
37745 delete config.layout;
37747 if (layout.xtype && !layout.getEl) {
37748 // then layout needs constructing..
37749 layout = Roo.factory(layout, Roo);
37753 config.el = config.layout.getEl();
37755 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37757 config.layout.monitorWindowResize = false; // turn off autosizing
37758 this.layout = config.layout;
37759 this.layout.getEl().addClass("roo-layout-nested-layout");
37766 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37768 setSize : function(width, height){
37769 if(!this.ignoreResize(width, height)){
37770 var size = this.adjustForComponents(width, height);
37771 var el = this.layout.getEl();
37772 if (size.height < 1) {
37773 el.setWidth(size.width);
37775 el.setSize(size.width, size.height);
37777 var touch = el.dom.offsetWidth;
37778 this.layout.layout();
37779 // ie requires a double layout on the first pass
37780 if(Roo.isIE && !this.initialized){
37781 this.initialized = true;
37782 this.layout.layout();
37787 // activate all subpanels if not currently active..
37789 setActiveState : function(active){
37790 this.active = active;
37791 this.setActiveClass(active);
37794 this.fireEvent("deactivate", this);
37798 this.fireEvent("activate", this);
37799 // not sure if this should happen before or after..
37800 if (!this.layout) {
37801 return; // should not happen..
37804 for (var r in this.layout.regions) {
37805 reg = this.layout.getRegion(r);
37806 if (reg.getActivePanel()) {
37807 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37808 reg.setActivePanel(reg.getActivePanel());
37811 if (!reg.panels.length) {
37814 reg.showPanel(reg.getPanel(0));
37823 * Returns the nested BorderLayout for this panel
37824 * @return {Roo.BorderLayout}
37826 getLayout : function(){
37827 return this.layout;
37831 * Adds a xtype elements to the layout of the nested panel
37835 xtype : 'ContentPanel',
37842 xtype : 'NestedLayoutPanel',
37848 items : [ ... list of content panels or nested layout panels.. ]
37852 * @param {Object} cfg Xtype definition of item to add.
37854 addxtype : function(cfg) {
37855 return this.layout.addxtype(cfg);
37860 * Ext JS Library 1.1.1
37861 * Copyright(c) 2006-2007, Ext JS, LLC.
37863 * Originally Released Under LGPL - original licence link has changed is not relivant.
37866 * <script type="text/javascript">
37869 * @class Roo.TabPanel
37870 * @extends Roo.util.Observable
37871 * A lightweight tab container.
37875 // basic tabs 1, built from existing content
37876 var tabs = new Roo.TabPanel("tabs1");
37877 tabs.addTab("script", "View Script");
37878 tabs.addTab("markup", "View Markup");
37879 tabs.activate("script");
37881 // more advanced tabs, built from javascript
37882 var jtabs = new Roo.TabPanel("jtabs");
37883 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37885 // set up the UpdateManager
37886 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37887 var updater = tab2.getUpdateManager();
37888 updater.setDefaultUrl("ajax1.htm");
37889 tab2.on('activate', updater.refresh, updater, true);
37891 // Use setUrl for Ajax loading
37892 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37893 tab3.setUrl("ajax2.htm", null, true);
37896 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37899 jtabs.activate("jtabs-1");
37902 * Create a new TabPanel.
37903 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37904 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37906 Roo.bootstrap.panel.Tabs = function(config){
37908 * The container element for this TabPanel.
37909 * @type Roo.Element
37911 this.el = Roo.get(config.el);
37914 if(typeof config == "boolean"){
37915 this.tabPosition = config ? "bottom" : "top";
37917 Roo.apply(this, config);
37921 if(this.tabPosition == "bottom"){
37922 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37923 this.el.addClass("roo-tabs-bottom");
37925 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37926 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37927 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37929 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37931 if(this.tabPosition != "bottom"){
37932 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37933 * @type Roo.Element
37935 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37936 this.el.addClass("roo-tabs-top");
37940 this.bodyEl.setStyle("position", "relative");
37942 this.active = null;
37943 this.activateDelegate = this.activate.createDelegate(this);
37948 * Fires when the active tab changes
37949 * @param {Roo.TabPanel} this
37950 * @param {Roo.TabPanelItem} activePanel The new active tab
37954 * @event beforetabchange
37955 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37956 * @param {Roo.TabPanel} this
37957 * @param {Object} e Set cancel to true on this object to cancel the tab change
37958 * @param {Roo.TabPanelItem} tab The tab being changed to
37960 "beforetabchange" : true
37963 Roo.EventManager.onWindowResize(this.onResize, this);
37964 this.cpad = this.el.getPadding("lr");
37965 this.hiddenCount = 0;
37968 // toolbar on the tabbar support...
37969 if (this.toolbar) {
37970 alert("no toolbar support yet");
37971 this.toolbar = false;
37973 var tcfg = this.toolbar;
37974 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37975 this.toolbar = new Roo.Toolbar(tcfg);
37976 if (Roo.isSafari) {
37977 var tbl = tcfg.container.child('table', true);
37978 tbl.setAttribute('width', '100%');
37986 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37989 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37991 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37993 tabPosition : "top",
37995 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37997 currentTabWidth : 0,
37999 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38003 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38007 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38009 preferredTabWidth : 175,
38011 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38013 resizeTabs : false,
38015 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38017 monitorResize : true,
38019 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38024 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38025 * @param {String} id The id of the div to use <b>or create</b>
38026 * @param {String} text The text for the tab
38027 * @param {String} content (optional) Content to put in the TabPanelItem body
38028 * @param {Boolean} closable (optional) True to create a close icon on the tab
38029 * @return {Roo.TabPanelItem} The created TabPanelItem
38031 addTab : function(id, text, content, closable, tpl)
38033 var item = new Roo.bootstrap.panel.TabItem({
38037 closable : closable,
38040 this.addTabItem(item);
38042 item.setContent(content);
38048 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38049 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38050 * @return {Roo.TabPanelItem}
38052 getTab : function(id){
38053 return this.items[id];
38057 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38058 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38060 hideTab : function(id){
38061 var t = this.items[id];
38064 this.hiddenCount++;
38065 this.autoSizeTabs();
38070 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38071 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38073 unhideTab : function(id){
38074 var t = this.items[id];
38076 t.setHidden(false);
38077 this.hiddenCount--;
38078 this.autoSizeTabs();
38083 * Adds an existing {@link Roo.TabPanelItem}.
38084 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38086 addTabItem : function(item)
38088 this.items[item.id] = item;
38089 this.items.push(item);
38090 this.autoSizeTabs();
38091 // if(this.resizeTabs){
38092 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38093 // this.autoSizeTabs();
38095 // item.autoSize();
38100 * Removes a {@link Roo.TabPanelItem}.
38101 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38103 removeTab : function(id){
38104 var items = this.items;
38105 var tab = items[id];
38106 if(!tab) { return; }
38107 var index = items.indexOf(tab);
38108 if(this.active == tab && items.length > 1){
38109 var newTab = this.getNextAvailable(index);
38114 this.stripEl.dom.removeChild(tab.pnode.dom);
38115 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38116 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38118 items.splice(index, 1);
38119 delete this.items[tab.id];
38120 tab.fireEvent("close", tab);
38121 tab.purgeListeners();
38122 this.autoSizeTabs();
38125 getNextAvailable : function(start){
38126 var items = this.items;
38128 // look for a next tab that will slide over to
38129 // replace the one being removed
38130 while(index < items.length){
38131 var item = items[++index];
38132 if(item && !item.isHidden()){
38136 // if one isn't found select the previous tab (on the left)
38139 var item = items[--index];
38140 if(item && !item.isHidden()){
38148 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38149 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38151 disableTab : function(id){
38152 var tab = this.items[id];
38153 if(tab && this.active != tab){
38159 * Enables a {@link Roo.TabPanelItem} that is disabled.
38160 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38162 enableTab : function(id){
38163 var tab = this.items[id];
38168 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38169 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38170 * @return {Roo.TabPanelItem} The TabPanelItem.
38172 activate : function(id)
38174 var tab = this.items[id];
38178 if(tab == this.active || tab.disabled){
38182 this.fireEvent("beforetabchange", this, e, tab);
38183 if(e.cancel !== true && !tab.disabled){
38185 this.active.hide();
38187 this.active = this.items[id];
38188 this.active.show();
38189 this.fireEvent("tabchange", this, this.active);
38195 * Gets the active {@link Roo.TabPanelItem}.
38196 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38198 getActiveTab : function(){
38199 return this.active;
38203 * Updates the tab body element to fit the height of the container element
38204 * for overflow scrolling
38205 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38207 syncHeight : function(targetHeight){
38208 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38209 var bm = this.bodyEl.getMargins();
38210 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38211 this.bodyEl.setHeight(newHeight);
38215 onResize : function(){
38216 if(this.monitorResize){
38217 this.autoSizeTabs();
38222 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38224 beginUpdate : function(){
38225 this.updating = true;
38229 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38231 endUpdate : function(){
38232 this.updating = false;
38233 this.autoSizeTabs();
38237 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38239 autoSizeTabs : function()
38241 var count = this.items.length;
38242 var vcount = count - this.hiddenCount;
38243 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38247 this.stripEl.hide();
38249 this.stripEl.show();
38252 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38253 var availWidth = Math.floor(w / vcount);
38254 var b = this.stripBody;
38255 if(b.getWidth() > w){
38256 var tabs = this.items;
38257 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38258 if(availWidth < this.minTabWidth){
38259 /*if(!this.sleft){ // incomplete scrolling code
38260 this.createScrollButtons();
38263 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38266 if(this.currentTabWidth < this.preferredTabWidth){
38267 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38273 * Returns the number of tabs in this TabPanel.
38276 getCount : function(){
38277 return this.items.length;
38281 * Resizes all the tabs to the passed width
38282 * @param {Number} The new width
38284 setTabWidth : function(width){
38285 this.currentTabWidth = width;
38286 for(var i = 0, len = this.items.length; i < len; i++) {
38287 if(!this.items[i].isHidden()) {
38288 this.items[i].setWidth(width);
38294 * Destroys this TabPanel
38295 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38297 destroy : function(removeEl){
38298 Roo.EventManager.removeResizeListener(this.onResize, this);
38299 for(var i = 0, len = this.items.length; i < len; i++){
38300 this.items[i].purgeListeners();
38302 if(removeEl === true){
38303 this.el.update("");
38308 createStrip : function(container)
38310 var strip = document.createElement("nav");
38311 strip.className = Roo.bootstrap.version == 4 ?
38312 "navbar-light bg-light" :
38313 "navbar navbar-default"; //"x-tabs-wrap";
38314 container.appendChild(strip);
38318 createStripList : function(strip)
38320 // div wrapper for retard IE
38321 // returns the "tr" element.
38322 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38323 //'<div class="x-tabs-strip-wrap">'+
38324 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38325 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38326 return strip.firstChild; //.firstChild.firstChild.firstChild;
38328 createBody : function(container)
38330 var body = document.createElement("div");
38331 Roo.id(body, "tab-body");
38332 //Roo.fly(body).addClass("x-tabs-body");
38333 Roo.fly(body).addClass("tab-content");
38334 container.appendChild(body);
38337 createItemBody :function(bodyEl, id){
38338 var body = Roo.getDom(id);
38340 body = document.createElement("div");
38343 //Roo.fly(body).addClass("x-tabs-item-body");
38344 Roo.fly(body).addClass("tab-pane");
38345 bodyEl.insertBefore(body, bodyEl.firstChild);
38349 createStripElements : function(stripEl, text, closable, tpl)
38351 var td = document.createElement("li"); // was td..
38352 td.className = 'nav-item';
38354 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38357 stripEl.appendChild(td);
38359 td.className = "x-tabs-closable";
38360 if(!this.closeTpl){
38361 this.closeTpl = new Roo.Template(
38362 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38363 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38364 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38367 var el = this.closeTpl.overwrite(td, {"text": text});
38368 var close = el.getElementsByTagName("div")[0];
38369 var inner = el.getElementsByTagName("em")[0];
38370 return {"el": el, "close": close, "inner": inner};
38373 // not sure what this is..
38374 // if(!this.tabTpl){
38375 //this.tabTpl = new Roo.Template(
38376 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38377 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38379 // this.tabTpl = new Roo.Template(
38380 // '<a href="#">' +
38381 // '<span unselectable="on"' +
38382 // (this.disableTooltips ? '' : ' title="{text}"') +
38383 // ' >{text}</span></a>'
38389 var template = tpl || this.tabTpl || false;
38392 template = new Roo.Template(
38393 Roo.bootstrap.version == 4 ?
38395 '<a class="nav-link" href="#" unselectable="on"' +
38396 (this.disableTooltips ? '' : ' title="{text}"') +
38399 '<a class="nav-link" href="#">' +
38400 '<span unselectable="on"' +
38401 (this.disableTooltips ? '' : ' title="{text}"') +
38402 ' >{text}</span></a>'
38407 switch (typeof(template)) {
38411 template = new Roo.Template(template);
38417 var el = template.overwrite(td, {"text": text});
38419 var inner = el.getElementsByTagName("span")[0];
38421 return {"el": el, "inner": inner};
38429 * @class Roo.TabPanelItem
38430 * @extends Roo.util.Observable
38431 * Represents an individual item (tab plus body) in a TabPanel.
38432 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38433 * @param {String} id The id of this TabPanelItem
38434 * @param {String} text The text for the tab of this TabPanelItem
38435 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38437 Roo.bootstrap.panel.TabItem = function(config){
38439 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38440 * @type Roo.TabPanel
38442 this.tabPanel = config.panel;
38444 * The id for this TabPanelItem
38447 this.id = config.id;
38449 this.disabled = false;
38451 this.text = config.text;
38453 this.loaded = false;
38454 this.closable = config.closable;
38457 * The body element for this TabPanelItem.
38458 * @type Roo.Element
38460 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38461 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38462 this.bodyEl.setStyle("display", "block");
38463 this.bodyEl.setStyle("zoom", "1");
38464 //this.hideAction();
38466 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38468 this.el = Roo.get(els.el);
38469 this.inner = Roo.get(els.inner, true);
38470 this.textEl = Roo.bootstrap.version == 4 ?
38471 this.el : Roo.get(this.el.dom.firstChild, true);
38473 this.linode = Roo.get(els.el.parentNode, true);
38474 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38477 // this.el.on("mousedown", this.onTabMouseDown, this);
38478 this.el.on("click", this.onTabClick, this);
38480 if(config.closable){
38481 var c = Roo.get(els.close, true);
38482 c.dom.title = this.closeText;
38483 c.addClassOnOver("close-over");
38484 c.on("click", this.closeClick, this);
38490 * Fires when this tab becomes the active tab.
38491 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38492 * @param {Roo.TabPanelItem} this
38496 * @event beforeclose
38497 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38498 * @param {Roo.TabPanelItem} this
38499 * @param {Object} e Set cancel to true on this object to cancel the close.
38501 "beforeclose": true,
38504 * Fires when this tab is closed.
38505 * @param {Roo.TabPanelItem} this
38509 * @event deactivate
38510 * Fires when this tab is no longer the active tab.
38511 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38512 * @param {Roo.TabPanelItem} this
38514 "deactivate" : true
38516 this.hidden = false;
38518 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38521 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38523 purgeListeners : function(){
38524 Roo.util.Observable.prototype.purgeListeners.call(this);
38525 this.el.removeAllListeners();
38528 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38531 this.status_node.addClass("active");
38534 this.tabPanel.stripWrap.repaint();
38536 this.fireEvent("activate", this.tabPanel, this);
38540 * Returns true if this tab is the active tab.
38541 * @return {Boolean}
38543 isActive : function(){
38544 return this.tabPanel.getActiveTab() == this;
38548 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38551 this.status_node.removeClass("active");
38553 this.fireEvent("deactivate", this.tabPanel, this);
38556 hideAction : function(){
38557 this.bodyEl.hide();
38558 this.bodyEl.setStyle("position", "absolute");
38559 this.bodyEl.setLeft("-20000px");
38560 this.bodyEl.setTop("-20000px");
38563 showAction : function(){
38564 this.bodyEl.setStyle("position", "relative");
38565 this.bodyEl.setTop("");
38566 this.bodyEl.setLeft("");
38567 this.bodyEl.show();
38571 * Set the tooltip for the tab.
38572 * @param {String} tooltip The tab's tooltip
38574 setTooltip : function(text){
38575 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38576 this.textEl.dom.qtip = text;
38577 this.textEl.dom.removeAttribute('title');
38579 this.textEl.dom.title = text;
38583 onTabClick : function(e){
38584 e.preventDefault();
38585 this.tabPanel.activate(this.id);
38588 onTabMouseDown : function(e){
38589 e.preventDefault();
38590 this.tabPanel.activate(this.id);
38593 getWidth : function(){
38594 return this.inner.getWidth();
38597 setWidth : function(width){
38598 var iwidth = width - this.linode.getPadding("lr");
38599 this.inner.setWidth(iwidth);
38600 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38601 this.linode.setWidth(width);
38605 * Show or hide the tab
38606 * @param {Boolean} hidden True to hide or false to show.
38608 setHidden : function(hidden){
38609 this.hidden = hidden;
38610 this.linode.setStyle("display", hidden ? "none" : "");
38614 * Returns true if this tab is "hidden"
38615 * @return {Boolean}
38617 isHidden : function(){
38618 return this.hidden;
38622 * Returns the text for this tab
38625 getText : function(){
38629 autoSize : function(){
38630 //this.el.beginMeasure();
38631 this.textEl.setWidth(1);
38633 * #2804 [new] Tabs in Roojs
38634 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38636 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38637 //this.el.endMeasure();
38641 * Sets the text for the tab (Note: this also sets the tooltip text)
38642 * @param {String} text The tab's text and tooltip
38644 setText : function(text){
38646 this.textEl.update(text);
38647 this.setTooltip(text);
38648 //if(!this.tabPanel.resizeTabs){
38649 // this.autoSize();
38653 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38655 activate : function(){
38656 this.tabPanel.activate(this.id);
38660 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38662 disable : function(){
38663 if(this.tabPanel.active != this){
38664 this.disabled = true;
38665 this.status_node.addClass("disabled");
38670 * Enables this TabPanelItem if it was previously disabled.
38672 enable : function(){
38673 this.disabled = false;
38674 this.status_node.removeClass("disabled");
38678 * Sets the content for this TabPanelItem.
38679 * @param {String} content The content
38680 * @param {Boolean} loadScripts true to look for and load scripts
38682 setContent : function(content, loadScripts){
38683 this.bodyEl.update(content, loadScripts);
38687 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38688 * @return {Roo.UpdateManager} The UpdateManager
38690 getUpdateManager : function(){
38691 return this.bodyEl.getUpdateManager();
38695 * Set a URL to be used to load the content for this TabPanelItem.
38696 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38697 * @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)
38698 * @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)
38699 * @return {Roo.UpdateManager} The UpdateManager
38701 setUrl : function(url, params, loadOnce){
38702 if(this.refreshDelegate){
38703 this.un('activate', this.refreshDelegate);
38705 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38706 this.on("activate", this.refreshDelegate);
38707 return this.bodyEl.getUpdateManager();
38711 _handleRefresh : function(url, params, loadOnce){
38712 if(!loadOnce || !this.loaded){
38713 var updater = this.bodyEl.getUpdateManager();
38714 updater.update(url, params, this._setLoaded.createDelegate(this));
38719 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38720 * Will fail silently if the setUrl method has not been called.
38721 * This does not activate the panel, just updates its content.
38723 refresh : function(){
38724 if(this.refreshDelegate){
38725 this.loaded = false;
38726 this.refreshDelegate();
38731 _setLoaded : function(){
38732 this.loaded = true;
38736 closeClick : function(e){
38739 this.fireEvent("beforeclose", this, o);
38740 if(o.cancel !== true){
38741 this.tabPanel.removeTab(this.id);
38745 * The text displayed in the tooltip for the close icon.
38748 closeText : "Close this tab"
38751 * This script refer to:
38752 * Title: International Telephone Input
38753 * Author: Jack O'Connor
38754 * Code version: v12.1.12
38755 * Availability: https://github.com/jackocnr/intl-tel-input.git
38758 Roo.bootstrap.PhoneInputData = function() {
38761 "Afghanistan (افغانستان)",
38766 "Albania (Shqipëri)",
38771 "Algeria (الجزائر)",
38796 "Antigua and Barbuda",
38806 "Armenia (Հայաստան)",
38822 "Austria (Österreich)",
38827 "Azerbaijan (Azərbaycan)",
38837 "Bahrain (البحرين)",
38842 "Bangladesh (বাংলাদেশ)",
38852 "Belarus (Беларусь)",
38857 "Belgium (België)",
38887 "Bosnia and Herzegovina (Босна и Херцеговина)",
38902 "British Indian Ocean Territory",
38907 "British Virgin Islands",
38917 "Bulgaria (България)",
38927 "Burundi (Uburundi)",
38932 "Cambodia (កម្ពុជា)",
38937 "Cameroon (Cameroun)",
38946 ["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"]
38949 "Cape Verde (Kabu Verdi)",
38954 "Caribbean Netherlands",
38965 "Central African Republic (République centrafricaine)",
38985 "Christmas Island",
38991 "Cocos (Keeling) Islands",
39002 "Comoros (جزر القمر)",
39007 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39012 "Congo (Republic) (Congo-Brazzaville)",
39032 "Croatia (Hrvatska)",
39053 "Czech Republic (Česká republika)",
39058 "Denmark (Danmark)",
39073 "Dominican Republic (República Dominicana)",
39077 ["809", "829", "849"]
39095 "Equatorial Guinea (Guinea Ecuatorial)",
39115 "Falkland Islands (Islas Malvinas)",
39120 "Faroe Islands (Føroyar)",
39141 "French Guiana (Guyane française)",
39146 "French Polynesia (Polynésie française)",
39161 "Georgia (საქართველო)",
39166 "Germany (Deutschland)",
39186 "Greenland (Kalaallit Nunaat)",
39223 "Guinea-Bissau (Guiné Bissau)",
39248 "Hungary (Magyarország)",
39253 "Iceland (Ísland)",
39273 "Iraq (العراق)",
39289 "Israel (ישראל)",
39316 "Jordan (الأردن)",
39321 "Kazakhstan (Казахстан)",
39342 "Kuwait (الكويت)",
39347 "Kyrgyzstan (Кыргызстан)",
39357 "Latvia (Latvija)",
39362 "Lebanon (لبنان)",
39377 "Libya (ليبيا)",
39387 "Lithuania (Lietuva)",
39402 "Macedonia (FYROM) (Македонија)",
39407 "Madagascar (Madagasikara)",
39437 "Marshall Islands",
39447 "Mauritania (موريتانيا)",
39452 "Mauritius (Moris)",
39473 "Moldova (Republica Moldova)",
39483 "Mongolia (Монгол)",
39488 "Montenegro (Crna Gora)",
39498 "Morocco (المغرب)",
39504 "Mozambique (Moçambique)",
39509 "Myanmar (Burma) (မြန်မာ)",
39514 "Namibia (Namibië)",
39529 "Netherlands (Nederland)",
39534 "New Caledonia (Nouvelle-Calédonie)",
39569 "North Korea (조선 민주주의 인민 공화국)",
39574 "Northern Mariana Islands",
39590 "Pakistan (پاکستان)",
39600 "Palestine (فلسطين)",
39610 "Papua New Guinea",
39652 "Réunion (La Réunion)",
39658 "Romania (România)",
39674 "Saint Barthélemy",
39685 "Saint Kitts and Nevis",
39695 "Saint Martin (Saint-Martin (partie française))",
39701 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39706 "Saint Vincent and the Grenadines",
39721 "São Tomé and Príncipe (São Tomé e Príncipe)",
39726 "Saudi Arabia (المملكة العربية السعودية)",
39731 "Senegal (Sénégal)",
39761 "Slovakia (Slovensko)",
39766 "Slovenia (Slovenija)",
39776 "Somalia (Soomaaliya)",
39786 "South Korea (대한민국)",
39791 "South Sudan (جنوب السودان)",
39801 "Sri Lanka (ශ්රී ලංකාව)",
39806 "Sudan (السودان)",
39816 "Svalbard and Jan Mayen",
39827 "Sweden (Sverige)",
39832 "Switzerland (Schweiz)",
39837 "Syria (سوريا)",
39882 "Trinidad and Tobago",
39887 "Tunisia (تونس)",
39892 "Turkey (Türkiye)",
39902 "Turks and Caicos Islands",
39912 "U.S. Virgin Islands",
39922 "Ukraine (Україна)",
39927 "United Arab Emirates (الإمارات العربية المتحدة)",
39949 "Uzbekistan (Oʻzbekiston)",
39959 "Vatican City (Città del Vaticano)",
39970 "Vietnam (Việt Nam)",
39975 "Wallis and Futuna (Wallis-et-Futuna)",
39980 "Western Sahara (الصحراء الغربية)",
39986 "Yemen (اليمن)",
40010 * This script refer to:
40011 * Title: International Telephone Input
40012 * Author: Jack O'Connor
40013 * Code version: v12.1.12
40014 * Availability: https://github.com/jackocnr/intl-tel-input.git
40018 * @class Roo.bootstrap.PhoneInput
40019 * @extends Roo.bootstrap.TriggerField
40020 * An input with International dial-code selection
40022 * @cfg {String} defaultDialCode default '+852'
40023 * @cfg {Array} preferedCountries default []
40026 * Create a new PhoneInput.
40027 * @param {Object} config Configuration options
40030 Roo.bootstrap.PhoneInput = function(config) {
40031 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40034 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40036 listWidth: undefined,
40038 selectedClass: 'active',
40040 invalidClass : "has-warning",
40042 validClass: 'has-success',
40044 allowed: '0123456789',
40049 * @cfg {String} defaultDialCode The default dial code when initializing the input
40051 defaultDialCode: '+852',
40054 * @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
40056 preferedCountries: false,
40058 getAutoCreate : function()
40060 var data = Roo.bootstrap.PhoneInputData();
40061 var align = this.labelAlign || this.parentLabelAlign();
40064 this.allCountries = [];
40065 this.dialCodeMapping = [];
40067 for (var i = 0; i < data.length; i++) {
40069 this.allCountries[i] = {
40073 priority: c[3] || 0,
40074 areaCodes: c[4] || null
40076 this.dialCodeMapping[c[2]] = {
40079 priority: c[3] || 0,
40080 areaCodes: c[4] || null
40092 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40093 maxlength: this.max_length,
40094 cls : 'form-control tel-input',
40095 autocomplete: 'new-password'
40098 var hiddenInput = {
40101 cls: 'hidden-tel-input'
40105 hiddenInput.name = this.name;
40108 if (this.disabled) {
40109 input.disabled = true;
40112 var flag_container = {
40129 cls: this.hasFeedback ? 'has-feedback' : '',
40135 cls: 'dial-code-holder',
40142 cls: 'roo-select2-container input-group',
40149 if (this.fieldLabel.length) {
40152 tooltip: 'This field is required'
40158 cls: 'control-label',
40164 html: this.fieldLabel
40167 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40173 if(this.indicatorpos == 'right') {
40174 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40181 if(align == 'left') {
40189 if(this.labelWidth > 12){
40190 label.style = "width: " + this.labelWidth + 'px';
40192 if(this.labelWidth < 13 && this.labelmd == 0){
40193 this.labelmd = this.labelWidth;
40195 if(this.labellg > 0){
40196 label.cls += ' col-lg-' + this.labellg;
40197 input.cls += ' col-lg-' + (12 - this.labellg);
40199 if(this.labelmd > 0){
40200 label.cls += ' col-md-' + this.labelmd;
40201 container.cls += ' col-md-' + (12 - this.labelmd);
40203 if(this.labelsm > 0){
40204 label.cls += ' col-sm-' + this.labelsm;
40205 container.cls += ' col-sm-' + (12 - this.labelsm);
40207 if(this.labelxs > 0){
40208 label.cls += ' col-xs-' + this.labelxs;
40209 container.cls += ' col-xs-' + (12 - this.labelxs);
40219 var settings = this;
40221 ['xs','sm','md','lg'].map(function(size){
40222 if (settings[size]) {
40223 cfg.cls += ' col-' + size + '-' + settings[size];
40227 this.store = new Roo.data.Store({
40228 proxy : new Roo.data.MemoryProxy({}),
40229 reader : new Roo.data.JsonReader({
40240 'name' : 'dialCode',
40244 'name' : 'priority',
40248 'name' : 'areaCodes',
40255 if(!this.preferedCountries) {
40256 this.preferedCountries = [
40263 var p = this.preferedCountries.reverse();
40266 for (var i = 0; i < p.length; i++) {
40267 for (var j = 0; j < this.allCountries.length; j++) {
40268 if(this.allCountries[j].iso2 == p[i]) {
40269 var t = this.allCountries[j];
40270 this.allCountries.splice(j,1);
40271 this.allCountries.unshift(t);
40277 this.store.proxy.data = {
40279 data: this.allCountries
40285 initEvents : function()
40288 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40290 this.indicator = this.indicatorEl();
40291 this.flag = this.flagEl();
40292 this.dialCodeHolder = this.dialCodeHolderEl();
40294 this.trigger = this.el.select('div.flag-box',true).first();
40295 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40300 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40301 _this.list.setWidth(lw);
40304 this.list.on('mouseover', this.onViewOver, this);
40305 this.list.on('mousemove', this.onViewMove, this);
40306 this.inputEl().on("keyup", this.onKeyUp, this);
40307 this.inputEl().on("keypress", this.onKeyPress, this);
40309 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40311 this.view = new Roo.View(this.list, this.tpl, {
40312 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40315 this.view.on('click', this.onViewClick, this);
40316 this.setValue(this.defaultDialCode);
40319 onTriggerClick : function(e)
40321 Roo.log('trigger click');
40326 if(this.isExpanded()){
40328 this.hasFocus = false;
40330 this.store.load({});
40331 this.hasFocus = true;
40336 isExpanded : function()
40338 return this.list.isVisible();
40341 collapse : function()
40343 if(!this.isExpanded()){
40347 Roo.get(document).un('mousedown', this.collapseIf, this);
40348 Roo.get(document).un('mousewheel', this.collapseIf, this);
40349 this.fireEvent('collapse', this);
40353 expand : function()
40357 if(this.isExpanded() || !this.hasFocus){
40361 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40362 this.list.setWidth(lw);
40365 this.restrictHeight();
40367 Roo.get(document).on('mousedown', this.collapseIf, this);
40368 Roo.get(document).on('mousewheel', this.collapseIf, this);
40370 this.fireEvent('expand', this);
40373 restrictHeight : function()
40375 this.list.alignTo(this.inputEl(), this.listAlign);
40376 this.list.alignTo(this.inputEl(), this.listAlign);
40379 onViewOver : function(e, t)
40381 if(this.inKeyMode){
40384 var item = this.view.findItemFromChild(t);
40387 var index = this.view.indexOf(item);
40388 this.select(index, false);
40393 onViewClick : function(view, doFocus, el, e)
40395 var index = this.view.getSelectedIndexes()[0];
40397 var r = this.store.getAt(index);
40400 this.onSelect(r, index);
40402 if(doFocus !== false && !this.blockFocus){
40403 this.inputEl().focus();
40407 onViewMove : function(e, t)
40409 this.inKeyMode = false;
40412 select : function(index, scrollIntoView)
40414 this.selectedIndex = index;
40415 this.view.select(index);
40416 if(scrollIntoView !== false){
40417 var el = this.view.getNode(index);
40419 this.list.scrollChildIntoView(el, false);
40424 createList : function()
40426 this.list = Roo.get(document.body).createChild({
40428 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40429 style: 'display:none'
40432 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40435 collapseIf : function(e)
40437 var in_combo = e.within(this.el);
40438 var in_list = e.within(this.list);
40439 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40441 if (in_combo || in_list || is_list) {
40447 onSelect : function(record, index)
40449 if(this.fireEvent('beforeselect', this, record, index) !== false){
40451 this.setFlagClass(record.data.iso2);
40452 this.setDialCode(record.data.dialCode);
40453 this.hasFocus = false;
40455 this.fireEvent('select', this, record, index);
40459 flagEl : function()
40461 var flag = this.el.select('div.flag',true).first();
40468 dialCodeHolderEl : function()
40470 var d = this.el.select('input.dial-code-holder',true).first();
40477 setDialCode : function(v)
40479 this.dialCodeHolder.dom.value = '+'+v;
40482 setFlagClass : function(n)
40484 this.flag.dom.className = 'flag '+n;
40487 getValue : function()
40489 var v = this.inputEl().getValue();
40490 if(this.dialCodeHolder) {
40491 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40496 setValue : function(v)
40498 var d = this.getDialCode(v);
40500 //invalid dial code
40501 if(v.length == 0 || !d || d.length == 0) {
40503 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40504 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40510 this.setFlagClass(this.dialCodeMapping[d].iso2);
40511 this.setDialCode(d);
40512 this.inputEl().dom.value = v.replace('+'+d,'');
40513 this.hiddenEl().dom.value = this.getValue();
40518 getDialCode : function(v)
40522 if (v.length == 0) {
40523 return this.dialCodeHolder.dom.value;
40527 if (v.charAt(0) != "+") {
40530 var numericChars = "";
40531 for (var i = 1; i < v.length; i++) {
40532 var c = v.charAt(i);
40535 if (this.dialCodeMapping[numericChars]) {
40536 dialCode = v.substr(1, i);
40538 if (numericChars.length == 4) {
40548 this.setValue(this.defaultDialCode);
40552 hiddenEl : function()
40554 return this.el.select('input.hidden-tel-input',true).first();
40557 // after setting val
40558 onKeyUp : function(e){
40559 this.setValue(this.getValue());
40562 onKeyPress : function(e){
40563 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40570 * @class Roo.bootstrap.MoneyField
40571 * @extends Roo.bootstrap.ComboBox
40572 * Bootstrap MoneyField class
40575 * Create a new MoneyField.
40576 * @param {Object} config Configuration options
40579 Roo.bootstrap.MoneyField = function(config) {
40581 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40585 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40588 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40590 allowDecimals : true,
40592 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40594 decimalSeparator : ".",
40596 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40598 decimalPrecision : 0,
40600 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40602 allowNegative : true,
40604 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40608 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40610 minValue : Number.NEGATIVE_INFINITY,
40612 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40614 maxValue : Number.MAX_VALUE,
40616 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40618 minText : "The minimum value for this field is {0}",
40620 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40622 maxText : "The maximum value for this field is {0}",
40624 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40625 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40627 nanText : "{0} is not a valid number",
40629 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40633 * @cfg {String} defaults currency of the MoneyField
40634 * value should be in lkey
40636 defaultCurrency : false,
40638 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40640 thousandsDelimiter : false,
40642 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40653 getAutoCreate : function()
40655 var align = this.labelAlign || this.parentLabelAlign();
40667 cls : 'form-control roo-money-amount-input',
40668 autocomplete: 'new-password'
40671 var hiddenInput = {
40675 cls: 'hidden-number-input'
40678 if(this.max_length) {
40679 input.maxlength = this.max_length;
40683 hiddenInput.name = this.name;
40686 if (this.disabled) {
40687 input.disabled = true;
40690 var clg = 12 - this.inputlg;
40691 var cmd = 12 - this.inputmd;
40692 var csm = 12 - this.inputsm;
40693 var cxs = 12 - this.inputxs;
40697 cls : 'row roo-money-field',
40701 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40705 cls: 'roo-select2-container input-group',
40709 cls : 'form-control roo-money-currency-input',
40710 autocomplete: 'new-password',
40712 name : this.currencyName
40716 cls : 'input-group-addon',
40730 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40734 cls: this.hasFeedback ? 'has-feedback' : '',
40745 if (this.fieldLabel.length) {
40748 tooltip: 'This field is required'
40754 cls: 'control-label',
40760 html: this.fieldLabel
40763 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40769 if(this.indicatorpos == 'right') {
40770 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40777 if(align == 'left') {
40785 if(this.labelWidth > 12){
40786 label.style = "width: " + this.labelWidth + 'px';
40788 if(this.labelWidth < 13 && this.labelmd == 0){
40789 this.labelmd = this.labelWidth;
40791 if(this.labellg > 0){
40792 label.cls += ' col-lg-' + this.labellg;
40793 input.cls += ' col-lg-' + (12 - this.labellg);
40795 if(this.labelmd > 0){
40796 label.cls += ' col-md-' + this.labelmd;
40797 container.cls += ' col-md-' + (12 - this.labelmd);
40799 if(this.labelsm > 0){
40800 label.cls += ' col-sm-' + this.labelsm;
40801 container.cls += ' col-sm-' + (12 - this.labelsm);
40803 if(this.labelxs > 0){
40804 label.cls += ' col-xs-' + this.labelxs;
40805 container.cls += ' col-xs-' + (12 - this.labelxs);
40816 var settings = this;
40818 ['xs','sm','md','lg'].map(function(size){
40819 if (settings[size]) {
40820 cfg.cls += ' col-' + size + '-' + settings[size];
40827 initEvents : function()
40829 this.indicator = this.indicatorEl();
40831 this.initCurrencyEvent();
40833 this.initNumberEvent();
40836 initCurrencyEvent : function()
40839 throw "can not find store for combo";
40842 this.store = Roo.factory(this.store, Roo.data);
40843 this.store.parent = this;
40847 this.triggerEl = this.el.select('.input-group-addon', true).first();
40849 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40854 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40855 _this.list.setWidth(lw);
40858 this.list.on('mouseover', this.onViewOver, this);
40859 this.list.on('mousemove', this.onViewMove, this);
40860 this.list.on('scroll', this.onViewScroll, this);
40863 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40866 this.view = new Roo.View(this.list, this.tpl, {
40867 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40870 this.view.on('click', this.onViewClick, this);
40872 this.store.on('beforeload', this.onBeforeLoad, this);
40873 this.store.on('load', this.onLoad, this);
40874 this.store.on('loadexception', this.onLoadException, this);
40876 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40877 "up" : function(e){
40878 this.inKeyMode = true;
40882 "down" : function(e){
40883 if(!this.isExpanded()){
40884 this.onTriggerClick();
40886 this.inKeyMode = true;
40891 "enter" : function(e){
40894 if(this.fireEvent("specialkey", this, e)){
40895 this.onViewClick(false);
40901 "esc" : function(e){
40905 "tab" : function(e){
40908 if(this.fireEvent("specialkey", this, e)){
40909 this.onViewClick(false);
40917 doRelay : function(foo, bar, hname){
40918 if(hname == 'down' || this.scope.isExpanded()){
40919 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40927 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40931 initNumberEvent : function(e)
40933 this.inputEl().on("keydown" , this.fireKey, this);
40934 this.inputEl().on("focus", this.onFocus, this);
40935 this.inputEl().on("blur", this.onBlur, this);
40937 this.inputEl().relayEvent('keyup', this);
40939 if(this.indicator){
40940 this.indicator.addClass('invisible');
40943 this.originalValue = this.getValue();
40945 if(this.validationEvent == 'keyup'){
40946 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40947 this.inputEl().on('keyup', this.filterValidation, this);
40949 else if(this.validationEvent !== false){
40950 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40953 if(this.selectOnFocus){
40954 this.on("focus", this.preFocus, this);
40957 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40958 this.inputEl().on("keypress", this.filterKeys, this);
40960 this.inputEl().relayEvent('keypress', this);
40963 var allowed = "0123456789";
40965 if(this.allowDecimals){
40966 allowed += this.decimalSeparator;
40969 if(this.allowNegative){
40973 if(this.thousandsDelimiter) {
40977 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40979 var keyPress = function(e){
40981 var k = e.getKey();
40983 var c = e.getCharCode();
40986 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40987 allowed.indexOf(String.fromCharCode(c)) === -1
40993 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40997 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41002 this.inputEl().on("keypress", keyPress, this);
41006 onTriggerClick : function(e)
41013 this.loadNext = false;
41015 if(this.isExpanded()){
41020 this.hasFocus = true;
41022 if(this.triggerAction == 'all') {
41023 this.doQuery(this.allQuery, true);
41027 this.doQuery(this.getRawValue());
41030 getCurrency : function()
41032 var v = this.currencyEl().getValue();
41037 restrictHeight : function()
41039 this.list.alignTo(this.currencyEl(), this.listAlign);
41040 this.list.alignTo(this.currencyEl(), this.listAlign);
41043 onViewClick : function(view, doFocus, el, e)
41045 var index = this.view.getSelectedIndexes()[0];
41047 var r = this.store.getAt(index);
41050 this.onSelect(r, index);
41054 onSelect : function(record, index){
41056 if(this.fireEvent('beforeselect', this, record, index) !== false){
41058 this.setFromCurrencyData(index > -1 ? record.data : false);
41062 this.fireEvent('select', this, record, index);
41066 setFromCurrencyData : function(o)
41070 this.lastCurrency = o;
41072 if (this.currencyField) {
41073 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41075 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41078 this.lastSelectionText = currency;
41080 //setting default currency
41081 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41082 this.setCurrency(this.defaultCurrency);
41086 this.setCurrency(currency);
41089 setFromData : function(o)
41093 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41095 this.setFromCurrencyData(c);
41100 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41102 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41105 this.setValue(value);
41109 setCurrency : function(v)
41111 this.currencyValue = v;
41114 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41119 setValue : function(v)
41121 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41127 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41129 this.inputEl().dom.value = (v == '') ? '' :
41130 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41132 if(!this.allowZero && v === '0') {
41133 this.hiddenEl().dom.value = '';
41134 this.inputEl().dom.value = '';
41141 getRawValue : function()
41143 var v = this.inputEl().getValue();
41148 getValue : function()
41150 return this.fixPrecision(this.parseValue(this.getRawValue()));
41153 parseValue : function(value)
41155 if(this.thousandsDelimiter) {
41157 r = new RegExp(",", "g");
41158 value = value.replace(r, "");
41161 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41162 return isNaN(value) ? '' : value;
41166 fixPrecision : function(value)
41168 if(this.thousandsDelimiter) {
41170 r = new RegExp(",", "g");
41171 value = value.replace(r, "");
41174 var nan = isNaN(value);
41176 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41177 return nan ? '' : value;
41179 return parseFloat(value).toFixed(this.decimalPrecision);
41182 decimalPrecisionFcn : function(v)
41184 return Math.floor(v);
41187 validateValue : function(value)
41189 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41193 var num = this.parseValue(value);
41196 this.markInvalid(String.format(this.nanText, value));
41200 if(num < this.minValue){
41201 this.markInvalid(String.format(this.minText, this.minValue));
41205 if(num > this.maxValue){
41206 this.markInvalid(String.format(this.maxText, this.maxValue));
41213 validate : function()
41215 if(this.disabled || this.allowBlank){
41220 var currency = this.getCurrency();
41222 if(this.validateValue(this.getRawValue()) && currency.length){
41227 this.markInvalid();
41231 getName: function()
41236 beforeBlur : function()
41242 var v = this.parseValue(this.getRawValue());
41249 onBlur : function()
41253 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41254 //this.el.removeClass(this.focusClass);
41257 this.hasFocus = false;
41259 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41263 var v = this.getValue();
41265 if(String(v) !== String(this.startValue)){
41266 this.fireEvent('change', this, v, this.startValue);
41269 this.fireEvent("blur", this);
41272 inputEl : function()
41274 return this.el.select('.roo-money-amount-input', true).first();
41277 currencyEl : function()
41279 return this.el.select('.roo-money-currency-input', true).first();
41282 hiddenEl : function()
41284 return this.el.select('input.hidden-number-input',true).first();