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
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.el.select('.modal-footer div').first());
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;
2844 cls: "modal-dialog " + size,
2847 cls : "modal-content",
2850 cls : 'modal-header',
2855 cls : 'modal-footer',
2859 cls: 'btn-' + this.buttonPosition
2876 modal.cls += ' fade';
2882 getChildContainer : function() {
2887 getButtonContainer : function() {
2888 return this.el.select('.modal-footer div',true).first();
2891 initEvents : function()
2893 if (this.allow_close) {
2894 this.closeEl.on('click', this.hide, this);
2896 Roo.EventManager.onWindowResize(this.resize, this, true);
2903 this.maskEl.setSize(
2904 Roo.lib.Dom.getViewWidth(true),
2905 Roo.lib.Dom.getViewHeight(true)
2908 if (this.fitwindow) {
2910 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2911 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2916 if(this.max_width !== 0) {
2918 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2921 this.setSize(w, this.height);
2925 if(this.max_height) {
2926 this.setSize(w,Math.min(
2928 Roo.lib.Dom.getViewportHeight(true) - 60
2934 if(!this.fit_content) {
2935 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2939 this.setSize(w, Math.min(
2941 this.headerEl.getHeight() +
2942 this.footerEl.getHeight() +
2943 this.getChildHeight(this.bodyEl.dom.childNodes),
2944 Roo.lib.Dom.getViewportHeight(true) - 60)
2950 setSize : function(w,h)
2961 if (!this.rendered) {
2965 //this.el.setStyle('display', 'block');
2966 this.el.removeClass('hideing');
2967 this.el.dom.style.display='block';
2969 Roo.get(document.body).addClass('modal-open');
2971 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2974 this.el.addClass('show');
2975 this.el.addClass('in');
2978 this.el.addClass('show');
2979 this.el.addClass('in');
2982 // not sure how we can show data in here..
2984 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2987 Roo.get(document.body).addClass("x-body-masked");
2989 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2990 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2991 this.maskEl.dom.style.display = 'block';
2992 this.maskEl.addClass('show');
2997 this.fireEvent('show', this);
2999 // set zindex here - otherwise it appears to be ignored...
3000 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3003 this.items.forEach( function(e) {
3004 e.layout ? e.layout() : false;
3012 if(this.fireEvent("beforehide", this) !== false){
3014 this.maskEl.removeClass('show');
3016 this.maskEl.dom.style.display = '';
3017 Roo.get(document.body).removeClass("x-body-masked");
3018 this.el.removeClass('in');
3019 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3021 if(this.animate){ // why
3022 this.el.addClass('hideing');
3023 this.el.removeClass('show');
3025 if (!this.el.hasClass('hideing')) {
3026 return; // it's been shown again...
3029 this.el.dom.style.display='';
3031 Roo.get(document.body).removeClass('modal-open');
3032 this.el.removeClass('hideing');
3036 this.el.removeClass('show');
3037 this.el.dom.style.display='';
3038 Roo.get(document.body).removeClass('modal-open');
3041 this.fireEvent('hide', this);
3044 isVisible : function()
3047 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3051 addButton : function(str, cb)
3055 var b = Roo.apply({}, { html : str } );
3056 b.xns = b.xns || Roo.bootstrap;
3057 b.xtype = b.xtype || 'Button';
3058 if (typeof(b.listeners) == 'undefined') {
3059 b.listeners = { click : cb.createDelegate(this) };
3062 var btn = Roo.factory(b);
3064 btn.render(this.el.select('.modal-footer div').first());
3070 setDefaultButton : function(btn)
3072 //this.el.select('.modal-footer').()
3076 resizeTo: function(w,h)
3080 this.dialogEl.setWidth(w);
3081 if (this.diff === false) {
3082 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3085 this.bodyEl.setHeight(h - this.diff);
3087 this.fireEvent('resize', this);
3090 setContentSize : function(w, h)
3094 onButtonClick: function(btn,e)
3097 this.fireEvent('btnclick', btn.name, e);
3100 * Set the title of the Dialog
3101 * @param {String} str new Title
3103 setTitle: function(str) {
3104 this.titleEl.dom.innerHTML = str;
3107 * Set the body of the Dialog
3108 * @param {String} str new Title
3110 setBody: function(str) {
3111 this.bodyEl.dom.innerHTML = str;
3114 * Set the body of the Dialog using the template
3115 * @param {Obj} data - apply this data to the template and replace the body contents.
3117 applyBody: function(obj)
3120 Roo.log("Error - using apply Body without a template");
3123 this.tmpl.overwrite(this.bodyEl, obj);
3126 getChildHeight : function(child_nodes)
3130 child_nodes.length == 0
3135 var child_height = 0;
3137 for(var i = 0; i < child_nodes.length; i++) {
3140 * for modal with tabs...
3141 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3143 var layout_childs = child_nodes[i].childNodes;
3145 for(var j = 0; j < layout_childs.length; j++) {
3147 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3149 var layout_body_childs = layout_childs[j].childNodes;
3151 for(var k = 0; k < layout_body_childs.length; k++) {
3153 if(layout_body_childs[k].classList.contains('navbar')) {
3154 child_height += layout_body_childs[k].offsetHeight;
3158 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3160 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3162 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3164 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3165 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3180 child_height += child_nodes[i].offsetHeight;
3181 // Roo.log(child_nodes[i].offsetHeight);
3184 return child_height;
3190 Roo.apply(Roo.bootstrap.Modal, {
3192 * Button config that displays a single OK button
3201 * Button config that displays Yes and No buttons
3217 * Button config that displays OK and Cancel buttons
3232 * Button config that displays Yes, No and Cancel buttons
3256 * messagebox - can be used as a replace
3260 * @class Roo.MessageBox
3261 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3265 Roo.Msg.alert('Status', 'Changes saved successfully.');
3267 // Prompt for user data:
3268 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3270 // process text value...
3274 // Show a dialog using config options:
3276 title:'Save Changes?',
3277 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3278 buttons: Roo.Msg.YESNOCANCEL,
3285 Roo.bootstrap.MessageBox = function(){
3286 var dlg, opt, mask, waitTimer;
3287 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3288 var buttons, activeTextEl, bwidth;
3292 var handleButton = function(button){
3294 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3298 var handleHide = function(){
3300 dlg.el.removeClass(opt.cls);
3303 // Roo.TaskMgr.stop(waitTimer);
3304 // waitTimer = null;
3309 var updateButtons = function(b){
3312 buttons["ok"].hide();
3313 buttons["cancel"].hide();
3314 buttons["yes"].hide();
3315 buttons["no"].hide();
3316 //dlg.footer.dom.style.display = 'none';
3319 dlg.footerEl.dom.style.display = '';
3320 for(var k in buttons){
3321 if(typeof buttons[k] != "function"){
3324 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3325 width += buttons[k].el.getWidth()+15;
3335 var handleEsc = function(d, k, e){
3336 if(opt && opt.closable !== false){
3346 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3347 * @return {Roo.BasicDialog} The BasicDialog element
3349 getDialog : function(){
3351 dlg = new Roo.bootstrap.Modal( {
3354 //constraintoviewport:false,
3356 //collapsible : false,
3361 //buttonAlign:"center",
3362 closeClick : function(){
3363 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3366 handleButton("cancel");
3371 dlg.on("hide", handleHide);
3373 //dlg.addKeyListener(27, handleEsc);
3375 this.buttons = buttons;
3376 var bt = this.buttonText;
3377 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3378 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3379 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3380 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3382 bodyEl = dlg.bodyEl.createChild({
3384 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3385 '<textarea class="roo-mb-textarea"></textarea>' +
3386 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3388 msgEl = bodyEl.dom.firstChild;
3389 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3390 textboxEl.enableDisplayMode();
3391 textboxEl.addKeyListener([10,13], function(){
3392 if(dlg.isVisible() && opt && opt.buttons){
3395 }else if(opt.buttons.yes){
3396 handleButton("yes");
3400 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3401 textareaEl.enableDisplayMode();
3402 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3403 progressEl.enableDisplayMode();
3405 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3406 var pf = progressEl.dom.firstChild;
3408 pp = Roo.get(pf.firstChild);
3409 pp.setHeight(pf.offsetHeight);
3417 * Updates the message box body text
3418 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3419 * the XHTML-compliant non-breaking space character '&#160;')
3420 * @return {Roo.MessageBox} This message box
3422 updateText : function(text)
3424 if(!dlg.isVisible() && !opt.width){
3425 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3426 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3428 msgEl.innerHTML = text || ' ';
3430 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3431 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3433 Math.min(opt.width || cw , this.maxWidth),
3434 Math.max(opt.minWidth || this.minWidth, bwidth)
3437 activeTextEl.setWidth(w);
3439 if(dlg.isVisible()){
3440 dlg.fixedcenter = false;
3442 // to big, make it scroll. = But as usual stupid IE does not support
3445 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3446 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3447 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3449 bodyEl.dom.style.height = '';
3450 bodyEl.dom.style.overflowY = '';
3453 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3455 bodyEl.dom.style.overflowX = '';
3458 dlg.setContentSize(w, bodyEl.getHeight());
3459 if(dlg.isVisible()){
3460 dlg.fixedcenter = true;
3466 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3467 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3468 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3469 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3470 * @return {Roo.MessageBox} This message box
3472 updateProgress : function(value, text){
3474 this.updateText(text);
3477 if (pp) { // weird bug on my firefox - for some reason this is not defined
3478 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3479 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3485 * Returns true if the message box is currently displayed
3486 * @return {Boolean} True if the message box is visible, else false
3488 isVisible : function(){
3489 return dlg && dlg.isVisible();
3493 * Hides the message box if it is displayed
3496 if(this.isVisible()){
3502 * Displays a new message box, or reinitializes an existing message box, based on the config options
3503 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3504 * The following config object properties are supported:
3506 Property Type Description
3507 ---------- --------------- ------------------------------------------------------------------------------------
3508 animEl String/Element An id or Element from which the message box should animate as it opens and
3509 closes (defaults to undefined)
3510 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3511 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3512 closable Boolean False to hide the top-right close button (defaults to true). Note that
3513 progress and wait dialogs will ignore this property and always hide the
3514 close button as they can only be closed programmatically.
3515 cls String A custom CSS class to apply to the message box element
3516 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3517 displayed (defaults to 75)
3518 fn Function A callback function to execute after closing the dialog. The arguments to the
3519 function will be btn (the name of the button that was clicked, if applicable,
3520 e.g. "ok"), and text (the value of the active text field, if applicable).
3521 Progress and wait dialogs will ignore this option since they do not respond to
3522 user actions and can only be closed programmatically, so any required function
3523 should be called by the same code after it closes the dialog.
3524 icon String A CSS class that provides a background image to be used as an icon for
3525 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3526 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3527 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3528 modal Boolean False to allow user interaction with the page while the message box is
3529 displayed (defaults to true)
3530 msg String A string that will replace the existing message box body text (defaults
3531 to the XHTML-compliant non-breaking space character ' ')
3532 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3533 progress Boolean True to display a progress bar (defaults to false)
3534 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3535 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3536 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3537 title String The title text
3538 value String The string value to set into the active textbox element if displayed
3539 wait Boolean True to display a progress bar (defaults to false)
3540 width Number The width of the dialog in pixels
3547 msg: 'Please enter your address:',
3549 buttons: Roo.MessageBox.OKCANCEL,
3552 animEl: 'addAddressBtn'
3555 * @param {Object} config Configuration options
3556 * @return {Roo.MessageBox} This message box
3558 show : function(options)
3561 // this causes nightmares if you show one dialog after another
3562 // especially on callbacks..
3564 if(this.isVisible()){
3567 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3568 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3569 Roo.log("New Dialog Message:" + options.msg )
3570 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3571 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3574 var d = this.getDialog();
3576 d.setTitle(opt.title || " ");
3577 d.closeEl.setDisplayed(opt.closable !== false);
3578 activeTextEl = textboxEl;
3579 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3584 textareaEl.setHeight(typeof opt.multiline == "number" ?
3585 opt.multiline : this.defaultTextHeight);
3586 activeTextEl = textareaEl;
3595 progressEl.setDisplayed(opt.progress === true);
3596 this.updateProgress(0);
3597 activeTextEl.dom.value = opt.value || "";
3599 dlg.setDefaultButton(activeTextEl);
3601 var bs = opt.buttons;
3605 }else if(bs && bs.yes){
3606 db = buttons["yes"];
3608 dlg.setDefaultButton(db);
3610 bwidth = updateButtons(opt.buttons);
3611 this.updateText(opt.msg);
3613 d.el.addClass(opt.cls);
3615 d.proxyDrag = opt.proxyDrag === true;
3616 d.modal = opt.modal !== false;
3617 d.mask = opt.modal !== false ? mask : false;
3619 // force it to the end of the z-index stack so it gets a cursor in FF
3620 document.body.appendChild(dlg.el.dom);
3621 d.animateTarget = null;
3622 d.show(options.animEl);
3628 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3629 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3630 * and closing the message box when the process is complete.
3631 * @param {String} title The title bar text
3632 * @param {String} msg The message box body text
3633 * @return {Roo.MessageBox} This message box
3635 progress : function(title, msg){
3642 minWidth: this.minProgressWidth,
3649 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3650 * If a callback function is passed it will be called after the user clicks the button, and the
3651 * id of the button that was clicked will be passed as the only parameter to the callback
3652 * (could also be the top-right close button).
3653 * @param {String} title The title bar text
3654 * @param {String} msg The message box body text
3655 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3656 * @param {Object} scope (optional) The scope of the callback function
3657 * @return {Roo.MessageBox} This message box
3659 alert : function(title, msg, fn, scope)
3674 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3675 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3676 * You are responsible for closing the message box when the process is complete.
3677 * @param {String} msg The message box body text
3678 * @param {String} title (optional) The title bar text
3679 * @return {Roo.MessageBox} This message box
3681 wait : function(msg, title){
3692 waitTimer = Roo.TaskMgr.start({
3694 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3702 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3703 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3704 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3705 * @param {String} title The title bar text
3706 * @param {String} msg The message box body text
3707 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708 * @param {Object} scope (optional) The scope of the callback function
3709 * @return {Roo.MessageBox} This message box
3711 confirm : function(title, msg, fn, scope){
3715 buttons: this.YESNO,
3724 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3725 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3726 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3727 * (could also be the top-right close button) and the text that was entered will be passed as the two
3728 * parameters to the callback.
3729 * @param {String} title The title bar text
3730 * @param {String} msg The message box body text
3731 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3732 * @param {Object} scope (optional) The scope of the callback function
3733 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3734 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3735 * @return {Roo.MessageBox} This message box
3737 prompt : function(title, msg, fn, scope, multiline){
3741 buttons: this.OKCANCEL,
3746 multiline: multiline,
3753 * Button config that displays a single OK button
3758 * Button config that displays Yes and No buttons
3761 YESNO : {yes:true, no:true},
3763 * Button config that displays OK and Cancel buttons
3766 OKCANCEL : {ok:true, cancel:true},
3768 * Button config that displays Yes, No and Cancel buttons
3771 YESNOCANCEL : {yes:true, no:true, cancel:true},
3774 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3777 defaultTextHeight : 75,
3779 * The maximum width in pixels of the message box (defaults to 600)
3784 * The minimum width in pixels of the message box (defaults to 100)
3789 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3790 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3793 minProgressWidth : 250,
3795 * An object containing the default button text strings that can be overriden for localized language support.
3796 * Supported properties are: ok, cancel, yes and no.
3797 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3810 * Shorthand for {@link Roo.MessageBox}
3812 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3813 Roo.Msg = Roo.Msg || Roo.MessageBox;
3822 * @class Roo.bootstrap.Navbar
3823 * @extends Roo.bootstrap.Component
3824 * Bootstrap Navbar class
3827 * Create a new Navbar
3828 * @param {Object} config The config object
3832 Roo.bootstrap.Navbar = function(config){
3833 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3837 * @event beforetoggle
3838 * Fire before toggle the menu
3839 * @param {Roo.EventObject} e
3841 "beforetoggle" : true
3845 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3854 getAutoCreate : function(){
3857 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3861 initEvents :function ()
3863 //Roo.log(this.el.select('.navbar-toggle',true));
3864 this.el.select('.navbar-toggle',true).on('click', function() {
3865 if(this.fireEvent('beforetoggle', this) !== false){
3866 var ce = this.el.select('.navbar-collapse',true).first();
3867 ce.toggleClass('in'); // old...
3868 if (ce.hasClass('collapse')) {
3870 ce.removeClass('collapse');
3871 ce.addClass('show');
3872 var h = ce.getHeight();
3874 ce.removeClass('show');
3875 // at this point we should be able to see it..
3876 ce.addClass('collapsing');
3878 ce.setHeight(0); // resize it ...
3879 ce.on('transitionend', function() {
3880 Roo.log('done transition');
3881 ce.removeClass('collapsing');
3882 ce.addClass('show');
3883 ce.removeClass('collapse');
3885 ce.dom.style.height = '';
3886 }, this, { single: true} );
3890 ce.setHeight(ce.getHeight());
3891 ce.removeClass('show');
3892 ce.addClass('collapsing');
3894 ce.on('transitionend', function() {
3895 ce.dom.style.height = '';
3896 ce.removeClass('collapsing');
3897 ce.addClass('collapse');
3898 }, this, { single: true} );
3910 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3912 var size = this.el.getSize();
3913 this.maskEl.setSize(size.width, size.height);
3914 this.maskEl.enableDisplayMode("block");
3923 getChildContainer : function()
3925 if (this.el.select('.collapse').getCount()) {
3926 return this.el.select('.collapse',true).first();
3959 * @class Roo.bootstrap.NavSimplebar
3960 * @extends Roo.bootstrap.Navbar
3961 * Bootstrap Sidebar class
3963 * @cfg {Boolean} inverse is inverted color
3965 * @cfg {String} type (nav | pills | tabs)
3966 * @cfg {Boolean} arrangement stacked | justified
3967 * @cfg {String} align (left | right) alignment
3969 * @cfg {Boolean} main (true|false) main nav bar? default false
3970 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3972 * @cfg {String} tag (header|footer|nav|div) default is nav
3974 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3978 * Create a new Sidebar
3979 * @param {Object} config The config object
3983 Roo.bootstrap.NavSimplebar = function(config){
3984 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3987 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4003 getAutoCreate : function(){
4007 tag : this.tag || 'div',
4008 cls : 'navbar navbar-expand-lg'
4010 if (['light','white'].indexOf(this.weight) > -1) {
4011 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4013 cfg.cls += ' bg-' + this.weight;
4025 this.type = this.type || 'nav';
4026 if (['tabs','pills'].indexOf(this.type)!==-1) {
4027 cfg.cn[0].cls += ' nav-' + this.type
4031 if (this.type!=='nav') {
4032 Roo.log('nav type must be nav/tabs/pills')
4034 cfg.cn[0].cls += ' navbar-nav'
4040 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4041 cfg.cn[0].cls += ' nav-' + this.arrangement;
4045 if (this.align === 'right') {
4046 cfg.cn[0].cls += ' navbar-right';
4050 cfg.cls += ' navbar-inverse';
4074 * navbar-expand-md fixed-top
4078 * @class Roo.bootstrap.NavHeaderbar
4079 * @extends Roo.bootstrap.NavSimplebar
4080 * Bootstrap Sidebar class
4082 * @cfg {String} brand what is brand
4083 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4084 * @cfg {String} brand_href href of the brand
4085 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4086 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4087 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4088 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4091 * Create a new Sidebar
4092 * @param {Object} config The config object
4096 Roo.bootstrap.NavHeaderbar = function(config){
4097 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4101 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4108 desktopCenter : false,
4111 getAutoCreate : function(){
4114 tag: this.nav || 'nav',
4115 cls: 'navbar navbar-expand-md',
4121 if (this.desktopCenter) {
4122 cn.push({cls : 'container', cn : []});
4130 cls: 'navbar-toggle navbar-toggler',
4131 'data-toggle': 'collapse',
4136 html: 'Toggle navigation'
4140 cls: 'icon-bar navbar-toggler-icon'
4153 cn.push( Roo.bootstrap.version == 4 ? btn : {
4155 cls: 'navbar-header',
4164 cls: 'collapse navbar-collapse',
4168 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4170 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4171 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4173 // tag can override this..
4175 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4178 if (this.brand !== '') {
4179 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4180 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4182 href: this.brand_href ? this.brand_href : '#',
4183 cls: 'navbar-brand',
4191 cfg.cls += ' main-nav';
4199 getHeaderChildContainer : function()
4201 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4202 return this.el.select('.navbar-header',true).first();
4205 return this.getChildContainer();
4209 initEvents : function()
4211 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4213 if (this.autohide) {
4218 Roo.get(document).on('scroll',function(e) {
4219 var ns = Roo.get(document).getScroll().top;
4220 var os = prevScroll;
4224 ft.removeClass('slideDown');
4225 ft.addClass('slideUp');
4228 ft.removeClass('slideUp');
4229 ft.addClass('slideDown');
4250 * @class Roo.bootstrap.NavSidebar
4251 * @extends Roo.bootstrap.Navbar
4252 * Bootstrap Sidebar class
4255 * Create a new Sidebar
4256 * @param {Object} config The config object
4260 Roo.bootstrap.NavSidebar = function(config){
4261 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4264 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4266 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4268 getAutoCreate : function(){
4273 cls: 'sidebar sidebar-nav'
4295 * @class Roo.bootstrap.NavGroup
4296 * @extends Roo.bootstrap.Component
4297 * Bootstrap NavGroup class
4298 * @cfg {String} align (left|right)
4299 * @cfg {Boolean} inverse
4300 * @cfg {String} type (nav|pills|tab) default nav
4301 * @cfg {String} navId - reference Id for navbar.
4305 * Create a new nav group
4306 * @param {Object} config The config object
4309 Roo.bootstrap.NavGroup = function(config){
4310 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4313 Roo.bootstrap.NavGroup.register(this);
4317 * Fires when the active item changes
4318 * @param {Roo.bootstrap.NavGroup} this
4319 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4320 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4327 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4338 getAutoCreate : function()
4340 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4347 if (['tabs','pills'].indexOf(this.type)!==-1) {
4348 cfg.cls += ' nav-' + this.type
4350 if (this.type!=='nav') {
4351 Roo.log('nav type must be nav/tabs/pills')
4353 cfg.cls += ' navbar-nav'
4356 if (this.parent() && this.parent().sidebar) {
4359 cls: 'dashboard-menu sidebar-menu'
4365 if (this.form === true) {
4371 if (this.align === 'right') {
4372 cfg.cls += ' navbar-right ml-md-auto';
4374 cfg.cls += ' navbar-left';
4378 if (this.align === 'right') {
4379 cfg.cls += ' navbar-right ml-md-auto';
4381 cfg.cls += ' mr-auto';
4385 cfg.cls += ' navbar-inverse';
4393 * sets the active Navigation item
4394 * @param {Roo.bootstrap.NavItem} the new current navitem
4396 setActiveItem : function(item)
4399 Roo.each(this.navItems, function(v){
4404 v.setActive(false, true);
4411 item.setActive(true, true);
4412 this.fireEvent('changed', this, item, prev);
4417 * gets the active Navigation item
4418 * @return {Roo.bootstrap.NavItem} the current navitem
4420 getActive : function()
4424 Roo.each(this.navItems, function(v){
4435 indexOfNav : function()
4439 Roo.each(this.navItems, function(v,i){
4450 * adds a Navigation item
4451 * @param {Roo.bootstrap.NavItem} the navitem to add
4453 addItem : function(cfg)
4455 var cn = new Roo.bootstrap.NavItem(cfg);
4457 cn.parentId = this.id;
4458 cn.onRender(this.el, null);
4462 * register a Navigation item
4463 * @param {Roo.bootstrap.NavItem} the navitem to add
4465 register : function(item)
4467 this.navItems.push( item);
4468 item.navId = this.navId;
4473 * clear all the Navigation item
4476 clearAll : function()
4479 this.el.dom.innerHTML = '';
4482 getNavItem: function(tabId)
4485 Roo.each(this.navItems, function(e) {
4486 if (e.tabId == tabId) {
4496 setActiveNext : function()
4498 var i = this.indexOfNav(this.getActive());
4499 if (i > this.navItems.length) {
4502 this.setActiveItem(this.navItems[i+1]);
4504 setActivePrev : function()
4506 var i = this.indexOfNav(this.getActive());
4510 this.setActiveItem(this.navItems[i-1]);
4512 clearWasActive : function(except) {
4513 Roo.each(this.navItems, function(e) {
4514 if (e.tabId != except.tabId && e.was_active) {
4515 e.was_active = false;
4522 getWasActive : function ()
4525 Roo.each(this.navItems, function(e) {
4540 Roo.apply(Roo.bootstrap.NavGroup, {
4544 * register a Navigation Group
4545 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4547 register : function(navgrp)
4549 this.groups[navgrp.navId] = navgrp;
4553 * fetch a Navigation Group based on the navigation ID
4554 * @param {string} the navgroup to add
4555 * @returns {Roo.bootstrap.NavGroup} the navgroup
4557 get: function(navId) {
4558 if (typeof(this.groups[navId]) == 'undefined') {
4560 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4562 return this.groups[navId] ;
4577 * @class Roo.bootstrap.NavItem
4578 * @extends Roo.bootstrap.Component
4579 * Bootstrap Navbar.NavItem class
4580 * @cfg {String} href link to
4581 * @cfg {String} html content of button
4582 * @cfg {String} badge text inside badge
4583 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4584 * @cfg {String} glyphicon DEPRICATED - use fa
4585 * @cfg {String} icon DEPRICATED - use fa
4586 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4587 * @cfg {Boolean} active Is item active
4588 * @cfg {Boolean} disabled Is item disabled
4590 * @cfg {Boolean} preventDefault (true | false) default false
4591 * @cfg {String} tabId the tab that this item activates.
4592 * @cfg {String} tagtype (a|span) render as a href or span?
4593 * @cfg {Boolean} animateRef (true|false) link to element default false
4596 * Create a new Navbar Item
4597 * @param {Object} config The config object
4599 Roo.bootstrap.NavItem = function(config){
4600 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4605 * The raw click event for the entire grid.
4606 * @param {Roo.EventObject} e
4611 * Fires when the active item active state changes
4612 * @param {Roo.bootstrap.NavItem} this
4613 * @param {boolean} state the new state
4619 * Fires when scroll to element
4620 * @param {Roo.bootstrap.NavItem} this
4621 * @param {Object} options
4622 * @param {Roo.EventObject} e
4630 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4639 preventDefault : false,
4646 getAutoCreate : function(){
4655 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4657 if (this.disabled) {
4658 cfg.cls += ' disabled';
4661 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4665 href : this.href || "#",
4666 html: this.html || ''
4669 if (this.tagtype == 'a') {
4670 cfg.cn[0].cls = 'nav-link';
4673 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4676 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4678 if(this.glyphicon) {
4679 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4684 cfg.cn[0].html += " <span class='caret'></span>";
4688 if (this.badge !== '') {
4690 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4698 initEvents: function()
4700 if (typeof (this.menu) != 'undefined') {
4701 this.menu.parentType = this.xtype;
4702 this.menu.triggerEl = this.el;
4703 this.menu = this.addxtype(Roo.apply({}, this.menu));
4706 this.el.select('a',true).on('click', this.onClick, this);
4708 if(this.tagtype == 'span'){
4709 this.el.select('span',true).on('click', this.onClick, this);
4712 // at this point parent should be available..
4713 this.parent().register(this);
4716 onClick : function(e)
4718 if (e.getTarget('.dropdown-menu-item')) {
4719 // did you click on a menu itemm.... - then don't trigger onclick..
4724 this.preventDefault ||
4727 Roo.log("NavItem - prevent Default?");
4731 if (this.disabled) {
4735 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4736 if (tg && tg.transition) {
4737 Roo.log("waiting for the transitionend");
4743 //Roo.log("fire event clicked");
4744 if(this.fireEvent('click', this, e) === false){
4748 if(this.tagtype == 'span'){
4752 //Roo.log(this.href);
4753 var ael = this.el.select('a',true).first();
4756 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4757 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4758 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4759 return; // ignore... - it's a 'hash' to another page.
4761 Roo.log("NavItem - prevent Default?");
4763 this.scrollToElement(e);
4767 var p = this.parent();
4769 if (['tabs','pills'].indexOf(p.type)!==-1) {
4770 if (typeof(p.setActiveItem) !== 'undefined') {
4771 p.setActiveItem(this);
4775 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4776 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4777 // remove the collapsed menu expand...
4778 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4782 isActive: function () {
4785 setActive : function(state, fire, is_was_active)
4787 if (this.active && !state && this.navId) {
4788 this.was_active = true;
4789 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4791 nv.clearWasActive(this);
4795 this.active = state;
4798 this.el.removeClass('active');
4799 } else if (!this.el.hasClass('active')) {
4800 this.el.addClass('active');
4803 this.fireEvent('changed', this, state);
4806 // show a panel if it's registered and related..
4808 if (!this.navId || !this.tabId || !state || is_was_active) {
4812 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4816 var pan = tg.getPanelByName(this.tabId);
4820 // if we can not flip to new panel - go back to old nav highlight..
4821 if (false == tg.showPanel(pan)) {
4822 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4824 var onav = nv.getWasActive();
4826 onav.setActive(true, false, true);
4835 // this should not be here...
4836 setDisabled : function(state)
4838 this.disabled = state;
4840 this.el.removeClass('disabled');
4841 } else if (!this.el.hasClass('disabled')) {
4842 this.el.addClass('disabled');
4848 * Fetch the element to display the tooltip on.
4849 * @return {Roo.Element} defaults to this.el
4851 tooltipEl : function()
4853 return this.el.select('' + this.tagtype + '', true).first();
4856 scrollToElement : function(e)
4858 var c = document.body;
4861 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4863 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4864 c = document.documentElement;
4867 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4873 var o = target.calcOffsetsTo(c);
4880 this.fireEvent('scrollto', this, options, e);
4882 Roo.get(c).scrollTo('top', options.value, true);
4895 * <span> icon </span>
4896 * <span> text </span>
4897 * <span>badge </span>
4901 * @class Roo.bootstrap.NavSidebarItem
4902 * @extends Roo.bootstrap.NavItem
4903 * Bootstrap Navbar.NavSidebarItem class
4904 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4905 * {Boolean} open is the menu open
4906 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4907 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4908 * {String} buttonSize (sm|md|lg)the extra classes for the button
4909 * {Boolean} showArrow show arrow next to the text (default true)
4911 * Create a new Navbar Button
4912 * @param {Object} config The config object
4914 Roo.bootstrap.NavSidebarItem = function(config){
4915 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4920 * The raw click event for the entire grid.
4921 * @param {Roo.EventObject} e
4926 * Fires when the active item active state changes
4927 * @param {Roo.bootstrap.NavSidebarItem} this
4928 * @param {boolean} state the new state
4936 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4938 badgeWeight : 'default',
4944 buttonWeight : 'default',
4950 getAutoCreate : function(){
4955 href : this.href || '#',
4961 if(this.buttonView){
4964 href : this.href || '#',
4965 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4978 cfg.cls += ' active';
4981 if (this.disabled) {
4982 cfg.cls += ' disabled';
4985 cfg.cls += ' open x-open';
4988 if (this.glyphicon || this.icon) {
4989 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4990 a.cn.push({ tag : 'i', cls : c }) ;
4993 if(!this.buttonView){
4996 html : this.html || ''
5003 if (this.badge !== '') {
5004 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5010 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5013 a.cls += ' dropdown-toggle treeview' ;
5019 initEvents : function()
5021 if (typeof (this.menu) != 'undefined') {
5022 this.menu.parentType = this.xtype;
5023 this.menu.triggerEl = this.el;
5024 this.menu = this.addxtype(Roo.apply({}, this.menu));
5027 this.el.on('click', this.onClick, this);
5029 if(this.badge !== ''){
5030 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5035 onClick : function(e)
5042 if(this.preventDefault){
5046 this.fireEvent('click', this);
5049 disable : function()
5051 this.setDisabled(true);
5056 this.setDisabled(false);
5059 setDisabled : function(state)
5061 if(this.disabled == state){
5065 this.disabled = state;
5068 this.el.addClass('disabled');
5072 this.el.removeClass('disabled');
5077 setActive : function(state)
5079 if(this.active == state){
5083 this.active = state;
5086 this.el.addClass('active');
5090 this.el.removeClass('active');
5095 isActive: function ()
5100 setBadge : function(str)
5106 this.badgeEl.dom.innerHTML = str;
5123 * @class Roo.bootstrap.Row
5124 * @extends Roo.bootstrap.Component
5125 * Bootstrap Row class (contains columns...)
5129 * @param {Object} config The config object
5132 Roo.bootstrap.Row = function(config){
5133 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5136 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5138 getAutoCreate : function(){
5157 * @class Roo.bootstrap.Element
5158 * @extends Roo.bootstrap.Component
5159 * Bootstrap Element class
5160 * @cfg {String} html contents of the element
5161 * @cfg {String} tag tag of the element
5162 * @cfg {String} cls class of the element
5163 * @cfg {Boolean} preventDefault (true|false) default false
5164 * @cfg {Boolean} clickable (true|false) default false
5167 * Create a new Element
5168 * @param {Object} config The config object
5171 Roo.bootstrap.Element = function(config){
5172 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5178 * When a element is chick
5179 * @param {Roo.bootstrap.Element} this
5180 * @param {Roo.EventObject} e
5186 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5191 preventDefault: false,
5194 getAutoCreate : function(){
5198 // cls: this.cls, double assign in parent class Component.js :: onRender
5205 initEvents: function()
5207 Roo.bootstrap.Element.superclass.initEvents.call(this);
5210 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 if(this.preventDefault){
5221 this.fireEvent('click', this, e);
5224 getValue : function()
5226 return this.el.dom.innerHTML;
5229 setValue : function(value)
5231 this.el.dom.innerHTML = value;
5246 * @class Roo.bootstrap.Pagination
5247 * @extends Roo.bootstrap.Component
5248 * Bootstrap Pagination class
5249 * @cfg {String} size xs | sm | md | lg
5250 * @cfg {Boolean} inverse false | true
5253 * Create a new Pagination
5254 * @param {Object} config The config object
5257 Roo.bootstrap.Pagination = function(config){
5258 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5261 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5267 getAutoCreate : function(){
5273 cfg.cls += ' inverse';
5279 cfg.cls += " " + this.cls;
5297 * @class Roo.bootstrap.PaginationItem
5298 * @extends Roo.bootstrap.Component
5299 * Bootstrap PaginationItem class
5300 * @cfg {String} html text
5301 * @cfg {String} href the link
5302 * @cfg {Boolean} preventDefault (true | false) default true
5303 * @cfg {Boolean} active (true | false) default false
5304 * @cfg {Boolean} disabled default false
5308 * Create a new PaginationItem
5309 * @param {Object} config The config object
5313 Roo.bootstrap.PaginationItem = function(config){
5314 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5319 * The raw click event for the entire grid.
5320 * @param {Roo.EventObject} e
5326 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5330 preventDefault: true,
5335 getAutoCreate : function(){
5341 href : this.href ? this.href : '#',
5342 html : this.html ? this.html : ''
5352 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5356 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5362 initEvents: function() {
5364 this.el.on('click', this.onClick, this);
5367 onClick : function(e)
5369 Roo.log('PaginationItem on click ');
5370 if(this.preventDefault){
5378 this.fireEvent('click', this, e);
5394 * @class Roo.bootstrap.Slider
5395 * @extends Roo.bootstrap.Component
5396 * Bootstrap Slider class
5399 * Create a new Slider
5400 * @param {Object} config The config object
5403 Roo.bootstrap.Slider = function(config){
5404 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5407 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5409 getAutoCreate : function(){
5413 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5417 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5429 * Ext JS Library 1.1.1
5430 * Copyright(c) 2006-2007, Ext JS, LLC.
5432 * Originally Released Under LGPL - original licence link has changed is not relivant.
5435 * <script type="text/javascript">
5440 * @class Roo.grid.ColumnModel
5441 * @extends Roo.util.Observable
5442 * This is the default implementation of a ColumnModel used by the Grid. It defines
5443 * the columns in the grid.
5446 var colModel = new Roo.grid.ColumnModel([
5447 {header: "Ticker", width: 60, sortable: true, locked: true},
5448 {header: "Company Name", width: 150, sortable: true},
5449 {header: "Market Cap.", width: 100, sortable: true},
5450 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5451 {header: "Employees", width: 100, sortable: true, resizable: false}
5456 * The config options listed for this class are options which may appear in each
5457 * individual column definition.
5458 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5460 * @param {Object} config An Array of column config objects. See this class's
5461 * config objects for details.
5463 Roo.grid.ColumnModel = function(config){
5465 * The config passed into the constructor
5467 this.config = config;
5470 // if no id, create one
5471 // if the column does not have a dataIndex mapping,
5472 // map it to the order it is in the config
5473 for(var i = 0, len = config.length; i < len; i++){
5475 if(typeof c.dataIndex == "undefined"){
5478 if(typeof c.renderer == "string"){
5479 c.renderer = Roo.util.Format[c.renderer];
5481 if(typeof c.id == "undefined"){
5484 if(c.editor && c.editor.xtype){
5485 c.editor = Roo.factory(c.editor, Roo.grid);
5487 if(c.editor && c.editor.isFormField){
5488 c.editor = new Roo.grid.GridEditor(c.editor);
5490 this.lookup[c.id] = c;
5494 * The width of columns which have no width specified (defaults to 100)
5497 this.defaultWidth = 100;
5500 * Default sortable of columns which have no sortable specified (defaults to false)
5503 this.defaultSortable = false;
5507 * @event widthchange
5508 * Fires when the width of a column changes.
5509 * @param {ColumnModel} this
5510 * @param {Number} columnIndex The column index
5511 * @param {Number} newWidth The new width
5513 "widthchange": true,
5515 * @event headerchange
5516 * Fires when the text of a header changes.
5517 * @param {ColumnModel} this
5518 * @param {Number} columnIndex The column index
5519 * @param {Number} newText The new header text
5521 "headerchange": true,
5523 * @event hiddenchange
5524 * Fires when a column is hidden or "unhidden".
5525 * @param {ColumnModel} this
5526 * @param {Number} columnIndex The column index
5527 * @param {Boolean} hidden true if hidden, false otherwise
5529 "hiddenchange": true,
5531 * @event columnmoved
5532 * Fires when a column is moved.
5533 * @param {ColumnModel} this
5534 * @param {Number} oldIndex
5535 * @param {Number} newIndex
5537 "columnmoved" : true,
5539 * @event columlockchange
5540 * Fires when a column's locked state is changed
5541 * @param {ColumnModel} this
5542 * @param {Number} colIndex
5543 * @param {Boolean} locked true if locked
5545 "columnlockchange" : true
5547 Roo.grid.ColumnModel.superclass.constructor.call(this);
5549 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5551 * @cfg {String} header The header text to display in the Grid view.
5554 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5555 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5556 * specified, the column's index is used as an index into the Record's data Array.
5559 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5560 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5563 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5564 * Defaults to the value of the {@link #defaultSortable} property.
5565 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5568 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5571 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5574 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5577 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5580 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5581 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5582 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5583 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5586 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5589 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5592 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5595 * @cfg {String} cursor (Optional)
5598 * @cfg {String} tooltip (Optional)
5601 * @cfg {Number} xs (Optional)
5604 * @cfg {Number} sm (Optional)
5607 * @cfg {Number} md (Optional)
5610 * @cfg {Number} lg (Optional)
5613 * Returns the id of the column at the specified index.
5614 * @param {Number} index The column index
5615 * @return {String} the id
5617 getColumnId : function(index){
5618 return this.config[index].id;
5622 * Returns the column for a specified id.
5623 * @param {String} id The column id
5624 * @return {Object} the column
5626 getColumnById : function(id){
5627 return this.lookup[id];
5632 * Returns the column for a specified dataIndex.
5633 * @param {String} dataIndex The column dataIndex
5634 * @return {Object|Boolean} the column or false if not found
5636 getColumnByDataIndex: function(dataIndex){
5637 var index = this.findColumnIndex(dataIndex);
5638 return index > -1 ? this.config[index] : false;
5642 * Returns the index for a specified column id.
5643 * @param {String} id The column id
5644 * @return {Number} the index, or -1 if not found
5646 getIndexById : function(id){
5647 for(var i = 0, len = this.config.length; i < len; i++){
5648 if(this.config[i].id == id){
5656 * Returns the index for a specified column dataIndex.
5657 * @param {String} dataIndex The column dataIndex
5658 * @return {Number} the index, or -1 if not found
5661 findColumnIndex : function(dataIndex){
5662 for(var i = 0, len = this.config.length; i < len; i++){
5663 if(this.config[i].dataIndex == dataIndex){
5671 moveColumn : function(oldIndex, newIndex){
5672 var c = this.config[oldIndex];
5673 this.config.splice(oldIndex, 1);
5674 this.config.splice(newIndex, 0, c);
5675 this.dataMap = null;
5676 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5679 isLocked : function(colIndex){
5680 return this.config[colIndex].locked === true;
5683 setLocked : function(colIndex, value, suppressEvent){
5684 if(this.isLocked(colIndex) == value){
5687 this.config[colIndex].locked = value;
5689 this.fireEvent("columnlockchange", this, colIndex, value);
5693 getTotalLockedWidth : function(){
5695 for(var i = 0; i < this.config.length; i++){
5696 if(this.isLocked(i) && !this.isHidden(i)){
5697 this.totalWidth += this.getColumnWidth(i);
5703 getLockedCount : function(){
5704 for(var i = 0, len = this.config.length; i < len; i++){
5705 if(!this.isLocked(i)){
5710 return this.config.length;
5714 * Returns the number of columns.
5717 getColumnCount : function(visibleOnly){
5718 if(visibleOnly === true){
5720 for(var i = 0, len = this.config.length; i < len; i++){
5721 if(!this.isHidden(i)){
5727 return this.config.length;
5731 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5732 * @param {Function} fn
5733 * @param {Object} scope (optional)
5734 * @return {Array} result
5736 getColumnsBy : function(fn, scope){
5738 for(var i = 0, len = this.config.length; i < len; i++){
5739 var c = this.config[i];
5740 if(fn.call(scope||this, c, i) === true){
5748 * Returns true if the specified column is sortable.
5749 * @param {Number} col The column index
5752 isSortable : function(col){
5753 if(typeof this.config[col].sortable == "undefined"){
5754 return this.defaultSortable;
5756 return this.config[col].sortable;
5760 * Returns the rendering (formatting) function defined for the column.
5761 * @param {Number} col The column index.
5762 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5764 getRenderer : function(col){
5765 if(!this.config[col].renderer){
5766 return Roo.grid.ColumnModel.defaultRenderer;
5768 return this.config[col].renderer;
5772 * Sets the rendering (formatting) function for a column.
5773 * @param {Number} col The column index
5774 * @param {Function} fn The function to use to process the cell's raw data
5775 * to return HTML markup for the grid view. The render function is called with
5776 * the following parameters:<ul>
5777 * <li>Data value.</li>
5778 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5779 * <li>css A CSS style string to apply to the table cell.</li>
5780 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5781 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5782 * <li>Row index</li>
5783 * <li>Column index</li>
5784 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5786 setRenderer : function(col, fn){
5787 this.config[col].renderer = fn;
5791 * Returns the width for the specified column.
5792 * @param {Number} col The column index
5795 getColumnWidth : function(col){
5796 return this.config[col].width * 1 || this.defaultWidth;
5800 * Sets the width for a column.
5801 * @param {Number} col The column index
5802 * @param {Number} width The new width
5804 setColumnWidth : function(col, width, suppressEvent){
5805 this.config[col].width = width;
5806 this.totalWidth = null;
5808 this.fireEvent("widthchange", this, col, width);
5813 * Returns the total width of all columns.
5814 * @param {Boolean} includeHidden True to include hidden column widths
5817 getTotalWidth : function(includeHidden){
5818 if(!this.totalWidth){
5819 this.totalWidth = 0;
5820 for(var i = 0, len = this.config.length; i < len; i++){
5821 if(includeHidden || !this.isHidden(i)){
5822 this.totalWidth += this.getColumnWidth(i);
5826 return this.totalWidth;
5830 * Returns the header for the specified column.
5831 * @param {Number} col The column index
5834 getColumnHeader : function(col){
5835 return this.config[col].header;
5839 * Sets the header for a column.
5840 * @param {Number} col The column index
5841 * @param {String} header The new header
5843 setColumnHeader : function(col, header){
5844 this.config[col].header = header;
5845 this.fireEvent("headerchange", this, col, header);
5849 * Returns the tooltip for the specified column.
5850 * @param {Number} col The column index
5853 getColumnTooltip : function(col){
5854 return this.config[col].tooltip;
5857 * Sets the tooltip for a column.
5858 * @param {Number} col The column index
5859 * @param {String} tooltip The new tooltip
5861 setColumnTooltip : function(col, tooltip){
5862 this.config[col].tooltip = tooltip;
5866 * Returns the dataIndex for the specified column.
5867 * @param {Number} col The column index
5870 getDataIndex : function(col){
5871 return this.config[col].dataIndex;
5875 * Sets the dataIndex for a column.
5876 * @param {Number} col The column index
5877 * @param {Number} dataIndex The new dataIndex
5879 setDataIndex : function(col, dataIndex){
5880 this.config[col].dataIndex = dataIndex;
5886 * Returns true if the cell is editable.
5887 * @param {Number} colIndex The column index
5888 * @param {Number} rowIndex The row index - this is nto actually used..?
5891 isCellEditable : function(colIndex, rowIndex){
5892 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5896 * Returns the editor defined for the cell/column.
5897 * return false or null to disable editing.
5898 * @param {Number} colIndex The column index
5899 * @param {Number} rowIndex The row index
5902 getCellEditor : function(colIndex, rowIndex){
5903 return this.config[colIndex].editor;
5907 * Sets if a column is editable.
5908 * @param {Number} col The column index
5909 * @param {Boolean} editable True if the column is editable
5911 setEditable : function(col, editable){
5912 this.config[col].editable = editable;
5917 * Returns true if the column is hidden.
5918 * @param {Number} colIndex The column index
5921 isHidden : function(colIndex){
5922 return this.config[colIndex].hidden;
5927 * Returns true if the column width cannot be changed
5929 isFixed : function(colIndex){
5930 return this.config[colIndex].fixed;
5934 * Returns true if the column can be resized
5937 isResizable : function(colIndex){
5938 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5941 * Sets if a column is hidden.
5942 * @param {Number} colIndex The column index
5943 * @param {Boolean} hidden True if the column is hidden
5945 setHidden : function(colIndex, hidden){
5946 this.config[colIndex].hidden = hidden;
5947 this.totalWidth = null;
5948 this.fireEvent("hiddenchange", this, colIndex, hidden);
5952 * Sets the editor for a column.
5953 * @param {Number} col The column index
5954 * @param {Object} editor The editor object
5956 setEditor : function(col, editor){
5957 this.config[col].editor = editor;
5961 Roo.grid.ColumnModel.defaultRenderer = function(value)
5963 if(typeof value == "object") {
5966 if(typeof value == "string" && value.length < 1){
5970 return String.format("{0}", value);
5973 // Alias for backwards compatibility
5974 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5977 * Ext JS Library 1.1.1
5978 * Copyright(c) 2006-2007, Ext JS, LLC.
5980 * Originally Released Under LGPL - original licence link has changed is not relivant.
5983 * <script type="text/javascript">
5987 * @class Roo.LoadMask
5988 * A simple utility class for generically masking elements while loading data. If the element being masked has
5989 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5990 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5991 * element's UpdateManager load indicator and will be destroyed after the initial load.
5993 * Create a new LoadMask
5994 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5995 * @param {Object} config The config object
5997 Roo.LoadMask = function(el, config){
5998 this.el = Roo.get(el);
5999 Roo.apply(this, config);
6001 this.store.on('beforeload', this.onBeforeLoad, this);
6002 this.store.on('load', this.onLoad, this);
6003 this.store.on('loadexception', this.onLoadException, this);
6004 this.removeMask = false;
6006 var um = this.el.getUpdateManager();
6007 um.showLoadIndicator = false; // disable the default indicator
6008 um.on('beforeupdate', this.onBeforeLoad, this);
6009 um.on('update', this.onLoad, this);
6010 um.on('failure', this.onLoad, this);
6011 this.removeMask = true;
6015 Roo.LoadMask.prototype = {
6017 * @cfg {Boolean} removeMask
6018 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6019 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6023 * The text to display in a centered loading message box (defaults to 'Loading...')
6027 * @cfg {String} msgCls
6028 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6030 msgCls : 'x-mask-loading',
6033 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6039 * Disables the mask to prevent it from being displayed
6041 disable : function(){
6042 this.disabled = true;
6046 * Enables the mask so that it can be displayed
6048 enable : function(){
6049 this.disabled = false;
6052 onLoadException : function()
6056 if (typeof(arguments[3]) != 'undefined') {
6057 Roo.MessageBox.alert("Error loading",arguments[3]);
6061 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6062 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6069 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6074 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6078 onBeforeLoad : function(){
6080 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6085 destroy : function(){
6087 this.store.un('beforeload', this.onBeforeLoad, this);
6088 this.store.un('load', this.onLoad, this);
6089 this.store.un('loadexception', this.onLoadException, this);
6091 var um = this.el.getUpdateManager();
6092 um.un('beforeupdate', this.onBeforeLoad, this);
6093 um.un('update', this.onLoad, this);
6094 um.un('failure', this.onLoad, this);
6105 * @class Roo.bootstrap.Table
6106 * @extends Roo.bootstrap.Component
6107 * Bootstrap Table class
6108 * @cfg {String} cls table class
6109 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6110 * @cfg {String} bgcolor Specifies the background color for a table
6111 * @cfg {Number} border Specifies whether the table cells should have borders or not
6112 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6113 * @cfg {Number} cellspacing Specifies the space between cells
6114 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6115 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6116 * @cfg {String} sortable Specifies that the table should be sortable
6117 * @cfg {String} summary Specifies a summary of the content of a table
6118 * @cfg {Number} width Specifies the width of a table
6119 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6121 * @cfg {boolean} striped Should the rows be alternative striped
6122 * @cfg {boolean} bordered Add borders to the table
6123 * @cfg {boolean} hover Add hover highlighting
6124 * @cfg {boolean} condensed Format condensed
6125 * @cfg {boolean} responsive Format condensed
6126 * @cfg {Boolean} loadMask (true|false) default false
6127 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6128 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6129 * @cfg {Boolean} rowSelection (true|false) default false
6130 * @cfg {Boolean} cellSelection (true|false) default false
6131 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6132 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6133 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6134 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6138 * Create a new Table
6139 * @param {Object} config The config object
6142 Roo.bootstrap.Table = function(config){
6143 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6148 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6149 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6150 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6151 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6153 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6155 this.sm.grid = this;
6156 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6157 this.sm = this.selModel;
6158 this.sm.xmodule = this.xmodule || false;
6161 if (this.cm && typeof(this.cm.config) == 'undefined') {
6162 this.colModel = new Roo.grid.ColumnModel(this.cm);
6163 this.cm = this.colModel;
6164 this.cm.xmodule = this.xmodule || false;
6167 this.store= Roo.factory(this.store, Roo.data);
6168 this.ds = this.store;
6169 this.ds.xmodule = this.xmodule || false;
6172 if (this.footer && this.store) {
6173 this.footer.dataSource = this.ds;
6174 this.footer = Roo.factory(this.footer);
6181 * Fires when a cell is clicked
6182 * @param {Roo.bootstrap.Table} this
6183 * @param {Roo.Element} el
6184 * @param {Number} rowIndex
6185 * @param {Number} columnIndex
6186 * @param {Roo.EventObject} e
6190 * @event celldblclick
6191 * Fires when a cell is double clicked
6192 * @param {Roo.bootstrap.Table} this
6193 * @param {Roo.Element} el
6194 * @param {Number} rowIndex
6195 * @param {Number} columnIndex
6196 * @param {Roo.EventObject} e
6198 "celldblclick" : true,
6201 * Fires when a row is clicked
6202 * @param {Roo.bootstrap.Table} this
6203 * @param {Roo.Element} el
6204 * @param {Number} rowIndex
6205 * @param {Roo.EventObject} e
6209 * @event rowdblclick
6210 * Fires when a row is double clicked
6211 * @param {Roo.bootstrap.Table} this
6212 * @param {Roo.Element} el
6213 * @param {Number} rowIndex
6214 * @param {Roo.EventObject} e
6216 "rowdblclick" : true,
6219 * Fires when a mouseover occur
6220 * @param {Roo.bootstrap.Table} this
6221 * @param {Roo.Element} el
6222 * @param {Number} rowIndex
6223 * @param {Number} columnIndex
6224 * @param {Roo.EventObject} e
6229 * Fires when a mouseout occur
6230 * @param {Roo.bootstrap.Table} this
6231 * @param {Roo.Element} el
6232 * @param {Number} rowIndex
6233 * @param {Number} columnIndex
6234 * @param {Roo.EventObject} e
6239 * Fires when a row is rendered, so you can change add a style to it.
6240 * @param {Roo.bootstrap.Table} this
6241 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6245 * @event rowsrendered
6246 * Fires when all the rows have been rendered
6247 * @param {Roo.bootstrap.Table} this
6249 'rowsrendered' : true,
6251 * @event contextmenu
6252 * The raw contextmenu event for the entire grid.
6253 * @param {Roo.EventObject} e
6255 "contextmenu" : true,
6257 * @event rowcontextmenu
6258 * Fires when a row is right clicked
6259 * @param {Roo.bootstrap.Table} this
6260 * @param {Number} rowIndex
6261 * @param {Roo.EventObject} e
6263 "rowcontextmenu" : true,
6265 * @event cellcontextmenu
6266 * Fires when a cell is right clicked
6267 * @param {Roo.bootstrap.Table} this
6268 * @param {Number} rowIndex
6269 * @param {Number} cellIndex
6270 * @param {Roo.EventObject} e
6272 "cellcontextmenu" : true,
6274 * @event headercontextmenu
6275 * Fires when a header is right clicked
6276 * @param {Roo.bootstrap.Table} this
6277 * @param {Number} columnIndex
6278 * @param {Roo.EventObject} e
6280 "headercontextmenu" : true
6284 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6310 rowSelection : false,
6311 cellSelection : false,
6314 // Roo.Element - the tbody
6316 // Roo.Element - thead element
6319 container: false, // used by gridpanel...
6325 auto_hide_footer : false,
6327 getAutoCreate : function()
6329 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6336 if (this.scrollBody) {
6337 cfg.cls += ' table-body-fixed';
6340 cfg.cls += ' table-striped';
6344 cfg.cls += ' table-hover';
6346 if (this.bordered) {
6347 cfg.cls += ' table-bordered';
6349 if (this.condensed) {
6350 cfg.cls += ' table-condensed';
6352 if (this.responsive) {
6353 cfg.cls += ' table-responsive';
6357 cfg.cls+= ' ' +this.cls;
6360 // this lot should be simplifed...
6373 ].forEach(function(k) {
6381 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6384 if(this.store || this.cm){
6385 if(this.headerShow){
6386 cfg.cn.push(this.renderHeader());
6389 cfg.cn.push(this.renderBody());
6391 if(this.footerShow){
6392 cfg.cn.push(this.renderFooter());
6394 // where does this come from?
6395 //cfg.cls+= ' TableGrid';
6398 return { cn : [ cfg ] };
6401 initEvents : function()
6403 if(!this.store || !this.cm){
6406 if (this.selModel) {
6407 this.selModel.initEvents();
6411 //Roo.log('initEvents with ds!!!!');
6413 this.mainBody = this.el.select('tbody', true).first();
6414 this.mainHead = this.el.select('thead', true).first();
6415 this.mainFoot = this.el.select('tfoot', true).first();
6421 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6422 e.on('click', _this.sort, _this);
6425 this.mainBody.on("click", this.onClick, this);
6426 this.mainBody.on("dblclick", this.onDblClick, this);
6428 // why is this done????? = it breaks dialogs??
6429 //this.parent().el.setStyle('position', 'relative');
6433 this.footer.parentId = this.id;
6434 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6437 this.el.select('tfoot tr td').first().addClass('hide');
6442 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6445 this.store.on('load', this.onLoad, this);
6446 this.store.on('beforeload', this.onBeforeLoad, this);
6447 this.store.on('update', this.onUpdate, this);
6448 this.store.on('add', this.onAdd, this);
6449 this.store.on("clear", this.clear, this);
6451 this.el.on("contextmenu", this.onContextMenu, this);
6453 this.mainBody.on('scroll', this.onBodyScroll, this);
6455 this.cm.on("headerchange", this.onHeaderChange, this);
6457 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6461 onContextMenu : function(e, t)
6463 this.processEvent("contextmenu", e);
6466 processEvent : function(name, e)
6468 if (name != 'touchstart' ) {
6469 this.fireEvent(name, e);
6472 var t = e.getTarget();
6474 var cell = Roo.get(t);
6480 if(cell.findParent('tfoot', false, true)){
6484 if(cell.findParent('thead', false, true)){
6486 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6487 cell = Roo.get(t).findParent('th', false, true);
6489 Roo.log("failed to find th in thead?");
6490 Roo.log(e.getTarget());
6495 var cellIndex = cell.dom.cellIndex;
6497 var ename = name == 'touchstart' ? 'click' : name;
6498 this.fireEvent("header" + ename, this, cellIndex, e);
6503 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6504 cell = Roo.get(t).findParent('td', false, true);
6506 Roo.log("failed to find th in tbody?");
6507 Roo.log(e.getTarget());
6512 var row = cell.findParent('tr', false, true);
6513 var cellIndex = cell.dom.cellIndex;
6514 var rowIndex = row.dom.rowIndex - 1;
6518 this.fireEvent("row" + name, this, rowIndex, e);
6522 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6528 onMouseover : function(e, el)
6530 var cell = Roo.get(el);
6536 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6537 cell = cell.findParent('td', false, true);
6540 var row = cell.findParent('tr', false, true);
6541 var cellIndex = cell.dom.cellIndex;
6542 var rowIndex = row.dom.rowIndex - 1; // start from 0
6544 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6548 onMouseout : function(e, el)
6550 var cell = Roo.get(el);
6556 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6557 cell = cell.findParent('td', false, true);
6560 var row = cell.findParent('tr', false, true);
6561 var cellIndex = cell.dom.cellIndex;
6562 var rowIndex = row.dom.rowIndex - 1; // start from 0
6564 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6568 onClick : function(e, el)
6570 var cell = Roo.get(el);
6572 if(!cell || (!this.cellSelection && !this.rowSelection)){
6576 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6577 cell = cell.findParent('td', false, true);
6580 if(!cell || typeof(cell) == 'undefined'){
6584 var row = cell.findParent('tr', false, true);
6586 if(!row || typeof(row) == 'undefined'){
6590 var cellIndex = cell.dom.cellIndex;
6591 var rowIndex = this.getRowIndex(row);
6593 // why??? - should these not be based on SelectionModel?
6594 if(this.cellSelection){
6595 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6598 if(this.rowSelection){
6599 this.fireEvent('rowclick', this, row, rowIndex, e);
6605 onDblClick : function(e,el)
6607 var cell = Roo.get(el);
6609 if(!cell || (!this.cellSelection && !this.rowSelection)){
6613 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6614 cell = cell.findParent('td', false, true);
6617 if(!cell || typeof(cell) == 'undefined'){
6621 var row = cell.findParent('tr', false, true);
6623 if(!row || typeof(row) == 'undefined'){
6627 var cellIndex = cell.dom.cellIndex;
6628 var rowIndex = this.getRowIndex(row);
6630 if(this.cellSelection){
6631 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6634 if(this.rowSelection){
6635 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6639 sort : function(e,el)
6641 var col = Roo.get(el);
6643 if(!col.hasClass('sortable')){
6647 var sort = col.attr('sort');
6650 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6654 this.store.sortInfo = {field : sort, direction : dir};
6657 Roo.log("calling footer first");
6658 this.footer.onClick('first');
6661 this.store.load({ params : { start : 0 } });
6665 renderHeader : function()
6673 this.totalWidth = 0;
6675 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6677 var config = cm.config[i];
6681 cls : 'x-hcol-' + i,
6683 html: cm.getColumnHeader(i)
6688 if(typeof(config.sortable) != 'undefined' && config.sortable){
6690 c.html = '<i class="glyphicon"></i>' + c.html;
6693 if(typeof(config.lgHeader) != 'undefined'){
6694 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6697 if(typeof(config.mdHeader) != 'undefined'){
6698 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6701 if(typeof(config.smHeader) != 'undefined'){
6702 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6705 if(typeof(config.xsHeader) != 'undefined'){
6706 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6713 if(typeof(config.tooltip) != 'undefined'){
6714 c.tooltip = config.tooltip;
6717 if(typeof(config.colspan) != 'undefined'){
6718 c.colspan = config.colspan;
6721 if(typeof(config.hidden) != 'undefined' && config.hidden){
6722 c.style += ' display:none;';
6725 if(typeof(config.dataIndex) != 'undefined'){
6726 c.sort = config.dataIndex;
6731 if(typeof(config.align) != 'undefined' && config.align.length){
6732 c.style += ' text-align:' + config.align + ';';
6735 if(typeof(config.width) != 'undefined'){
6736 c.style += ' width:' + config.width + 'px;';
6737 this.totalWidth += config.width;
6739 this.totalWidth += 100; // assume minimum of 100 per column?
6742 if(typeof(config.cls) != 'undefined'){
6743 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6746 ['xs','sm','md','lg'].map(function(size){
6748 if(typeof(config[size]) == 'undefined'){
6752 if (!config[size]) { // 0 = hidden
6753 c.cls += ' hidden-' + size;
6757 c.cls += ' col-' + size + '-' + config[size];
6767 renderBody : function()
6777 colspan : this.cm.getColumnCount()
6787 renderFooter : function()
6797 colspan : this.cm.getColumnCount()
6811 // Roo.log('ds onload');
6816 var ds = this.store;
6818 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6819 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6820 if (_this.store.sortInfo) {
6822 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6823 e.select('i', true).addClass(['glyphicon-arrow-up']);
6826 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6827 e.select('i', true).addClass(['glyphicon-arrow-down']);
6832 var tbody = this.mainBody;
6834 if(ds.getCount() > 0){
6835 ds.data.each(function(d,rowIndex){
6836 var row = this.renderRow(cm, ds, rowIndex);
6838 tbody.createChild(row);
6842 if(row.cellObjects.length){
6843 Roo.each(row.cellObjects, function(r){
6844 _this.renderCellObject(r);
6851 var tfoot = this.el.select('tfoot', true).first();
6853 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6855 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6857 var total = this.ds.getTotalCount();
6859 if(this.footer.pageSize < total){
6860 this.mainFoot.show();
6864 Roo.each(this.el.select('tbody td', true).elements, function(e){
6865 e.on('mouseover', _this.onMouseover, _this);
6868 Roo.each(this.el.select('tbody td', true).elements, function(e){
6869 e.on('mouseout', _this.onMouseout, _this);
6871 this.fireEvent('rowsrendered', this);
6877 onUpdate : function(ds,record)
6879 this.refreshRow(record);
6883 onRemove : function(ds, record, index, isUpdate){
6884 if(isUpdate !== true){
6885 this.fireEvent("beforerowremoved", this, index, record);
6887 var bt = this.mainBody.dom;
6889 var rows = this.el.select('tbody > tr', true).elements;
6891 if(typeof(rows[index]) != 'undefined'){
6892 bt.removeChild(rows[index].dom);
6895 // if(bt.rows[index]){
6896 // bt.removeChild(bt.rows[index]);
6899 if(isUpdate !== true){
6900 //this.stripeRows(index);
6901 //this.syncRowHeights(index, index);
6903 this.fireEvent("rowremoved", this, index, record);
6907 onAdd : function(ds, records, rowIndex)
6909 //Roo.log('on Add called');
6910 // - note this does not handle multiple adding very well..
6911 var bt = this.mainBody.dom;
6912 for (var i =0 ; i < records.length;i++) {
6913 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6914 //Roo.log(records[i]);
6915 //Roo.log(this.store.getAt(rowIndex+i));
6916 this.insertRow(this.store, rowIndex + i, false);
6923 refreshRow : function(record){
6924 var ds = this.store, index;
6925 if(typeof record == 'number'){
6927 record = ds.getAt(index);
6929 index = ds.indexOf(record);
6931 this.insertRow(ds, index, true);
6933 this.onRemove(ds, record, index+1, true);
6935 //this.syncRowHeights(index, index);
6937 this.fireEvent("rowupdated", this, index, record);
6940 insertRow : function(dm, rowIndex, isUpdate){
6943 this.fireEvent("beforerowsinserted", this, rowIndex);
6945 //var s = this.getScrollState();
6946 var row = this.renderRow(this.cm, this.store, rowIndex);
6947 // insert before rowIndex..
6948 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6952 if(row.cellObjects.length){
6953 Roo.each(row.cellObjects, function(r){
6954 _this.renderCellObject(r);
6959 this.fireEvent("rowsinserted", this, rowIndex);
6960 //this.syncRowHeights(firstRow, lastRow);
6961 //this.stripeRows(firstRow);
6968 getRowDom : function(rowIndex)
6970 var rows = this.el.select('tbody > tr', true).elements;
6972 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6975 // returns the object tree for a tr..
6978 renderRow : function(cm, ds, rowIndex)
6980 var d = ds.getAt(rowIndex);
6984 cls : 'x-row-' + rowIndex,
6988 var cellObjects = [];
6990 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6991 var config = cm.config[i];
6993 var renderer = cm.getRenderer(i);
6997 if(typeof(renderer) !== 'undefined'){
6998 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7000 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7001 // and are rendered into the cells after the row is rendered - using the id for the element.
7003 if(typeof(value) === 'object'){
7013 rowIndex : rowIndex,
7018 this.fireEvent('rowclass', this, rowcfg);
7022 cls : rowcfg.rowClass + ' x-col-' + i,
7024 html: (typeof(value) === 'object') ? '' : value
7031 if(typeof(config.colspan) != 'undefined'){
7032 td.colspan = config.colspan;
7035 if(typeof(config.hidden) != 'undefined' && config.hidden){
7036 td.style += ' display:none;';
7039 if(typeof(config.align) != 'undefined' && config.align.length){
7040 td.style += ' text-align:' + config.align + ';';
7042 if(typeof(config.valign) != 'undefined' && config.valign.length){
7043 td.style += ' vertical-align:' + config.valign + ';';
7046 if(typeof(config.width) != 'undefined'){
7047 td.style += ' width:' + config.width + 'px;';
7050 if(typeof(config.cursor) != 'undefined'){
7051 td.style += ' cursor:' + config.cursor + ';';
7054 if(typeof(config.cls) != 'undefined'){
7055 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7058 ['xs','sm','md','lg'].map(function(size){
7060 if(typeof(config[size]) == 'undefined'){
7064 if (!config[size]) { // 0 = hidden
7065 td.cls += ' hidden-' + size;
7069 td.cls += ' col-' + size + '-' + config[size];
7077 row.cellObjects = cellObjects;
7085 onBeforeLoad : function()
7094 this.el.select('tbody', true).first().dom.innerHTML = '';
7097 * Show or hide a row.
7098 * @param {Number} rowIndex to show or hide
7099 * @param {Boolean} state hide
7101 setRowVisibility : function(rowIndex, state)
7103 var bt = this.mainBody.dom;
7105 var rows = this.el.select('tbody > tr', true).elements;
7107 if(typeof(rows[rowIndex]) == 'undefined'){
7110 rows[rowIndex].dom.style.display = state ? '' : 'none';
7114 getSelectionModel : function(){
7116 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7118 return this.selModel;
7121 * Render the Roo.bootstrap object from renderder
7123 renderCellObject : function(r)
7127 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7129 var t = r.cfg.render(r.container);
7132 Roo.each(r.cfg.cn, function(c){
7134 container: t.getChildContainer(),
7137 _this.renderCellObject(child);
7142 getRowIndex : function(row)
7146 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7157 * Returns the grid's underlying element = used by panel.Grid
7158 * @return {Element} The element
7160 getGridEl : function(){
7164 * Forces a resize - used by panel.Grid
7165 * @return {Element} The element
7167 autoSize : function()
7169 //var ctr = Roo.get(this.container.dom.parentElement);
7170 var ctr = Roo.get(this.el.dom);
7172 var thd = this.getGridEl().select('thead',true).first();
7173 var tbd = this.getGridEl().select('tbody', true).first();
7174 var tfd = this.getGridEl().select('tfoot', true).first();
7176 var cw = ctr.getWidth();
7180 tbd.setSize(ctr.getWidth(),
7181 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7183 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7186 cw = Math.max(cw, this.totalWidth);
7187 this.getGridEl().select('tr',true).setWidth(cw);
7188 // resize 'expandable coloumn?
7190 return; // we doe not have a view in this design..
7193 onBodyScroll: function()
7195 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7197 this.mainHead.setStyle({
7198 'position' : 'relative',
7199 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7205 var scrollHeight = this.mainBody.dom.scrollHeight;
7207 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7209 var height = this.mainBody.getHeight();
7211 if(scrollHeight - height == scrollTop) {
7213 var total = this.ds.getTotalCount();
7215 if(this.footer.cursor + this.footer.pageSize < total){
7217 this.footer.ds.load({
7219 start : this.footer.cursor + this.footer.pageSize,
7220 limit : this.footer.pageSize
7230 onHeaderChange : function()
7232 var header = this.renderHeader();
7233 var table = this.el.select('table', true).first();
7235 this.mainHead.remove();
7236 this.mainHead = table.createChild(header, this.mainBody, false);
7239 onHiddenChange : function(colModel, colIndex, hidden)
7241 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7242 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7244 this.CSS.updateRule(thSelector, "display", "");
7245 this.CSS.updateRule(tdSelector, "display", "");
7248 this.CSS.updateRule(thSelector, "display", "none");
7249 this.CSS.updateRule(tdSelector, "display", "none");
7252 this.onHeaderChange();
7256 setColumnWidth: function(col_index, width)
7258 // width = "md-2 xs-2..."
7259 if(!this.colModel.config[col_index]) {
7263 var w = width.split(" ");
7265 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7267 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7270 for(var j = 0; j < w.length; j++) {
7276 var size_cls = w[j].split("-");
7278 if(!Number.isInteger(size_cls[1] * 1)) {
7282 if(!this.colModel.config[col_index][size_cls[0]]) {
7286 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7290 h_row[0].classList.replace(
7291 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7292 "col-"+size_cls[0]+"-"+size_cls[1]
7295 for(var i = 0; i < rows.length; i++) {
7297 var size_cls = w[j].split("-");
7299 if(!Number.isInteger(size_cls[1] * 1)) {
7303 if(!this.colModel.config[col_index][size_cls[0]]) {
7307 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7311 rows[i].classList.replace(
7312 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7313 "col-"+size_cls[0]+"-"+size_cls[1]
7317 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7332 * @class Roo.bootstrap.TableCell
7333 * @extends Roo.bootstrap.Component
7334 * Bootstrap TableCell class
7335 * @cfg {String} html cell contain text
7336 * @cfg {String} cls cell class
7337 * @cfg {String} tag cell tag (td|th) default td
7338 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7339 * @cfg {String} align Aligns the content in a cell
7340 * @cfg {String} axis Categorizes cells
7341 * @cfg {String} bgcolor Specifies the background color of a cell
7342 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7343 * @cfg {Number} colspan Specifies the number of columns a cell should span
7344 * @cfg {String} headers Specifies one or more header cells a cell is related to
7345 * @cfg {Number} height Sets the height of a cell
7346 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7347 * @cfg {Number} rowspan Sets the number of rows a cell should span
7348 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7349 * @cfg {String} valign Vertical aligns the content in a cell
7350 * @cfg {Number} width Specifies the width of a cell
7353 * Create a new TableCell
7354 * @param {Object} config The config object
7357 Roo.bootstrap.TableCell = function(config){
7358 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7361 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7381 getAutoCreate : function(){
7382 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7402 cfg.align=this.align
7408 cfg.bgcolor=this.bgcolor
7411 cfg.charoff=this.charoff
7414 cfg.colspan=this.colspan
7417 cfg.headers=this.headers
7420 cfg.height=this.height
7423 cfg.nowrap=this.nowrap
7426 cfg.rowspan=this.rowspan
7429 cfg.scope=this.scope
7432 cfg.valign=this.valign
7435 cfg.width=this.width
7454 * @class Roo.bootstrap.TableRow
7455 * @extends Roo.bootstrap.Component
7456 * Bootstrap TableRow class
7457 * @cfg {String} cls row class
7458 * @cfg {String} align Aligns the content in a table row
7459 * @cfg {String} bgcolor Specifies a background color for a table row
7460 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7461 * @cfg {String} valign Vertical aligns the content in a table row
7464 * Create a new TableRow
7465 * @param {Object} config The config object
7468 Roo.bootstrap.TableRow = function(config){
7469 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7472 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7480 getAutoCreate : function(){
7481 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7491 cfg.align = this.align;
7494 cfg.bgcolor = this.bgcolor;
7497 cfg.charoff = this.charoff;
7500 cfg.valign = this.valign;
7518 * @class Roo.bootstrap.TableBody
7519 * @extends Roo.bootstrap.Component
7520 * Bootstrap TableBody class
7521 * @cfg {String} cls element class
7522 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7523 * @cfg {String} align Aligns the content inside the element
7524 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7525 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7528 * Create a new TableBody
7529 * @param {Object} config The config object
7532 Roo.bootstrap.TableBody = function(config){
7533 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7536 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7544 getAutoCreate : function(){
7545 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7559 cfg.align = this.align;
7562 cfg.charoff = this.charoff;
7565 cfg.valign = this.valign;
7572 // initEvents : function()
7579 // this.store = Roo.factory(this.store, Roo.data);
7580 // this.store.on('load', this.onLoad, this);
7582 // this.store.load();
7586 // onLoad: function ()
7588 // this.fireEvent('load', this);
7598 * Ext JS Library 1.1.1
7599 * Copyright(c) 2006-2007, Ext JS, LLC.
7601 * Originally Released Under LGPL - original licence link has changed is not relivant.
7604 * <script type="text/javascript">
7607 // as we use this in bootstrap.
7608 Roo.namespace('Roo.form');
7610 * @class Roo.form.Action
7611 * Internal Class used to handle form actions
7613 * @param {Roo.form.BasicForm} el The form element or its id
7614 * @param {Object} config Configuration options
7619 // define the action interface
7620 Roo.form.Action = function(form, options){
7622 this.options = options || {};
7625 * Client Validation Failed
7628 Roo.form.Action.CLIENT_INVALID = 'client';
7630 * Server Validation Failed
7633 Roo.form.Action.SERVER_INVALID = 'server';
7635 * Connect to Server Failed
7638 Roo.form.Action.CONNECT_FAILURE = 'connect';
7640 * Reading Data from Server Failed
7643 Roo.form.Action.LOAD_FAILURE = 'load';
7645 Roo.form.Action.prototype = {
7647 failureType : undefined,
7648 response : undefined,
7652 run : function(options){
7657 success : function(response){
7662 handleResponse : function(response){
7666 // default connection failure
7667 failure : function(response){
7669 this.response = response;
7670 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7671 this.form.afterAction(this, false);
7674 processResponse : function(response){
7675 this.response = response;
7676 if(!response.responseText){
7679 this.result = this.handleResponse(response);
7683 // utility functions used internally
7684 getUrl : function(appendParams){
7685 var url = this.options.url || this.form.url || this.form.el.dom.action;
7687 var p = this.getParams();
7689 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7695 getMethod : function(){
7696 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7699 getParams : function(){
7700 var bp = this.form.baseParams;
7701 var p = this.options.params;
7703 if(typeof p == "object"){
7704 p = Roo.urlEncode(Roo.applyIf(p, bp));
7705 }else if(typeof p == 'string' && bp){
7706 p += '&' + Roo.urlEncode(bp);
7709 p = Roo.urlEncode(bp);
7714 createCallback : function(){
7716 success: this.success,
7717 failure: this.failure,
7719 timeout: (this.form.timeout*1000),
7720 upload: this.form.fileUpload ? this.success : undefined
7725 Roo.form.Action.Submit = function(form, options){
7726 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7729 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7732 haveProgress : false,
7733 uploadComplete : false,
7735 // uploadProgress indicator.
7736 uploadProgress : function()
7738 if (!this.form.progressUrl) {
7742 if (!this.haveProgress) {
7743 Roo.MessageBox.progress("Uploading", "Uploading");
7745 if (this.uploadComplete) {
7746 Roo.MessageBox.hide();
7750 this.haveProgress = true;
7752 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7754 var c = new Roo.data.Connection();
7756 url : this.form.progressUrl,
7761 success : function(req){
7762 //console.log(data);
7766 rdata = Roo.decode(req.responseText)
7768 Roo.log("Invalid data from server..");
7772 if (!rdata || !rdata.success) {
7774 Roo.MessageBox.alert(Roo.encode(rdata));
7777 var data = rdata.data;
7779 if (this.uploadComplete) {
7780 Roo.MessageBox.hide();
7785 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7786 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7789 this.uploadProgress.defer(2000,this);
7792 failure: function(data) {
7793 Roo.log('progress url failed ');
7804 // run get Values on the form, so it syncs any secondary forms.
7805 this.form.getValues();
7807 var o = this.options;
7808 var method = this.getMethod();
7809 var isPost = method == 'POST';
7810 if(o.clientValidation === false || this.form.isValid()){
7812 if (this.form.progressUrl) {
7813 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7814 (new Date() * 1) + '' + Math.random());
7819 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7820 form:this.form.el.dom,
7821 url:this.getUrl(!isPost),
7823 params:isPost ? this.getParams() : null,
7824 isUpload: this.form.fileUpload
7827 this.uploadProgress();
7829 }else if (o.clientValidation !== false){ // client validation failed
7830 this.failureType = Roo.form.Action.CLIENT_INVALID;
7831 this.form.afterAction(this, false);
7835 success : function(response)
7837 this.uploadComplete= true;
7838 if (this.haveProgress) {
7839 Roo.MessageBox.hide();
7843 var result = this.processResponse(response);
7844 if(result === true || result.success){
7845 this.form.afterAction(this, true);
7849 this.form.markInvalid(result.errors);
7850 this.failureType = Roo.form.Action.SERVER_INVALID;
7852 this.form.afterAction(this, false);
7854 failure : function(response)
7856 this.uploadComplete= true;
7857 if (this.haveProgress) {
7858 Roo.MessageBox.hide();
7861 this.response = response;
7862 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7863 this.form.afterAction(this, false);
7866 handleResponse : function(response){
7867 if(this.form.errorReader){
7868 var rs = this.form.errorReader.read(response);
7871 for(var i = 0, len = rs.records.length; i < len; i++) {
7872 var r = rs.records[i];
7876 if(errors.length < 1){
7880 success : rs.success,
7886 ret = Roo.decode(response.responseText);
7890 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7900 Roo.form.Action.Load = function(form, options){
7901 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7902 this.reader = this.form.reader;
7905 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7910 Roo.Ajax.request(Roo.apply(
7911 this.createCallback(), {
7912 method:this.getMethod(),
7913 url:this.getUrl(false),
7914 params:this.getParams()
7918 success : function(response){
7920 var result = this.processResponse(response);
7921 if(result === true || !result.success || !result.data){
7922 this.failureType = Roo.form.Action.LOAD_FAILURE;
7923 this.form.afterAction(this, false);
7926 this.form.clearInvalid();
7927 this.form.setValues(result.data);
7928 this.form.afterAction(this, true);
7931 handleResponse : function(response){
7932 if(this.form.reader){
7933 var rs = this.form.reader.read(response);
7934 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7936 success : rs.success,
7940 return Roo.decode(response.responseText);
7944 Roo.form.Action.ACTION_TYPES = {
7945 'load' : Roo.form.Action.Load,
7946 'submit' : Roo.form.Action.Submit
7955 * @class Roo.bootstrap.Form
7956 * @extends Roo.bootstrap.Component
7957 * Bootstrap Form class
7958 * @cfg {String} method GET | POST (default POST)
7959 * @cfg {String} labelAlign top | left (default top)
7960 * @cfg {String} align left | right - for navbars
7961 * @cfg {Boolean} loadMask load mask when submit (default true)
7966 * @param {Object} config The config object
7970 Roo.bootstrap.Form = function(config){
7972 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7974 Roo.bootstrap.Form.popover.apply();
7978 * @event clientvalidation
7979 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7980 * @param {Form} this
7981 * @param {Boolean} valid true if the form has passed client-side validation
7983 clientvalidation: true,
7985 * @event beforeaction
7986 * Fires before any action is performed. Return false to cancel the action.
7987 * @param {Form} this
7988 * @param {Action} action The action to be performed
7992 * @event actionfailed
7993 * Fires when an action fails.
7994 * @param {Form} this
7995 * @param {Action} action The action that failed
7997 actionfailed : true,
7999 * @event actioncomplete
8000 * Fires when an action is completed.
8001 * @param {Form} this
8002 * @param {Action} action The action that completed
8004 actioncomplete : true
8008 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8011 * @cfg {String} method
8012 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8017 * The URL to use for form actions if one isn't supplied in the action options.
8020 * @cfg {Boolean} fileUpload
8021 * Set to true if this form is a file upload.
8025 * @cfg {Object} baseParams
8026 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8030 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8034 * @cfg {Sting} align (left|right) for navbar forms
8039 activeAction : null,
8042 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8043 * element by passing it or its id or mask the form itself by passing in true.
8046 waitMsgTarget : false,
8051 * @cfg {Boolean} errorMask (true|false) default false
8056 * @cfg {Number} maskOffset Default 100
8061 * @cfg {Boolean} maskBody
8065 getAutoCreate : function(){
8069 method : this.method || 'POST',
8070 id : this.id || Roo.id(),
8073 if (this.parent().xtype.match(/^Nav/)) {
8074 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8078 if (this.labelAlign == 'left' ) {
8079 cfg.cls += ' form-horizontal';
8085 initEvents : function()
8087 this.el.on('submit', this.onSubmit, this);
8088 // this was added as random key presses on the form where triggering form submit.
8089 this.el.on('keypress', function(e) {
8090 if (e.getCharCode() != 13) {
8093 // we might need to allow it for textareas.. and some other items.
8094 // check e.getTarget().
8096 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8100 Roo.log("keypress blocked");
8108 onSubmit : function(e){
8113 * Returns true if client-side validation on the form is successful.
8116 isValid : function(){
8117 var items = this.getItems();
8121 items.each(function(f){
8127 Roo.log('invalid field: ' + f.name);
8131 if(!target && f.el.isVisible(true)){
8137 if(this.errorMask && !valid){
8138 Roo.bootstrap.Form.popover.mask(this, target);
8145 * Returns true if any fields in this form have changed since their original load.
8148 isDirty : function(){
8150 var items = this.getItems();
8151 items.each(function(f){
8161 * Performs a predefined action (submit or load) or custom actions you define on this form.
8162 * @param {String} actionName The name of the action type
8163 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8164 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8165 * accept other config options):
8167 Property Type Description
8168 ---------------- --------------- ----------------------------------------------------------------------------------
8169 url String The url for the action (defaults to the form's url)
8170 method String The form method to use (defaults to the form's method, or POST if not defined)
8171 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8172 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8173 validate the form on the client (defaults to false)
8175 * @return {BasicForm} this
8177 doAction : function(action, options){
8178 if(typeof action == 'string'){
8179 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8181 if(this.fireEvent('beforeaction', this, action) !== false){
8182 this.beforeAction(action);
8183 action.run.defer(100, action);
8189 beforeAction : function(action){
8190 var o = action.options;
8195 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8197 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8200 // not really supported yet.. ??
8202 //if(this.waitMsgTarget === true){
8203 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8204 //}else if(this.waitMsgTarget){
8205 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8206 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8208 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8214 afterAction : function(action, success){
8215 this.activeAction = null;
8216 var o = action.options;
8221 Roo.get(document.body).unmask();
8227 //if(this.waitMsgTarget === true){
8228 // this.el.unmask();
8229 //}else if(this.waitMsgTarget){
8230 // this.waitMsgTarget.unmask();
8232 // Roo.MessageBox.updateProgress(1);
8233 // Roo.MessageBox.hide();
8240 Roo.callback(o.success, o.scope, [this, action]);
8241 this.fireEvent('actioncomplete', this, action);
8245 // failure condition..
8246 // we have a scenario where updates need confirming.
8247 // eg. if a locking scenario exists..
8248 // we look for { errors : { needs_confirm : true }} in the response.
8250 (typeof(action.result) != 'undefined') &&
8251 (typeof(action.result.errors) != 'undefined') &&
8252 (typeof(action.result.errors.needs_confirm) != 'undefined')
8255 Roo.log("not supported yet");
8258 Roo.MessageBox.confirm(
8259 "Change requires confirmation",
8260 action.result.errorMsg,
8265 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8275 Roo.callback(o.failure, o.scope, [this, action]);
8276 // show an error message if no failed handler is set..
8277 if (!this.hasListener('actionfailed')) {
8278 Roo.log("need to add dialog support");
8280 Roo.MessageBox.alert("Error",
8281 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8282 action.result.errorMsg :
8283 "Saving Failed, please check your entries or try again"
8288 this.fireEvent('actionfailed', this, action);
8293 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8294 * @param {String} id The value to search for
8297 findField : function(id){
8298 var items = this.getItems();
8299 var field = items.get(id);
8301 items.each(function(f){
8302 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8309 return field || null;
8312 * Mark fields in this form invalid in bulk.
8313 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8314 * @return {BasicForm} this
8316 markInvalid : function(errors){
8317 if(errors instanceof Array){
8318 for(var i = 0, len = errors.length; i < len; i++){
8319 var fieldError = errors[i];
8320 var f = this.findField(fieldError.id);
8322 f.markInvalid(fieldError.msg);
8328 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8329 field.markInvalid(errors[id]);
8333 //Roo.each(this.childForms || [], function (f) {
8334 // f.markInvalid(errors);
8341 * Set values for fields in this form in bulk.
8342 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8343 * @return {BasicForm} this
8345 setValues : function(values){
8346 if(values instanceof Array){ // array of objects
8347 for(var i = 0, len = values.length; i < len; i++){
8349 var f = this.findField(v.id);
8351 f.setValue(v.value);
8352 if(this.trackResetOnLoad){
8353 f.originalValue = f.getValue();
8357 }else{ // object hash
8360 if(typeof values[id] != 'function' && (field = this.findField(id))){
8362 if (field.setFromData &&
8364 field.displayField &&
8365 // combos' with local stores can
8366 // be queried via setValue()
8367 // to set their value..
8368 (field.store && !field.store.isLocal)
8372 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8373 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8374 field.setFromData(sd);
8376 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8378 field.setFromData(values);
8381 field.setValue(values[id]);
8385 if(this.trackResetOnLoad){
8386 field.originalValue = field.getValue();
8392 //Roo.each(this.childForms || [], function (f) {
8393 // f.setValues(values);
8400 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8401 * they are returned as an array.
8402 * @param {Boolean} asString
8405 getValues : function(asString){
8406 //if (this.childForms) {
8407 // copy values from the child forms
8408 // Roo.each(this.childForms, function (f) {
8409 // this.setValues(f.getValues());
8415 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8416 if(asString === true){
8419 return Roo.urlDecode(fs);
8423 * Returns the fields in this form as an object with key/value pairs.
8424 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8427 getFieldValues : function(with_hidden)
8429 var items = this.getItems();
8431 items.each(function(f){
8437 var v = f.getValue();
8439 if (f.inputType =='radio') {
8440 if (typeof(ret[f.getName()]) == 'undefined') {
8441 ret[f.getName()] = ''; // empty..
8444 if (!f.el.dom.checked) {
8452 if(f.xtype == 'MoneyField'){
8453 ret[f.currencyName] = f.getCurrency();
8456 // not sure if this supported any more..
8457 if ((typeof(v) == 'object') && f.getRawValue) {
8458 v = f.getRawValue() ; // dates..
8460 // combo boxes where name != hiddenName...
8461 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8462 ret[f.name] = f.getRawValue();
8464 ret[f.getName()] = v;
8471 * Clears all invalid messages in this form.
8472 * @return {BasicForm} this
8474 clearInvalid : function(){
8475 var items = this.getItems();
8477 items.each(function(f){
8486 * @return {BasicForm} this
8489 var items = this.getItems();
8490 items.each(function(f){
8494 Roo.each(this.childForms || [], function (f) {
8502 getItems : function()
8504 var r=new Roo.util.MixedCollection(false, function(o){
8505 return o.id || (o.id = Roo.id());
8507 var iter = function(el) {
8514 Roo.each(el.items,function(e) {
8523 hideFields : function(items)
8525 Roo.each(items, function(i){
8527 var f = this.findField(i);
8538 showFields : function(items)
8540 Roo.each(items, function(i){
8542 var f = this.findField(i);
8555 Roo.apply(Roo.bootstrap.Form, {
8582 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8583 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8584 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8585 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8588 this.maskEl.top.enableDisplayMode("block");
8589 this.maskEl.left.enableDisplayMode("block");
8590 this.maskEl.bottom.enableDisplayMode("block");
8591 this.maskEl.right.enableDisplayMode("block");
8593 this.toolTip = new Roo.bootstrap.Tooltip({
8594 cls : 'roo-form-error-popover',
8596 'left' : ['r-l', [-2,0], 'right'],
8597 'right' : ['l-r', [2,0], 'left'],
8598 'bottom' : ['tl-bl', [0,2], 'top'],
8599 'top' : [ 'bl-tl', [0,-2], 'bottom']
8603 this.toolTip.render(Roo.get(document.body));
8605 this.toolTip.el.enableDisplayMode("block");
8607 Roo.get(document.body).on('click', function(){
8611 Roo.get(document.body).on('touchstart', function(){
8615 this.isApplied = true
8618 mask : function(form, target)
8622 this.target = target;
8624 if(!this.form.errorMask || !target.el){
8628 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8630 Roo.log(scrollable);
8632 var ot = this.target.el.calcOffsetsTo(scrollable);
8634 var scrollTo = ot[1] - this.form.maskOffset;
8636 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8638 scrollable.scrollTo('top', scrollTo);
8640 var box = this.target.el.getBox();
8642 var zIndex = Roo.bootstrap.Modal.zIndex++;
8645 this.maskEl.top.setStyle('position', 'absolute');
8646 this.maskEl.top.setStyle('z-index', zIndex);
8647 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8648 this.maskEl.top.setLeft(0);
8649 this.maskEl.top.setTop(0);
8650 this.maskEl.top.show();
8652 this.maskEl.left.setStyle('position', 'absolute');
8653 this.maskEl.left.setStyle('z-index', zIndex);
8654 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8655 this.maskEl.left.setLeft(0);
8656 this.maskEl.left.setTop(box.y - this.padding);
8657 this.maskEl.left.show();
8659 this.maskEl.bottom.setStyle('position', 'absolute');
8660 this.maskEl.bottom.setStyle('z-index', zIndex);
8661 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8662 this.maskEl.bottom.setLeft(0);
8663 this.maskEl.bottom.setTop(box.bottom + this.padding);
8664 this.maskEl.bottom.show();
8666 this.maskEl.right.setStyle('position', 'absolute');
8667 this.maskEl.right.setStyle('z-index', zIndex);
8668 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8669 this.maskEl.right.setLeft(box.right + this.padding);
8670 this.maskEl.right.setTop(box.y - this.padding);
8671 this.maskEl.right.show();
8673 this.toolTip.bindEl = this.target.el;
8675 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8677 var tip = this.target.blankText;
8679 if(this.target.getValue() !== '' ) {
8681 if (this.target.invalidText.length) {
8682 tip = this.target.invalidText;
8683 } else if (this.target.regexText.length){
8684 tip = this.target.regexText;
8688 this.toolTip.show(tip);
8690 this.intervalID = window.setInterval(function() {
8691 Roo.bootstrap.Form.popover.unmask();
8694 window.onwheel = function(){ return false;};
8696 (function(){ this.isMasked = true; }).defer(500, this);
8702 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8706 this.maskEl.top.setStyle('position', 'absolute');
8707 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8708 this.maskEl.top.hide();
8710 this.maskEl.left.setStyle('position', 'absolute');
8711 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8712 this.maskEl.left.hide();
8714 this.maskEl.bottom.setStyle('position', 'absolute');
8715 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8716 this.maskEl.bottom.hide();
8718 this.maskEl.right.setStyle('position', 'absolute');
8719 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8720 this.maskEl.right.hide();
8722 this.toolTip.hide();
8724 this.toolTip.el.hide();
8726 window.onwheel = function(){ return true;};
8728 if(this.intervalID){
8729 window.clearInterval(this.intervalID);
8730 this.intervalID = false;
8733 this.isMasked = false;
8743 * Ext JS Library 1.1.1
8744 * Copyright(c) 2006-2007, Ext JS, LLC.
8746 * Originally Released Under LGPL - original licence link has changed is not relivant.
8749 * <script type="text/javascript">
8752 * @class Roo.form.VTypes
8753 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8756 Roo.form.VTypes = function(){
8757 // closure these in so they are only created once.
8758 var alpha = /^[a-zA-Z_]+$/;
8759 var alphanum = /^[a-zA-Z0-9_]+$/;
8760 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8761 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8763 // All these messages and functions are configurable
8766 * The function used to validate email addresses
8767 * @param {String} value The email address
8769 'email' : function(v){
8770 return email.test(v);
8773 * The error text to display when the email validation function returns false
8776 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8778 * The keystroke filter mask to be applied on email input
8781 'emailMask' : /[a-z0-9_\.\-@]/i,
8784 * The function used to validate URLs
8785 * @param {String} value The URL
8787 'url' : function(v){
8791 * The error text to display when the url validation function returns false
8794 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8797 * The function used to validate alpha values
8798 * @param {String} value The value
8800 'alpha' : function(v){
8801 return alpha.test(v);
8804 * The error text to display when the alpha validation function returns false
8807 'alphaText' : 'This field should only contain letters and _',
8809 * The keystroke filter mask to be applied on alpha input
8812 'alphaMask' : /[a-z_]/i,
8815 * The function used to validate alphanumeric values
8816 * @param {String} value The value
8818 'alphanum' : function(v){
8819 return alphanum.test(v);
8822 * The error text to display when the alphanumeric validation function returns false
8825 'alphanumText' : 'This field should only contain letters, numbers and _',
8827 * The keystroke filter mask to be applied on alphanumeric input
8830 'alphanumMask' : /[a-z0-9_]/i
8840 * @class Roo.bootstrap.Input
8841 * @extends Roo.bootstrap.Component
8842 * Bootstrap Input class
8843 * @cfg {Boolean} disabled is it disabled
8844 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8845 * @cfg {String} name name of the input
8846 * @cfg {string} fieldLabel - the label associated
8847 * @cfg {string} placeholder - placeholder to put in text.
8848 * @cfg {string} before - input group add on before
8849 * @cfg {string} after - input group add on after
8850 * @cfg {string} size - (lg|sm) or leave empty..
8851 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8852 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8853 * @cfg {Number} md colspan out of 12 for computer-sized screens
8854 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8855 * @cfg {string} value default value of the input
8856 * @cfg {Number} labelWidth set the width of label
8857 * @cfg {Number} labellg set the width of label (1-12)
8858 * @cfg {Number} labelmd set the width of label (1-12)
8859 * @cfg {Number} labelsm set the width of label (1-12)
8860 * @cfg {Number} labelxs set the width of label (1-12)
8861 * @cfg {String} labelAlign (top|left)
8862 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8863 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8864 * @cfg {String} indicatorpos (left|right) default left
8865 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8866 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8868 * @cfg {String} align (left|center|right) Default left
8869 * @cfg {Boolean} forceFeedback (true|false) Default false
8872 * Create a new Input
8873 * @param {Object} config The config object
8876 Roo.bootstrap.Input = function(config){
8878 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8883 * Fires when this field receives input focus.
8884 * @param {Roo.form.Field} this
8889 * Fires when this field loses input focus.
8890 * @param {Roo.form.Field} this
8895 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8896 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8897 * @param {Roo.form.Field} this
8898 * @param {Roo.EventObject} e The event object
8903 * Fires just before the field blurs if the field value has changed.
8904 * @param {Roo.form.Field} this
8905 * @param {Mixed} newValue The new value
8906 * @param {Mixed} oldValue The original value
8911 * Fires after the field has been marked as invalid.
8912 * @param {Roo.form.Field} this
8913 * @param {String} msg The validation message
8918 * Fires after the field has been validated with no errors.
8919 * @param {Roo.form.Field} this
8924 * Fires after the key up
8925 * @param {Roo.form.Field} this
8926 * @param {Roo.EventObject} e The event Object
8932 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8934 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8935 automatic validation (defaults to "keyup").
8937 validationEvent : "keyup",
8939 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8941 validateOnBlur : true,
8943 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8945 validationDelay : 250,
8947 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8949 focusClass : "x-form-focus", // not needed???
8953 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8955 invalidClass : "has-warning",
8958 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8960 validClass : "has-success",
8963 * @cfg {Boolean} hasFeedback (true|false) default true
8968 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8970 invalidFeedbackClass : "glyphicon-warning-sign",
8973 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8975 validFeedbackClass : "glyphicon-ok",
8978 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8980 selectOnFocus : false,
8983 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8987 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8992 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8994 disableKeyFilter : false,
8997 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9001 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9005 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9007 blankText : "Please complete this mandatory field",
9010 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9014 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9016 maxLength : Number.MAX_VALUE,
9018 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9020 minLengthText : "The minimum length for this field is {0}",
9022 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9024 maxLengthText : "The maximum length for this field is {0}",
9028 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9029 * If available, this function will be called only after the basic validators all return true, and will be passed the
9030 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9034 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9035 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9036 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9040 * @cfg {String} regexText -- Depricated - use Invalid Text
9045 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9051 autocomplete: false,
9070 formatedValue : false,
9071 forceFeedback : false,
9073 indicatorpos : 'left',
9083 parentLabelAlign : function()
9086 while (parent.parent()) {
9087 parent = parent.parent();
9088 if (typeof(parent.labelAlign) !='undefined') {
9089 return parent.labelAlign;
9096 getAutoCreate : function()
9098 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9104 if(this.inputType != 'hidden'){
9105 cfg.cls = 'form-group' //input-group
9111 type : this.inputType,
9113 cls : 'form-control',
9114 placeholder : this.placeholder || '',
9115 autocomplete : this.autocomplete || 'new-password'
9118 if(this.capture.length){
9119 input.capture = this.capture;
9122 if(this.accept.length){
9123 input.accept = this.accept + "/*";
9127 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9130 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9131 input.maxLength = this.maxLength;
9134 if (this.disabled) {
9135 input.disabled=true;
9138 if (this.readOnly) {
9139 input.readonly=true;
9143 input.name = this.name;
9147 input.cls += ' input-' + this.size;
9151 ['xs','sm','md','lg'].map(function(size){
9152 if (settings[size]) {
9153 cfg.cls += ' col-' + size + '-' + settings[size];
9157 var inputblock = input;
9161 cls: 'glyphicon form-control-feedback'
9164 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9167 cls : 'has-feedback',
9175 if (this.before || this.after) {
9178 cls : 'input-group',
9182 if (this.before && typeof(this.before) == 'string') {
9184 inputblock.cn.push({
9186 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9190 if (this.before && typeof(this.before) == 'object') {
9191 this.before = Roo.factory(this.before);
9193 inputblock.cn.push({
9195 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9196 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9200 inputblock.cn.push(input);
9202 if (this.after && typeof(this.after) == 'string') {
9203 inputblock.cn.push({
9205 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9209 if (this.after && typeof(this.after) == 'object') {
9210 this.after = Roo.factory(this.after);
9212 inputblock.cn.push({
9214 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9215 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9219 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9220 inputblock.cls += ' has-feedback';
9221 inputblock.cn.push(feedback);
9226 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9227 tooltip : 'This field is required'
9229 if (Roo.bootstrap.version == 4) {
9232 style : 'display-none'
9235 if (align ==='left' && this.fieldLabel.length) {
9237 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9244 cls : 'control-label col-form-label',
9245 html : this.fieldLabel
9256 var labelCfg = cfg.cn[1];
9257 var contentCfg = cfg.cn[2];
9259 if(this.indicatorpos == 'right'){
9264 cls : 'control-label col-form-label',
9268 html : this.fieldLabel
9282 labelCfg = cfg.cn[0];
9283 contentCfg = cfg.cn[1];
9287 if(this.labelWidth > 12){
9288 labelCfg.style = "width: " + this.labelWidth + 'px';
9291 if(this.labelWidth < 13 && this.labelmd == 0){
9292 this.labelmd = this.labelWidth;
9295 if(this.labellg > 0){
9296 labelCfg.cls += ' col-lg-' + this.labellg;
9297 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9300 if(this.labelmd > 0){
9301 labelCfg.cls += ' col-md-' + this.labelmd;
9302 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9305 if(this.labelsm > 0){
9306 labelCfg.cls += ' col-sm-' + this.labelsm;
9307 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9310 if(this.labelxs > 0){
9311 labelCfg.cls += ' col-xs-' + this.labelxs;
9312 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9316 } else if ( this.fieldLabel.length) {
9321 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9322 tooltip : 'This field is required'
9326 //cls : 'input-group-addon',
9327 html : this.fieldLabel
9335 if(this.indicatorpos == 'right'){
9340 //cls : 'input-group-addon',
9341 html : this.fieldLabel
9346 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9347 tooltip : 'This field is required'
9367 if (this.parentType === 'Navbar' && this.parent().bar) {
9368 cfg.cls += ' navbar-form';
9371 if (this.parentType === 'NavGroup') {
9372 cfg.cls += ' navbar-form';
9380 * return the real input element.
9382 inputEl: function ()
9384 return this.el.select('input.form-control',true).first();
9387 tooltipEl : function()
9389 return this.inputEl();
9392 indicatorEl : function()
9394 if (Roo.bootstrap.version == 4) {
9395 return false; // not enabled in v4 yet.
9398 var indicator = this.el.select('i.roo-required-indicator',true).first();
9408 setDisabled : function(v)
9410 var i = this.inputEl().dom;
9412 i.removeAttribute('disabled');
9416 i.setAttribute('disabled','true');
9418 initEvents : function()
9421 this.inputEl().on("keydown" , this.fireKey, this);
9422 this.inputEl().on("focus", this.onFocus, this);
9423 this.inputEl().on("blur", this.onBlur, this);
9425 this.inputEl().relayEvent('keyup', this);
9427 this.indicator = this.indicatorEl();
9430 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9433 // reference to original value for reset
9434 this.originalValue = this.getValue();
9435 //Roo.form.TextField.superclass.initEvents.call(this);
9436 if(this.validationEvent == 'keyup'){
9437 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9438 this.inputEl().on('keyup', this.filterValidation, this);
9440 else if(this.validationEvent !== false){
9441 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9444 if(this.selectOnFocus){
9445 this.on("focus", this.preFocus, this);
9448 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9449 this.inputEl().on("keypress", this.filterKeys, this);
9451 this.inputEl().relayEvent('keypress', this);
9454 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9455 this.el.on("click", this.autoSize, this);
9458 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9459 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9462 if (typeof(this.before) == 'object') {
9463 this.before.render(this.el.select('.roo-input-before',true).first());
9465 if (typeof(this.after) == 'object') {
9466 this.after.render(this.el.select('.roo-input-after',true).first());
9469 this.inputEl().on('change', this.onChange, this);
9472 filterValidation : function(e){
9473 if(!e.isNavKeyPress()){
9474 this.validationTask.delay(this.validationDelay);
9478 * Validates the field value
9479 * @return {Boolean} True if the value is valid, else false
9481 validate : function(){
9482 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9483 if(this.disabled || this.validateValue(this.getRawValue())){
9494 * Validates a value according to the field's validation rules and marks the field as invalid
9495 * if the validation fails
9496 * @param {Mixed} value The value to validate
9497 * @return {Boolean} True if the value is valid, else false
9499 validateValue : function(value)
9501 if(this.getVisibilityEl().hasClass('hidden')){
9505 if(value.length < 1) { // if it's blank
9506 if(this.allowBlank){
9512 if(value.length < this.minLength){
9515 if(value.length > this.maxLength){
9519 var vt = Roo.form.VTypes;
9520 if(!vt[this.vtype](value, this)){
9524 if(typeof this.validator == "function"){
9525 var msg = this.validator(value);
9529 if (typeof(msg) == 'string') {
9530 this.invalidText = msg;
9534 if(this.regex && !this.regex.test(value)){
9542 fireKey : function(e){
9543 //Roo.log('field ' + e.getKey());
9544 if(e.isNavKeyPress()){
9545 this.fireEvent("specialkey", this, e);
9548 focus : function (selectText){
9550 this.inputEl().focus();
9551 if(selectText === true){
9552 this.inputEl().dom.select();
9558 onFocus : function(){
9559 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9560 // this.el.addClass(this.focusClass);
9563 this.hasFocus = true;
9564 this.startValue = this.getValue();
9565 this.fireEvent("focus", this);
9569 beforeBlur : Roo.emptyFn,
9573 onBlur : function(){
9575 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9576 //this.el.removeClass(this.focusClass);
9578 this.hasFocus = false;
9579 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9582 var v = this.getValue();
9583 if(String(v) !== String(this.startValue)){
9584 this.fireEvent('change', this, v, this.startValue);
9586 this.fireEvent("blur", this);
9589 onChange : function(e)
9591 var v = this.getValue();
9592 if(String(v) !== String(this.startValue)){
9593 this.fireEvent('change', this, v, this.startValue);
9599 * Resets the current field value to the originally loaded value and clears any validation messages
9602 this.setValue(this.originalValue);
9606 * Returns the name of the field
9607 * @return {Mixed} name The name field
9609 getName: function(){
9613 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9614 * @return {Mixed} value The field value
9616 getValue : function(){
9618 var v = this.inputEl().getValue();
9623 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9624 * @return {Mixed} value The field value
9626 getRawValue : function(){
9627 var v = this.inputEl().getValue();
9633 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9634 * @param {Mixed} value The value to set
9636 setRawValue : function(v){
9637 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9640 selectText : function(start, end){
9641 var v = this.getRawValue();
9643 start = start === undefined ? 0 : start;
9644 end = end === undefined ? v.length : end;
9645 var d = this.inputEl().dom;
9646 if(d.setSelectionRange){
9647 d.setSelectionRange(start, end);
9648 }else if(d.createTextRange){
9649 var range = d.createTextRange();
9650 range.moveStart("character", start);
9651 range.moveEnd("character", v.length-end);
9658 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9659 * @param {Mixed} value The value to set
9661 setValue : function(v){
9664 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9670 processValue : function(value){
9671 if(this.stripCharsRe){
9672 var newValue = value.replace(this.stripCharsRe, '');
9673 if(newValue !== value){
9674 this.setRawValue(newValue);
9681 preFocus : function(){
9683 if(this.selectOnFocus){
9684 this.inputEl().dom.select();
9687 filterKeys : function(e){
9689 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9692 var c = e.getCharCode(), cc = String.fromCharCode(c);
9693 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9696 if(!this.maskRe.test(cc)){
9701 * Clear any invalid styles/messages for this field
9703 clearInvalid : function(){
9705 if(!this.el || this.preventMark){ // not rendered
9710 this.el.removeClass(this.invalidClass);
9712 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9714 var feedback = this.el.select('.form-control-feedback', true).first();
9717 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9723 this.indicator.removeClass('visible');
9724 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9727 this.fireEvent('valid', this);
9731 * Mark this field as valid
9733 markValid : function()
9735 if(!this.el || this.preventMark){ // not rendered...
9739 this.el.removeClass([this.invalidClass, this.validClass]);
9741 var feedback = this.el.select('.form-control-feedback', true).first();
9744 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9748 this.indicator.removeClass('visible');
9749 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9756 if(this.allowBlank && !this.getRawValue().length){
9760 this.el.addClass(this.validClass);
9762 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9764 var feedback = this.el.select('.form-control-feedback', true).first();
9767 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9768 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9773 this.fireEvent('valid', this);
9777 * Mark this field as invalid
9778 * @param {String} msg The validation message
9780 markInvalid : function(msg)
9782 if(!this.el || this.preventMark){ // not rendered
9786 this.el.removeClass([this.invalidClass, this.validClass]);
9788 var feedback = this.el.select('.form-control-feedback', true).first();
9791 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9798 if(this.allowBlank && !this.getRawValue().length){
9803 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9804 this.indicator.addClass('visible');
9807 this.el.addClass(this.invalidClass);
9809 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9811 var feedback = this.el.select('.form-control-feedback', true).first();
9814 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9816 if(this.getValue().length || this.forceFeedback){
9817 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9824 this.fireEvent('invalid', this, msg);
9827 SafariOnKeyDown : function(event)
9829 // this is a workaround for a password hang bug on chrome/ webkit.
9830 if (this.inputEl().dom.type != 'password') {
9834 var isSelectAll = false;
9836 if(this.inputEl().dom.selectionEnd > 0){
9837 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9839 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9840 event.preventDefault();
9845 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9847 event.preventDefault();
9848 // this is very hacky as keydown always get's upper case.
9850 var cc = String.fromCharCode(event.getCharCode());
9851 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9855 adjustWidth : function(tag, w){
9856 tag = tag.toLowerCase();
9857 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9858 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9862 if(tag == 'textarea'){
9865 }else if(Roo.isOpera){
9869 if(tag == 'textarea'){
9877 setFieldLabel : function(v)
9883 if(this.indicatorEl()){
9884 var ar = this.el.select('label > span',true);
9886 if (ar.elements.length) {
9887 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9888 this.fieldLabel = v;
9892 var br = this.el.select('label',true);
9894 if(br.elements.length) {
9895 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9896 this.fieldLabel = v;
9900 Roo.log('Cannot Found any of label > span || label in input');
9904 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9905 this.fieldLabel = v;
9920 * @class Roo.bootstrap.TextArea
9921 * @extends Roo.bootstrap.Input
9922 * Bootstrap TextArea class
9923 * @cfg {Number} cols Specifies the visible width of a text area
9924 * @cfg {Number} rows Specifies the visible number of lines in a text area
9925 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9926 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9927 * @cfg {string} html text
9930 * Create a new TextArea
9931 * @param {Object} config The config object
9934 Roo.bootstrap.TextArea = function(config){
9935 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9939 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9949 getAutoCreate : function(){
9951 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9957 if(this.inputType != 'hidden'){
9958 cfg.cls = 'form-group' //input-group
9966 value : this.value || '',
9967 html: this.html || '',
9968 cls : 'form-control',
9969 placeholder : this.placeholder || ''
9973 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9974 input.maxLength = this.maxLength;
9978 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9982 input.cols = this.cols;
9985 if (this.readOnly) {
9986 input.readonly = true;
9990 input.name = this.name;
9994 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9998 ['xs','sm','md','lg'].map(function(size){
9999 if (settings[size]) {
10000 cfg.cls += ' col-' + size + '-' + settings[size];
10004 var inputblock = input;
10006 if(this.hasFeedback && !this.allowBlank){
10010 cls: 'glyphicon form-control-feedback'
10014 cls : 'has-feedback',
10023 if (this.before || this.after) {
10026 cls : 'input-group',
10030 inputblock.cn.push({
10032 cls : 'input-group-addon',
10037 inputblock.cn.push(input);
10039 if(this.hasFeedback && !this.allowBlank){
10040 inputblock.cls += ' has-feedback';
10041 inputblock.cn.push(feedback);
10045 inputblock.cn.push({
10047 cls : 'input-group-addon',
10054 if (align ==='left' && this.fieldLabel.length) {
10059 cls : 'control-label',
10060 html : this.fieldLabel
10071 if(this.labelWidth > 12){
10072 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10075 if(this.labelWidth < 13 && this.labelmd == 0){
10076 this.labelmd = this.labelWidth;
10079 if(this.labellg > 0){
10080 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10081 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10084 if(this.labelmd > 0){
10085 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10086 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10089 if(this.labelsm > 0){
10090 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10091 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10094 if(this.labelxs > 0){
10095 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10096 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10099 } else if ( this.fieldLabel.length) {
10104 //cls : 'input-group-addon',
10105 html : this.fieldLabel
10123 if (this.disabled) {
10124 input.disabled=true;
10131 * return the real textarea element.
10133 inputEl: function ()
10135 return this.el.select('textarea.form-control',true).first();
10139 * Clear any invalid styles/messages for this field
10141 clearInvalid : function()
10144 if(!this.el || this.preventMark){ // not rendered
10148 var label = this.el.select('label', true).first();
10149 var icon = this.el.select('i.fa-star', true).first();
10155 this.el.removeClass(this.invalidClass);
10157 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10159 var feedback = this.el.select('.form-control-feedback', true).first();
10162 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10167 this.fireEvent('valid', this);
10171 * Mark this field as valid
10173 markValid : function()
10175 if(!this.el || this.preventMark){ // not rendered
10179 this.el.removeClass([this.invalidClass, this.validClass]);
10181 var feedback = this.el.select('.form-control-feedback', true).first();
10184 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10187 if(this.disabled || this.allowBlank){
10191 var label = this.el.select('label', true).first();
10192 var icon = this.el.select('i.fa-star', true).first();
10198 this.el.addClass(this.validClass);
10200 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10202 var feedback = this.el.select('.form-control-feedback', true).first();
10205 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10206 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10211 this.fireEvent('valid', this);
10215 * Mark this field as invalid
10216 * @param {String} msg The validation message
10218 markInvalid : function(msg)
10220 if(!this.el || this.preventMark){ // not rendered
10224 this.el.removeClass([this.invalidClass, this.validClass]);
10226 var feedback = this.el.select('.form-control-feedback', true).first();
10229 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10232 if(this.disabled || this.allowBlank){
10236 var label = this.el.select('label', true).first();
10237 var icon = this.el.select('i.fa-star', true).first();
10239 if(!this.getValue().length && label && !icon){
10240 this.el.createChild({
10242 cls : 'text-danger fa fa-lg fa-star',
10243 tooltip : 'This field is required',
10244 style : 'margin-right:5px;'
10248 this.el.addClass(this.invalidClass);
10250 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10252 var feedback = this.el.select('.form-control-feedback', true).first();
10255 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10257 if(this.getValue().length || this.forceFeedback){
10258 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10265 this.fireEvent('invalid', this, msg);
10273 * trigger field - base class for combo..
10278 * @class Roo.bootstrap.TriggerField
10279 * @extends Roo.bootstrap.Input
10280 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10281 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10282 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10283 * for which you can provide a custom implementation. For example:
10285 var trigger = new Roo.bootstrap.TriggerField();
10286 trigger.onTriggerClick = myTriggerFn;
10287 trigger.applyTo('my-field');
10290 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10291 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10292 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10293 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10294 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10297 * Create a new TriggerField.
10298 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10299 * to the base TextField)
10301 Roo.bootstrap.TriggerField = function(config){
10302 this.mimicing = false;
10303 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10306 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10308 * @cfg {String} triggerClass A CSS class to apply to the trigger
10311 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10316 * @cfg {Boolean} removable (true|false) special filter default false
10320 /** @cfg {Boolean} grow @hide */
10321 /** @cfg {Number} growMin @hide */
10322 /** @cfg {Number} growMax @hide */
10328 autoSize: Roo.emptyFn,
10332 deferHeight : true,
10335 actionMode : 'wrap',
10340 getAutoCreate : function(){
10342 var align = this.labelAlign || this.parentLabelAlign();
10347 cls: 'form-group' //input-group
10354 type : this.inputType,
10355 cls : 'form-control',
10356 autocomplete: 'new-password',
10357 placeholder : this.placeholder || ''
10361 input.name = this.name;
10364 input.cls += ' input-' + this.size;
10367 if (this.disabled) {
10368 input.disabled=true;
10371 var inputblock = input;
10373 if(this.hasFeedback && !this.allowBlank){
10377 cls: 'glyphicon form-control-feedback'
10380 if(this.removable && !this.editable && !this.tickable){
10382 cls : 'has-feedback',
10388 cls : 'roo-combo-removable-btn close'
10395 cls : 'has-feedback',
10404 if(this.removable && !this.editable && !this.tickable){
10406 cls : 'roo-removable',
10412 cls : 'roo-combo-removable-btn close'
10419 if (this.before || this.after) {
10422 cls : 'input-group',
10426 inputblock.cn.push({
10428 cls : 'input-group-addon input-group-prepend input-group-text',
10433 inputblock.cn.push(input);
10435 if(this.hasFeedback && !this.allowBlank){
10436 inputblock.cls += ' has-feedback';
10437 inputblock.cn.push(feedback);
10441 inputblock.cn.push({
10443 cls : 'input-group-addon input-group-append input-group-text',
10452 var ibwrap = inputblock;
10457 cls: 'roo-select2-choices',
10461 cls: 'roo-select2-search-field',
10473 cls: 'roo-select2-container input-group',
10478 cls: 'form-hidden-field'
10484 if(!this.multiple && this.showToggleBtn){
10490 if (this.caret != false) {
10493 cls: 'fa fa-' + this.caret
10500 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10505 cls: 'combobox-clear',
10519 combobox.cls += ' roo-select2-container-multi';
10523 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10524 tooltip : 'This field is required'
10526 if (Roo.bootstrap.version == 4) {
10529 style : 'display:none'
10534 if (align ==='left' && this.fieldLabel.length) {
10536 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10543 cls : 'control-label',
10544 html : this.fieldLabel
10556 var labelCfg = cfg.cn[1];
10557 var contentCfg = cfg.cn[2];
10559 if(this.indicatorpos == 'right'){
10564 cls : 'control-label',
10568 html : this.fieldLabel
10582 labelCfg = cfg.cn[0];
10583 contentCfg = cfg.cn[1];
10586 if(this.labelWidth > 12){
10587 labelCfg.style = "width: " + this.labelWidth + 'px';
10590 if(this.labelWidth < 13 && this.labelmd == 0){
10591 this.labelmd = this.labelWidth;
10594 if(this.labellg > 0){
10595 labelCfg.cls += ' col-lg-' + this.labellg;
10596 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10599 if(this.labelmd > 0){
10600 labelCfg.cls += ' col-md-' + this.labelmd;
10601 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10604 if(this.labelsm > 0){
10605 labelCfg.cls += ' col-sm-' + this.labelsm;
10606 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10609 if(this.labelxs > 0){
10610 labelCfg.cls += ' col-xs-' + this.labelxs;
10611 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10614 } else if ( this.fieldLabel.length) {
10615 // Roo.log(" label");
10620 //cls : 'input-group-addon',
10621 html : this.fieldLabel
10629 if(this.indicatorpos == 'right'){
10637 html : this.fieldLabel
10651 // Roo.log(" no label && no align");
10658 ['xs','sm','md','lg'].map(function(size){
10659 if (settings[size]) {
10660 cfg.cls += ' col-' + size + '-' + settings[size];
10671 onResize : function(w, h){
10672 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10673 // if(typeof w == 'number'){
10674 // var x = w - this.trigger.getWidth();
10675 // this.inputEl().setWidth(this.adjustWidth('input', x));
10676 // this.trigger.setStyle('left', x+'px');
10681 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10684 getResizeEl : function(){
10685 return this.inputEl();
10689 getPositionEl : function(){
10690 return this.inputEl();
10694 alignErrorIcon : function(){
10695 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10699 initEvents : function(){
10703 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10704 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10705 if(!this.multiple && this.showToggleBtn){
10706 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10707 if(this.hideTrigger){
10708 this.trigger.setDisplayed(false);
10710 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10714 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10717 if(this.removable && !this.editable && !this.tickable){
10718 var close = this.closeTriggerEl();
10721 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10722 close.on('click', this.removeBtnClick, this, close);
10726 //this.trigger.addClassOnOver('x-form-trigger-over');
10727 //this.trigger.addClassOnClick('x-form-trigger-click');
10730 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10734 closeTriggerEl : function()
10736 var close = this.el.select('.roo-combo-removable-btn', true).first();
10737 return close ? close : false;
10740 removeBtnClick : function(e, h, el)
10742 e.preventDefault();
10744 if(this.fireEvent("remove", this) !== false){
10746 this.fireEvent("afterremove", this)
10750 createList : function()
10752 this.list = Roo.get(document.body).createChild({
10753 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10754 cls: 'typeahead typeahead-long dropdown-menu',
10755 style: 'display:none'
10758 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10763 initTrigger : function(){
10768 onDestroy : function(){
10770 this.trigger.removeAllListeners();
10771 // this.trigger.remove();
10774 // this.wrap.remove();
10776 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10780 onFocus : function(){
10781 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10783 if(!this.mimicing){
10784 this.wrap.addClass('x-trigger-wrap-focus');
10785 this.mimicing = true;
10786 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10787 if(this.monitorTab){
10788 this.el.on("keydown", this.checkTab, this);
10795 checkTab : function(e){
10796 if(e.getKey() == e.TAB){
10797 this.triggerBlur();
10802 onBlur : function(){
10807 mimicBlur : function(e, t){
10809 if(!this.wrap.contains(t) && this.validateBlur()){
10810 this.triggerBlur();
10816 triggerBlur : function(){
10817 this.mimicing = false;
10818 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10819 if(this.monitorTab){
10820 this.el.un("keydown", this.checkTab, this);
10822 //this.wrap.removeClass('x-trigger-wrap-focus');
10823 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10827 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10828 validateBlur : function(e, t){
10833 onDisable : function(){
10834 this.inputEl().dom.disabled = true;
10835 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10837 // this.wrap.addClass('x-item-disabled');
10842 onEnable : function(){
10843 this.inputEl().dom.disabled = false;
10844 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10846 // this.el.removeClass('x-item-disabled');
10851 onShow : function(){
10852 var ae = this.getActionEl();
10855 ae.dom.style.display = '';
10856 ae.dom.style.visibility = 'visible';
10862 onHide : function(){
10863 var ae = this.getActionEl();
10864 ae.dom.style.display = 'none';
10868 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10869 * by an implementing function.
10871 * @param {EventObject} e
10873 onTriggerClick : Roo.emptyFn
10877 * Ext JS Library 1.1.1
10878 * Copyright(c) 2006-2007, Ext JS, LLC.
10880 * Originally Released Under LGPL - original licence link has changed is not relivant.
10883 * <script type="text/javascript">
10888 * @class Roo.data.SortTypes
10890 * Defines the default sorting (casting?) comparison functions used when sorting data.
10892 Roo.data.SortTypes = {
10894 * Default sort that does nothing
10895 * @param {Mixed} s The value being converted
10896 * @return {Mixed} The comparison value
10898 none : function(s){
10903 * The regular expression used to strip tags
10907 stripTagsRE : /<\/?[^>]+>/gi,
10910 * Strips all HTML tags to sort on text only
10911 * @param {Mixed} s The value being converted
10912 * @return {String} The comparison value
10914 asText : function(s){
10915 return String(s).replace(this.stripTagsRE, "");
10919 * Strips all HTML tags to sort on text only - Case insensitive
10920 * @param {Mixed} s The value being converted
10921 * @return {String} The comparison value
10923 asUCText : function(s){
10924 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10928 * Case insensitive string
10929 * @param {Mixed} s The value being converted
10930 * @return {String} The comparison value
10932 asUCString : function(s) {
10933 return String(s).toUpperCase();
10938 * @param {Mixed} s The value being converted
10939 * @return {Number} The comparison value
10941 asDate : function(s) {
10945 if(s instanceof Date){
10946 return s.getTime();
10948 return Date.parse(String(s));
10953 * @param {Mixed} s The value being converted
10954 * @return {Float} The comparison value
10956 asFloat : function(s) {
10957 var val = parseFloat(String(s).replace(/,/g, ""));
10966 * @param {Mixed} s The value being converted
10967 * @return {Number} The comparison value
10969 asInt : function(s) {
10970 var val = parseInt(String(s).replace(/,/g, ""));
10978 * Ext JS Library 1.1.1
10979 * Copyright(c) 2006-2007, Ext JS, LLC.
10981 * Originally Released Under LGPL - original licence link has changed is not relivant.
10984 * <script type="text/javascript">
10988 * @class Roo.data.Record
10989 * Instances of this class encapsulate both record <em>definition</em> information, and record
10990 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10991 * to access Records cached in an {@link Roo.data.Store} object.<br>
10993 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10994 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10997 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10999 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11000 * {@link #create}. The parameters are the same.
11001 * @param {Array} data An associative Array of data values keyed by the field name.
11002 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11003 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11004 * not specified an integer id is generated.
11006 Roo.data.Record = function(data, id){
11007 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11012 * Generate a constructor for a specific record layout.
11013 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11014 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11015 * Each field definition object may contain the following properties: <ul>
11016 * <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,
11017 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11018 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11019 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11020 * is being used, then this is a string containing the javascript expression to reference the data relative to
11021 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11022 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11023 * this may be omitted.</p></li>
11024 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11025 * <ul><li>auto (Default, implies no conversion)</li>
11030 * <li>date</li></ul></p></li>
11031 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11032 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11033 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11034 * by the Reader into an object that will be stored in the Record. It is passed the
11035 * following parameters:<ul>
11036 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11038 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11040 * <br>usage:<br><pre><code>
11041 var TopicRecord = Roo.data.Record.create(
11042 {name: 'title', mapping: 'topic_title'},
11043 {name: 'author', mapping: 'username'},
11044 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11045 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11046 {name: 'lastPoster', mapping: 'user2'},
11047 {name: 'excerpt', mapping: 'post_text'}
11050 var myNewRecord = new TopicRecord({
11051 title: 'Do my job please',
11054 lastPost: new Date(),
11055 lastPoster: 'Animal',
11056 excerpt: 'No way dude!'
11058 myStore.add(myNewRecord);
11063 Roo.data.Record.create = function(o){
11064 var f = function(){
11065 f.superclass.constructor.apply(this, arguments);
11067 Roo.extend(f, Roo.data.Record);
11068 var p = f.prototype;
11069 p.fields = new Roo.util.MixedCollection(false, function(field){
11072 for(var i = 0, len = o.length; i < len; i++){
11073 p.fields.add(new Roo.data.Field(o[i]));
11075 f.getField = function(name){
11076 return p.fields.get(name);
11081 Roo.data.Record.AUTO_ID = 1000;
11082 Roo.data.Record.EDIT = 'edit';
11083 Roo.data.Record.REJECT = 'reject';
11084 Roo.data.Record.COMMIT = 'commit';
11086 Roo.data.Record.prototype = {
11088 * Readonly flag - true if this record has been modified.
11097 join : function(store){
11098 this.store = store;
11102 * Set the named field to the specified value.
11103 * @param {String} name The name of the field to set.
11104 * @param {Object} value The value to set the field to.
11106 set : function(name, value){
11107 if(this.data[name] == value){
11111 if(!this.modified){
11112 this.modified = {};
11114 if(typeof this.modified[name] == 'undefined'){
11115 this.modified[name] = this.data[name];
11117 this.data[name] = value;
11118 if(!this.editing && this.store){
11119 this.store.afterEdit(this);
11124 * Get the value of the named field.
11125 * @param {String} name The name of the field to get the value of.
11126 * @return {Object} The value of the field.
11128 get : function(name){
11129 return this.data[name];
11133 beginEdit : function(){
11134 this.editing = true;
11135 this.modified = {};
11139 cancelEdit : function(){
11140 this.editing = false;
11141 delete this.modified;
11145 endEdit : function(){
11146 this.editing = false;
11147 if(this.dirty && this.store){
11148 this.store.afterEdit(this);
11153 * Usually called by the {@link Roo.data.Store} which owns the Record.
11154 * Rejects all changes made to the Record since either creation, or the last commit operation.
11155 * Modified fields are reverted to their original values.
11157 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11158 * of reject operations.
11160 reject : function(){
11161 var m = this.modified;
11163 if(typeof m[n] != "function"){
11164 this.data[n] = m[n];
11167 this.dirty = false;
11168 delete this.modified;
11169 this.editing = false;
11171 this.store.afterReject(this);
11176 * Usually called by the {@link Roo.data.Store} which owns the Record.
11177 * Commits all changes made to the Record since either creation, or the last commit operation.
11179 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11180 * of commit operations.
11182 commit : function(){
11183 this.dirty = false;
11184 delete this.modified;
11185 this.editing = false;
11187 this.store.afterCommit(this);
11192 hasError : function(){
11193 return this.error != null;
11197 clearError : function(){
11202 * Creates a copy of this record.
11203 * @param {String} id (optional) A new record id if you don't want to use this record's id
11206 copy : function(newId) {
11207 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11211 * Ext JS Library 1.1.1
11212 * Copyright(c) 2006-2007, Ext JS, LLC.
11214 * Originally Released Under LGPL - original licence link has changed is not relivant.
11217 * <script type="text/javascript">
11223 * @class Roo.data.Store
11224 * @extends Roo.util.Observable
11225 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11226 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11228 * 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
11229 * has no knowledge of the format of the data returned by the Proxy.<br>
11231 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11232 * instances from the data object. These records are cached and made available through accessor functions.
11234 * Creates a new Store.
11235 * @param {Object} config A config object containing the objects needed for the Store to access data,
11236 * and read the data into Records.
11238 Roo.data.Store = function(config){
11239 this.data = new Roo.util.MixedCollection(false);
11240 this.data.getKey = function(o){
11243 this.baseParams = {};
11245 this.paramNames = {
11250 "multisort" : "_multisort"
11253 if(config && config.data){
11254 this.inlineData = config.data;
11255 delete config.data;
11258 Roo.apply(this, config);
11260 if(this.reader){ // reader passed
11261 this.reader = Roo.factory(this.reader, Roo.data);
11262 this.reader.xmodule = this.xmodule || false;
11263 if(!this.recordType){
11264 this.recordType = this.reader.recordType;
11266 if(this.reader.onMetaChange){
11267 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11271 if(this.recordType){
11272 this.fields = this.recordType.prototype.fields;
11274 this.modified = [];
11278 * @event datachanged
11279 * Fires when the data cache has changed, and a widget which is using this Store
11280 * as a Record cache should refresh its view.
11281 * @param {Store} this
11283 datachanged : true,
11285 * @event metachange
11286 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11287 * @param {Store} this
11288 * @param {Object} meta The JSON metadata
11293 * Fires when Records have been added to the Store
11294 * @param {Store} this
11295 * @param {Roo.data.Record[]} records The array of Records added
11296 * @param {Number} index The index at which the record(s) were added
11301 * Fires when a Record has been removed from the Store
11302 * @param {Store} this
11303 * @param {Roo.data.Record} record The Record that was removed
11304 * @param {Number} index The index at which the record was removed
11309 * Fires when a Record has been updated
11310 * @param {Store} this
11311 * @param {Roo.data.Record} record The Record that was updated
11312 * @param {String} operation The update operation being performed. Value may be one of:
11314 Roo.data.Record.EDIT
11315 Roo.data.Record.REJECT
11316 Roo.data.Record.COMMIT
11322 * Fires when the data cache has been cleared.
11323 * @param {Store} this
11327 * @event beforeload
11328 * Fires before a request is made for a new data object. If the beforeload handler returns false
11329 * the load action will be canceled.
11330 * @param {Store} this
11331 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11335 * @event beforeloadadd
11336 * Fires after a new set of Records has been loaded.
11337 * @param {Store} this
11338 * @param {Roo.data.Record[]} records The Records that were loaded
11339 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11341 beforeloadadd : true,
11344 * Fires after a new set of Records has been loaded, before they are added to the store.
11345 * @param {Store} this
11346 * @param {Roo.data.Record[]} records The Records that were loaded
11347 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11348 * @params {Object} return from reader
11352 * @event loadexception
11353 * Fires if an exception occurs in the Proxy during loading.
11354 * Called with the signature of the Proxy's "loadexception" event.
11355 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11358 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11359 * @param {Object} load options
11360 * @param {Object} jsonData from your request (normally this contains the Exception)
11362 loadexception : true
11366 this.proxy = Roo.factory(this.proxy, Roo.data);
11367 this.proxy.xmodule = this.xmodule || false;
11368 this.relayEvents(this.proxy, ["loadexception"]);
11370 this.sortToggle = {};
11371 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11373 Roo.data.Store.superclass.constructor.call(this);
11375 if(this.inlineData){
11376 this.loadData(this.inlineData);
11377 delete this.inlineData;
11381 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11383 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11384 * without a remote query - used by combo/forms at present.
11388 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11391 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11394 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11395 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11398 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11399 * on any HTTP request
11402 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11405 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11409 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11410 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11412 remoteSort : false,
11415 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11416 * loaded or when a record is removed. (defaults to false).
11418 pruneModifiedRecords : false,
11421 lastOptions : null,
11424 * Add Records to the Store and fires the add event.
11425 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11427 add : function(records){
11428 records = [].concat(records);
11429 for(var i = 0, len = records.length; i < len; i++){
11430 records[i].join(this);
11432 var index = this.data.length;
11433 this.data.addAll(records);
11434 this.fireEvent("add", this, records, index);
11438 * Remove a Record from the Store and fires the remove event.
11439 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11441 remove : function(record){
11442 var index = this.data.indexOf(record);
11443 this.data.removeAt(index);
11445 if(this.pruneModifiedRecords){
11446 this.modified.remove(record);
11448 this.fireEvent("remove", this, record, index);
11452 * Remove all Records from the Store and fires the clear event.
11454 removeAll : function(){
11456 if(this.pruneModifiedRecords){
11457 this.modified = [];
11459 this.fireEvent("clear", this);
11463 * Inserts Records to the Store at the given index and fires the add event.
11464 * @param {Number} index The start index at which to insert the passed Records.
11465 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11467 insert : function(index, records){
11468 records = [].concat(records);
11469 for(var i = 0, len = records.length; i < len; i++){
11470 this.data.insert(index, records[i]);
11471 records[i].join(this);
11473 this.fireEvent("add", this, records, index);
11477 * Get the index within the cache of the passed Record.
11478 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11479 * @return {Number} The index of the passed Record. Returns -1 if not found.
11481 indexOf : function(record){
11482 return this.data.indexOf(record);
11486 * Get the index within the cache of the Record with the passed id.
11487 * @param {String} id The id of the Record to find.
11488 * @return {Number} The index of the Record. Returns -1 if not found.
11490 indexOfId : function(id){
11491 return this.data.indexOfKey(id);
11495 * Get the Record with the specified id.
11496 * @param {String} id The id of the Record to find.
11497 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11499 getById : function(id){
11500 return this.data.key(id);
11504 * Get the Record at the specified index.
11505 * @param {Number} index The index of the Record to find.
11506 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11508 getAt : function(index){
11509 return this.data.itemAt(index);
11513 * Returns a range of Records between specified indices.
11514 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11515 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11516 * @return {Roo.data.Record[]} An array of Records
11518 getRange : function(start, end){
11519 return this.data.getRange(start, end);
11523 storeOptions : function(o){
11524 o = Roo.apply({}, o);
11527 this.lastOptions = o;
11531 * Loads the Record cache from the configured Proxy using the configured Reader.
11533 * If using remote paging, then the first load call must specify the <em>start</em>
11534 * and <em>limit</em> properties in the options.params property to establish the initial
11535 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11537 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11538 * and this call will return before the new data has been loaded. Perform any post-processing
11539 * in a callback function, or in a "load" event handler.</strong>
11541 * @param {Object} options An object containing properties which control loading options:<ul>
11542 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11543 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11544 * passed the following arguments:<ul>
11545 * <li>r : Roo.data.Record[]</li>
11546 * <li>options: Options object from the load call</li>
11547 * <li>success: Boolean success indicator</li></ul></li>
11548 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11549 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11552 load : function(options){
11553 options = options || {};
11554 if(this.fireEvent("beforeload", this, options) !== false){
11555 this.storeOptions(options);
11556 var p = Roo.apply(options.params || {}, this.baseParams);
11557 // if meta was not loaded from remote source.. try requesting it.
11558 if (!this.reader.metaFromRemote) {
11559 p._requestMeta = 1;
11561 if(this.sortInfo && this.remoteSort){
11562 var pn = this.paramNames;
11563 p[pn["sort"]] = this.sortInfo.field;
11564 p[pn["dir"]] = this.sortInfo.direction;
11566 if (this.multiSort) {
11567 var pn = this.paramNames;
11568 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11571 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11576 * Reloads the Record cache from the configured Proxy using the configured Reader and
11577 * the options from the last load operation performed.
11578 * @param {Object} options (optional) An object containing properties which may override the options
11579 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11580 * the most recently used options are reused).
11582 reload : function(options){
11583 this.load(Roo.applyIf(options||{}, this.lastOptions));
11587 // Called as a callback by the Reader during a load operation.
11588 loadRecords : function(o, options, success){
11589 if(!o || success === false){
11590 if(success !== false){
11591 this.fireEvent("load", this, [], options, o);
11593 if(options.callback){
11594 options.callback.call(options.scope || this, [], options, false);
11598 // if data returned failure - throw an exception.
11599 if (o.success === false) {
11600 // show a message if no listener is registered.
11601 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11602 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11604 // loadmask wil be hooked into this..
11605 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11608 var r = o.records, t = o.totalRecords || r.length;
11610 this.fireEvent("beforeloadadd", this, r, options, o);
11612 if(!options || options.add !== true){
11613 if(this.pruneModifiedRecords){
11614 this.modified = [];
11616 for(var i = 0, len = r.length; i < len; i++){
11620 this.data = this.snapshot;
11621 delete this.snapshot;
11624 this.data.addAll(r);
11625 this.totalLength = t;
11627 this.fireEvent("datachanged", this);
11629 this.totalLength = Math.max(t, this.data.length+r.length);
11633 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11635 var e = new Roo.data.Record({});
11637 e.set(this.parent.displayField, this.parent.emptyTitle);
11638 e.set(this.parent.valueField, '');
11643 this.fireEvent("load", this, r, options, o);
11644 if(options.callback){
11645 options.callback.call(options.scope || this, r, options, true);
11651 * Loads data from a passed data block. A Reader which understands the format of the data
11652 * must have been configured in the constructor.
11653 * @param {Object} data The data block from which to read the Records. The format of the data expected
11654 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11655 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11657 loadData : function(o, append){
11658 var r = this.reader.readRecords(o);
11659 this.loadRecords(r, {add: append}, true);
11663 * Gets the number of cached records.
11665 * <em>If using paging, this may not be the total size of the dataset. If the data object
11666 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11667 * the data set size</em>
11669 getCount : function(){
11670 return this.data.length || 0;
11674 * Gets the total number of records in the dataset as returned by the server.
11676 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11677 * the dataset size</em>
11679 getTotalCount : function(){
11680 return this.totalLength || 0;
11684 * Returns the sort state of the Store as an object with two properties:
11686 field {String} The name of the field by which the Records are sorted
11687 direction {String} The sort order, "ASC" or "DESC"
11690 getSortState : function(){
11691 return this.sortInfo;
11695 applySort : function(){
11696 if(this.sortInfo && !this.remoteSort){
11697 var s = this.sortInfo, f = s.field;
11698 var st = this.fields.get(f).sortType;
11699 var fn = function(r1, r2){
11700 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11701 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11703 this.data.sort(s.direction, fn);
11704 if(this.snapshot && this.snapshot != this.data){
11705 this.snapshot.sort(s.direction, fn);
11711 * Sets the default sort column and order to be used by the next load operation.
11712 * @param {String} fieldName The name of the field to sort by.
11713 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11715 setDefaultSort : function(field, dir){
11716 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11720 * Sort the Records.
11721 * If remote sorting is used, the sort is performed on the server, and the cache is
11722 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
11727 var f = this.fields.get(fieldName);
11729 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11731 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11732 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11737 this.sortToggle[f.name] = dir;
11738 this.sortInfo = {field: f.name, direction: dir};
11739 if(!this.remoteSort){
11741 this.fireEvent("datachanged", this);
11743 this.load(this.lastOptions);
11748 * Calls the specified function for each of the Records in the cache.
11749 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11750 * Returning <em>false</em> aborts and exits the iteration.
11751 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11753 each : function(fn, scope){
11754 this.data.each(fn, scope);
11758 * Gets all records modified since the last commit. Modified records are persisted across load operations
11759 * (e.g., during paging).
11760 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11762 getModifiedRecords : function(){
11763 return this.modified;
11767 createFilterFn : function(property, value, anyMatch){
11768 if(!value.exec){ // not a regex
11769 value = String(value);
11770 if(value.length == 0){
11773 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11775 return function(r){
11776 return value.test(r.data[property]);
11781 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11782 * @param {String} property A field on your records
11783 * @param {Number} start The record index to start at (defaults to 0)
11784 * @param {Number} end The last record index to include (defaults to length - 1)
11785 * @return {Number} The sum
11787 sum : function(property, start, end){
11788 var rs = this.data.items, v = 0;
11789 start = start || 0;
11790 end = (end || end === 0) ? end : rs.length-1;
11792 for(var i = start; i <= end; i++){
11793 v += (rs[i].data[property] || 0);
11799 * Filter the records by a specified property.
11800 * @param {String} field A field on your records
11801 * @param {String/RegExp} value Either a string that the field
11802 * should start with or a RegExp to test against the field
11803 * @param {Boolean} anyMatch True to match any part not just the beginning
11805 filter : function(property, value, anyMatch){
11806 var fn = this.createFilterFn(property, value, anyMatch);
11807 return fn ? this.filterBy(fn) : this.clearFilter();
11811 * Filter by a function. The specified function will be called with each
11812 * record in this data source. If the function returns true the record is included,
11813 * otherwise it is filtered.
11814 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11815 * @param {Object} scope (optional) The scope of the function (defaults to this)
11817 filterBy : function(fn, scope){
11818 this.snapshot = this.snapshot || this.data;
11819 this.data = this.queryBy(fn, scope||this);
11820 this.fireEvent("datachanged", this);
11824 * Query the records by a specified property.
11825 * @param {String} field A field on your records
11826 * @param {String/RegExp} value Either a string that the field
11827 * should start with or a RegExp to test against the field
11828 * @param {Boolean} anyMatch True to match any part not just the beginning
11829 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11831 query : function(property, value, anyMatch){
11832 var fn = this.createFilterFn(property, value, anyMatch);
11833 return fn ? this.queryBy(fn) : this.data.clone();
11837 * Query by a function. The specified function will be called with each
11838 * record in this data source. If the function returns true the record is included
11840 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11841 * @param {Object} scope (optional) The scope of the function (defaults to this)
11842 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11844 queryBy : function(fn, scope){
11845 var data = this.snapshot || this.data;
11846 return data.filterBy(fn, scope||this);
11850 * Collects unique values for a particular dataIndex from this store.
11851 * @param {String} dataIndex The property to collect
11852 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11853 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11854 * @return {Array} An array of the unique values
11856 collect : function(dataIndex, allowNull, bypassFilter){
11857 var d = (bypassFilter === true && this.snapshot) ?
11858 this.snapshot.items : this.data.items;
11859 var v, sv, r = [], l = {};
11860 for(var i = 0, len = d.length; i < len; i++){
11861 v = d[i].data[dataIndex];
11863 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11872 * Revert to a view of the Record cache with no filtering applied.
11873 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11875 clearFilter : function(suppressEvent){
11876 if(this.snapshot && this.snapshot != this.data){
11877 this.data = this.snapshot;
11878 delete this.snapshot;
11879 if(suppressEvent !== true){
11880 this.fireEvent("datachanged", this);
11886 afterEdit : function(record){
11887 if(this.modified.indexOf(record) == -1){
11888 this.modified.push(record);
11890 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11894 afterReject : function(record){
11895 this.modified.remove(record);
11896 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11900 afterCommit : function(record){
11901 this.modified.remove(record);
11902 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11906 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11907 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11909 commitChanges : function(){
11910 var m = this.modified.slice(0);
11911 this.modified = [];
11912 for(var i = 0, len = m.length; i < len; i++){
11918 * Cancel outstanding changes on all changed records.
11920 rejectChanges : function(){
11921 var m = this.modified.slice(0);
11922 this.modified = [];
11923 for(var i = 0, len = m.length; i < len; i++){
11928 onMetaChange : function(meta, rtype, o){
11929 this.recordType = rtype;
11930 this.fields = rtype.prototype.fields;
11931 delete this.snapshot;
11932 this.sortInfo = meta.sortInfo || this.sortInfo;
11933 this.modified = [];
11934 this.fireEvent('metachange', this, this.reader.meta);
11937 moveIndex : function(data, type)
11939 var index = this.indexOf(data);
11941 var newIndex = index + type;
11945 this.insert(newIndex, data);
11950 * Ext JS Library 1.1.1
11951 * Copyright(c) 2006-2007, Ext JS, LLC.
11953 * Originally Released Under LGPL - original licence link has changed is not relivant.
11956 * <script type="text/javascript">
11960 * @class Roo.data.SimpleStore
11961 * @extends Roo.data.Store
11962 * Small helper class to make creating Stores from Array data easier.
11963 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11964 * @cfg {Array} fields An array of field definition objects, or field name strings.
11965 * @cfg {Array} data The multi-dimensional array of data
11967 * @param {Object} config
11969 Roo.data.SimpleStore = function(config){
11970 Roo.data.SimpleStore.superclass.constructor.call(this, {
11972 reader: new Roo.data.ArrayReader({
11975 Roo.data.Record.create(config.fields)
11977 proxy : new Roo.data.MemoryProxy(config.data)
11981 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11983 * Ext JS Library 1.1.1
11984 * Copyright(c) 2006-2007, Ext JS, LLC.
11986 * Originally Released Under LGPL - original licence link has changed is not relivant.
11989 * <script type="text/javascript">
11994 * @extends Roo.data.Store
11995 * @class Roo.data.JsonStore
11996 * Small helper class to make creating Stores for JSON data easier. <br/>
11998 var store = new Roo.data.JsonStore({
11999 url: 'get-images.php',
12001 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12004 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12005 * JsonReader and HttpProxy (unless inline data is provided).</b>
12006 * @cfg {Array} fields An array of field definition objects, or field name strings.
12008 * @param {Object} config
12010 Roo.data.JsonStore = function(c){
12011 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12012 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12013 reader: new Roo.data.JsonReader(c, c.fields)
12016 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12018 * Ext JS Library 1.1.1
12019 * Copyright(c) 2006-2007, Ext JS, LLC.
12021 * Originally Released Under LGPL - original licence link has changed is not relivant.
12024 * <script type="text/javascript">
12028 Roo.data.Field = function(config){
12029 if(typeof config == "string"){
12030 config = {name: config};
12032 Roo.apply(this, config);
12035 this.type = "auto";
12038 var st = Roo.data.SortTypes;
12039 // named sortTypes are supported, here we look them up
12040 if(typeof this.sortType == "string"){
12041 this.sortType = st[this.sortType];
12044 // set default sortType for strings and dates
12045 if(!this.sortType){
12048 this.sortType = st.asUCString;
12051 this.sortType = st.asDate;
12054 this.sortType = st.none;
12059 var stripRe = /[\$,%]/g;
12061 // prebuilt conversion function for this field, instead of
12062 // switching every time we're reading a value
12064 var cv, dateFormat = this.dateFormat;
12069 cv = function(v){ return v; };
12072 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12076 return v !== undefined && v !== null && v !== '' ?
12077 parseInt(String(v).replace(stripRe, ""), 10) : '';
12082 return v !== undefined && v !== null && v !== '' ?
12083 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12088 cv = function(v){ return v === true || v === "true" || v == 1; };
12095 if(v instanceof Date){
12099 if(dateFormat == "timestamp"){
12100 return new Date(v*1000);
12102 return Date.parseDate(v, dateFormat);
12104 var parsed = Date.parse(v);
12105 return parsed ? new Date(parsed) : null;
12114 Roo.data.Field.prototype = {
12122 * Ext JS Library 1.1.1
12123 * Copyright(c) 2006-2007, Ext JS, LLC.
12125 * Originally Released Under LGPL - original licence link has changed is not relivant.
12128 * <script type="text/javascript">
12131 // Base class for reading structured data from a data source. This class is intended to be
12132 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12135 * @class Roo.data.DataReader
12136 * Base class for reading structured data from a data source. This class is intended to be
12137 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12140 Roo.data.DataReader = function(meta, recordType){
12144 this.recordType = recordType instanceof Array ?
12145 Roo.data.Record.create(recordType) : recordType;
12148 Roo.data.DataReader.prototype = {
12150 * Create an empty record
12151 * @param {Object} data (optional) - overlay some values
12152 * @return {Roo.data.Record} record created.
12154 newRow : function(d) {
12156 this.recordType.prototype.fields.each(function(c) {
12158 case 'int' : da[c.name] = 0; break;
12159 case 'date' : da[c.name] = new Date(); break;
12160 case 'float' : da[c.name] = 0.0; break;
12161 case 'boolean' : da[c.name] = false; break;
12162 default : da[c.name] = ""; break;
12166 return new this.recordType(Roo.apply(da, d));
12171 * Ext JS Library 1.1.1
12172 * Copyright(c) 2006-2007, Ext JS, LLC.
12174 * Originally Released Under LGPL - original licence link has changed is not relivant.
12177 * <script type="text/javascript">
12181 * @class Roo.data.DataProxy
12182 * @extends Roo.data.Observable
12183 * This class is an abstract base class for implementations which provide retrieval of
12184 * unformatted data objects.<br>
12186 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12187 * (of the appropriate type which knows how to parse the data object) to provide a block of
12188 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12190 * Custom implementations must implement the load method as described in
12191 * {@link Roo.data.HttpProxy#load}.
12193 Roo.data.DataProxy = function(){
12196 * @event beforeload
12197 * Fires before a network request is made to retrieve a data object.
12198 * @param {Object} This DataProxy object.
12199 * @param {Object} params The params parameter to the load function.
12204 * Fires before the load method's callback is called.
12205 * @param {Object} This DataProxy object.
12206 * @param {Object} o The data object.
12207 * @param {Object} arg The callback argument object passed to the load function.
12211 * @event loadexception
12212 * Fires if an Exception occurs during data retrieval.
12213 * @param {Object} This DataProxy object.
12214 * @param {Object} o The data object.
12215 * @param {Object} arg The callback argument object passed to the load function.
12216 * @param {Object} e The Exception.
12218 loadexception : true
12220 Roo.data.DataProxy.superclass.constructor.call(this);
12223 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12226 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12230 * Ext JS Library 1.1.1
12231 * Copyright(c) 2006-2007, Ext JS, LLC.
12233 * Originally Released Under LGPL - original licence link has changed is not relivant.
12236 * <script type="text/javascript">
12239 * @class Roo.data.MemoryProxy
12240 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12241 * to the Reader when its load method is called.
12243 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12245 Roo.data.MemoryProxy = function(data){
12249 Roo.data.MemoryProxy.superclass.constructor.call(this);
12253 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12256 * Load data from the requested source (in this case an in-memory
12257 * data object passed to the constructor), read the data object into
12258 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12259 * process that block using the passed callback.
12260 * @param {Object} params This parameter is not used by the MemoryProxy class.
12261 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12262 * object into a block of Roo.data.Records.
12263 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12264 * The function must be passed <ul>
12265 * <li>The Record block object</li>
12266 * <li>The "arg" argument from the load function</li>
12267 * <li>A boolean success indicator</li>
12269 * @param {Object} scope The scope in which to call the callback
12270 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12272 load : function(params, reader, callback, scope, arg){
12273 params = params || {};
12276 result = reader.readRecords(this.data);
12278 this.fireEvent("loadexception", this, arg, null, e);
12279 callback.call(scope, null, arg, false);
12282 callback.call(scope, result, arg, true);
12286 update : function(params, records){
12291 * Ext JS Library 1.1.1
12292 * Copyright(c) 2006-2007, Ext JS, LLC.
12294 * Originally Released Under LGPL - original licence link has changed is not relivant.
12297 * <script type="text/javascript">
12300 * @class Roo.data.HttpProxy
12301 * @extends Roo.data.DataProxy
12302 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12303 * configured to reference a certain URL.<br><br>
12305 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12306 * from which the running page was served.<br><br>
12308 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12310 * Be aware that to enable the browser to parse an XML document, the server must set
12311 * the Content-Type header in the HTTP response to "text/xml".
12313 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12314 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12315 * will be used to make the request.
12317 Roo.data.HttpProxy = function(conn){
12318 Roo.data.HttpProxy.superclass.constructor.call(this);
12319 // is conn a conn config or a real conn?
12321 this.useAjax = !conn || !conn.events;
12325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12326 // thse are take from connection...
12329 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12332 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12333 * extra parameters to each request made by this object. (defaults to undefined)
12336 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12337 * to each request made by this object. (defaults to undefined)
12340 * @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)
12343 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12346 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12352 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12356 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12357 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12358 * a finer-grained basis than the DataProxy events.
12360 getConnection : function(){
12361 return this.useAjax ? Roo.Ajax : this.conn;
12365 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12366 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12367 * process that block using the passed callback.
12368 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12369 * for the request to the remote server.
12370 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12371 * object into a block of Roo.data.Records.
12372 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12373 * The function must be passed <ul>
12374 * <li>The Record block object</li>
12375 * <li>The "arg" argument from the load function</li>
12376 * <li>A boolean success indicator</li>
12378 * @param {Object} scope The scope in which to call the callback
12379 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12381 load : function(params, reader, callback, scope, arg){
12382 if(this.fireEvent("beforeload", this, params) !== false){
12384 params : params || {},
12386 callback : callback,
12391 callback : this.loadResponse,
12395 Roo.applyIf(o, this.conn);
12396 if(this.activeRequest){
12397 Roo.Ajax.abort(this.activeRequest);
12399 this.activeRequest = Roo.Ajax.request(o);
12401 this.conn.request(o);
12404 callback.call(scope||this, null, arg, false);
12409 loadResponse : function(o, success, response){
12410 delete this.activeRequest;
12412 this.fireEvent("loadexception", this, o, response);
12413 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12418 result = o.reader.read(response);
12420 this.fireEvent("loadexception", this, o, response, e);
12421 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12425 this.fireEvent("load", this, o, o.request.arg);
12426 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12430 update : function(dataSet){
12435 updateResponse : function(dataSet){
12440 * Ext JS Library 1.1.1
12441 * Copyright(c) 2006-2007, Ext JS, LLC.
12443 * Originally Released Under LGPL - original licence link has changed is not relivant.
12446 * <script type="text/javascript">
12450 * @class Roo.data.ScriptTagProxy
12451 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12452 * other than the originating domain of the running page.<br><br>
12454 * <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
12455 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12457 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12458 * source code that is used as the source inside a <script> tag.<br><br>
12460 * In order for the browser to process the returned data, the server must wrap the data object
12461 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12462 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12463 * depending on whether the callback name was passed:
12466 boolean scriptTag = false;
12467 String cb = request.getParameter("callback");
12470 response.setContentType("text/javascript");
12472 response.setContentType("application/x-json");
12474 Writer out = response.getWriter();
12476 out.write(cb + "(");
12478 out.print(dataBlock.toJsonString());
12485 * @param {Object} config A configuration object.
12487 Roo.data.ScriptTagProxy = function(config){
12488 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12489 Roo.apply(this, config);
12490 this.head = document.getElementsByTagName("head")[0];
12493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12497 * @cfg {String} url The URL from which to request the data object.
12500 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12504 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12505 * the server the name of the callback function set up by the load call to process the returned data object.
12506 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12507 * javascript output which calls this named function passing the data object as its only parameter.
12509 callbackParam : "callback",
12511 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12512 * name to the request.
12517 * Load data from the configured URL, read the data object into
12518 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12519 * process that block using the passed callback.
12520 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12521 * for the request to the remote server.
12522 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12523 * object into a block of Roo.data.Records.
12524 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12525 * The function must be passed <ul>
12526 * <li>The Record block object</li>
12527 * <li>The "arg" argument from the load function</li>
12528 * <li>A boolean success indicator</li>
12530 * @param {Object} scope The scope in which to call the callback
12531 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12533 load : function(params, reader, callback, scope, arg){
12534 if(this.fireEvent("beforeload", this, params) !== false){
12536 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12538 var url = this.url;
12539 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12541 url += "&_dc=" + (new Date().getTime());
12543 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12546 cb : "stcCallback"+transId,
12547 scriptId : "stcScript"+transId,
12551 callback : callback,
12557 window[trans.cb] = function(o){
12558 conn.handleResponse(o, trans);
12561 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12563 if(this.autoAbort !== false){
12567 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12569 var script = document.createElement("script");
12570 script.setAttribute("src", url);
12571 script.setAttribute("type", "text/javascript");
12572 script.setAttribute("id", trans.scriptId);
12573 this.head.appendChild(script);
12575 this.trans = trans;
12577 callback.call(scope||this, null, arg, false);
12582 isLoading : function(){
12583 return this.trans ? true : false;
12587 * Abort the current server request.
12589 abort : function(){
12590 if(this.isLoading()){
12591 this.destroyTrans(this.trans);
12596 destroyTrans : function(trans, isLoaded){
12597 this.head.removeChild(document.getElementById(trans.scriptId));
12598 clearTimeout(trans.timeoutId);
12600 window[trans.cb] = undefined;
12602 delete window[trans.cb];
12605 // if hasn't been loaded, wait for load to remove it to prevent script error
12606 window[trans.cb] = function(){
12607 window[trans.cb] = undefined;
12609 delete window[trans.cb];
12616 handleResponse : function(o, trans){
12617 this.trans = false;
12618 this.destroyTrans(trans, true);
12621 result = trans.reader.readRecords(o);
12623 this.fireEvent("loadexception", this, o, trans.arg, e);
12624 trans.callback.call(trans.scope||window, null, trans.arg, false);
12627 this.fireEvent("load", this, o, trans.arg);
12628 trans.callback.call(trans.scope||window, result, trans.arg, true);
12632 handleFailure : function(trans){
12633 this.trans = false;
12634 this.destroyTrans(trans, false);
12635 this.fireEvent("loadexception", this, null, trans.arg);
12636 trans.callback.call(trans.scope||window, null, trans.arg, false);
12640 * Ext JS Library 1.1.1
12641 * Copyright(c) 2006-2007, Ext JS, LLC.
12643 * Originally Released Under LGPL - original licence link has changed is not relivant.
12646 * <script type="text/javascript">
12650 * @class Roo.data.JsonReader
12651 * @extends Roo.data.DataReader
12652 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12653 * based on mappings in a provided Roo.data.Record constructor.
12655 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12656 * in the reply previously.
12661 var RecordDef = Roo.data.Record.create([
12662 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12663 {name: 'occupation'} // This field will use "occupation" as the mapping.
12665 var myReader = new Roo.data.JsonReader({
12666 totalProperty: "results", // The property which contains the total dataset size (optional)
12667 root: "rows", // The property which contains an Array of row objects
12668 id: "id" // The property within each row object that provides an ID for the record (optional)
12672 * This would consume a JSON file like this:
12674 { 'results': 2, 'rows': [
12675 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12676 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12679 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12680 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12681 * paged from the remote server.
12682 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12683 * @cfg {String} root name of the property which contains the Array of row objects.
12684 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12685 * @cfg {Array} fields Array of field definition objects
12687 * Create a new JsonReader
12688 * @param {Object} meta Metadata configuration options
12689 * @param {Object} recordType Either an Array of field definition objects,
12690 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12692 Roo.data.JsonReader = function(meta, recordType){
12695 // set some defaults:
12696 Roo.applyIf(meta, {
12697 totalProperty: 'total',
12698 successProperty : 'success',
12703 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12705 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12708 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12709 * Used by Store query builder to append _requestMeta to params.
12712 metaFromRemote : false,
12714 * This method is only used by a DataProxy which has retrieved data from a remote server.
12715 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12716 * @return {Object} data A data block which is used by an Roo.data.Store object as
12717 * a cache of Roo.data.Records.
12719 read : function(response){
12720 var json = response.responseText;
12722 var o = /* eval:var:o */ eval("("+json+")");
12724 throw {message: "JsonReader.read: Json object not found"};
12730 this.metaFromRemote = true;
12731 this.meta = o.metaData;
12732 this.recordType = Roo.data.Record.create(o.metaData.fields);
12733 this.onMetaChange(this.meta, this.recordType, o);
12735 return this.readRecords(o);
12738 // private function a store will implement
12739 onMetaChange : function(meta, recordType, o){
12746 simpleAccess: function(obj, subsc) {
12753 getJsonAccessor: function(){
12755 return function(expr) {
12757 return(re.test(expr))
12758 ? new Function("obj", "return obj." + expr)
12763 return Roo.emptyFn;
12768 * Create a data block containing Roo.data.Records from an XML document.
12769 * @param {Object} o An object which contains an Array of row objects in the property specified
12770 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12771 * which contains the total size of the dataset.
12772 * @return {Object} data A data block which is used by an Roo.data.Store object as
12773 * a cache of Roo.data.Records.
12775 readRecords : function(o){
12777 * After any data loads, the raw JSON data is available for further custom processing.
12781 var s = this.meta, Record = this.recordType,
12782 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12784 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12786 if(s.totalProperty) {
12787 this.getTotal = this.getJsonAccessor(s.totalProperty);
12789 if(s.successProperty) {
12790 this.getSuccess = this.getJsonAccessor(s.successProperty);
12792 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12794 var g = this.getJsonAccessor(s.id);
12795 this.getId = function(rec) {
12797 return (r === undefined || r === "") ? null : r;
12800 this.getId = function(){return null;};
12803 for(var jj = 0; jj < fl; jj++){
12805 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12806 this.ef[jj] = this.getJsonAccessor(map);
12810 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12811 if(s.totalProperty){
12812 var vt = parseInt(this.getTotal(o), 10);
12817 if(s.successProperty){
12818 var vs = this.getSuccess(o);
12819 if(vs === false || vs === 'false'){
12824 for(var i = 0; i < c; i++){
12827 var id = this.getId(n);
12828 for(var j = 0; j < fl; j++){
12830 var v = this.ef[j](n);
12832 Roo.log('missing convert for ' + f.name);
12836 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12838 var record = new Record(values, id);
12840 records[i] = record;
12846 totalRecords : totalRecords
12851 * Ext JS Library 1.1.1
12852 * Copyright(c) 2006-2007, Ext JS, LLC.
12854 * Originally Released Under LGPL - original licence link has changed is not relivant.
12857 * <script type="text/javascript">
12861 * @class Roo.data.ArrayReader
12862 * @extends Roo.data.DataReader
12863 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12864 * Each element of that Array represents a row of data fields. The
12865 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12866 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12870 var RecordDef = Roo.data.Record.create([
12871 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12872 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12874 var myReader = new Roo.data.ArrayReader({
12875 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12879 * This would consume an Array like this:
12881 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12883 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12885 * Create a new JsonReader
12886 * @param {Object} meta Metadata configuration options.
12887 * @param {Object} recordType Either an Array of field definition objects
12888 * as specified to {@link Roo.data.Record#create},
12889 * or an {@link Roo.data.Record} object
12890 * created using {@link Roo.data.Record#create}.
12892 Roo.data.ArrayReader = function(meta, recordType){
12893 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12896 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12898 * Create a data block containing Roo.data.Records from an XML document.
12899 * @param {Object} o An Array of row objects which represents the dataset.
12900 * @return {Object} data A data block which is used by an Roo.data.Store object as
12901 * a cache of Roo.data.Records.
12903 readRecords : function(o){
12904 var sid = this.meta ? this.meta.id : null;
12905 var recordType = this.recordType, fields = recordType.prototype.fields;
12908 for(var i = 0; i < root.length; i++){
12911 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12912 for(var j = 0, jlen = fields.length; j < jlen; j++){
12913 var f = fields.items[j];
12914 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12915 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12917 values[f.name] = v;
12919 var record = new recordType(values, id);
12921 records[records.length] = record;
12925 totalRecords : records.length
12934 * @class Roo.bootstrap.ComboBox
12935 * @extends Roo.bootstrap.TriggerField
12936 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12937 * @cfg {Boolean} append (true|false) default false
12938 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12939 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12940 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12941 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12942 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12943 * @cfg {Boolean} animate default true
12944 * @cfg {Boolean} emptyResultText only for touch device
12945 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12946 * @cfg {String} emptyTitle default ''
12948 * Create a new ComboBox.
12949 * @param {Object} config Configuration options
12951 Roo.bootstrap.ComboBox = function(config){
12952 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12956 * Fires when the dropdown list is expanded
12957 * @param {Roo.bootstrap.ComboBox} combo This combo box
12962 * Fires when the dropdown list is collapsed
12963 * @param {Roo.bootstrap.ComboBox} combo This combo box
12967 * @event beforeselect
12968 * Fires before a list item is selected. Return false to cancel the selection.
12969 * @param {Roo.bootstrap.ComboBox} combo This combo box
12970 * @param {Roo.data.Record} record The data record returned from the underlying store
12971 * @param {Number} index The index of the selected item in the dropdown list
12973 'beforeselect' : true,
12976 * Fires when a list item is selected
12977 * @param {Roo.bootstrap.ComboBox} combo This combo box
12978 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12979 * @param {Number} index The index of the selected item in the dropdown list
12983 * @event beforequery
12984 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12985 * The event object passed has these properties:
12986 * @param {Roo.bootstrap.ComboBox} combo This combo box
12987 * @param {String} query The query
12988 * @param {Boolean} forceAll true to force "all" query
12989 * @param {Boolean} cancel true to cancel the query
12990 * @param {Object} e The query event object
12992 'beforequery': true,
12995 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12996 * @param {Roo.bootstrap.ComboBox} combo This combo box
13001 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13002 * @param {Roo.bootstrap.ComboBox} combo This combo box
13003 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13008 * Fires when the remove value from the combobox array
13009 * @param {Roo.bootstrap.ComboBox} combo This combo box
13013 * @event afterremove
13014 * Fires when the remove value from the combobox array
13015 * @param {Roo.bootstrap.ComboBox} combo This combo box
13017 'afterremove' : true,
13019 * @event specialfilter
13020 * Fires when specialfilter
13021 * @param {Roo.bootstrap.ComboBox} combo This combo box
13023 'specialfilter' : true,
13026 * Fires when tick the element
13027 * @param {Roo.bootstrap.ComboBox} combo This combo box
13031 * @event touchviewdisplay
13032 * Fires when touch view require special display (default is using displayField)
13033 * @param {Roo.bootstrap.ComboBox} combo This combo box
13034 * @param {Object} cfg set html .
13036 'touchviewdisplay' : true
13041 this.tickItems = [];
13043 this.selectedIndex = -1;
13044 if(this.mode == 'local'){
13045 if(config.queryDelay === undefined){
13046 this.queryDelay = 10;
13048 if(config.minChars === undefined){
13054 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13057 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13058 * rendering into an Roo.Editor, defaults to false)
13061 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13062 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13065 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13068 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13069 * the dropdown list (defaults to undefined, with no header element)
13073 * @cfg {String/Roo.Template} tpl The template to use to render the output
13077 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13079 listWidth: undefined,
13081 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13082 * mode = 'remote' or 'text' if mode = 'local')
13084 displayField: undefined,
13087 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13088 * mode = 'remote' or 'value' if mode = 'local').
13089 * Note: use of a valueField requires the user make a selection
13090 * in order for a value to be mapped.
13092 valueField: undefined,
13094 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13099 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13100 * field's data value (defaults to the underlying DOM element's name)
13102 hiddenName: undefined,
13104 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13108 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13110 selectedClass: 'active',
13113 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13117 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13118 * anchor positions (defaults to 'tl-bl')
13120 listAlign: 'tl-bl?',
13122 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13126 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13127 * query specified by the allQuery config option (defaults to 'query')
13129 triggerAction: 'query',
13131 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13132 * (defaults to 4, does not apply if editable = false)
13136 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13137 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13141 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13142 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13146 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13147 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13151 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13152 * when editable = true (defaults to false)
13154 selectOnFocus:false,
13156 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13158 queryParam: 'query',
13160 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13161 * when mode = 'remote' (defaults to 'Loading...')
13163 loadingText: 'Loading...',
13165 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13169 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13173 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13174 * traditional select (defaults to true)
13178 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13182 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13186 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13187 * listWidth has a higher value)
13191 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13192 * allow the user to set arbitrary text into the field (defaults to false)
13194 forceSelection:false,
13196 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13197 * if typeAhead = true (defaults to 250)
13199 typeAheadDelay : 250,
13201 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13202 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13204 valueNotFoundText : undefined,
13206 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13208 blockFocus : false,
13211 * @cfg {Boolean} disableClear Disable showing of clear button.
13213 disableClear : false,
13215 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13217 alwaysQuery : false,
13220 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13225 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13227 invalidClass : "has-warning",
13230 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13232 validClass : "has-success",
13235 * @cfg {Boolean} specialFilter (true|false) special filter default false
13237 specialFilter : false,
13240 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13242 mobileTouchView : true,
13245 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13247 useNativeIOS : false,
13250 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13252 mobile_restrict_height : false,
13254 ios_options : false,
13266 btnPosition : 'right',
13267 triggerList : true,
13268 showToggleBtn : true,
13270 emptyResultText: 'Empty',
13271 triggerText : 'Select',
13274 // element that contains real text value.. (when hidden is used..)
13276 getAutoCreate : function()
13281 * Render classic select for iso
13284 if(Roo.isIOS && this.useNativeIOS){
13285 cfg = this.getAutoCreateNativeIOS();
13293 if(Roo.isTouch && this.mobileTouchView){
13294 cfg = this.getAutoCreateTouchView();
13301 if(!this.tickable){
13302 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13307 * ComboBox with tickable selections
13310 var align = this.labelAlign || this.parentLabelAlign();
13313 cls : 'form-group roo-combobox-tickable' //input-group
13316 var btn_text_select = '';
13317 var btn_text_done = '';
13318 var btn_text_cancel = '';
13320 if (this.btn_text_show) {
13321 btn_text_select = 'Select';
13322 btn_text_done = 'Done';
13323 btn_text_cancel = 'Cancel';
13328 cls : 'tickable-buttons',
13333 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13334 //html : this.triggerText
13335 html: btn_text_select
13341 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13343 html: btn_text_done
13349 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13351 html: btn_text_cancel
13357 buttons.cn.unshift({
13359 cls: 'roo-select2-search-field-input'
13365 Roo.each(buttons.cn, function(c){
13367 c.cls += ' btn-' + _this.size;
13370 if (_this.disabled) {
13377 style : 'display: contents',
13382 cls: 'form-hidden-field'
13386 cls: 'roo-select2-choices',
13390 cls: 'roo-select2-search-field',
13401 cls: 'roo-select2-container input-group roo-select2-container-multi',
13407 // cls: 'typeahead typeahead-long dropdown-menu',
13408 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13413 if(this.hasFeedback && !this.allowBlank){
13417 cls: 'glyphicon form-control-feedback'
13420 combobox.cn.push(feedback);
13425 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13426 tooltip : 'This field is required'
13428 if (Roo.bootstrap.version == 4) {
13431 style : 'display:none'
13434 if (align ==='left' && this.fieldLabel.length) {
13436 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13443 cls : 'control-label col-form-label',
13444 html : this.fieldLabel
13456 var labelCfg = cfg.cn[1];
13457 var contentCfg = cfg.cn[2];
13460 if(this.indicatorpos == 'right'){
13466 cls : 'control-label col-form-label',
13470 html : this.fieldLabel
13486 labelCfg = cfg.cn[0];
13487 contentCfg = cfg.cn[1];
13491 if(this.labelWidth > 12){
13492 labelCfg.style = "width: " + this.labelWidth + 'px';
13495 if(this.labelWidth < 13 && this.labelmd == 0){
13496 this.labelmd = this.labelWidth;
13499 if(this.labellg > 0){
13500 labelCfg.cls += ' col-lg-' + this.labellg;
13501 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13504 if(this.labelmd > 0){
13505 labelCfg.cls += ' col-md-' + this.labelmd;
13506 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13509 if(this.labelsm > 0){
13510 labelCfg.cls += ' col-sm-' + this.labelsm;
13511 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13514 if(this.labelxs > 0){
13515 labelCfg.cls += ' col-xs-' + this.labelxs;
13516 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13520 } else if ( this.fieldLabel.length) {
13521 // Roo.log(" label");
13526 //cls : 'input-group-addon',
13527 html : this.fieldLabel
13532 if(this.indicatorpos == 'right'){
13536 //cls : 'input-group-addon',
13537 html : this.fieldLabel
13547 // Roo.log(" no label && no align");
13554 ['xs','sm','md','lg'].map(function(size){
13555 if (settings[size]) {
13556 cfg.cls += ' col-' + size + '-' + settings[size];
13564 _initEventsCalled : false,
13567 initEvents: function()
13569 if (this._initEventsCalled) { // as we call render... prevent looping...
13572 this._initEventsCalled = true;
13575 throw "can not find store for combo";
13578 this.indicator = this.indicatorEl();
13580 this.store = Roo.factory(this.store, Roo.data);
13581 this.store.parent = this;
13583 // if we are building from html. then this element is so complex, that we can not really
13584 // use the rendered HTML.
13585 // so we have to trash and replace the previous code.
13586 if (Roo.XComponent.build_from_html) {
13587 // remove this element....
13588 var e = this.el.dom, k=0;
13589 while (e ) { e = e.previousSibling; ++k;}
13594 this.rendered = false;
13596 this.render(this.parent().getChildContainer(true), k);
13599 if(Roo.isIOS && this.useNativeIOS){
13600 this.initIOSView();
13608 if(Roo.isTouch && this.mobileTouchView){
13609 this.initTouchView();
13614 this.initTickableEvents();
13618 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13620 if(this.hiddenName){
13622 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13624 this.hiddenField.dom.value =
13625 this.hiddenValue !== undefined ? this.hiddenValue :
13626 this.value !== undefined ? this.value : '';
13628 // prevent input submission
13629 this.el.dom.removeAttribute('name');
13630 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13635 // this.el.dom.setAttribute('autocomplete', 'off');
13638 var cls = 'x-combo-list';
13640 //this.list = new Roo.Layer({
13641 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13647 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13648 _this.list.setWidth(lw);
13651 this.list.on('mouseover', this.onViewOver, this);
13652 this.list.on('mousemove', this.onViewMove, this);
13653 this.list.on('scroll', this.onViewScroll, this);
13656 this.list.swallowEvent('mousewheel');
13657 this.assetHeight = 0;
13660 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13661 this.assetHeight += this.header.getHeight();
13664 this.innerList = this.list.createChild({cls:cls+'-inner'});
13665 this.innerList.on('mouseover', this.onViewOver, this);
13666 this.innerList.on('mousemove', this.onViewMove, this);
13667 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13669 if(this.allowBlank && !this.pageSize && !this.disableClear){
13670 this.footer = this.list.createChild({cls:cls+'-ft'});
13671 this.pageTb = new Roo.Toolbar(this.footer);
13675 this.footer = this.list.createChild({cls:cls+'-ft'});
13676 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13677 {pageSize: this.pageSize});
13681 if (this.pageTb && this.allowBlank && !this.disableClear) {
13683 this.pageTb.add(new Roo.Toolbar.Fill(), {
13684 cls: 'x-btn-icon x-btn-clear',
13686 handler: function()
13689 _this.clearValue();
13690 _this.onSelect(false, -1);
13695 this.assetHeight += this.footer.getHeight();
13700 this.tpl = Roo.bootstrap.version == 4 ?
13701 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13702 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13705 this.view = new Roo.View(this.list, this.tpl, {
13706 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13708 //this.view.wrapEl.setDisplayed(false);
13709 this.view.on('click', this.onViewClick, this);
13712 this.store.on('beforeload', this.onBeforeLoad, this);
13713 this.store.on('load', this.onLoad, this);
13714 this.store.on('loadexception', this.onLoadException, this);
13716 if(this.resizable){
13717 this.resizer = new Roo.Resizable(this.list, {
13718 pinned:true, handles:'se'
13720 this.resizer.on('resize', function(r, w, h){
13721 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13722 this.listWidth = w;
13723 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13724 this.restrictHeight();
13726 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13729 if(!this.editable){
13730 this.editable = true;
13731 this.setEditable(false);
13736 if (typeof(this.events.add.listeners) != 'undefined') {
13738 this.addicon = this.wrap.createChild(
13739 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13741 this.addicon.on('click', function(e) {
13742 this.fireEvent('add', this);
13745 if (typeof(this.events.edit.listeners) != 'undefined') {
13747 this.editicon = this.wrap.createChild(
13748 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13749 if (this.addicon) {
13750 this.editicon.setStyle('margin-left', '40px');
13752 this.editicon.on('click', function(e) {
13754 // we fire even if inothing is selected..
13755 this.fireEvent('edit', this, this.lastData );
13761 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13762 "up" : function(e){
13763 this.inKeyMode = true;
13767 "down" : function(e){
13768 if(!this.isExpanded()){
13769 this.onTriggerClick();
13771 this.inKeyMode = true;
13776 "enter" : function(e){
13777 // this.onViewClick();
13781 if(this.fireEvent("specialkey", this, e)){
13782 this.onViewClick(false);
13788 "esc" : function(e){
13792 "tab" : function(e){
13795 if(this.fireEvent("specialkey", this, e)){
13796 this.onViewClick(false);
13804 doRelay : function(foo, bar, hname){
13805 if(hname == 'down' || this.scope.isExpanded()){
13806 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13815 this.queryDelay = Math.max(this.queryDelay || 10,
13816 this.mode == 'local' ? 10 : 250);
13819 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13821 if(this.typeAhead){
13822 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13824 if(this.editable !== false){
13825 this.inputEl().on("keyup", this.onKeyUp, this);
13827 if(this.forceSelection){
13828 this.inputEl().on('blur', this.doForce, this);
13832 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13833 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13837 initTickableEvents: function()
13841 if(this.hiddenName){
13843 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13845 this.hiddenField.dom.value =
13846 this.hiddenValue !== undefined ? this.hiddenValue :
13847 this.value !== undefined ? this.value : '';
13849 // prevent input submission
13850 this.el.dom.removeAttribute('name');
13851 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13856 // this.list = this.el.select('ul.dropdown-menu',true).first();
13858 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13859 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13860 if(this.triggerList){
13861 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13864 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13865 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13867 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13868 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13870 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13871 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13873 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13874 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13875 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13878 this.cancelBtn.hide();
13883 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13884 _this.list.setWidth(lw);
13887 this.list.on('mouseover', this.onViewOver, this);
13888 this.list.on('mousemove', this.onViewMove, this);
13890 this.list.on('scroll', this.onViewScroll, this);
13893 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13894 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13897 this.view = new Roo.View(this.list, this.tpl, {
13902 selectedClass: this.selectedClass
13905 //this.view.wrapEl.setDisplayed(false);
13906 this.view.on('click', this.onViewClick, this);
13910 this.store.on('beforeload', this.onBeforeLoad, this);
13911 this.store.on('load', this.onLoad, this);
13912 this.store.on('loadexception', this.onLoadException, this);
13915 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13916 "up" : function(e){
13917 this.inKeyMode = true;
13921 "down" : function(e){
13922 this.inKeyMode = true;
13926 "enter" : function(e){
13927 if(this.fireEvent("specialkey", this, e)){
13928 this.onViewClick(false);
13934 "esc" : function(e){
13935 this.onTickableFooterButtonClick(e, false, false);
13938 "tab" : function(e){
13939 this.fireEvent("specialkey", this, e);
13941 this.onTickableFooterButtonClick(e, false, false);
13948 doRelay : function(e, fn, key){
13949 if(this.scope.isExpanded()){
13950 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13959 this.queryDelay = Math.max(this.queryDelay || 10,
13960 this.mode == 'local' ? 10 : 250);
13963 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13965 if(this.typeAhead){
13966 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13969 if(this.editable !== false){
13970 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13973 this.indicator = this.indicatorEl();
13975 if(this.indicator){
13976 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13977 this.indicator.hide();
13982 onDestroy : function(){
13984 this.view.setStore(null);
13985 this.view.el.removeAllListeners();
13986 this.view.el.remove();
13987 this.view.purgeListeners();
13990 this.list.dom.innerHTML = '';
13994 this.store.un('beforeload', this.onBeforeLoad, this);
13995 this.store.un('load', this.onLoad, this);
13996 this.store.un('loadexception', this.onLoadException, this);
13998 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14002 fireKey : function(e){
14003 if(e.isNavKeyPress() && !this.list.isVisible()){
14004 this.fireEvent("specialkey", this, e);
14009 onResize: function(w, h){
14010 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14012 // if(typeof w != 'number'){
14013 // // we do not handle it!?!?
14016 // var tw = this.trigger.getWidth();
14017 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14018 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14020 // this.inputEl().setWidth( this.adjustWidth('input', x));
14022 // //this.trigger.setStyle('left', x+'px');
14024 // if(this.list && this.listWidth === undefined){
14025 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14026 // this.list.setWidth(lw);
14027 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14035 * Allow or prevent the user from directly editing the field text. If false is passed,
14036 * the user will only be able to select from the items defined in the dropdown list. This method
14037 * is the runtime equivalent of setting the 'editable' config option at config time.
14038 * @param {Boolean} value True to allow the user to directly edit the field text
14040 setEditable : function(value){
14041 if(value == this.editable){
14044 this.editable = value;
14046 this.inputEl().dom.setAttribute('readOnly', true);
14047 this.inputEl().on('mousedown', this.onTriggerClick, this);
14048 this.inputEl().addClass('x-combo-noedit');
14050 this.inputEl().dom.setAttribute('readOnly', false);
14051 this.inputEl().un('mousedown', this.onTriggerClick, this);
14052 this.inputEl().removeClass('x-combo-noedit');
14058 onBeforeLoad : function(combo,opts){
14059 if(!this.hasFocus){
14063 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14065 this.restrictHeight();
14066 this.selectedIndex = -1;
14070 onLoad : function(){
14072 this.hasQuery = false;
14074 if(!this.hasFocus){
14078 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14079 this.loading.hide();
14082 if(this.store.getCount() > 0){
14085 this.restrictHeight();
14086 if(this.lastQuery == this.allQuery){
14087 if(this.editable && !this.tickable){
14088 this.inputEl().dom.select();
14092 !this.selectByValue(this.value, true) &&
14095 !this.store.lastOptions ||
14096 typeof(this.store.lastOptions.add) == 'undefined' ||
14097 this.store.lastOptions.add != true
14100 this.select(0, true);
14103 if(this.autoFocus){
14106 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14107 this.taTask.delay(this.typeAheadDelay);
14111 this.onEmptyResults();
14117 onLoadException : function()
14119 this.hasQuery = false;
14121 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14122 this.loading.hide();
14125 if(this.tickable && this.editable){
14130 // only causes errors at present
14131 //Roo.log(this.store.reader.jsonData);
14132 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14134 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14140 onTypeAhead : function(){
14141 if(this.store.getCount() > 0){
14142 var r = this.store.getAt(0);
14143 var newValue = r.data[this.displayField];
14144 var len = newValue.length;
14145 var selStart = this.getRawValue().length;
14147 if(selStart != len){
14148 this.setRawValue(newValue);
14149 this.selectText(selStart, newValue.length);
14155 onSelect : function(record, index){
14157 if(this.fireEvent('beforeselect', this, record, index) !== false){
14159 this.setFromData(index > -1 ? record.data : false);
14162 this.fireEvent('select', this, record, index);
14167 * Returns the currently selected field value or empty string if no value is set.
14168 * @return {String} value The selected value
14170 getValue : function()
14172 if(Roo.isIOS && this.useNativeIOS){
14173 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14177 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14180 if(this.valueField){
14181 return typeof this.value != 'undefined' ? this.value : '';
14183 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14187 getRawValue : function()
14189 if(Roo.isIOS && this.useNativeIOS){
14190 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14193 var v = this.inputEl().getValue();
14199 * Clears any text/value currently set in the field
14201 clearValue : function(){
14203 if(this.hiddenField){
14204 this.hiddenField.dom.value = '';
14207 this.setRawValue('');
14208 this.lastSelectionText = '';
14209 this.lastData = false;
14211 var close = this.closeTriggerEl();
14222 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14223 * will be displayed in the field. If the value does not match the data value of an existing item,
14224 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14225 * Otherwise the field will be blank (although the value will still be set).
14226 * @param {String} value The value to match
14228 setValue : function(v)
14230 if(Roo.isIOS && this.useNativeIOS){
14231 this.setIOSValue(v);
14241 if(this.valueField){
14242 var r = this.findRecord(this.valueField, v);
14244 text = r.data[this.displayField];
14245 }else if(this.valueNotFoundText !== undefined){
14246 text = this.valueNotFoundText;
14249 this.lastSelectionText = text;
14250 if(this.hiddenField){
14251 this.hiddenField.dom.value = v;
14253 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14256 var close = this.closeTriggerEl();
14259 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14265 * @property {Object} the last set data for the element
14270 * Sets the value of the field based on a object which is related to the record format for the store.
14271 * @param {Object} value the value to set as. or false on reset?
14273 setFromData : function(o){
14280 var dv = ''; // display value
14281 var vv = ''; // value value..
14283 if (this.displayField) {
14284 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14286 // this is an error condition!!!
14287 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14290 if(this.valueField){
14291 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14294 var close = this.closeTriggerEl();
14297 if(dv.length || vv * 1 > 0){
14299 this.blockFocus=true;
14305 if(this.hiddenField){
14306 this.hiddenField.dom.value = vv;
14308 this.lastSelectionText = dv;
14309 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14313 // no hidden field.. - we store the value in 'value', but still display
14314 // display field!!!!
14315 this.lastSelectionText = dv;
14316 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14323 reset : function(){
14324 // overridden so that last data is reset..
14331 this.setValue(this.originalValue);
14332 //this.clearInvalid();
14333 this.lastData = false;
14335 this.view.clearSelections();
14341 findRecord : function(prop, value){
14343 if(this.store.getCount() > 0){
14344 this.store.each(function(r){
14345 if(r.data[prop] == value){
14355 getName: function()
14357 // returns hidden if it's set..
14358 if (!this.rendered) {return ''};
14359 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14363 onViewMove : function(e, t){
14364 this.inKeyMode = false;
14368 onViewOver : function(e, t){
14369 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14372 var item = this.view.findItemFromChild(t);
14375 var index = this.view.indexOf(item);
14376 this.select(index, false);
14381 onViewClick : function(view, doFocus, el, e)
14383 var index = this.view.getSelectedIndexes()[0];
14385 var r = this.store.getAt(index);
14389 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14396 Roo.each(this.tickItems, function(v,k){
14398 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14400 _this.tickItems.splice(k, 1);
14402 if(typeof(e) == 'undefined' && view == false){
14403 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14415 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14416 this.tickItems.push(r.data);
14419 if(typeof(e) == 'undefined' && view == false){
14420 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14427 this.onSelect(r, index);
14429 if(doFocus !== false && !this.blockFocus){
14430 this.inputEl().focus();
14435 restrictHeight : function(){
14436 //this.innerList.dom.style.height = '';
14437 //var inner = this.innerList.dom;
14438 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14439 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14440 //this.list.beginUpdate();
14441 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14442 this.list.alignTo(this.inputEl(), this.listAlign);
14443 this.list.alignTo(this.inputEl(), this.listAlign);
14444 //this.list.endUpdate();
14448 onEmptyResults : function(){
14450 if(this.tickable && this.editable){
14451 this.hasFocus = false;
14452 this.restrictHeight();
14460 * Returns true if the dropdown list is expanded, else false.
14462 isExpanded : function(){
14463 return this.list.isVisible();
14467 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14468 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14469 * @param {String} value The data value of the item to select
14470 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14471 * selected item if it is not currently in view (defaults to true)
14472 * @return {Boolean} True if the value matched an item in the list, else false
14474 selectByValue : function(v, scrollIntoView){
14475 if(v !== undefined && v !== null){
14476 var r = this.findRecord(this.valueField || this.displayField, v);
14478 this.select(this.store.indexOf(r), scrollIntoView);
14486 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14487 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14488 * @param {Number} index The zero-based index of the list item to select
14489 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14490 * selected item if it is not currently in view (defaults to true)
14492 select : function(index, scrollIntoView){
14493 this.selectedIndex = index;
14494 this.view.select(index);
14495 if(scrollIntoView !== false){
14496 var el = this.view.getNode(index);
14498 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14501 this.list.scrollChildIntoView(el, false);
14507 selectNext : function(){
14508 var ct = this.store.getCount();
14510 if(this.selectedIndex == -1){
14512 }else if(this.selectedIndex < ct-1){
14513 this.select(this.selectedIndex+1);
14519 selectPrev : function(){
14520 var ct = this.store.getCount();
14522 if(this.selectedIndex == -1){
14524 }else if(this.selectedIndex != 0){
14525 this.select(this.selectedIndex-1);
14531 onKeyUp : function(e){
14532 if(this.editable !== false && !e.isSpecialKey()){
14533 this.lastKey = e.getKey();
14534 this.dqTask.delay(this.queryDelay);
14539 validateBlur : function(){
14540 return !this.list || !this.list.isVisible();
14544 initQuery : function(){
14546 var v = this.getRawValue();
14548 if(this.tickable && this.editable){
14549 v = this.tickableInputEl().getValue();
14556 doForce : function(){
14557 if(this.inputEl().dom.value.length > 0){
14558 this.inputEl().dom.value =
14559 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14565 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14566 * query allowing the query action to be canceled if needed.
14567 * @param {String} query The SQL query to execute
14568 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14569 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14570 * saved in the current store (defaults to false)
14572 doQuery : function(q, forceAll){
14574 if(q === undefined || q === null){
14579 forceAll: forceAll,
14583 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14588 forceAll = qe.forceAll;
14589 if(forceAll === true || (q.length >= this.minChars)){
14591 this.hasQuery = true;
14593 if(this.lastQuery != q || this.alwaysQuery){
14594 this.lastQuery = q;
14595 if(this.mode == 'local'){
14596 this.selectedIndex = -1;
14598 this.store.clearFilter();
14601 if(this.specialFilter){
14602 this.fireEvent('specialfilter', this);
14607 this.store.filter(this.displayField, q);
14610 this.store.fireEvent("datachanged", this.store);
14617 this.store.baseParams[this.queryParam] = q;
14619 var options = {params : this.getParams(q)};
14622 options.add = true;
14623 options.params.start = this.page * this.pageSize;
14626 this.store.load(options);
14629 * this code will make the page width larger, at the beginning, the list not align correctly,
14630 * we should expand the list on onLoad
14631 * so command out it
14636 this.selectedIndex = -1;
14641 this.loadNext = false;
14645 getParams : function(q){
14647 //p[this.queryParam] = q;
14651 p.limit = this.pageSize;
14657 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14659 collapse : function(){
14660 if(!this.isExpanded()){
14666 this.hasFocus = false;
14670 this.cancelBtn.hide();
14671 this.trigger.show();
14674 this.tickableInputEl().dom.value = '';
14675 this.tickableInputEl().blur();
14680 Roo.get(document).un('mousedown', this.collapseIf, this);
14681 Roo.get(document).un('mousewheel', this.collapseIf, this);
14682 if (!this.editable) {
14683 Roo.get(document).un('keydown', this.listKeyPress, this);
14685 this.fireEvent('collapse', this);
14691 collapseIf : function(e){
14692 var in_combo = e.within(this.el);
14693 var in_list = e.within(this.list);
14694 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14696 if (in_combo || in_list || is_list) {
14697 //e.stopPropagation();
14702 this.onTickableFooterButtonClick(e, false, false);
14710 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14712 expand : function(){
14714 if(this.isExpanded() || !this.hasFocus){
14718 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14719 this.list.setWidth(lw);
14725 this.restrictHeight();
14729 this.tickItems = Roo.apply([], this.item);
14732 this.cancelBtn.show();
14733 this.trigger.hide();
14736 this.tickableInputEl().focus();
14741 Roo.get(document).on('mousedown', this.collapseIf, this);
14742 Roo.get(document).on('mousewheel', this.collapseIf, this);
14743 if (!this.editable) {
14744 Roo.get(document).on('keydown', this.listKeyPress, this);
14747 this.fireEvent('expand', this);
14751 // Implements the default empty TriggerField.onTriggerClick function
14752 onTriggerClick : function(e)
14754 Roo.log('trigger click');
14756 if(this.disabled || !this.triggerList){
14761 this.loadNext = false;
14763 if(this.isExpanded()){
14765 if (!this.blockFocus) {
14766 this.inputEl().focus();
14770 this.hasFocus = true;
14771 if(this.triggerAction == 'all') {
14772 this.doQuery(this.allQuery, true);
14774 this.doQuery(this.getRawValue());
14776 if (!this.blockFocus) {
14777 this.inputEl().focus();
14782 onTickableTriggerClick : function(e)
14789 this.loadNext = false;
14790 this.hasFocus = true;
14792 if(this.triggerAction == 'all') {
14793 this.doQuery(this.allQuery, true);
14795 this.doQuery(this.getRawValue());
14799 onSearchFieldClick : function(e)
14801 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14802 this.onTickableFooterButtonClick(e, false, false);
14806 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14811 this.loadNext = false;
14812 this.hasFocus = true;
14814 if(this.triggerAction == 'all') {
14815 this.doQuery(this.allQuery, true);
14817 this.doQuery(this.getRawValue());
14821 listKeyPress : function(e)
14823 //Roo.log('listkeypress');
14824 // scroll to first matching element based on key pres..
14825 if (e.isSpecialKey()) {
14828 var k = String.fromCharCode(e.getKey()).toUpperCase();
14831 var csel = this.view.getSelectedNodes();
14832 var cselitem = false;
14834 var ix = this.view.indexOf(csel[0]);
14835 cselitem = this.store.getAt(ix);
14836 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14842 this.store.each(function(v) {
14844 // start at existing selection.
14845 if (cselitem.id == v.id) {
14851 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14852 match = this.store.indexOf(v);
14858 if (match === false) {
14859 return true; // no more action?
14862 this.view.select(match);
14863 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14864 sn.scrollIntoView(sn.dom.parentNode, false);
14867 onViewScroll : function(e, t){
14869 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){
14873 this.hasQuery = true;
14875 this.loading = this.list.select('.loading', true).first();
14877 if(this.loading === null){
14878 this.list.createChild({
14880 cls: 'loading roo-select2-more-results roo-select2-active',
14881 html: 'Loading more results...'
14884 this.loading = this.list.select('.loading', true).first();
14886 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14888 this.loading.hide();
14891 this.loading.show();
14896 this.loadNext = true;
14898 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14903 addItem : function(o)
14905 var dv = ''; // display value
14907 if (this.displayField) {
14908 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14910 // this is an error condition!!!
14911 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14918 var choice = this.choices.createChild({
14920 cls: 'roo-select2-search-choice',
14929 cls: 'roo-select2-search-choice-close fa fa-times',
14934 }, this.searchField);
14936 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14938 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14946 this.inputEl().dom.value = '';
14951 onRemoveItem : function(e, _self, o)
14953 e.preventDefault();
14955 this.lastItem = Roo.apply([], this.item);
14957 var index = this.item.indexOf(o.data) * 1;
14960 Roo.log('not this item?!');
14964 this.item.splice(index, 1);
14969 this.fireEvent('remove', this, e);
14975 syncValue : function()
14977 if(!this.item.length){
14984 Roo.each(this.item, function(i){
14985 if(_this.valueField){
14986 value.push(i[_this.valueField]);
14993 this.value = value.join(',');
14995 if(this.hiddenField){
14996 this.hiddenField.dom.value = this.value;
14999 this.store.fireEvent("datachanged", this.store);
15004 clearItem : function()
15006 if(!this.multiple){
15012 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15020 if(this.tickable && !Roo.isTouch){
15021 this.view.refresh();
15025 inputEl: function ()
15027 if(Roo.isIOS && this.useNativeIOS){
15028 return this.el.select('select.roo-ios-select', true).first();
15031 if(Roo.isTouch && this.mobileTouchView){
15032 return this.el.select('input.form-control',true).first();
15036 return this.searchField;
15039 return this.el.select('input.form-control',true).first();
15042 onTickableFooterButtonClick : function(e, btn, el)
15044 e.preventDefault();
15046 this.lastItem = Roo.apply([], this.item);
15048 if(btn && btn.name == 'cancel'){
15049 this.tickItems = Roo.apply([], this.item);
15058 Roo.each(this.tickItems, function(o){
15066 validate : function()
15068 if(this.getVisibilityEl().hasClass('hidden')){
15072 var v = this.getRawValue();
15075 v = this.getValue();
15078 if(this.disabled || this.allowBlank || v.length){
15083 this.markInvalid();
15087 tickableInputEl : function()
15089 if(!this.tickable || !this.editable){
15090 return this.inputEl();
15093 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15097 getAutoCreateTouchView : function()
15102 cls: 'form-group' //input-group
15108 type : this.inputType,
15109 cls : 'form-control x-combo-noedit',
15110 autocomplete: 'new-password',
15111 placeholder : this.placeholder || '',
15116 input.name = this.name;
15120 input.cls += ' input-' + this.size;
15123 if (this.disabled) {
15124 input.disabled = true;
15135 inputblock.cls += ' input-group';
15137 inputblock.cn.unshift({
15139 cls : 'input-group-addon input-group-prepend input-group-text',
15144 if(this.removable && !this.multiple){
15145 inputblock.cls += ' roo-removable';
15147 inputblock.cn.push({
15150 cls : 'roo-combo-removable-btn close'
15154 if(this.hasFeedback && !this.allowBlank){
15156 inputblock.cls += ' has-feedback';
15158 inputblock.cn.push({
15160 cls: 'glyphicon form-control-feedback'
15167 inputblock.cls += (this.before) ? '' : ' input-group';
15169 inputblock.cn.push({
15171 cls : 'input-group-addon input-group-append input-group-text',
15177 var ibwrap = inputblock;
15182 cls: 'roo-select2-choices',
15186 cls: 'roo-select2-search-field',
15199 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15204 cls: 'form-hidden-field'
15210 if(!this.multiple && this.showToggleBtn){
15217 if (this.caret != false) {
15220 cls: 'fa fa-' + this.caret
15227 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15232 cls: 'combobox-clear',
15246 combobox.cls += ' roo-select2-container-multi';
15249 var align = this.labelAlign || this.parentLabelAlign();
15251 if (align ==='left' && this.fieldLabel.length) {
15256 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15257 tooltip : 'This field is required'
15261 cls : 'control-label col-form-label',
15262 html : this.fieldLabel
15273 var labelCfg = cfg.cn[1];
15274 var contentCfg = cfg.cn[2];
15277 if(this.indicatorpos == 'right'){
15282 cls : 'control-label col-form-label',
15286 html : this.fieldLabel
15290 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15291 tooltip : 'This field is required'
15304 labelCfg = cfg.cn[0];
15305 contentCfg = cfg.cn[1];
15310 if(this.labelWidth > 12){
15311 labelCfg.style = "width: " + this.labelWidth + 'px';
15314 if(this.labelWidth < 13 && this.labelmd == 0){
15315 this.labelmd = this.labelWidth;
15318 if(this.labellg > 0){
15319 labelCfg.cls += ' col-lg-' + this.labellg;
15320 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15323 if(this.labelmd > 0){
15324 labelCfg.cls += ' col-md-' + this.labelmd;
15325 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15328 if(this.labelsm > 0){
15329 labelCfg.cls += ' col-sm-' + this.labelsm;
15330 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15333 if(this.labelxs > 0){
15334 labelCfg.cls += ' col-xs-' + this.labelxs;
15335 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15339 } else if ( this.fieldLabel.length) {
15343 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15344 tooltip : 'This field is required'
15348 cls : 'control-label',
15349 html : this.fieldLabel
15360 if(this.indicatorpos == 'right'){
15364 cls : 'control-label',
15365 html : this.fieldLabel,
15369 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15370 tooltip : 'This field is required'
15387 var settings = this;
15389 ['xs','sm','md','lg'].map(function(size){
15390 if (settings[size]) {
15391 cfg.cls += ' col-' + size + '-' + settings[size];
15398 initTouchView : function()
15400 this.renderTouchView();
15402 this.touchViewEl.on('scroll', function(){
15403 this.el.dom.scrollTop = 0;
15406 this.originalValue = this.getValue();
15408 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15410 this.inputEl().on("click", this.showTouchView, this);
15411 if (this.triggerEl) {
15412 this.triggerEl.on("click", this.showTouchView, this);
15416 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15417 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15419 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15421 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15422 this.store.on('load', this.onTouchViewLoad, this);
15423 this.store.on('loadexception', this.onTouchViewLoadException, this);
15425 if(this.hiddenName){
15427 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15429 this.hiddenField.dom.value =
15430 this.hiddenValue !== undefined ? this.hiddenValue :
15431 this.value !== undefined ? this.value : '';
15433 this.el.dom.removeAttribute('name');
15434 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15438 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15439 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15442 if(this.removable && !this.multiple){
15443 var close = this.closeTriggerEl();
15445 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15446 close.on('click', this.removeBtnClick, this, close);
15450 * fix the bug in Safari iOS8
15452 this.inputEl().on("focus", function(e){
15453 document.activeElement.blur();
15456 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15463 renderTouchView : function()
15465 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15466 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15469 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15471 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15472 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15473 this.touchViewBodyEl.setStyle('overflow', 'auto');
15475 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15476 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15478 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15479 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15483 showTouchView : function()
15489 this.touchViewHeaderEl.hide();
15491 if(this.modalTitle.length){
15492 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15493 this.touchViewHeaderEl.show();
15496 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15497 this.touchViewEl.show();
15499 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15501 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15502 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15504 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15506 if(this.modalTitle.length){
15507 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15510 this.touchViewBodyEl.setHeight(bodyHeight);
15514 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15516 this.touchViewEl.addClass('in');
15519 if(this._touchViewMask){
15520 Roo.get(document.body).addClass("x-body-masked");
15521 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15522 this._touchViewMask.setStyle('z-index', 10000);
15523 this._touchViewMask.addClass('show');
15526 this.doTouchViewQuery();
15530 hideTouchView : function()
15532 this.touchViewEl.removeClass('in');
15536 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15538 this.touchViewEl.setStyle('display', 'none');
15541 if(this._touchViewMask){
15542 this._touchViewMask.removeClass('show');
15543 Roo.get(document.body).removeClass("x-body-masked");
15547 setTouchViewValue : function()
15554 Roo.each(this.tickItems, function(o){
15559 this.hideTouchView();
15562 doTouchViewQuery : function()
15571 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15575 if(!this.alwaysQuery || this.mode == 'local'){
15576 this.onTouchViewLoad();
15583 onTouchViewBeforeLoad : function(combo,opts)
15589 onTouchViewLoad : function()
15591 if(this.store.getCount() < 1){
15592 this.onTouchViewEmptyResults();
15596 this.clearTouchView();
15598 var rawValue = this.getRawValue();
15600 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15602 this.tickItems = [];
15604 this.store.data.each(function(d, rowIndex){
15605 var row = this.touchViewListGroup.createChild(template);
15607 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15608 row.addClass(d.data.cls);
15611 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15614 html : d.data[this.displayField]
15617 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15618 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15621 row.removeClass('selected');
15622 if(!this.multiple && this.valueField &&
15623 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15626 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15627 row.addClass('selected');
15630 if(this.multiple && this.valueField &&
15631 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15635 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15636 this.tickItems.push(d.data);
15639 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15643 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15645 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15647 if(this.modalTitle.length){
15648 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15651 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15653 if(this.mobile_restrict_height && listHeight < bodyHeight){
15654 this.touchViewBodyEl.setHeight(listHeight);
15659 if(firstChecked && listHeight > bodyHeight){
15660 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15665 onTouchViewLoadException : function()
15667 this.hideTouchView();
15670 onTouchViewEmptyResults : function()
15672 this.clearTouchView();
15674 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15676 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15680 clearTouchView : function()
15682 this.touchViewListGroup.dom.innerHTML = '';
15685 onTouchViewClick : function(e, el, o)
15687 e.preventDefault();
15690 var rowIndex = o.rowIndex;
15692 var r = this.store.getAt(rowIndex);
15694 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15696 if(!this.multiple){
15697 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15698 c.dom.removeAttribute('checked');
15701 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15703 this.setFromData(r.data);
15705 var close = this.closeTriggerEl();
15711 this.hideTouchView();
15713 this.fireEvent('select', this, r, rowIndex);
15718 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15719 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15720 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15724 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15725 this.addItem(r.data);
15726 this.tickItems.push(r.data);
15730 getAutoCreateNativeIOS : function()
15733 cls: 'form-group' //input-group,
15738 cls : 'roo-ios-select'
15742 combobox.name = this.name;
15745 if (this.disabled) {
15746 combobox.disabled = true;
15749 var settings = this;
15751 ['xs','sm','md','lg'].map(function(size){
15752 if (settings[size]) {
15753 cfg.cls += ' col-' + size + '-' + settings[size];
15763 initIOSView : function()
15765 this.store.on('load', this.onIOSViewLoad, this);
15770 onIOSViewLoad : function()
15772 if(this.store.getCount() < 1){
15776 this.clearIOSView();
15778 if(this.allowBlank) {
15780 var default_text = '-- SELECT --';
15782 if(this.placeholder.length){
15783 default_text = this.placeholder;
15786 if(this.emptyTitle.length){
15787 default_text += ' - ' + this.emptyTitle + ' -';
15790 var opt = this.inputEl().createChild({
15793 html : default_text
15797 o[this.valueField] = 0;
15798 o[this.displayField] = default_text;
15800 this.ios_options.push({
15807 this.store.data.each(function(d, rowIndex){
15811 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15812 html = d.data[this.displayField];
15817 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15818 value = d.data[this.valueField];
15827 if(this.value == d.data[this.valueField]){
15828 option['selected'] = true;
15831 var opt = this.inputEl().createChild(option);
15833 this.ios_options.push({
15840 this.inputEl().on('change', function(){
15841 this.fireEvent('select', this);
15846 clearIOSView: function()
15848 this.inputEl().dom.innerHTML = '';
15850 this.ios_options = [];
15853 setIOSValue: function(v)
15857 if(!this.ios_options){
15861 Roo.each(this.ios_options, function(opts){
15863 opts.el.dom.removeAttribute('selected');
15865 if(opts.data[this.valueField] != v){
15869 opts.el.dom.setAttribute('selected', true);
15875 * @cfg {Boolean} grow
15879 * @cfg {Number} growMin
15883 * @cfg {Number} growMax
15892 Roo.apply(Roo.bootstrap.ComboBox, {
15896 cls: 'modal-header',
15918 cls: 'list-group-item',
15922 cls: 'roo-combobox-list-group-item-value'
15926 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15940 listItemCheckbox : {
15942 cls: 'list-group-item',
15946 cls: 'roo-combobox-list-group-item-value'
15950 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15966 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15971 cls: 'modal-footer',
15979 cls: 'col-xs-6 text-left',
15982 cls: 'btn btn-danger roo-touch-view-cancel',
15988 cls: 'col-xs-6 text-right',
15991 cls: 'btn btn-success roo-touch-view-ok',
16002 Roo.apply(Roo.bootstrap.ComboBox, {
16004 touchViewTemplate : {
16006 cls: 'modal fade roo-combobox-touch-view',
16010 cls: 'modal-dialog',
16011 style : 'position:fixed', // we have to fix position....
16015 cls: 'modal-content',
16017 Roo.bootstrap.ComboBox.header,
16018 Roo.bootstrap.ComboBox.body,
16019 Roo.bootstrap.ComboBox.footer
16028 * Ext JS Library 1.1.1
16029 * Copyright(c) 2006-2007, Ext JS, LLC.
16031 * Originally Released Under LGPL - original licence link has changed is not relivant.
16034 * <script type="text/javascript">
16039 * @extends Roo.util.Observable
16040 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16041 * This class also supports single and multi selection modes. <br>
16042 * Create a data model bound view:
16044 var store = new Roo.data.Store(...);
16046 var view = new Roo.View({
16048 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16050 singleSelect: true,
16051 selectedClass: "ydataview-selected",
16055 // listen for node click?
16056 view.on("click", function(vw, index, node, e){
16057 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16061 dataModel.load("foobar.xml");
16063 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16065 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16066 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16068 * Note: old style constructor is still suported (container, template, config)
16071 * Create a new View
16072 * @param {Object} config The config object
16075 Roo.View = function(config, depreciated_tpl, depreciated_config){
16077 this.parent = false;
16079 if (typeof(depreciated_tpl) == 'undefined') {
16080 // new way.. - universal constructor.
16081 Roo.apply(this, config);
16082 this.el = Roo.get(this.el);
16085 this.el = Roo.get(config);
16086 this.tpl = depreciated_tpl;
16087 Roo.apply(this, depreciated_config);
16089 this.wrapEl = this.el.wrap().wrap();
16090 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16093 if(typeof(this.tpl) == "string"){
16094 this.tpl = new Roo.Template(this.tpl);
16096 // support xtype ctors..
16097 this.tpl = new Roo.factory(this.tpl, Roo);
16101 this.tpl.compile();
16106 * @event beforeclick
16107 * Fires before a click is processed. Returns false to cancel the default action.
16108 * @param {Roo.View} this
16109 * @param {Number} index The index of the target node
16110 * @param {HTMLElement} node The target node
16111 * @param {Roo.EventObject} e The raw event object
16113 "beforeclick" : true,
16116 * Fires when a template node is clicked.
16117 * @param {Roo.View} this
16118 * @param {Number} index The index of the target node
16119 * @param {HTMLElement} node The target node
16120 * @param {Roo.EventObject} e The raw event object
16125 * Fires when a template node is double clicked.
16126 * @param {Roo.View} this
16127 * @param {Number} index The index of the target node
16128 * @param {HTMLElement} node The target node
16129 * @param {Roo.EventObject} e The raw event object
16133 * @event contextmenu
16134 * Fires when a template node is right clicked.
16135 * @param {Roo.View} this
16136 * @param {Number} index The index of the target node
16137 * @param {HTMLElement} node The target node
16138 * @param {Roo.EventObject} e The raw event object
16140 "contextmenu" : true,
16142 * @event selectionchange
16143 * Fires when the selected nodes change.
16144 * @param {Roo.View} this
16145 * @param {Array} selections Array of the selected nodes
16147 "selectionchange" : true,
16150 * @event beforeselect
16151 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16152 * @param {Roo.View} this
16153 * @param {HTMLElement} node The node to be selected
16154 * @param {Array} selections Array of currently selected nodes
16156 "beforeselect" : true,
16158 * @event preparedata
16159 * Fires on every row to render, to allow you to change the data.
16160 * @param {Roo.View} this
16161 * @param {Object} data to be rendered (change this)
16163 "preparedata" : true
16171 "click": this.onClick,
16172 "dblclick": this.onDblClick,
16173 "contextmenu": this.onContextMenu,
16177 this.selections = [];
16179 this.cmp = new Roo.CompositeElementLite([]);
16181 this.store = Roo.factory(this.store, Roo.data);
16182 this.setStore(this.store, true);
16185 if ( this.footer && this.footer.xtype) {
16187 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16189 this.footer.dataSource = this.store;
16190 this.footer.container = fctr;
16191 this.footer = Roo.factory(this.footer, Roo);
16192 fctr.insertFirst(this.el);
16194 // this is a bit insane - as the paging toolbar seems to detach the el..
16195 // dom.parentNode.parentNode.parentNode
16196 // they get detached?
16200 Roo.View.superclass.constructor.call(this);
16205 Roo.extend(Roo.View, Roo.util.Observable, {
16208 * @cfg {Roo.data.Store} store Data store to load data from.
16213 * @cfg {String|Roo.Element} el The container element.
16218 * @cfg {String|Roo.Template} tpl The template used by this View
16222 * @cfg {String} dataName the named area of the template to use as the data area
16223 * Works with domtemplates roo-name="name"
16227 * @cfg {String} selectedClass The css class to add to selected nodes
16229 selectedClass : "x-view-selected",
16231 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16236 * @cfg {String} text to display on mask (default Loading)
16240 * @cfg {Boolean} multiSelect Allow multiple selection
16242 multiSelect : false,
16244 * @cfg {Boolean} singleSelect Allow single selection
16246 singleSelect: false,
16249 * @cfg {Boolean} toggleSelect - selecting
16251 toggleSelect : false,
16254 * @cfg {Boolean} tickable - selecting
16259 * Returns the element this view is bound to.
16260 * @return {Roo.Element}
16262 getEl : function(){
16263 return this.wrapEl;
16269 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16271 refresh : function(){
16272 //Roo.log('refresh');
16275 // if we are using something like 'domtemplate', then
16276 // the what gets used is:
16277 // t.applySubtemplate(NAME, data, wrapping data..)
16278 // the outer template then get' applied with
16279 // the store 'extra data'
16280 // and the body get's added to the
16281 // roo-name="data" node?
16282 // <span class='roo-tpl-{name}'></span> ?????
16286 this.clearSelections();
16287 this.el.update("");
16289 var records = this.store.getRange();
16290 if(records.length < 1) {
16292 // is this valid?? = should it render a template??
16294 this.el.update(this.emptyText);
16298 if (this.dataName) {
16299 this.el.update(t.apply(this.store.meta)); //????
16300 el = this.el.child('.roo-tpl-' + this.dataName);
16303 for(var i = 0, len = records.length; i < len; i++){
16304 var data = this.prepareData(records[i].data, i, records[i]);
16305 this.fireEvent("preparedata", this, data, i, records[i]);
16307 var d = Roo.apply({}, data);
16310 Roo.apply(d, {'roo-id' : Roo.id()});
16314 Roo.each(this.parent.item, function(item){
16315 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16318 Roo.apply(d, {'roo-data-checked' : 'checked'});
16322 html[html.length] = Roo.util.Format.trim(
16324 t.applySubtemplate(this.dataName, d, this.store.meta) :
16331 el.update(html.join(""));
16332 this.nodes = el.dom.childNodes;
16333 this.updateIndexes(0);
16338 * Function to override to reformat the data that is sent to
16339 * the template for each node.
16340 * DEPRICATED - use the preparedata event handler.
16341 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16342 * a JSON object for an UpdateManager bound view).
16344 prepareData : function(data, index, record)
16346 this.fireEvent("preparedata", this, data, index, record);
16350 onUpdate : function(ds, record){
16351 // Roo.log('on update');
16352 this.clearSelections();
16353 var index = this.store.indexOf(record);
16354 var n = this.nodes[index];
16355 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16356 n.parentNode.removeChild(n);
16357 this.updateIndexes(index, index);
16363 onAdd : function(ds, records, index)
16365 //Roo.log(['on Add', ds, records, index] );
16366 this.clearSelections();
16367 if(this.nodes.length == 0){
16371 var n = this.nodes[index];
16372 for(var i = 0, len = records.length; i < len; i++){
16373 var d = this.prepareData(records[i].data, i, records[i]);
16375 this.tpl.insertBefore(n, d);
16378 this.tpl.append(this.el, d);
16381 this.updateIndexes(index);
16384 onRemove : function(ds, record, index){
16385 // Roo.log('onRemove');
16386 this.clearSelections();
16387 var el = this.dataName ?
16388 this.el.child('.roo-tpl-' + this.dataName) :
16391 el.dom.removeChild(this.nodes[index]);
16392 this.updateIndexes(index);
16396 * Refresh an individual node.
16397 * @param {Number} index
16399 refreshNode : function(index){
16400 this.onUpdate(this.store, this.store.getAt(index));
16403 updateIndexes : function(startIndex, endIndex){
16404 var ns = this.nodes;
16405 startIndex = startIndex || 0;
16406 endIndex = endIndex || ns.length - 1;
16407 for(var i = startIndex; i <= endIndex; i++){
16408 ns[i].nodeIndex = i;
16413 * Changes the data store this view uses and refresh the view.
16414 * @param {Store} store
16416 setStore : function(store, initial){
16417 if(!initial && this.store){
16418 this.store.un("datachanged", this.refresh);
16419 this.store.un("add", this.onAdd);
16420 this.store.un("remove", this.onRemove);
16421 this.store.un("update", this.onUpdate);
16422 this.store.un("clear", this.refresh);
16423 this.store.un("beforeload", this.onBeforeLoad);
16424 this.store.un("load", this.onLoad);
16425 this.store.un("loadexception", this.onLoad);
16429 store.on("datachanged", this.refresh, this);
16430 store.on("add", this.onAdd, this);
16431 store.on("remove", this.onRemove, this);
16432 store.on("update", this.onUpdate, this);
16433 store.on("clear", this.refresh, this);
16434 store.on("beforeload", this.onBeforeLoad, this);
16435 store.on("load", this.onLoad, this);
16436 store.on("loadexception", this.onLoad, this);
16444 * onbeforeLoad - masks the loading area.
16447 onBeforeLoad : function(store,opts)
16449 //Roo.log('onBeforeLoad');
16451 this.el.update("");
16453 this.el.mask(this.mask ? this.mask : "Loading" );
16455 onLoad : function ()
16462 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16463 * @param {HTMLElement} node
16464 * @return {HTMLElement} The template node
16466 findItemFromChild : function(node){
16467 var el = this.dataName ?
16468 this.el.child('.roo-tpl-' + this.dataName,true) :
16471 if(!node || node.parentNode == el){
16474 var p = node.parentNode;
16475 while(p && p != el){
16476 if(p.parentNode == el){
16485 onClick : function(e){
16486 var item = this.findItemFromChild(e.getTarget());
16488 var index = this.indexOf(item);
16489 if(this.onItemClick(item, index, e) !== false){
16490 this.fireEvent("click", this, index, item, e);
16493 this.clearSelections();
16498 onContextMenu : function(e){
16499 var item = this.findItemFromChild(e.getTarget());
16501 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16506 onDblClick : function(e){
16507 var item = this.findItemFromChild(e.getTarget());
16509 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16513 onItemClick : function(item, index, e)
16515 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16518 if (this.toggleSelect) {
16519 var m = this.isSelected(item) ? 'unselect' : 'select';
16522 _t[m](item, true, false);
16525 if(this.multiSelect || this.singleSelect){
16526 if(this.multiSelect && e.shiftKey && this.lastSelection){
16527 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16529 this.select(item, this.multiSelect && e.ctrlKey);
16530 this.lastSelection = item;
16533 if(!this.tickable){
16534 e.preventDefault();
16542 * Get the number of selected nodes.
16545 getSelectionCount : function(){
16546 return this.selections.length;
16550 * Get the currently selected nodes.
16551 * @return {Array} An array of HTMLElements
16553 getSelectedNodes : function(){
16554 return this.selections;
16558 * Get the indexes of the selected nodes.
16561 getSelectedIndexes : function(){
16562 var indexes = [], s = this.selections;
16563 for(var i = 0, len = s.length; i < len; i++){
16564 indexes.push(s[i].nodeIndex);
16570 * Clear all selections
16571 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16573 clearSelections : function(suppressEvent){
16574 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16575 this.cmp.elements = this.selections;
16576 this.cmp.removeClass(this.selectedClass);
16577 this.selections = [];
16578 if(!suppressEvent){
16579 this.fireEvent("selectionchange", this, this.selections);
16585 * Returns true if the passed node is selected
16586 * @param {HTMLElement/Number} node The node or node index
16587 * @return {Boolean}
16589 isSelected : function(node){
16590 var s = this.selections;
16594 node = this.getNode(node);
16595 return s.indexOf(node) !== -1;
16600 * @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
16601 * @param {Boolean} keepExisting (optional) true to keep existing selections
16602 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16604 select : function(nodeInfo, keepExisting, suppressEvent){
16605 if(nodeInfo instanceof Array){
16607 this.clearSelections(true);
16609 for(var i = 0, len = nodeInfo.length; i < len; i++){
16610 this.select(nodeInfo[i], true, true);
16614 var node = this.getNode(nodeInfo);
16615 if(!node || this.isSelected(node)){
16616 return; // already selected.
16619 this.clearSelections(true);
16622 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16623 Roo.fly(node).addClass(this.selectedClass);
16624 this.selections.push(node);
16625 if(!suppressEvent){
16626 this.fireEvent("selectionchange", this, this.selections);
16634 * @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
16635 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16636 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16638 unselect : function(nodeInfo, keepExisting, suppressEvent)
16640 if(nodeInfo instanceof Array){
16641 Roo.each(this.selections, function(s) {
16642 this.unselect(s, nodeInfo);
16646 var node = this.getNode(nodeInfo);
16647 if(!node || !this.isSelected(node)){
16648 //Roo.log("not selected");
16649 return; // not selected.
16653 Roo.each(this.selections, function(s) {
16655 Roo.fly(node).removeClass(this.selectedClass);
16662 this.selections= ns;
16663 this.fireEvent("selectionchange", this, this.selections);
16667 * Gets a template node.
16668 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16669 * @return {HTMLElement} The node or null if it wasn't found
16671 getNode : function(nodeInfo){
16672 if(typeof nodeInfo == "string"){
16673 return document.getElementById(nodeInfo);
16674 }else if(typeof nodeInfo == "number"){
16675 return this.nodes[nodeInfo];
16681 * Gets a range template nodes.
16682 * @param {Number} startIndex
16683 * @param {Number} endIndex
16684 * @return {Array} An array of nodes
16686 getNodes : function(start, end){
16687 var ns = this.nodes;
16688 start = start || 0;
16689 end = typeof end == "undefined" ? ns.length - 1 : end;
16692 for(var i = start; i <= end; i++){
16696 for(var i = start; i >= end; i--){
16704 * Finds the index of the passed node
16705 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16706 * @return {Number} The index of the node or -1
16708 indexOf : function(node){
16709 node = this.getNode(node);
16710 if(typeof node.nodeIndex == "number"){
16711 return node.nodeIndex;
16713 var ns = this.nodes;
16714 for(var i = 0, len = ns.length; i < len; i++){
16725 * based on jquery fullcalendar
16729 Roo.bootstrap = Roo.bootstrap || {};
16731 * @class Roo.bootstrap.Calendar
16732 * @extends Roo.bootstrap.Component
16733 * Bootstrap Calendar class
16734 * @cfg {Boolean} loadMask (true|false) default false
16735 * @cfg {Object} header generate the user specific header of the calendar, default false
16738 * Create a new Container
16739 * @param {Object} config The config object
16744 Roo.bootstrap.Calendar = function(config){
16745 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16749 * Fires when a date is selected
16750 * @param {DatePicker} this
16751 * @param {Date} date The selected date
16755 * @event monthchange
16756 * Fires when the displayed month changes
16757 * @param {DatePicker} this
16758 * @param {Date} date The selected month
16760 'monthchange': true,
16762 * @event evententer
16763 * Fires when mouse over an event
16764 * @param {Calendar} this
16765 * @param {event} Event
16767 'evententer': true,
16769 * @event eventleave
16770 * Fires when the mouse leaves an
16771 * @param {Calendar} this
16774 'eventleave': true,
16776 * @event eventclick
16777 * Fires when the mouse click an
16778 * @param {Calendar} this
16787 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16790 * @cfg {Number} startDay
16791 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16799 getAutoCreate : function(){
16802 var fc_button = function(name, corner, style, content ) {
16803 return Roo.apply({},{
16805 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16807 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16810 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16821 style : 'width:100%',
16828 cls : 'fc-header-left',
16830 fc_button('prev', 'left', 'arrow', '‹' ),
16831 fc_button('next', 'right', 'arrow', '›' ),
16832 { tag: 'span', cls: 'fc-header-space' },
16833 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16841 cls : 'fc-header-center',
16845 cls: 'fc-header-title',
16848 html : 'month / year'
16856 cls : 'fc-header-right',
16858 /* fc_button('month', 'left', '', 'month' ),
16859 fc_button('week', '', '', 'week' ),
16860 fc_button('day', 'right', '', 'day' )
16872 header = this.header;
16875 var cal_heads = function() {
16877 // fixme - handle this.
16879 for (var i =0; i < Date.dayNames.length; i++) {
16880 var d = Date.dayNames[i];
16883 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16884 html : d.substring(0,3)
16888 ret[0].cls += ' fc-first';
16889 ret[6].cls += ' fc-last';
16892 var cal_cell = function(n) {
16895 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16900 cls: 'fc-day-number',
16904 cls: 'fc-day-content',
16908 style: 'position: relative;' // height: 17px;
16920 var cal_rows = function() {
16923 for (var r = 0; r < 6; r++) {
16930 for (var i =0; i < Date.dayNames.length; i++) {
16931 var d = Date.dayNames[i];
16932 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16935 row.cn[0].cls+=' fc-first';
16936 row.cn[0].cn[0].style = 'min-height:90px';
16937 row.cn[6].cls+=' fc-last';
16941 ret[0].cls += ' fc-first';
16942 ret[4].cls += ' fc-prev-last';
16943 ret[5].cls += ' fc-last';
16950 cls: 'fc-border-separate',
16951 style : 'width:100%',
16959 cls : 'fc-first fc-last',
16977 cls : 'fc-content',
16978 style : "position: relative;",
16981 cls : 'fc-view fc-view-month fc-grid',
16982 style : 'position: relative',
16983 unselectable : 'on',
16986 cls : 'fc-event-container',
16987 style : 'position:absolute;z-index:8;top:0;left:0;'
17005 initEvents : function()
17008 throw "can not find store for calendar";
17014 style: "text-align:center",
17018 style: "background-color:white;width:50%;margin:250 auto",
17022 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17033 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17035 var size = this.el.select('.fc-content', true).first().getSize();
17036 this.maskEl.setSize(size.width, size.height);
17037 this.maskEl.enableDisplayMode("block");
17038 if(!this.loadMask){
17039 this.maskEl.hide();
17042 this.store = Roo.factory(this.store, Roo.data);
17043 this.store.on('load', this.onLoad, this);
17044 this.store.on('beforeload', this.onBeforeLoad, this);
17048 this.cells = this.el.select('.fc-day',true);
17049 //Roo.log(this.cells);
17050 this.textNodes = this.el.query('.fc-day-number');
17051 this.cells.addClassOnOver('fc-state-hover');
17053 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17054 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17055 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17056 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17058 this.on('monthchange', this.onMonthChange, this);
17060 this.update(new Date().clearTime());
17063 resize : function() {
17064 var sz = this.el.getSize();
17066 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17067 this.el.select('.fc-day-content div',true).setHeight(34);
17072 showPrevMonth : function(e){
17073 this.update(this.activeDate.add("mo", -1));
17075 showToday : function(e){
17076 this.update(new Date().clearTime());
17079 showNextMonth : function(e){
17080 this.update(this.activeDate.add("mo", 1));
17084 showPrevYear : function(){
17085 this.update(this.activeDate.add("y", -1));
17089 showNextYear : function(){
17090 this.update(this.activeDate.add("y", 1));
17095 update : function(date)
17097 var vd = this.activeDate;
17098 this.activeDate = date;
17099 // if(vd && this.el){
17100 // var t = date.getTime();
17101 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17102 // Roo.log('using add remove');
17104 // this.fireEvent('monthchange', this, date);
17106 // this.cells.removeClass("fc-state-highlight");
17107 // this.cells.each(function(c){
17108 // if(c.dateValue == t){
17109 // c.addClass("fc-state-highlight");
17110 // setTimeout(function(){
17111 // try{c.dom.firstChild.focus();}catch(e){}
17121 var days = date.getDaysInMonth();
17123 var firstOfMonth = date.getFirstDateOfMonth();
17124 var startingPos = firstOfMonth.getDay()-this.startDay;
17126 if(startingPos < this.startDay){
17130 var pm = date.add(Date.MONTH, -1);
17131 var prevStart = pm.getDaysInMonth()-startingPos;
17133 this.cells = this.el.select('.fc-day',true);
17134 this.textNodes = this.el.query('.fc-day-number');
17135 this.cells.addClassOnOver('fc-state-hover');
17137 var cells = this.cells.elements;
17138 var textEls = this.textNodes;
17140 Roo.each(cells, function(cell){
17141 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17144 days += startingPos;
17146 // convert everything to numbers so it's fast
17147 var day = 86400000;
17148 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17151 //Roo.log(prevStart);
17153 var today = new Date().clearTime().getTime();
17154 var sel = date.clearTime().getTime();
17155 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17156 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17157 var ddMatch = this.disabledDatesRE;
17158 var ddText = this.disabledDatesText;
17159 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17160 var ddaysText = this.disabledDaysText;
17161 var format = this.format;
17163 var setCellClass = function(cal, cell){
17167 //Roo.log('set Cell Class');
17169 var t = d.getTime();
17173 cell.dateValue = t;
17175 cell.className += " fc-today";
17176 cell.className += " fc-state-highlight";
17177 cell.title = cal.todayText;
17180 // disable highlight in other month..
17181 //cell.className += " fc-state-highlight";
17186 cell.className = " fc-state-disabled";
17187 cell.title = cal.minText;
17191 cell.className = " fc-state-disabled";
17192 cell.title = cal.maxText;
17196 if(ddays.indexOf(d.getDay()) != -1){
17197 cell.title = ddaysText;
17198 cell.className = " fc-state-disabled";
17201 if(ddMatch && format){
17202 var fvalue = d.dateFormat(format);
17203 if(ddMatch.test(fvalue)){
17204 cell.title = ddText.replace("%0", fvalue);
17205 cell.className = " fc-state-disabled";
17209 if (!cell.initialClassName) {
17210 cell.initialClassName = cell.dom.className;
17213 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17218 for(; i < startingPos; i++) {
17219 textEls[i].innerHTML = (++prevStart);
17220 d.setDate(d.getDate()+1);
17222 cells[i].className = "fc-past fc-other-month";
17223 setCellClass(this, cells[i]);
17228 for(; i < days; i++){
17229 intDay = i - startingPos + 1;
17230 textEls[i].innerHTML = (intDay);
17231 d.setDate(d.getDate()+1);
17233 cells[i].className = ''; // "x-date-active";
17234 setCellClass(this, cells[i]);
17238 for(; i < 42; i++) {
17239 textEls[i].innerHTML = (++extraDays);
17240 d.setDate(d.getDate()+1);
17242 cells[i].className = "fc-future fc-other-month";
17243 setCellClass(this, cells[i]);
17246 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17248 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17250 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17251 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17253 if(totalRows != 6){
17254 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17255 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17258 this.fireEvent('monthchange', this, date);
17262 if(!this.internalRender){
17263 var main = this.el.dom.firstChild;
17264 var w = main.offsetWidth;
17265 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17266 Roo.fly(main).setWidth(w);
17267 this.internalRender = true;
17268 // opera does not respect the auto grow header center column
17269 // then, after it gets a width opera refuses to recalculate
17270 // without a second pass
17271 if(Roo.isOpera && !this.secondPass){
17272 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17273 this.secondPass = true;
17274 this.update.defer(10, this, [date]);
17281 findCell : function(dt) {
17282 dt = dt.clearTime().getTime();
17284 this.cells.each(function(c){
17285 //Roo.log("check " +c.dateValue + '?=' + dt);
17286 if(c.dateValue == dt){
17296 findCells : function(ev) {
17297 var s = ev.start.clone().clearTime().getTime();
17299 var e= ev.end.clone().clearTime().getTime();
17302 this.cells.each(function(c){
17303 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17305 if(c.dateValue > e){
17308 if(c.dateValue < s){
17317 // findBestRow: function(cells)
17321 // for (var i =0 ; i < cells.length;i++) {
17322 // ret = Math.max(cells[i].rows || 0,ret);
17329 addItem : function(ev)
17331 // look for vertical location slot in
17332 var cells = this.findCells(ev);
17334 // ev.row = this.findBestRow(cells);
17336 // work out the location.
17340 for(var i =0; i < cells.length; i++) {
17342 cells[i].row = cells[0].row;
17345 cells[i].row = cells[i].row + 1;
17355 if (crow.start.getY() == cells[i].getY()) {
17357 crow.end = cells[i];
17374 cells[0].events.push(ev);
17376 this.calevents.push(ev);
17379 clearEvents: function() {
17381 if(!this.calevents){
17385 Roo.each(this.cells.elements, function(c){
17391 Roo.each(this.calevents, function(e) {
17392 Roo.each(e.els, function(el) {
17393 el.un('mouseenter' ,this.onEventEnter, this);
17394 el.un('mouseleave' ,this.onEventLeave, this);
17399 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17405 renderEvents: function()
17409 this.cells.each(function(c) {
17418 if(c.row != c.events.length){
17419 r = 4 - (4 - (c.row - c.events.length));
17422 c.events = ev.slice(0, r);
17423 c.more = ev.slice(r);
17425 if(c.more.length && c.more.length == 1){
17426 c.events.push(c.more.pop());
17429 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17433 this.cells.each(function(c) {
17435 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17438 for (var e = 0; e < c.events.length; e++){
17439 var ev = c.events[e];
17440 var rows = ev.rows;
17442 for(var i = 0; i < rows.length; i++) {
17444 // how many rows should it span..
17447 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17448 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17450 unselectable : "on",
17453 cls: 'fc-event-inner',
17457 // cls: 'fc-event-time',
17458 // html : cells.length > 1 ? '' : ev.time
17462 cls: 'fc-event-title',
17463 html : String.format('{0}', ev.title)
17470 cls: 'ui-resizable-handle ui-resizable-e',
17471 html : '  '
17478 cfg.cls += ' fc-event-start';
17480 if ((i+1) == rows.length) {
17481 cfg.cls += ' fc-event-end';
17484 var ctr = _this.el.select('.fc-event-container',true).first();
17485 var cg = ctr.createChild(cfg);
17487 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17488 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17490 var r = (c.more.length) ? 1 : 0;
17491 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17492 cg.setWidth(ebox.right - sbox.x -2);
17494 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17495 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17496 cg.on('click', _this.onEventClick, _this, ev);
17507 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17508 style : 'position: absolute',
17509 unselectable : "on",
17512 cls: 'fc-event-inner',
17516 cls: 'fc-event-title',
17524 cls: 'ui-resizable-handle ui-resizable-e',
17525 html : '  '
17531 var ctr = _this.el.select('.fc-event-container',true).first();
17532 var cg = ctr.createChild(cfg);
17534 var sbox = c.select('.fc-day-content',true).first().getBox();
17535 var ebox = c.select('.fc-day-content',true).first().getBox();
17537 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17538 cg.setWidth(ebox.right - sbox.x -2);
17540 cg.on('click', _this.onMoreEventClick, _this, c.more);
17550 onEventEnter: function (e, el,event,d) {
17551 this.fireEvent('evententer', this, el, event);
17554 onEventLeave: function (e, el,event,d) {
17555 this.fireEvent('eventleave', this, el, event);
17558 onEventClick: function (e, el,event,d) {
17559 this.fireEvent('eventclick', this, el, event);
17562 onMonthChange: function () {
17566 onMoreEventClick: function(e, el, more)
17570 this.calpopover.placement = 'right';
17571 this.calpopover.setTitle('More');
17573 this.calpopover.setContent('');
17575 var ctr = this.calpopover.el.select('.popover-content', true).first();
17577 Roo.each(more, function(m){
17579 cls : 'fc-event-hori fc-event-draggable',
17582 var cg = ctr.createChild(cfg);
17584 cg.on('click', _this.onEventClick, _this, m);
17587 this.calpopover.show(el);
17592 onLoad: function ()
17594 this.calevents = [];
17597 if(this.store.getCount() > 0){
17598 this.store.data.each(function(d){
17601 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17602 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17603 time : d.data.start_time,
17604 title : d.data.title,
17605 description : d.data.description,
17606 venue : d.data.venue
17611 this.renderEvents();
17613 if(this.calevents.length && this.loadMask){
17614 this.maskEl.hide();
17618 onBeforeLoad: function()
17620 this.clearEvents();
17622 this.maskEl.show();
17636 * @class Roo.bootstrap.Popover
17637 * @extends Roo.bootstrap.Component
17638 * Bootstrap Popover class
17639 * @cfg {String} html contents of the popover (or false to use children..)
17640 * @cfg {String} title of popover (or false to hide)
17641 * @cfg {String} placement how it is placed
17642 * @cfg {String} trigger click || hover (or false to trigger manually)
17643 * @cfg {String} over what (parent or false to trigger manually.)
17644 * @cfg {Number} delay - delay before showing
17647 * Create a new Popover
17648 * @param {Object} config The config object
17651 Roo.bootstrap.Popover = function(config){
17652 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17658 * After the popover show
17660 * @param {Roo.bootstrap.Popover} this
17665 * After the popover hide
17667 * @param {Roo.bootstrap.Popover} this
17673 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17675 title: 'Fill in a title',
17678 placement : 'right',
17679 trigger : 'hover', // hover
17685 can_build_overlaid : false,
17687 getChildContainer : function()
17689 return this.el.select('.popover-content',true).first();
17692 getAutoCreate : function(){
17695 cls : 'popover roo-dynamic',
17696 style: 'display:block',
17702 cls : 'popover-inner',
17706 cls: 'popover-title popover-header',
17710 cls : 'popover-content popover-body',
17721 setTitle: function(str)
17724 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17726 setContent: function(str)
17729 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17731 // as it get's added to the bottom of the page.
17732 onRender : function(ct, position)
17734 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17736 var cfg = Roo.apply({}, this.getAutoCreate());
17740 cfg.cls += ' ' + this.cls;
17743 cfg.style = this.style;
17745 //Roo.log("adding to ");
17746 this.el = Roo.get(document.body).createChild(cfg, position);
17747 // Roo.log(this.el);
17752 initEvents : function()
17754 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17755 this.el.enableDisplayMode('block');
17757 if (this.over === false) {
17760 if (this.triggers === false) {
17763 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17764 var triggers = this.trigger ? this.trigger.split(' ') : [];
17765 Roo.each(triggers, function(trigger) {
17767 if (trigger == 'click') {
17768 on_el.on('click', this.toggle, this);
17769 } else if (trigger != 'manual') {
17770 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17771 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17773 on_el.on(eventIn ,this.enter, this);
17774 on_el.on(eventOut, this.leave, this);
17785 toggle : function () {
17786 this.hoverState == 'in' ? this.leave() : this.enter();
17789 enter : function () {
17791 clearTimeout(this.timeout);
17793 this.hoverState = 'in';
17795 if (!this.delay || !this.delay.show) {
17800 this.timeout = setTimeout(function () {
17801 if (_t.hoverState == 'in') {
17804 }, this.delay.show)
17807 leave : function() {
17808 clearTimeout(this.timeout);
17810 this.hoverState = 'out';
17812 if (!this.delay || !this.delay.hide) {
17817 this.timeout = setTimeout(function () {
17818 if (_t.hoverState == 'out') {
17821 }, this.delay.hide)
17824 show : function (on_el)
17827 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17831 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17832 if (this.html !== false) {
17833 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17835 this.el.removeClass([
17836 'fade','top','bottom', 'left', 'right','in',
17837 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17839 if (!this.title.length) {
17840 this.el.select('.popover-title',true).hide();
17843 var placement = typeof this.placement == 'function' ?
17844 this.placement.call(this, this.el, on_el) :
17847 var autoToken = /\s?auto?\s?/i;
17848 var autoPlace = autoToken.test(placement);
17850 placement = placement.replace(autoToken, '') || 'top';
17854 //this.el.setXY([0,0]);
17856 this.el.dom.style.display='block';
17857 this.el.addClass(placement);
17859 //this.el.appendTo(on_el);
17861 var p = this.getPosition();
17862 var box = this.el.getBox();
17867 var align = Roo.bootstrap.Popover.alignment[placement];
17870 this.el.alignTo(on_el, align[0],align[1]);
17871 //var arrow = this.el.select('.arrow',true).first();
17872 //arrow.set(align[2],
17874 this.el.addClass('in');
17877 if (this.el.hasClass('fade')) {
17881 this.hoverState = 'in';
17883 this.fireEvent('show', this);
17888 this.el.setXY([0,0]);
17889 this.el.removeClass('in');
17891 this.hoverState = null;
17893 this.fireEvent('hide', this);
17898 Roo.bootstrap.Popover.alignment = {
17899 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17900 'right' : ['l-r', [10,0], 'left bs-popover-left'],
17901 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17902 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17913 * @class Roo.bootstrap.Progress
17914 * @extends Roo.bootstrap.Component
17915 * Bootstrap Progress class
17916 * @cfg {Boolean} striped striped of the progress bar
17917 * @cfg {Boolean} active animated of the progress bar
17921 * Create a new Progress
17922 * @param {Object} config The config object
17925 Roo.bootstrap.Progress = function(config){
17926 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17929 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17934 getAutoCreate : function(){
17942 cfg.cls += ' progress-striped';
17946 cfg.cls += ' active';
17965 * @class Roo.bootstrap.ProgressBar
17966 * @extends Roo.bootstrap.Component
17967 * Bootstrap ProgressBar class
17968 * @cfg {Number} aria_valuenow aria-value now
17969 * @cfg {Number} aria_valuemin aria-value min
17970 * @cfg {Number} aria_valuemax aria-value max
17971 * @cfg {String} label label for the progress bar
17972 * @cfg {String} panel (success | info | warning | danger )
17973 * @cfg {String} role role of the progress bar
17974 * @cfg {String} sr_only text
17978 * Create a new ProgressBar
17979 * @param {Object} config The config object
17982 Roo.bootstrap.ProgressBar = function(config){
17983 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17986 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17990 aria_valuemax : 100,
17996 getAutoCreate : function()
18001 cls: 'progress-bar',
18002 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18014 cfg.role = this.role;
18017 if(this.aria_valuenow){
18018 cfg['aria-valuenow'] = this.aria_valuenow;
18021 if(this.aria_valuemin){
18022 cfg['aria-valuemin'] = this.aria_valuemin;
18025 if(this.aria_valuemax){
18026 cfg['aria-valuemax'] = this.aria_valuemax;
18029 if(this.label && !this.sr_only){
18030 cfg.html = this.label;
18034 cfg.cls += ' progress-bar-' + this.panel;
18040 update : function(aria_valuenow)
18042 this.aria_valuenow = aria_valuenow;
18044 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18059 * @class Roo.bootstrap.TabGroup
18060 * @extends Roo.bootstrap.Column
18061 * Bootstrap Column class
18062 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18063 * @cfg {Boolean} carousel true to make the group behave like a carousel
18064 * @cfg {Boolean} bullets show bullets for the panels
18065 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18066 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18067 * @cfg {Boolean} showarrow (true|false) show arrow default true
18070 * Create a new TabGroup
18071 * @param {Object} config The config object
18074 Roo.bootstrap.TabGroup = function(config){
18075 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18077 this.navId = Roo.id();
18080 Roo.bootstrap.TabGroup.register(this);
18084 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18087 transition : false,
18092 slideOnTouch : false,
18095 getAutoCreate : function()
18097 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18099 cfg.cls += ' tab-content';
18101 if (this.carousel) {
18102 cfg.cls += ' carousel slide';
18105 cls : 'carousel-inner',
18109 if(this.bullets && !Roo.isTouch){
18112 cls : 'carousel-bullets',
18116 if(this.bullets_cls){
18117 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18124 cfg.cn[0].cn.push(bullets);
18127 if(this.showarrow){
18128 cfg.cn[0].cn.push({
18130 class : 'carousel-arrow',
18134 class : 'carousel-prev',
18138 class : 'fa fa-chevron-left'
18144 class : 'carousel-next',
18148 class : 'fa fa-chevron-right'
18161 initEvents: function()
18163 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18164 // this.el.on("touchstart", this.onTouchStart, this);
18167 if(this.autoslide){
18170 this.slideFn = window.setInterval(function() {
18171 _this.showPanelNext();
18175 if(this.showarrow){
18176 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18177 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18183 // onTouchStart : function(e, el, o)
18185 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18189 // this.showPanelNext();
18193 getChildContainer : function()
18195 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18199 * register a Navigation item
18200 * @param {Roo.bootstrap.NavItem} the navitem to add
18202 register : function(item)
18204 this.tabs.push( item);
18205 item.navId = this.navId; // not really needed..
18210 getActivePanel : function()
18213 Roo.each(this.tabs, function(t) {
18223 getPanelByName : function(n)
18226 Roo.each(this.tabs, function(t) {
18227 if (t.tabId == n) {
18235 indexOfPanel : function(p)
18238 Roo.each(this.tabs, function(t,i) {
18239 if (t.tabId == p.tabId) {
18248 * show a specific panel
18249 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18250 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18252 showPanel : function (pan)
18254 if(this.transition || typeof(pan) == 'undefined'){
18255 Roo.log("waiting for the transitionend");
18259 if (typeof(pan) == 'number') {
18260 pan = this.tabs[pan];
18263 if (typeof(pan) == 'string') {
18264 pan = this.getPanelByName(pan);
18267 var cur = this.getActivePanel();
18270 Roo.log('pan or acitve pan is undefined');
18274 if (pan.tabId == this.getActivePanel().tabId) {
18278 if (false === cur.fireEvent('beforedeactivate')) {
18282 if(this.bullets > 0 && !Roo.isTouch){
18283 this.setActiveBullet(this.indexOfPanel(pan));
18286 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18288 this.transition = true;
18289 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18290 var lr = dir == 'next' ? 'left' : 'right';
18291 pan.el.addClass(dir); // or prev
18292 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18293 cur.el.addClass(lr); // or right
18294 pan.el.addClass(lr);
18297 cur.el.on('transitionend', function() {
18298 Roo.log("trans end?");
18300 pan.el.removeClass([lr,dir]);
18301 pan.setActive(true);
18303 cur.el.removeClass([lr]);
18304 cur.setActive(false);
18306 _this.transition = false;
18308 }, this, { single: true } );
18313 cur.setActive(false);
18314 pan.setActive(true);
18319 showPanelNext : function()
18321 var i = this.indexOfPanel(this.getActivePanel());
18323 if (i >= this.tabs.length - 1 && !this.autoslide) {
18327 if (i >= this.tabs.length - 1 && this.autoslide) {
18331 this.showPanel(this.tabs[i+1]);
18334 showPanelPrev : function()
18336 var i = this.indexOfPanel(this.getActivePanel());
18338 if (i < 1 && !this.autoslide) {
18342 if (i < 1 && this.autoslide) {
18343 i = this.tabs.length;
18346 this.showPanel(this.tabs[i-1]);
18350 addBullet: function()
18352 if(!this.bullets || Roo.isTouch){
18355 var ctr = this.el.select('.carousel-bullets',true).first();
18356 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18357 var bullet = ctr.createChild({
18358 cls : 'bullet bullet-' + i
18359 },ctr.dom.lastChild);
18364 bullet.on('click', (function(e, el, o, ii, t){
18366 e.preventDefault();
18368 this.showPanel(ii);
18370 if(this.autoslide && this.slideFn){
18371 clearInterval(this.slideFn);
18372 this.slideFn = window.setInterval(function() {
18373 _this.showPanelNext();
18377 }).createDelegate(this, [i, bullet], true));
18382 setActiveBullet : function(i)
18388 Roo.each(this.el.select('.bullet', true).elements, function(el){
18389 el.removeClass('selected');
18392 var bullet = this.el.select('.bullet-' + i, true).first();
18398 bullet.addClass('selected');
18409 Roo.apply(Roo.bootstrap.TabGroup, {
18413 * register a Navigation Group
18414 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18416 register : function(navgrp)
18418 this.groups[navgrp.navId] = navgrp;
18422 * fetch a Navigation Group based on the navigation ID
18423 * if one does not exist , it will get created.
18424 * @param {string} the navgroup to add
18425 * @returns {Roo.bootstrap.NavGroup} the navgroup
18427 get: function(navId) {
18428 if (typeof(this.groups[navId]) == 'undefined') {
18429 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18431 return this.groups[navId] ;
18446 * @class Roo.bootstrap.TabPanel
18447 * @extends Roo.bootstrap.Component
18448 * Bootstrap TabPanel class
18449 * @cfg {Boolean} active panel active
18450 * @cfg {String} html panel content
18451 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18452 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18453 * @cfg {String} href click to link..
18457 * Create a new TabPanel
18458 * @param {Object} config The config object
18461 Roo.bootstrap.TabPanel = function(config){
18462 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18466 * Fires when the active status changes
18467 * @param {Roo.bootstrap.TabPanel} this
18468 * @param {Boolean} state the new state
18473 * @event beforedeactivate
18474 * Fires before a tab is de-activated - can be used to do validation on a form.
18475 * @param {Roo.bootstrap.TabPanel} this
18476 * @return {Boolean} false if there is an error
18479 'beforedeactivate': true
18482 this.tabId = this.tabId || Roo.id();
18486 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18494 getAutoCreate : function(){
18497 // item is needed for carousel - not sure if it has any effect otherwise
18498 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18499 html: this.html || ''
18503 cfg.cls += ' active';
18507 cfg.tabId = this.tabId;
18514 initEvents: function()
18516 var p = this.parent();
18518 this.navId = this.navId || p.navId;
18520 if (typeof(this.navId) != 'undefined') {
18521 // not really needed.. but just in case.. parent should be a NavGroup.
18522 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18526 var i = tg.tabs.length - 1;
18528 if(this.active && tg.bullets > 0 && i < tg.bullets){
18529 tg.setActiveBullet(i);
18533 this.el.on('click', this.onClick, this);
18536 this.el.on("touchstart", this.onTouchStart, this);
18537 this.el.on("touchmove", this.onTouchMove, this);
18538 this.el.on("touchend", this.onTouchEnd, this);
18543 onRender : function(ct, position)
18545 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18548 setActive : function(state)
18550 Roo.log("panel - set active " + this.tabId + "=" + state);
18552 this.active = state;
18554 this.el.removeClass('active');
18556 } else if (!this.el.hasClass('active')) {
18557 this.el.addClass('active');
18560 this.fireEvent('changed', this, state);
18563 onClick : function(e)
18565 e.preventDefault();
18567 if(!this.href.length){
18571 window.location.href = this.href;
18580 onTouchStart : function(e)
18582 this.swiping = false;
18584 this.startX = e.browserEvent.touches[0].clientX;
18585 this.startY = e.browserEvent.touches[0].clientY;
18588 onTouchMove : function(e)
18590 this.swiping = true;
18592 this.endX = e.browserEvent.touches[0].clientX;
18593 this.endY = e.browserEvent.touches[0].clientY;
18596 onTouchEnd : function(e)
18603 var tabGroup = this.parent();
18605 if(this.endX > this.startX){ // swiping right
18606 tabGroup.showPanelPrev();
18610 if(this.startX > this.endX){ // swiping left
18611 tabGroup.showPanelNext();
18630 * @class Roo.bootstrap.DateField
18631 * @extends Roo.bootstrap.Input
18632 * Bootstrap DateField class
18633 * @cfg {Number} weekStart default 0
18634 * @cfg {String} viewMode default empty, (months|years)
18635 * @cfg {String} minViewMode default empty, (months|years)
18636 * @cfg {Number} startDate default -Infinity
18637 * @cfg {Number} endDate default Infinity
18638 * @cfg {Boolean} todayHighlight default false
18639 * @cfg {Boolean} todayBtn default false
18640 * @cfg {Boolean} calendarWeeks default false
18641 * @cfg {Object} daysOfWeekDisabled default empty
18642 * @cfg {Boolean} singleMode default false (true | false)
18644 * @cfg {Boolean} keyboardNavigation default true
18645 * @cfg {String} language default en
18648 * Create a new DateField
18649 * @param {Object} config The config object
18652 Roo.bootstrap.DateField = function(config){
18653 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18657 * Fires when this field show.
18658 * @param {Roo.bootstrap.DateField} this
18659 * @param {Mixed} date The date value
18664 * Fires when this field hide.
18665 * @param {Roo.bootstrap.DateField} this
18666 * @param {Mixed} date The date value
18671 * Fires when select a date.
18672 * @param {Roo.bootstrap.DateField} this
18673 * @param {Mixed} date The date value
18677 * @event beforeselect
18678 * Fires when before select a date.
18679 * @param {Roo.bootstrap.DateField} this
18680 * @param {Mixed} date The date value
18682 beforeselect : true
18686 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18689 * @cfg {String} format
18690 * The default date format string which can be overriden for localization support. The format must be
18691 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18695 * @cfg {String} altFormats
18696 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18697 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18699 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18707 todayHighlight : false,
18713 keyboardNavigation: true,
18715 calendarWeeks: false,
18717 startDate: -Infinity,
18721 daysOfWeekDisabled: [],
18725 singleMode : false,
18727 UTCDate: function()
18729 return new Date(Date.UTC.apply(Date, arguments));
18732 UTCToday: function()
18734 var today = new Date();
18735 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18738 getDate: function() {
18739 var d = this.getUTCDate();
18740 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18743 getUTCDate: function() {
18747 setDate: function(d) {
18748 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18751 setUTCDate: function(d) {
18753 this.setValue(this.formatDate(this.date));
18756 onRender: function(ct, position)
18759 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18761 this.language = this.language || 'en';
18762 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18763 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18765 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18766 this.format = this.format || 'm/d/y';
18767 this.isInline = false;
18768 this.isInput = true;
18769 this.component = this.el.select('.add-on', true).first() || false;
18770 this.component = (this.component && this.component.length === 0) ? false : this.component;
18771 this.hasInput = this.component && this.inputEl().length;
18773 if (typeof(this.minViewMode === 'string')) {
18774 switch (this.minViewMode) {
18776 this.minViewMode = 1;
18779 this.minViewMode = 2;
18782 this.minViewMode = 0;
18787 if (typeof(this.viewMode === 'string')) {
18788 switch (this.viewMode) {
18801 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18803 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18805 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18807 this.picker().on('mousedown', this.onMousedown, this);
18808 this.picker().on('click', this.onClick, this);
18810 this.picker().addClass('datepicker-dropdown');
18812 this.startViewMode = this.viewMode;
18814 if(this.singleMode){
18815 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18816 v.setVisibilityMode(Roo.Element.DISPLAY);
18820 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18821 v.setStyle('width', '189px');
18825 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18826 if(!this.calendarWeeks){
18831 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18832 v.attr('colspan', function(i, val){
18833 return parseInt(val) + 1;
18838 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18840 this.setStartDate(this.startDate);
18841 this.setEndDate(this.endDate);
18843 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18850 if(this.isInline) {
18855 picker : function()
18857 return this.pickerEl;
18858 // return this.el.select('.datepicker', true).first();
18861 fillDow: function()
18863 var dowCnt = this.weekStart;
18872 if(this.calendarWeeks){
18880 while (dowCnt < this.weekStart + 7) {
18884 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18888 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18891 fillMonths: function()
18894 var months = this.picker().select('>.datepicker-months td', true).first();
18896 months.dom.innerHTML = '';
18902 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18905 months.createChild(month);
18912 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;
18914 if (this.date < this.startDate) {
18915 this.viewDate = new Date(this.startDate);
18916 } else if (this.date > this.endDate) {
18917 this.viewDate = new Date(this.endDate);
18919 this.viewDate = new Date(this.date);
18927 var d = new Date(this.viewDate),
18928 year = d.getUTCFullYear(),
18929 month = d.getUTCMonth(),
18930 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18931 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18932 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18933 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18934 currentDate = this.date && this.date.valueOf(),
18935 today = this.UTCToday();
18937 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18939 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18941 // this.picker.select('>tfoot th.today').
18942 // .text(dates[this.language].today)
18943 // .toggle(this.todayBtn !== false);
18945 this.updateNavArrows();
18948 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18950 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18952 prevMonth.setUTCDate(day);
18954 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18956 var nextMonth = new Date(prevMonth);
18958 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18960 nextMonth = nextMonth.valueOf();
18962 var fillMonths = false;
18964 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18966 while(prevMonth.valueOf() <= nextMonth) {
18969 if (prevMonth.getUTCDay() === this.weekStart) {
18971 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18979 if(this.calendarWeeks){
18980 // ISO 8601: First week contains first thursday.
18981 // ISO also states week starts on Monday, but we can be more abstract here.
18983 // Start of current week: based on weekstart/current date
18984 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18985 // Thursday of this week
18986 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18987 // First Thursday of year, year from thursday
18988 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18989 // Calendar week: ms between thursdays, div ms per day, div 7 days
18990 calWeek = (th - yth) / 864e5 / 7 + 1;
18992 fillMonths.cn.push({
19000 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19002 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19005 if (this.todayHighlight &&
19006 prevMonth.getUTCFullYear() == today.getFullYear() &&
19007 prevMonth.getUTCMonth() == today.getMonth() &&
19008 prevMonth.getUTCDate() == today.getDate()) {
19009 clsName += ' today';
19012 if (currentDate && prevMonth.valueOf() === currentDate) {
19013 clsName += ' active';
19016 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19017 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19018 clsName += ' disabled';
19021 fillMonths.cn.push({
19023 cls: 'day ' + clsName,
19024 html: prevMonth.getDate()
19027 prevMonth.setDate(prevMonth.getDate()+1);
19030 var currentYear = this.date && this.date.getUTCFullYear();
19031 var currentMonth = this.date && this.date.getUTCMonth();
19033 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19035 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19036 v.removeClass('active');
19038 if(currentYear === year && k === currentMonth){
19039 v.addClass('active');
19042 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19043 v.addClass('disabled');
19049 year = parseInt(year/10, 10) * 10;
19051 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19053 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19056 for (var i = -1; i < 11; i++) {
19057 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19059 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19067 showMode: function(dir)
19070 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19073 Roo.each(this.picker().select('>div',true).elements, function(v){
19074 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19077 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19082 if(this.isInline) {
19086 this.picker().removeClass(['bottom', 'top']);
19088 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19090 * place to the top of element!
19094 this.picker().addClass('top');
19095 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19100 this.picker().addClass('bottom');
19102 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19105 parseDate : function(value)
19107 if(!value || value instanceof Date){
19110 var v = Date.parseDate(value, this.format);
19111 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19112 v = Date.parseDate(value, 'Y-m-d');
19114 if(!v && this.altFormats){
19115 if(!this.altFormatsArray){
19116 this.altFormatsArray = this.altFormats.split("|");
19118 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19119 v = Date.parseDate(value, this.altFormatsArray[i]);
19125 formatDate : function(date, fmt)
19127 return (!date || !(date instanceof Date)) ?
19128 date : date.dateFormat(fmt || this.format);
19131 onFocus : function()
19133 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19137 onBlur : function()
19139 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19141 var d = this.inputEl().getValue();
19148 showPopup : function()
19150 this.picker().show();
19154 this.fireEvent('showpopup', this, this.date);
19157 hidePopup : function()
19159 if(this.isInline) {
19162 this.picker().hide();
19163 this.viewMode = this.startViewMode;
19166 this.fireEvent('hidepopup', this, this.date);
19170 onMousedown: function(e)
19172 e.stopPropagation();
19173 e.preventDefault();
19178 Roo.bootstrap.DateField.superclass.keyup.call(this);
19182 setValue: function(v)
19184 if(this.fireEvent('beforeselect', this, v) !== false){
19185 var d = new Date(this.parseDate(v) ).clearTime();
19187 if(isNaN(d.getTime())){
19188 this.date = this.viewDate = '';
19189 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19193 v = this.formatDate(d);
19195 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19197 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19201 this.fireEvent('select', this, this.date);
19205 getValue: function()
19207 return this.formatDate(this.date);
19210 fireKey: function(e)
19212 if (!this.picker().isVisible()){
19213 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19219 var dateChanged = false,
19221 newDate, newViewDate;
19226 e.preventDefault();
19230 if (!this.keyboardNavigation) {
19233 dir = e.keyCode == 37 ? -1 : 1;
19236 newDate = this.moveYear(this.date, dir);
19237 newViewDate = this.moveYear(this.viewDate, dir);
19238 } else if (e.shiftKey){
19239 newDate = this.moveMonth(this.date, dir);
19240 newViewDate = this.moveMonth(this.viewDate, dir);
19242 newDate = new Date(this.date);
19243 newDate.setUTCDate(this.date.getUTCDate() + dir);
19244 newViewDate = new Date(this.viewDate);
19245 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19247 if (this.dateWithinRange(newDate)){
19248 this.date = newDate;
19249 this.viewDate = newViewDate;
19250 this.setValue(this.formatDate(this.date));
19252 e.preventDefault();
19253 dateChanged = true;
19258 if (!this.keyboardNavigation) {
19261 dir = e.keyCode == 38 ? -1 : 1;
19263 newDate = this.moveYear(this.date, dir);
19264 newViewDate = this.moveYear(this.viewDate, dir);
19265 } else if (e.shiftKey){
19266 newDate = this.moveMonth(this.date, dir);
19267 newViewDate = this.moveMonth(this.viewDate, dir);
19269 newDate = new Date(this.date);
19270 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19271 newViewDate = new Date(this.viewDate);
19272 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19274 if (this.dateWithinRange(newDate)){
19275 this.date = newDate;
19276 this.viewDate = newViewDate;
19277 this.setValue(this.formatDate(this.date));
19279 e.preventDefault();
19280 dateChanged = true;
19284 this.setValue(this.formatDate(this.date));
19286 e.preventDefault();
19289 this.setValue(this.formatDate(this.date));
19303 onClick: function(e)
19305 e.stopPropagation();
19306 e.preventDefault();
19308 var target = e.getTarget();
19310 if(target.nodeName.toLowerCase() === 'i'){
19311 target = Roo.get(target).dom.parentNode;
19314 var nodeName = target.nodeName;
19315 var className = target.className;
19316 var html = target.innerHTML;
19317 //Roo.log(nodeName);
19319 switch(nodeName.toLowerCase()) {
19321 switch(className) {
19327 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19328 switch(this.viewMode){
19330 this.viewDate = this.moveMonth(this.viewDate, dir);
19334 this.viewDate = this.moveYear(this.viewDate, dir);
19340 var date = new Date();
19341 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19343 this.setValue(this.formatDate(this.date));
19350 if (className.indexOf('disabled') < 0) {
19351 this.viewDate.setUTCDate(1);
19352 if (className.indexOf('month') > -1) {
19353 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19355 var year = parseInt(html, 10) || 0;
19356 this.viewDate.setUTCFullYear(year);
19360 if(this.singleMode){
19361 this.setValue(this.formatDate(this.viewDate));
19372 //Roo.log(className);
19373 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19374 var day = parseInt(html, 10) || 1;
19375 var year = this.viewDate.getUTCFullYear(),
19376 month = this.viewDate.getUTCMonth();
19378 if (className.indexOf('old') > -1) {
19385 } else if (className.indexOf('new') > -1) {
19393 //Roo.log([year,month,day]);
19394 this.date = this.UTCDate(year, month, day,0,0,0,0);
19395 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19397 //Roo.log(this.formatDate(this.date));
19398 this.setValue(this.formatDate(this.date));
19405 setStartDate: function(startDate)
19407 this.startDate = startDate || -Infinity;
19408 if (this.startDate !== -Infinity) {
19409 this.startDate = this.parseDate(this.startDate);
19412 this.updateNavArrows();
19415 setEndDate: function(endDate)
19417 this.endDate = endDate || Infinity;
19418 if (this.endDate !== Infinity) {
19419 this.endDate = this.parseDate(this.endDate);
19422 this.updateNavArrows();
19425 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19427 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19428 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19429 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19431 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19432 return parseInt(d, 10);
19435 this.updateNavArrows();
19438 updateNavArrows: function()
19440 if(this.singleMode){
19444 var d = new Date(this.viewDate),
19445 year = d.getUTCFullYear(),
19446 month = d.getUTCMonth();
19448 Roo.each(this.picker().select('.prev', true).elements, function(v){
19450 switch (this.viewMode) {
19453 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19459 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19466 Roo.each(this.picker().select('.next', true).elements, function(v){
19468 switch (this.viewMode) {
19471 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19477 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19485 moveMonth: function(date, dir)
19490 var new_date = new Date(date.valueOf()),
19491 day = new_date.getUTCDate(),
19492 month = new_date.getUTCMonth(),
19493 mag = Math.abs(dir),
19495 dir = dir > 0 ? 1 : -1;
19498 // If going back one month, make sure month is not current month
19499 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19501 return new_date.getUTCMonth() == month;
19503 // If going forward one month, make sure month is as expected
19504 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19506 return new_date.getUTCMonth() != new_month;
19508 new_month = month + dir;
19509 new_date.setUTCMonth(new_month);
19510 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19511 if (new_month < 0 || new_month > 11) {
19512 new_month = (new_month + 12) % 12;
19515 // For magnitudes >1, move one month at a time...
19516 for (var i=0; i<mag; i++) {
19517 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19518 new_date = this.moveMonth(new_date, dir);
19520 // ...then reset the day, keeping it in the new month
19521 new_month = new_date.getUTCMonth();
19522 new_date.setUTCDate(day);
19524 return new_month != new_date.getUTCMonth();
19527 // Common date-resetting loop -- if date is beyond end of month, make it
19530 new_date.setUTCDate(--day);
19531 new_date.setUTCMonth(new_month);
19536 moveYear: function(date, dir)
19538 return this.moveMonth(date, dir*12);
19541 dateWithinRange: function(date)
19543 return date >= this.startDate && date <= this.endDate;
19549 this.picker().remove();
19552 validateValue : function(value)
19554 if(this.getVisibilityEl().hasClass('hidden')){
19558 if(value.length < 1) {
19559 if(this.allowBlank){
19565 if(value.length < this.minLength){
19568 if(value.length > this.maxLength){
19572 var vt = Roo.form.VTypes;
19573 if(!vt[this.vtype](value, this)){
19577 if(typeof this.validator == "function"){
19578 var msg = this.validator(value);
19584 if(this.regex && !this.regex.test(value)){
19588 if(typeof(this.parseDate(value)) == 'undefined'){
19592 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19596 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19606 this.date = this.viewDate = '';
19608 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19613 Roo.apply(Roo.bootstrap.DateField, {
19624 html: '<i class="fa fa-arrow-left"/>'
19634 html: '<i class="fa fa-arrow-right"/>'
19676 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19677 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19678 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19679 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19680 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19693 navFnc: 'FullYear',
19698 navFnc: 'FullYear',
19703 Roo.apply(Roo.bootstrap.DateField, {
19707 cls: 'datepicker dropdown-menu roo-dynamic',
19711 cls: 'datepicker-days',
19715 cls: 'table-condensed',
19717 Roo.bootstrap.DateField.head,
19721 Roo.bootstrap.DateField.footer
19728 cls: 'datepicker-months',
19732 cls: 'table-condensed',
19734 Roo.bootstrap.DateField.head,
19735 Roo.bootstrap.DateField.content,
19736 Roo.bootstrap.DateField.footer
19743 cls: 'datepicker-years',
19747 cls: 'table-condensed',
19749 Roo.bootstrap.DateField.head,
19750 Roo.bootstrap.DateField.content,
19751 Roo.bootstrap.DateField.footer
19770 * @class Roo.bootstrap.TimeField
19771 * @extends Roo.bootstrap.Input
19772 * Bootstrap DateField class
19776 * Create a new TimeField
19777 * @param {Object} config The config object
19780 Roo.bootstrap.TimeField = function(config){
19781 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19785 * Fires when this field show.
19786 * @param {Roo.bootstrap.DateField} thisthis
19787 * @param {Mixed} date The date value
19792 * Fires when this field hide.
19793 * @param {Roo.bootstrap.DateField} this
19794 * @param {Mixed} date The date value
19799 * Fires when select a date.
19800 * @param {Roo.bootstrap.DateField} this
19801 * @param {Mixed} date The date value
19807 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19810 * @cfg {String} format
19811 * The default time format string which can be overriden for localization support. The format must be
19812 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19816 onRender: function(ct, position)
19819 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19821 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19823 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19825 this.pop = this.picker().select('>.datepicker-time',true).first();
19826 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19828 this.picker().on('mousedown', this.onMousedown, this);
19829 this.picker().on('click', this.onClick, this);
19831 this.picker().addClass('datepicker-dropdown');
19836 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19837 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19838 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19839 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19840 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19841 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19845 fireKey: function(e){
19846 if (!this.picker().isVisible()){
19847 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19853 e.preventDefault();
19861 this.onTogglePeriod();
19864 this.onIncrementMinutes();
19867 this.onDecrementMinutes();
19876 onClick: function(e) {
19877 e.stopPropagation();
19878 e.preventDefault();
19881 picker : function()
19883 return this.el.select('.datepicker', true).first();
19886 fillTime: function()
19888 var time = this.pop.select('tbody', true).first();
19890 time.dom.innerHTML = '';
19905 cls: 'hours-up glyphicon glyphicon-chevron-up'
19925 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19946 cls: 'timepicker-hour',
19961 cls: 'timepicker-minute',
19976 cls: 'btn btn-primary period',
19998 cls: 'hours-down glyphicon glyphicon-chevron-down'
20018 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20036 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20043 var hours = this.time.getHours();
20044 var minutes = this.time.getMinutes();
20057 hours = hours - 12;
20061 hours = '0' + hours;
20065 minutes = '0' + minutes;
20068 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20069 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20070 this.pop.select('button', true).first().dom.innerHTML = period;
20076 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20078 var cls = ['bottom'];
20080 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20087 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20092 this.picker().addClass(cls.join('-'));
20096 Roo.each(cls, function(c){
20098 _this.picker().setTop(_this.inputEl().getHeight());
20102 _this.picker().setTop(0 - _this.picker().getHeight());
20107 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20111 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20118 onFocus : function()
20120 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20124 onBlur : function()
20126 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20132 this.picker().show();
20137 this.fireEvent('show', this, this.date);
20142 this.picker().hide();
20145 this.fireEvent('hide', this, this.date);
20148 setTime : function()
20151 this.setValue(this.time.format(this.format));
20153 this.fireEvent('select', this, this.date);
20158 onMousedown: function(e){
20159 e.stopPropagation();
20160 e.preventDefault();
20163 onIncrementHours: function()
20165 Roo.log('onIncrementHours');
20166 this.time = this.time.add(Date.HOUR, 1);
20171 onDecrementHours: function()
20173 Roo.log('onDecrementHours');
20174 this.time = this.time.add(Date.HOUR, -1);
20178 onIncrementMinutes: function()
20180 Roo.log('onIncrementMinutes');
20181 this.time = this.time.add(Date.MINUTE, 1);
20185 onDecrementMinutes: function()
20187 Roo.log('onDecrementMinutes');
20188 this.time = this.time.add(Date.MINUTE, -1);
20192 onTogglePeriod: function()
20194 Roo.log('onTogglePeriod');
20195 this.time = this.time.add(Date.HOUR, 12);
20202 Roo.apply(Roo.bootstrap.TimeField, {
20232 cls: 'btn btn-info ok',
20244 Roo.apply(Roo.bootstrap.TimeField, {
20248 cls: 'datepicker dropdown-menu',
20252 cls: 'datepicker-time',
20256 cls: 'table-condensed',
20258 Roo.bootstrap.TimeField.content,
20259 Roo.bootstrap.TimeField.footer
20278 * @class Roo.bootstrap.MonthField
20279 * @extends Roo.bootstrap.Input
20280 * Bootstrap MonthField class
20282 * @cfg {String} language default en
20285 * Create a new MonthField
20286 * @param {Object} config The config object
20289 Roo.bootstrap.MonthField = function(config){
20290 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20295 * Fires when this field show.
20296 * @param {Roo.bootstrap.MonthField} this
20297 * @param {Mixed} date The date value
20302 * Fires when this field hide.
20303 * @param {Roo.bootstrap.MonthField} this
20304 * @param {Mixed} date The date value
20309 * Fires when select a date.
20310 * @param {Roo.bootstrap.MonthField} this
20311 * @param {String} oldvalue The old value
20312 * @param {String} newvalue The new value
20318 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20320 onRender: function(ct, position)
20323 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20325 this.language = this.language || 'en';
20326 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20327 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20329 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20330 this.isInline = false;
20331 this.isInput = true;
20332 this.component = this.el.select('.add-on', true).first() || false;
20333 this.component = (this.component && this.component.length === 0) ? false : this.component;
20334 this.hasInput = this.component && this.inputEL().length;
20336 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20338 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20340 this.picker().on('mousedown', this.onMousedown, this);
20341 this.picker().on('click', this.onClick, this);
20343 this.picker().addClass('datepicker-dropdown');
20345 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20346 v.setStyle('width', '189px');
20353 if(this.isInline) {
20359 setValue: function(v, suppressEvent)
20361 var o = this.getValue();
20363 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20367 if(suppressEvent !== true){
20368 this.fireEvent('select', this, o, v);
20373 getValue: function()
20378 onClick: function(e)
20380 e.stopPropagation();
20381 e.preventDefault();
20383 var target = e.getTarget();
20385 if(target.nodeName.toLowerCase() === 'i'){
20386 target = Roo.get(target).dom.parentNode;
20389 var nodeName = target.nodeName;
20390 var className = target.className;
20391 var html = target.innerHTML;
20393 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20397 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20399 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20405 picker : function()
20407 return this.pickerEl;
20410 fillMonths: function()
20413 var months = this.picker().select('>.datepicker-months td', true).first();
20415 months.dom.innerHTML = '';
20421 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20424 months.createChild(month);
20433 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20434 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20437 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20438 e.removeClass('active');
20440 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20441 e.addClass('active');
20448 if(this.isInline) {
20452 this.picker().removeClass(['bottom', 'top']);
20454 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20456 * place to the top of element!
20460 this.picker().addClass('top');
20461 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20466 this.picker().addClass('bottom');
20468 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20471 onFocus : function()
20473 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20477 onBlur : function()
20479 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20481 var d = this.inputEl().getValue();
20490 this.picker().show();
20491 this.picker().select('>.datepicker-months', true).first().show();
20495 this.fireEvent('show', this, this.date);
20500 if(this.isInline) {
20503 this.picker().hide();
20504 this.fireEvent('hide', this, this.date);
20508 onMousedown: function(e)
20510 e.stopPropagation();
20511 e.preventDefault();
20516 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20520 fireKey: function(e)
20522 if (!this.picker().isVisible()){
20523 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20534 e.preventDefault();
20538 dir = e.keyCode == 37 ? -1 : 1;
20540 this.vIndex = this.vIndex + dir;
20542 if(this.vIndex < 0){
20546 if(this.vIndex > 11){
20550 if(isNaN(this.vIndex)){
20554 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20560 dir = e.keyCode == 38 ? -1 : 1;
20562 this.vIndex = this.vIndex + dir * 4;
20564 if(this.vIndex < 0){
20568 if(this.vIndex > 11){
20572 if(isNaN(this.vIndex)){
20576 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20581 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20582 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20586 e.preventDefault();
20589 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20590 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20606 this.picker().remove();
20611 Roo.apply(Roo.bootstrap.MonthField, {
20630 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20631 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20636 Roo.apply(Roo.bootstrap.MonthField, {
20640 cls: 'datepicker dropdown-menu roo-dynamic',
20644 cls: 'datepicker-months',
20648 cls: 'table-condensed',
20650 Roo.bootstrap.DateField.content
20670 * @class Roo.bootstrap.CheckBox
20671 * @extends Roo.bootstrap.Input
20672 * Bootstrap CheckBox class
20674 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20675 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20676 * @cfg {String} boxLabel The text that appears beside the checkbox
20677 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20678 * @cfg {Boolean} checked initnal the element
20679 * @cfg {Boolean} inline inline the element (default false)
20680 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20681 * @cfg {String} tooltip label tooltip
20684 * Create a new CheckBox
20685 * @param {Object} config The config object
20688 Roo.bootstrap.CheckBox = function(config){
20689 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20694 * Fires when the element is checked or unchecked.
20695 * @param {Roo.bootstrap.CheckBox} this This input
20696 * @param {Boolean} checked The new checked value
20701 * Fires when the element is click.
20702 * @param {Roo.bootstrap.CheckBox} this This input
20709 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20711 inputType: 'checkbox',
20720 getAutoCreate : function()
20722 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20728 cfg.cls = 'form-group ' + this.inputType; //input-group
20731 cfg.cls += ' ' + this.inputType + '-inline';
20737 type : this.inputType,
20738 value : this.inputValue,
20739 cls : 'roo-' + this.inputType, //'form-box',
20740 placeholder : this.placeholder || ''
20744 if(this.inputType != 'radio'){
20748 cls : 'roo-hidden-value',
20749 value : this.checked ? this.inputValue : this.valueOff
20754 if (this.weight) { // Validity check?
20755 cfg.cls += " " + this.inputType + "-" + this.weight;
20758 if (this.disabled) {
20759 input.disabled=true;
20763 input.checked = this.checked;
20768 input.name = this.name;
20770 if(this.inputType != 'radio'){
20771 hidden.name = this.name;
20772 input.name = '_hidden_' + this.name;
20777 input.cls += ' input-' + this.size;
20782 ['xs','sm','md','lg'].map(function(size){
20783 if (settings[size]) {
20784 cfg.cls += ' col-' + size + '-' + settings[size];
20788 var inputblock = input;
20790 if (this.before || this.after) {
20793 cls : 'input-group',
20798 inputblock.cn.push({
20800 cls : 'input-group-addon',
20805 inputblock.cn.push(input);
20807 if(this.inputType != 'radio'){
20808 inputblock.cn.push(hidden);
20812 inputblock.cn.push({
20814 cls : 'input-group-addon',
20821 if (align ==='left' && this.fieldLabel.length) {
20822 // Roo.log("left and has label");
20827 cls : 'control-label',
20828 html : this.fieldLabel
20838 if(this.labelWidth > 12){
20839 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20842 if(this.labelWidth < 13 && this.labelmd == 0){
20843 this.labelmd = this.labelWidth;
20846 if(this.labellg > 0){
20847 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20848 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20851 if(this.labelmd > 0){
20852 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20853 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20856 if(this.labelsm > 0){
20857 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20858 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20861 if(this.labelxs > 0){
20862 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20863 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20866 } else if ( this.fieldLabel.length) {
20867 // Roo.log(" label");
20871 tag: this.boxLabel ? 'span' : 'label',
20873 cls: 'control-label box-input-label',
20874 //cls : 'input-group-addon',
20875 html : this.fieldLabel
20884 // Roo.log(" no label && no align");
20885 cfg.cn = [ inputblock ] ;
20891 var boxLabelCfg = {
20893 //'for': id, // box label is handled by onclick - so no for...
20895 html: this.boxLabel
20899 boxLabelCfg.tooltip = this.tooltip;
20902 cfg.cn.push(boxLabelCfg);
20905 if(this.inputType != 'radio'){
20906 cfg.cn.push(hidden);
20914 * return the real input element.
20916 inputEl: function ()
20918 return this.el.select('input.roo-' + this.inputType,true).first();
20920 hiddenEl: function ()
20922 return this.el.select('input.roo-hidden-value',true).first();
20925 labelEl: function()
20927 return this.el.select('label.control-label',true).first();
20929 /* depricated... */
20933 return this.labelEl();
20936 boxLabelEl: function()
20938 return this.el.select('label.box-label',true).first();
20941 initEvents : function()
20943 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20945 this.inputEl().on('click', this.onClick, this);
20947 if (this.boxLabel) {
20948 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20951 this.startValue = this.getValue();
20954 Roo.bootstrap.CheckBox.register(this);
20958 onClick : function(e)
20960 if(this.fireEvent('click', this, e) !== false){
20961 this.setChecked(!this.checked);
20966 setChecked : function(state,suppressEvent)
20968 this.startValue = this.getValue();
20970 if(this.inputType == 'radio'){
20972 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973 e.dom.checked = false;
20976 this.inputEl().dom.checked = true;
20978 this.inputEl().dom.value = this.inputValue;
20980 if(suppressEvent !== true){
20981 this.fireEvent('check', this, true);
20989 this.checked = state;
20991 this.inputEl().dom.checked = state;
20994 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20996 if(suppressEvent !== true){
20997 this.fireEvent('check', this, state);
21003 getValue : function()
21005 if(this.inputType == 'radio'){
21006 return this.getGroupValue();
21009 return this.hiddenEl().dom.value;
21013 getGroupValue : function()
21015 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21019 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21022 setValue : function(v,suppressEvent)
21024 if(this.inputType == 'radio'){
21025 this.setGroupValue(v, suppressEvent);
21029 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21034 setGroupValue : function(v, suppressEvent)
21036 this.startValue = this.getValue();
21038 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21039 e.dom.checked = false;
21041 if(e.dom.value == v){
21042 e.dom.checked = true;
21046 if(suppressEvent !== true){
21047 this.fireEvent('check', this, true);
21055 validate : function()
21057 if(this.getVisibilityEl().hasClass('hidden')){
21063 (this.inputType == 'radio' && this.validateRadio()) ||
21064 (this.inputType == 'checkbox' && this.validateCheckbox())
21070 this.markInvalid();
21074 validateRadio : function()
21076 if(this.getVisibilityEl().hasClass('hidden')){
21080 if(this.allowBlank){
21086 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087 if(!e.dom.checked){
21099 validateCheckbox : function()
21102 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21103 //return (this.getValue() == this.inputValue) ? true : false;
21106 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21114 for(var i in group){
21115 if(group[i].el.isVisible(true)){
21123 for(var i in group){
21128 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21135 * Mark this field as valid
21137 markValid : function()
21141 this.fireEvent('valid', this);
21143 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21146 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21153 if(this.inputType == 'radio'){
21154 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21156 e.findParent('.form-group', false, true).addClass(_this.validClass);
21163 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21164 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21168 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21174 for(var i in group){
21175 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21176 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21181 * Mark this field as invalid
21182 * @param {String} msg The validation message
21184 markInvalid : function(msg)
21186 if(this.allowBlank){
21192 this.fireEvent('invalid', this, msg);
21194 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21197 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21201 label.markInvalid();
21204 if(this.inputType == 'radio'){
21205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21206 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21207 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21214 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21215 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21219 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21225 for(var i in group){
21226 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21227 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21232 clearInvalid : function()
21234 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21236 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21238 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21240 if (label && label.iconEl) {
21241 label.iconEl.removeClass(label.validClass);
21242 label.iconEl.removeClass(label.invalidClass);
21246 disable : function()
21248 if(this.inputType != 'radio'){
21249 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21256 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21257 _this.getActionEl().addClass(this.disabledClass);
21258 e.dom.disabled = true;
21262 this.disabled = true;
21263 this.fireEvent("disable", this);
21267 enable : function()
21269 if(this.inputType != 'radio'){
21270 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21277 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21278 _this.getActionEl().removeClass(this.disabledClass);
21279 e.dom.disabled = false;
21283 this.disabled = false;
21284 this.fireEvent("enable", this);
21288 setBoxLabel : function(v)
21293 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21299 Roo.apply(Roo.bootstrap.CheckBox, {
21304 * register a CheckBox Group
21305 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21307 register : function(checkbox)
21309 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21310 this.groups[checkbox.groupId] = {};
21313 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21317 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21321 * fetch a CheckBox Group based on the group ID
21322 * @param {string} the group ID
21323 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21325 get: function(groupId) {
21326 if (typeof(this.groups[groupId]) == 'undefined') {
21330 return this.groups[groupId] ;
21343 * @class Roo.bootstrap.Radio
21344 * @extends Roo.bootstrap.Component
21345 * Bootstrap Radio class
21346 * @cfg {String} boxLabel - the label associated
21347 * @cfg {String} value - the value of radio
21350 * Create a new Radio
21351 * @param {Object} config The config object
21353 Roo.bootstrap.Radio = function(config){
21354 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21358 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21364 getAutoCreate : function()
21368 cls : 'form-group radio',
21373 html : this.boxLabel
21381 initEvents : function()
21383 this.parent().register(this);
21385 this.el.on('click', this.onClick, this);
21389 onClick : function(e)
21391 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21392 this.setChecked(true);
21396 setChecked : function(state, suppressEvent)
21398 this.parent().setValue(this.value, suppressEvent);
21402 setBoxLabel : function(v)
21407 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21422 * @class Roo.bootstrap.SecurePass
21423 * @extends Roo.bootstrap.Input
21424 * Bootstrap SecurePass class
21428 * Create a new SecurePass
21429 * @param {Object} config The config object
21432 Roo.bootstrap.SecurePass = function (config) {
21433 // these go here, so the translation tool can replace them..
21435 PwdEmpty: "Please type a password, and then retype it to confirm.",
21436 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21437 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21438 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21439 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21440 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21441 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21442 TooWeak: "Your password is Too Weak."
21444 this.meterLabel = "Password strength:";
21445 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21446 this.meterClass = [
21447 "roo-password-meter-tooweak",
21448 "roo-password-meter-weak",
21449 "roo-password-meter-medium",
21450 "roo-password-meter-strong",
21451 "roo-password-meter-grey"
21456 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21459 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21461 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21463 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21464 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21465 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21466 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21467 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21468 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21469 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21479 * @cfg {String/Object} Label for the strength meter (defaults to
21480 * 'Password strength:')
21485 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21486 * ['Weak', 'Medium', 'Strong'])
21489 pwdStrengths: false,
21502 initEvents: function ()
21504 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21506 if (this.el.is('input[type=password]') && Roo.isSafari) {
21507 this.el.on('keydown', this.SafariOnKeyDown, this);
21510 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21513 onRender: function (ct, position)
21515 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21516 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21517 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21519 this.trigger.createChild({
21524 cls: 'roo-password-meter-grey col-xs-12',
21527 //width: this.meterWidth + 'px'
21531 cls: 'roo-password-meter-text'
21537 if (this.hideTrigger) {
21538 this.trigger.setDisplayed(false);
21540 this.setSize(this.width || '', this.height || '');
21543 onDestroy: function ()
21545 if (this.trigger) {
21546 this.trigger.removeAllListeners();
21547 this.trigger.remove();
21550 this.wrap.remove();
21552 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21555 checkStrength: function ()
21557 var pwd = this.inputEl().getValue();
21558 if (pwd == this._lastPwd) {
21563 if (this.ClientSideStrongPassword(pwd)) {
21565 } else if (this.ClientSideMediumPassword(pwd)) {
21567 } else if (this.ClientSideWeakPassword(pwd)) {
21573 Roo.log('strength1: ' + strength);
21575 //var pm = this.trigger.child('div/div/div').dom;
21576 var pm = this.trigger.child('div/div');
21577 pm.removeClass(this.meterClass);
21578 pm.addClass(this.meterClass[strength]);
21581 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21583 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21585 this._lastPwd = pwd;
21589 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21591 this._lastPwd = '';
21593 var pm = this.trigger.child('div/div');
21594 pm.removeClass(this.meterClass);
21595 pm.addClass('roo-password-meter-grey');
21598 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21601 this.inputEl().dom.type='password';
21604 validateValue: function (value)
21607 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21610 if (value.length == 0) {
21611 if (this.allowBlank) {
21612 this.clearInvalid();
21616 this.markInvalid(this.errors.PwdEmpty);
21617 this.errorMsg = this.errors.PwdEmpty;
21625 if ('[\x21-\x7e]*'.match(value)) {
21626 this.markInvalid(this.errors.PwdBadChar);
21627 this.errorMsg = this.errors.PwdBadChar;
21630 if (value.length < 6) {
21631 this.markInvalid(this.errors.PwdShort);
21632 this.errorMsg = this.errors.PwdShort;
21635 if (value.length > 16) {
21636 this.markInvalid(this.errors.PwdLong);
21637 this.errorMsg = this.errors.PwdLong;
21641 if (this.ClientSideStrongPassword(value)) {
21643 } else if (this.ClientSideMediumPassword(value)) {
21645 } else if (this.ClientSideWeakPassword(value)) {
21652 if (strength < 2) {
21653 //this.markInvalid(this.errors.TooWeak);
21654 this.errorMsg = this.errors.TooWeak;
21659 console.log('strength2: ' + strength);
21661 //var pm = this.trigger.child('div/div/div').dom;
21663 var pm = this.trigger.child('div/div');
21664 pm.removeClass(this.meterClass);
21665 pm.addClass(this.meterClass[strength]);
21667 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21669 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21671 this.errorMsg = '';
21675 CharacterSetChecks: function (type)
21678 this.fResult = false;
21681 isctype: function (character, type)
21684 case this.kCapitalLetter:
21685 if (character >= 'A' && character <= 'Z') {
21690 case this.kSmallLetter:
21691 if (character >= 'a' && character <= 'z') {
21697 if (character >= '0' && character <= '9') {
21702 case this.kPunctuation:
21703 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21714 IsLongEnough: function (pwd, size)
21716 return !(pwd == null || isNaN(size) || pwd.length < size);
21719 SpansEnoughCharacterSets: function (word, nb)
21721 if (!this.IsLongEnough(word, nb))
21726 var characterSetChecks = new Array(
21727 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21728 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21731 for (var index = 0; index < word.length; ++index) {
21732 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21733 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21734 characterSetChecks[nCharSet].fResult = true;
21741 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21742 if (characterSetChecks[nCharSet].fResult) {
21747 if (nCharSets < nb) {
21753 ClientSideStrongPassword: function (pwd)
21755 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21758 ClientSideMediumPassword: function (pwd)
21760 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21763 ClientSideWeakPassword: function (pwd)
21765 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21768 })//<script type="text/javascript">
21771 * Based Ext JS Library 1.1.1
21772 * Copyright(c) 2006-2007, Ext JS, LLC.
21778 * @class Roo.HtmlEditorCore
21779 * @extends Roo.Component
21780 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21782 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21785 Roo.HtmlEditorCore = function(config){
21788 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21793 * @event initialize
21794 * Fires when the editor is fully initialized (including the iframe)
21795 * @param {Roo.HtmlEditorCore} this
21800 * Fires when the editor is first receives the focus. Any insertion must wait
21801 * until after this event.
21802 * @param {Roo.HtmlEditorCore} this
21806 * @event beforesync
21807 * Fires before the textarea is updated with content from the editor iframe. Return false
21808 * to cancel the sync.
21809 * @param {Roo.HtmlEditorCore} this
21810 * @param {String} html
21814 * @event beforepush
21815 * Fires before the iframe editor is updated with content from the textarea. Return false
21816 * to cancel the push.
21817 * @param {Roo.HtmlEditorCore} this
21818 * @param {String} html
21823 * Fires when the textarea is updated with content from the editor iframe.
21824 * @param {Roo.HtmlEditorCore} this
21825 * @param {String} html
21830 * Fires when the iframe editor is updated with content from the textarea.
21831 * @param {Roo.HtmlEditorCore} this
21832 * @param {String} html
21837 * @event editorevent
21838 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21839 * @param {Roo.HtmlEditorCore} this
21845 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21847 // defaults : white / black...
21848 this.applyBlacklists();
21855 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21859 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21865 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21870 * @cfg {Number} height (in pixels)
21874 * @cfg {Number} width (in pixels)
21879 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21882 stylesheets: false,
21887 // private properties
21888 validationEvent : false,
21890 initialized : false,
21892 sourceEditMode : false,
21893 onFocus : Roo.emptyFn,
21895 hideMode:'offsets',
21899 // blacklist + whitelisted elements..
21906 * Protected method that will not generally be called directly. It
21907 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21908 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21910 getDocMarkup : function(){
21914 // inherit styels from page...??
21915 if (this.stylesheets === false) {
21917 Roo.get(document.head).select('style').each(function(node) {
21918 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21921 Roo.get(document.head).select('link').each(function(node) {
21922 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21925 } else if (!this.stylesheets.length) {
21927 st = '<style type="text/css">' +
21928 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21931 st = '<style type="text/css">' +
21936 st += '<style type="text/css">' +
21937 'IMG { cursor: pointer } ' +
21940 var cls = 'roo-htmleditor-body';
21942 if(this.bodyCls.length){
21943 cls += ' ' + this.bodyCls;
21946 return '<html><head>' + st +
21947 //<style type="text/css">' +
21948 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21950 ' </head><body class="' + cls + '"></body></html>';
21954 onRender : function(ct, position)
21957 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21958 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21961 this.el.dom.style.border = '0 none';
21962 this.el.dom.setAttribute('tabIndex', -1);
21963 this.el.addClass('x-hidden hide');
21967 if(Roo.isIE){ // fix IE 1px bogus margin
21968 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21972 this.frameId = Roo.id();
21976 var iframe = this.owner.wrap.createChild({
21978 cls: 'form-control', // bootstrap..
21980 name: this.frameId,
21981 frameBorder : 'no',
21982 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21987 this.iframe = iframe.dom;
21989 this.assignDocWin();
21991 this.doc.designMode = 'on';
21994 this.doc.write(this.getDocMarkup());
21998 var task = { // must defer to wait for browser to be ready
22000 //console.log("run task?" + this.doc.readyState);
22001 this.assignDocWin();
22002 if(this.doc.body || this.doc.readyState == 'complete'){
22004 this.doc.designMode="on";
22008 Roo.TaskMgr.stop(task);
22009 this.initEditor.defer(10, this);
22016 Roo.TaskMgr.start(task);
22021 onResize : function(w, h)
22023 Roo.log('resize: ' +w + ',' + h );
22024 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22028 if(typeof w == 'number'){
22030 this.iframe.style.width = w + 'px';
22032 if(typeof h == 'number'){
22034 this.iframe.style.height = h + 'px';
22036 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22043 * Toggles the editor between standard and source edit mode.
22044 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22046 toggleSourceEdit : function(sourceEditMode){
22048 this.sourceEditMode = sourceEditMode === true;
22050 if(this.sourceEditMode){
22052 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22055 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22056 //this.iframe.className = '';
22059 //this.setSize(this.owner.wrap.getSize());
22060 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22067 * Protected method that will not generally be called directly. If you need/want
22068 * custom HTML cleanup, this is the method you should override.
22069 * @param {String} html The HTML to be cleaned
22070 * return {String} The cleaned HTML
22072 cleanHtml : function(html){
22073 html = String(html);
22074 if(html.length > 5){
22075 if(Roo.isSafari){ // strip safari nonsense
22076 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22079 if(html == ' '){
22086 * HTML Editor -> Textarea
22087 * Protected method that will not generally be called directly. Syncs the contents
22088 * of the editor iframe with the textarea.
22090 syncValue : function(){
22091 if(this.initialized){
22092 var bd = (this.doc.body || this.doc.documentElement);
22093 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22094 var html = bd.innerHTML;
22096 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22097 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22099 html = '<div style="'+m[0]+'">' + html + '</div>';
22102 html = this.cleanHtml(html);
22103 // fix up the special chars.. normaly like back quotes in word...
22104 // however we do not want to do this with chinese..
22105 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22106 var cc = b.charCodeAt();
22108 (cc >= 0x4E00 && cc < 0xA000 ) ||
22109 (cc >= 0x3400 && cc < 0x4E00 ) ||
22110 (cc >= 0xf900 && cc < 0xfb00 )
22116 if(this.owner.fireEvent('beforesync', this, html) !== false){
22117 this.el.dom.value = html;
22118 this.owner.fireEvent('sync', this, html);
22124 * Protected method that will not generally be called directly. Pushes the value of the textarea
22125 * into the iframe editor.
22127 pushValue : function(){
22128 if(this.initialized){
22129 var v = this.el.dom.value.trim();
22131 // if(v.length < 1){
22135 if(this.owner.fireEvent('beforepush', this, v) !== false){
22136 var d = (this.doc.body || this.doc.documentElement);
22138 this.cleanUpPaste();
22139 this.el.dom.value = d.innerHTML;
22140 this.owner.fireEvent('push', this, v);
22146 deferFocus : function(){
22147 this.focus.defer(10, this);
22151 focus : function(){
22152 if(this.win && !this.sourceEditMode){
22159 assignDocWin: function()
22161 var iframe = this.iframe;
22164 this.doc = iframe.contentWindow.document;
22165 this.win = iframe.contentWindow;
22167 // if (!Roo.get(this.frameId)) {
22170 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22171 // this.win = Roo.get(this.frameId).dom.contentWindow;
22173 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22177 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22178 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22183 initEditor : function(){
22184 //console.log("INIT EDITOR");
22185 this.assignDocWin();
22189 this.doc.designMode="on";
22191 this.doc.write(this.getDocMarkup());
22194 var dbody = (this.doc.body || this.doc.documentElement);
22195 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22196 // this copies styles from the containing element into thsi one..
22197 // not sure why we need all of this..
22198 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22200 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22201 //ss['background-attachment'] = 'fixed'; // w3c
22202 dbody.bgProperties = 'fixed'; // ie
22203 //Roo.DomHelper.applyStyles(dbody, ss);
22204 Roo.EventManager.on(this.doc, {
22205 //'mousedown': this.onEditorEvent,
22206 'mouseup': this.onEditorEvent,
22207 'dblclick': this.onEditorEvent,
22208 'click': this.onEditorEvent,
22209 'keyup': this.onEditorEvent,
22214 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22216 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22217 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22219 this.initialized = true;
22221 this.owner.fireEvent('initialize', this);
22226 onDestroy : function(){
22232 //for (var i =0; i < this.toolbars.length;i++) {
22233 // // fixme - ask toolbars for heights?
22234 // this.toolbars[i].onDestroy();
22237 //this.wrap.dom.innerHTML = '';
22238 //this.wrap.remove();
22243 onFirstFocus : function(){
22245 this.assignDocWin();
22248 this.activated = true;
22251 if(Roo.isGecko){ // prevent silly gecko errors
22253 var s = this.win.getSelection();
22254 if(!s.focusNode || s.focusNode.nodeType != 3){
22255 var r = s.getRangeAt(0);
22256 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22261 this.execCmd('useCSS', true);
22262 this.execCmd('styleWithCSS', false);
22265 this.owner.fireEvent('activate', this);
22269 adjustFont: function(btn){
22270 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22271 //if(Roo.isSafari){ // safari
22274 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22275 if(Roo.isSafari){ // safari
22276 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22277 v = (v < 10) ? 10 : v;
22278 v = (v > 48) ? 48 : v;
22279 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22284 v = Math.max(1, v+adjust);
22286 this.execCmd('FontSize', v );
22289 onEditorEvent : function(e)
22291 this.owner.fireEvent('editorevent', this, e);
22292 // this.updateToolbar();
22293 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22296 insertTag : function(tg)
22298 // could be a bit smarter... -> wrap the current selected tRoo..
22299 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22301 range = this.createRange(this.getSelection());
22302 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22303 wrappingNode.appendChild(range.extractContents());
22304 range.insertNode(wrappingNode);
22311 this.execCmd("formatblock", tg);
22315 insertText : function(txt)
22319 var range = this.createRange();
22320 range.deleteContents();
22321 //alert(Sender.getAttribute('label'));
22323 range.insertNode(this.doc.createTextNode(txt));
22329 * Executes a Midas editor command on the editor document and performs necessary focus and
22330 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22331 * @param {String} cmd The Midas command
22332 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22334 relayCmd : function(cmd, value){
22336 this.execCmd(cmd, value);
22337 this.owner.fireEvent('editorevent', this);
22338 //this.updateToolbar();
22339 this.owner.deferFocus();
22343 * Executes a Midas editor command directly on the editor document.
22344 * For visual commands, you should use {@link #relayCmd} instead.
22345 * <b>This should only be called after the editor is initialized.</b>
22346 * @param {String} cmd The Midas command
22347 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22349 execCmd : function(cmd, value){
22350 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22357 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22359 * @param {String} text | dom node..
22361 insertAtCursor : function(text)
22364 if(!this.activated){
22370 var r = this.doc.selection.createRange();
22381 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22385 // from jquery ui (MIT licenced)
22387 var win = this.win;
22389 if (win.getSelection && win.getSelection().getRangeAt) {
22390 range = win.getSelection().getRangeAt(0);
22391 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22392 range.insertNode(node);
22393 } else if (win.document.selection && win.document.selection.createRange) {
22394 // no firefox support
22395 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22396 win.document.selection.createRange().pasteHTML(txt);
22398 // no firefox support
22399 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22400 this.execCmd('InsertHTML', txt);
22409 mozKeyPress : function(e){
22411 var c = e.getCharCode(), cmd;
22414 c = String.fromCharCode(c).toLowerCase();
22428 this.cleanUpPaste.defer(100, this);
22436 e.preventDefault();
22444 fixKeys : function(){ // load time branching for fastest keydown performance
22446 return function(e){
22447 var k = e.getKey(), r;
22450 r = this.doc.selection.createRange();
22453 r.pasteHTML('    ');
22460 r = this.doc.selection.createRange();
22462 var target = r.parentElement();
22463 if(!target || target.tagName.toLowerCase() != 'li'){
22465 r.pasteHTML('<br />');
22471 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22472 this.cleanUpPaste.defer(100, this);
22478 }else if(Roo.isOpera){
22479 return function(e){
22480 var k = e.getKey();
22484 this.execCmd('InsertHTML','    ');
22487 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22488 this.cleanUpPaste.defer(100, this);
22493 }else if(Roo.isSafari){
22494 return function(e){
22495 var k = e.getKey();
22499 this.execCmd('InsertText','\t');
22503 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22504 this.cleanUpPaste.defer(100, this);
22512 getAllAncestors: function()
22514 var p = this.getSelectedNode();
22517 a.push(p); // push blank onto stack..
22518 p = this.getParentElement();
22522 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22526 a.push(this.doc.body);
22530 lastSelNode : false,
22533 getSelection : function()
22535 this.assignDocWin();
22536 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22539 getSelectedNode: function()
22541 // this may only work on Gecko!!!
22543 // should we cache this!!!!
22548 var range = this.createRange(this.getSelection()).cloneRange();
22551 var parent = range.parentElement();
22553 var testRange = range.duplicate();
22554 testRange.moveToElementText(parent);
22555 if (testRange.inRange(range)) {
22558 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22561 parent = parent.parentElement;
22566 // is ancestor a text element.
22567 var ac = range.commonAncestorContainer;
22568 if (ac.nodeType == 3) {
22569 ac = ac.parentNode;
22572 var ar = ac.childNodes;
22575 var other_nodes = [];
22576 var has_other_nodes = false;
22577 for (var i=0;i<ar.length;i++) {
22578 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22581 // fullly contained node.
22583 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22588 // probably selected..
22589 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22590 other_nodes.push(ar[i]);
22594 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22599 has_other_nodes = true;
22601 if (!nodes.length && other_nodes.length) {
22602 nodes= other_nodes;
22604 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22610 createRange: function(sel)
22612 // this has strange effects when using with
22613 // top toolbar - not sure if it's a great idea.
22614 //this.editor.contentWindow.focus();
22615 if (typeof sel != "undefined") {
22617 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22619 return this.doc.createRange();
22622 return this.doc.createRange();
22625 getParentElement: function()
22628 this.assignDocWin();
22629 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22631 var range = this.createRange(sel);
22634 var p = range.commonAncestorContainer;
22635 while (p.nodeType == 3) { // text node
22646 * Range intersection.. the hard stuff...
22650 * [ -- selected range --- ]
22654 * if end is before start or hits it. fail.
22655 * if start is after end or hits it fail.
22657 * if either hits (but other is outside. - then it's not
22663 // @see http://www.thismuchiknow.co.uk/?p=64.
22664 rangeIntersectsNode : function(range, node)
22666 var nodeRange = node.ownerDocument.createRange();
22668 nodeRange.selectNode(node);
22670 nodeRange.selectNodeContents(node);
22673 var rangeStartRange = range.cloneRange();
22674 rangeStartRange.collapse(true);
22676 var rangeEndRange = range.cloneRange();
22677 rangeEndRange.collapse(false);
22679 var nodeStartRange = nodeRange.cloneRange();
22680 nodeStartRange.collapse(true);
22682 var nodeEndRange = nodeRange.cloneRange();
22683 nodeEndRange.collapse(false);
22685 return rangeStartRange.compareBoundaryPoints(
22686 Range.START_TO_START, nodeEndRange) == -1 &&
22687 rangeEndRange.compareBoundaryPoints(
22688 Range.START_TO_START, nodeStartRange) == 1;
22692 rangeCompareNode : function(range, node)
22694 var nodeRange = node.ownerDocument.createRange();
22696 nodeRange.selectNode(node);
22698 nodeRange.selectNodeContents(node);
22702 range.collapse(true);
22704 nodeRange.collapse(true);
22706 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22707 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22709 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22711 var nodeIsBefore = ss == 1;
22712 var nodeIsAfter = ee == -1;
22714 if (nodeIsBefore && nodeIsAfter) {
22717 if (!nodeIsBefore && nodeIsAfter) {
22718 return 1; //right trailed.
22721 if (nodeIsBefore && !nodeIsAfter) {
22722 return 2; // left trailed.
22728 // private? - in a new class?
22729 cleanUpPaste : function()
22731 // cleans up the whole document..
22732 Roo.log('cleanuppaste');
22734 this.cleanUpChildren(this.doc.body);
22735 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22736 if (clean != this.doc.body.innerHTML) {
22737 this.doc.body.innerHTML = clean;
22742 cleanWordChars : function(input) {// change the chars to hex code
22743 var he = Roo.HtmlEditorCore;
22745 var output = input;
22746 Roo.each(he.swapCodes, function(sw) {
22747 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22749 output = output.replace(swapper, sw[1]);
22756 cleanUpChildren : function (n)
22758 if (!n.childNodes.length) {
22761 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22762 this.cleanUpChild(n.childNodes[i]);
22769 cleanUpChild : function (node)
22772 //console.log(node);
22773 if (node.nodeName == "#text") {
22774 // clean up silly Windows -- stuff?
22777 if (node.nodeName == "#comment") {
22778 node.parentNode.removeChild(node);
22779 // clean up silly Windows -- stuff?
22782 var lcname = node.tagName.toLowerCase();
22783 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22784 // whitelist of tags..
22786 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22788 node.parentNode.removeChild(node);
22793 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22795 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22796 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22798 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22799 // remove_keep_children = true;
22802 if (remove_keep_children) {
22803 this.cleanUpChildren(node);
22804 // inserts everything just before this node...
22805 while (node.childNodes.length) {
22806 var cn = node.childNodes[0];
22807 node.removeChild(cn);
22808 node.parentNode.insertBefore(cn, node);
22810 node.parentNode.removeChild(node);
22814 if (!node.attributes || !node.attributes.length) {
22815 this.cleanUpChildren(node);
22819 function cleanAttr(n,v)
22822 if (v.match(/^\./) || v.match(/^\//)) {
22825 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22828 if (v.match(/^#/)) {
22831 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22832 node.removeAttribute(n);
22836 var cwhite = this.cwhite;
22837 var cblack = this.cblack;
22839 function cleanStyle(n,v)
22841 if (v.match(/expression/)) { //XSS?? should we even bother..
22842 node.removeAttribute(n);
22846 var parts = v.split(/;/);
22849 Roo.each(parts, function(p) {
22850 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22854 var l = p.split(':').shift().replace(/\s+/g,'');
22855 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22857 if ( cwhite.length && cblack.indexOf(l) > -1) {
22858 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22859 //node.removeAttribute(n);
22863 // only allow 'c whitelisted system attributes'
22864 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22865 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22866 //node.removeAttribute(n);
22876 if (clean.length) {
22877 node.setAttribute(n, clean.join(';'));
22879 node.removeAttribute(n);
22885 for (var i = node.attributes.length-1; i > -1 ; i--) {
22886 var a = node.attributes[i];
22889 if (a.name.toLowerCase().substr(0,2)=='on') {
22890 node.removeAttribute(a.name);
22893 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22894 node.removeAttribute(a.name);
22897 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22898 cleanAttr(a.name,a.value); // fixme..
22901 if (a.name == 'style') {
22902 cleanStyle(a.name,a.value);
22905 /// clean up MS crap..
22906 // tecnically this should be a list of valid class'es..
22909 if (a.name == 'class') {
22910 if (a.value.match(/^Mso/)) {
22911 node.className = '';
22914 if (a.value.match(/^body$/)) {
22915 node.className = '';
22926 this.cleanUpChildren(node);
22932 * Clean up MS wordisms...
22934 cleanWord : function(node)
22939 this.cleanWord(this.doc.body);
22942 if (node.nodeName == "#text") {
22943 // clean up silly Windows -- stuff?
22946 if (node.nodeName == "#comment") {
22947 node.parentNode.removeChild(node);
22948 // clean up silly Windows -- stuff?
22952 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22953 node.parentNode.removeChild(node);
22957 // remove - but keep children..
22958 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22959 while (node.childNodes.length) {
22960 var cn = node.childNodes[0];
22961 node.removeChild(cn);
22962 node.parentNode.insertBefore(cn, node);
22964 node.parentNode.removeChild(node);
22965 this.iterateChildren(node, this.cleanWord);
22969 if (node.className.length) {
22971 var cn = node.className.split(/\W+/);
22973 Roo.each(cn, function(cls) {
22974 if (cls.match(/Mso[a-zA-Z]+/)) {
22979 node.className = cna.length ? cna.join(' ') : '';
22981 node.removeAttribute("class");
22985 if (node.hasAttribute("lang")) {
22986 node.removeAttribute("lang");
22989 if (node.hasAttribute("style")) {
22991 var styles = node.getAttribute("style").split(";");
22993 Roo.each(styles, function(s) {
22994 if (!s.match(/:/)) {
22997 var kv = s.split(":");
22998 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23001 // what ever is left... we allow.
23004 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23005 if (!nstyle.length) {
23006 node.removeAttribute('style');
23009 this.iterateChildren(node, this.cleanWord);
23015 * iterateChildren of a Node, calling fn each time, using this as the scole..
23016 * @param {DomNode} node node to iterate children of.
23017 * @param {Function} fn method of this class to call on each item.
23019 iterateChildren : function(node, fn)
23021 if (!node.childNodes.length) {
23024 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23025 fn.call(this, node.childNodes[i])
23031 * cleanTableWidths.
23033 * Quite often pasting from word etc.. results in tables with column and widths.
23034 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23037 cleanTableWidths : function(node)
23042 this.cleanTableWidths(this.doc.body);
23047 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23050 Roo.log(node.tagName);
23051 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23052 this.iterateChildren(node, this.cleanTableWidths);
23055 if (node.hasAttribute('width')) {
23056 node.removeAttribute('width');
23060 if (node.hasAttribute("style")) {
23063 var styles = node.getAttribute("style").split(";");
23065 Roo.each(styles, function(s) {
23066 if (!s.match(/:/)) {
23069 var kv = s.split(":");
23070 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23073 // what ever is left... we allow.
23076 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23077 if (!nstyle.length) {
23078 node.removeAttribute('style');
23082 this.iterateChildren(node, this.cleanTableWidths);
23090 domToHTML : function(currentElement, depth, nopadtext) {
23092 depth = depth || 0;
23093 nopadtext = nopadtext || false;
23095 if (!currentElement) {
23096 return this.domToHTML(this.doc.body);
23099 //Roo.log(currentElement);
23101 var allText = false;
23102 var nodeName = currentElement.nodeName;
23103 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23105 if (nodeName == '#text') {
23107 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23112 if (nodeName != 'BODY') {
23115 // Prints the node tagName, such as <A>, <IMG>, etc
23118 for(i = 0; i < currentElement.attributes.length;i++) {
23120 var aname = currentElement.attributes.item(i).name;
23121 if (!currentElement.attributes.item(i).value.length) {
23124 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23127 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23136 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23139 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23144 // Traverse the tree
23146 var currentElementChild = currentElement.childNodes.item(i);
23147 var allText = true;
23148 var innerHTML = '';
23150 while (currentElementChild) {
23151 // Formatting code (indent the tree so it looks nice on the screen)
23152 var nopad = nopadtext;
23153 if (lastnode == 'SPAN') {
23157 if (currentElementChild.nodeName == '#text') {
23158 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23159 toadd = nopadtext ? toadd : toadd.trim();
23160 if (!nopad && toadd.length > 80) {
23161 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23163 innerHTML += toadd;
23166 currentElementChild = currentElement.childNodes.item(i);
23172 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23174 // Recursively traverse the tree structure of the child node
23175 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23176 lastnode = currentElementChild.nodeName;
23178 currentElementChild=currentElement.childNodes.item(i);
23184 // The remaining code is mostly for formatting the tree
23185 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23190 ret+= "</"+tagName+">";
23196 applyBlacklists : function()
23198 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23199 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23203 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23204 if (b.indexOf(tag) > -1) {
23207 this.white.push(tag);
23211 Roo.each(w, function(tag) {
23212 if (b.indexOf(tag) > -1) {
23215 if (this.white.indexOf(tag) > -1) {
23218 this.white.push(tag);
23223 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23224 if (w.indexOf(tag) > -1) {
23227 this.black.push(tag);
23231 Roo.each(b, function(tag) {
23232 if (w.indexOf(tag) > -1) {
23235 if (this.black.indexOf(tag) > -1) {
23238 this.black.push(tag);
23243 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23244 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23248 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23249 if (b.indexOf(tag) > -1) {
23252 this.cwhite.push(tag);
23256 Roo.each(w, function(tag) {
23257 if (b.indexOf(tag) > -1) {
23260 if (this.cwhite.indexOf(tag) > -1) {
23263 this.cwhite.push(tag);
23268 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23269 if (w.indexOf(tag) > -1) {
23272 this.cblack.push(tag);
23276 Roo.each(b, function(tag) {
23277 if (w.indexOf(tag) > -1) {
23280 if (this.cblack.indexOf(tag) > -1) {
23283 this.cblack.push(tag);
23288 setStylesheets : function(stylesheets)
23290 if(typeof(stylesheets) == 'string'){
23291 Roo.get(this.iframe.contentDocument.head).createChild({
23293 rel : 'stylesheet',
23302 Roo.each(stylesheets, function(s) {
23307 Roo.get(_this.iframe.contentDocument.head).createChild({
23309 rel : 'stylesheet',
23318 removeStylesheets : function()
23322 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23327 setStyle : function(style)
23329 Roo.get(this.iframe.contentDocument.head).createChild({
23338 // hide stuff that is not compatible
23352 * @event specialkey
23356 * @cfg {String} fieldClass @hide
23359 * @cfg {String} focusClass @hide
23362 * @cfg {String} autoCreate @hide
23365 * @cfg {String} inputType @hide
23368 * @cfg {String} invalidClass @hide
23371 * @cfg {String} invalidText @hide
23374 * @cfg {String} msgFx @hide
23377 * @cfg {String} validateOnBlur @hide
23381 Roo.HtmlEditorCore.white = [
23382 'area', 'br', 'img', 'input', 'hr', 'wbr',
23384 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23385 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23386 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23387 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23388 'table', 'ul', 'xmp',
23390 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23393 'dir', 'menu', 'ol', 'ul', 'dl',
23399 Roo.HtmlEditorCore.black = [
23400 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23402 'base', 'basefont', 'bgsound', 'blink', 'body',
23403 'frame', 'frameset', 'head', 'html', 'ilayer',
23404 'iframe', 'layer', 'link', 'meta', 'object',
23405 'script', 'style' ,'title', 'xml' // clean later..
23407 Roo.HtmlEditorCore.clean = [
23408 'script', 'style', 'title', 'xml'
23410 Roo.HtmlEditorCore.remove = [
23415 Roo.HtmlEditorCore.ablack = [
23419 Roo.HtmlEditorCore.aclean = [
23420 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23424 Roo.HtmlEditorCore.pwhite= [
23425 'http', 'https', 'mailto'
23428 // white listed style attributes.
23429 Roo.HtmlEditorCore.cwhite= [
23430 // 'text-align', /// default is to allow most things..
23436 // black listed style attributes.
23437 Roo.HtmlEditorCore.cblack= [
23438 // 'font-size' -- this can be set by the project
23442 Roo.HtmlEditorCore.swapCodes =[
23461 * @class Roo.bootstrap.HtmlEditor
23462 * @extends Roo.bootstrap.TextArea
23463 * Bootstrap HtmlEditor class
23466 * Create a new HtmlEditor
23467 * @param {Object} config The config object
23470 Roo.bootstrap.HtmlEditor = function(config){
23471 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23472 if (!this.toolbars) {
23473 this.toolbars = [];
23476 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23479 * @event initialize
23480 * Fires when the editor is fully initialized (including the iframe)
23481 * @param {HtmlEditor} this
23486 * Fires when the editor is first receives the focus. Any insertion must wait
23487 * until after this event.
23488 * @param {HtmlEditor} this
23492 * @event beforesync
23493 * Fires before the textarea is updated with content from the editor iframe. Return false
23494 * to cancel the sync.
23495 * @param {HtmlEditor} this
23496 * @param {String} html
23500 * @event beforepush
23501 * Fires before the iframe editor is updated with content from the textarea. Return false
23502 * to cancel the push.
23503 * @param {HtmlEditor} this
23504 * @param {String} html
23509 * Fires when the textarea is updated with content from the editor iframe.
23510 * @param {HtmlEditor} this
23511 * @param {String} html
23516 * Fires when the iframe editor is updated with content from the textarea.
23517 * @param {HtmlEditor} this
23518 * @param {String} html
23522 * @event editmodechange
23523 * Fires when the editor switches edit modes
23524 * @param {HtmlEditor} this
23525 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23527 editmodechange: true,
23529 * @event editorevent
23530 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23531 * @param {HtmlEditor} this
23535 * @event firstfocus
23536 * Fires when on first focus - needed by toolbars..
23537 * @param {HtmlEditor} this
23542 * Auto save the htmlEditor value as a file into Events
23543 * @param {HtmlEditor} this
23547 * @event savedpreview
23548 * preview the saved version of htmlEditor
23549 * @param {HtmlEditor} this
23556 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23560 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23565 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23570 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23575 * @cfg {Number} height (in pixels)
23579 * @cfg {Number} width (in pixels)
23584 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23587 stylesheets: false,
23592 // private properties
23593 validationEvent : false,
23595 initialized : false,
23598 onFocus : Roo.emptyFn,
23600 hideMode:'offsets',
23602 tbContainer : false,
23606 toolbarContainer :function() {
23607 return this.wrap.select('.x-html-editor-tb',true).first();
23611 * Protected method that will not generally be called directly. It
23612 * is called when the editor creates its toolbar. Override this method if you need to
23613 * add custom toolbar buttons.
23614 * @param {HtmlEditor} editor
23616 createToolbar : function(){
23617 Roo.log('renewing');
23618 Roo.log("create toolbars");
23620 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23621 this.toolbars[0].render(this.toolbarContainer());
23625 // if (!editor.toolbars || !editor.toolbars.length) {
23626 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23629 // for (var i =0 ; i < editor.toolbars.length;i++) {
23630 // editor.toolbars[i] = Roo.factory(
23631 // typeof(editor.toolbars[i]) == 'string' ?
23632 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23633 // Roo.bootstrap.HtmlEditor);
23634 // editor.toolbars[i].init(editor);
23640 onRender : function(ct, position)
23642 // Roo.log("Call onRender: " + this.xtype);
23644 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23646 this.wrap = this.inputEl().wrap({
23647 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23650 this.editorcore.onRender(ct, position);
23652 if (this.resizable) {
23653 this.resizeEl = new Roo.Resizable(this.wrap, {
23657 minHeight : this.height,
23658 height: this.height,
23659 handles : this.resizable,
23662 resize : function(r, w, h) {
23663 _t.onResize(w,h); // -something
23669 this.createToolbar(this);
23672 if(!this.width && this.resizable){
23673 this.setSize(this.wrap.getSize());
23675 if (this.resizeEl) {
23676 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23677 // should trigger onReize..
23683 onResize : function(w, h)
23685 Roo.log('resize: ' +w + ',' + h );
23686 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23690 if(this.inputEl() ){
23691 if(typeof w == 'number'){
23692 var aw = w - this.wrap.getFrameWidth('lr');
23693 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23696 if(typeof h == 'number'){
23697 var tbh = -11; // fixme it needs to tool bar size!
23698 for (var i =0; i < this.toolbars.length;i++) {
23699 // fixme - ask toolbars for heights?
23700 tbh += this.toolbars[i].el.getHeight();
23701 //if (this.toolbars[i].footer) {
23702 // tbh += this.toolbars[i].footer.el.getHeight();
23710 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23711 ah -= 5; // knock a few pixes off for look..
23712 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23716 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23717 this.editorcore.onResize(ew,eh);
23722 * Toggles the editor between standard and source edit mode.
23723 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23725 toggleSourceEdit : function(sourceEditMode)
23727 this.editorcore.toggleSourceEdit(sourceEditMode);
23729 if(this.editorcore.sourceEditMode){
23730 Roo.log('editor - showing textarea');
23733 // Roo.log(this.syncValue());
23735 this.inputEl().removeClass(['hide', 'x-hidden']);
23736 this.inputEl().dom.removeAttribute('tabIndex');
23737 this.inputEl().focus();
23739 Roo.log('editor - hiding textarea');
23741 // Roo.log(this.pushValue());
23744 this.inputEl().addClass(['hide', 'x-hidden']);
23745 this.inputEl().dom.setAttribute('tabIndex', -1);
23746 //this.deferFocus();
23749 if(this.resizable){
23750 this.setSize(this.wrap.getSize());
23753 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23756 // private (for BoxComponent)
23757 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23759 // private (for BoxComponent)
23760 getResizeEl : function(){
23764 // private (for BoxComponent)
23765 getPositionEl : function(){
23770 initEvents : function(){
23771 this.originalValue = this.getValue();
23775 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23778 // markInvalid : Roo.emptyFn,
23780 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23783 // clearInvalid : Roo.emptyFn,
23785 setValue : function(v){
23786 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23787 this.editorcore.pushValue();
23792 deferFocus : function(){
23793 this.focus.defer(10, this);
23797 focus : function(){
23798 this.editorcore.focus();
23804 onDestroy : function(){
23810 for (var i =0; i < this.toolbars.length;i++) {
23811 // fixme - ask toolbars for heights?
23812 this.toolbars[i].onDestroy();
23815 this.wrap.dom.innerHTML = '';
23816 this.wrap.remove();
23821 onFirstFocus : function(){
23822 //Roo.log("onFirstFocus");
23823 this.editorcore.onFirstFocus();
23824 for (var i =0; i < this.toolbars.length;i++) {
23825 this.toolbars[i].onFirstFocus();
23831 syncValue : function()
23833 this.editorcore.syncValue();
23836 pushValue : function()
23838 this.editorcore.pushValue();
23842 // hide stuff that is not compatible
23856 * @event specialkey
23860 * @cfg {String} fieldClass @hide
23863 * @cfg {String} focusClass @hide
23866 * @cfg {String} autoCreate @hide
23869 * @cfg {String} inputType @hide
23872 * @cfg {String} invalidClass @hide
23875 * @cfg {String} invalidText @hide
23878 * @cfg {String} msgFx @hide
23881 * @cfg {String} validateOnBlur @hide
23890 Roo.namespace('Roo.bootstrap.htmleditor');
23892 * @class Roo.bootstrap.HtmlEditorToolbar1
23897 new Roo.bootstrap.HtmlEditor({
23900 new Roo.bootstrap.HtmlEditorToolbar1({
23901 disable : { fonts: 1 , format: 1, ..., ... , ...],
23907 * @cfg {Object} disable List of elements to disable..
23908 * @cfg {Array} btns List of additional buttons.
23912 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23915 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23918 Roo.apply(this, config);
23920 // default disabled, based on 'good practice'..
23921 this.disable = this.disable || {};
23922 Roo.applyIf(this.disable, {
23925 specialElements : true
23927 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23929 this.editor = config.editor;
23930 this.editorcore = config.editor.editorcore;
23932 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23934 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23935 // dont call parent... till later.
23937 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23942 editorcore : false,
23947 "h1","h2","h3","h4","h5","h6",
23949 "abbr", "acronym", "address", "cite", "samp", "var",
23953 onRender : function(ct, position)
23955 // Roo.log("Call onRender: " + this.xtype);
23957 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23959 this.el.dom.style.marginBottom = '0';
23961 var editorcore = this.editorcore;
23962 var editor= this.editor;
23965 var btn = function(id,cmd , toggle, handler, html){
23967 var event = toggle ? 'toggle' : 'click';
23972 xns: Roo.bootstrap,
23976 enableToggle:toggle !== false,
23978 pressed : toggle ? false : null,
23981 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23982 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23988 // var cb_box = function...
23993 xns: Roo.bootstrap,
23998 xns: Roo.bootstrap,
24002 Roo.each(this.formats, function(f) {
24003 style.menu.items.push({
24005 xns: Roo.bootstrap,
24006 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24011 editorcore.insertTag(this.tagname);
24018 children.push(style);
24020 btn('bold',false,true);
24021 btn('italic',false,true);
24022 btn('align-left', 'justifyleft',true);
24023 btn('align-center', 'justifycenter',true);
24024 btn('align-right' , 'justifyright',true);
24025 btn('link', false, false, function(btn) {
24026 //Roo.log("create link?");
24027 var url = prompt(this.createLinkText, this.defaultLinkValue);
24028 if(url && url != 'http:/'+'/'){
24029 this.editorcore.relayCmd('createlink', url);
24032 btn('list','insertunorderedlist',true);
24033 btn('pencil', false,true, function(btn){
24035 this.toggleSourceEdit(btn.pressed);
24038 if (this.editor.btns.length > 0) {
24039 for (var i = 0; i<this.editor.btns.length; i++) {
24040 children.push(this.editor.btns[i]);
24048 xns: Roo.bootstrap,
24053 xns: Roo.bootstrap,
24058 cog.menu.items.push({
24060 xns: Roo.bootstrap,
24061 html : Clean styles,
24066 editorcore.insertTag(this.tagname);
24075 this.xtype = 'NavSimplebar';
24077 for(var i=0;i< children.length;i++) {
24079 this.buttons.add(this.addxtypeChild(children[i]));
24083 editor.on('editorevent', this.updateToolbar, this);
24085 onBtnClick : function(id)
24087 this.editorcore.relayCmd(id);
24088 this.editorcore.focus();
24092 * Protected method that will not generally be called directly. It triggers
24093 * a toolbar update by reading the markup state of the current selection in the editor.
24095 updateToolbar: function(){
24097 if(!this.editorcore.activated){
24098 this.editor.onFirstFocus(); // is this neeed?
24102 var btns = this.buttons;
24103 var doc = this.editorcore.doc;
24104 btns.get('bold').setActive(doc.queryCommandState('bold'));
24105 btns.get('italic').setActive(doc.queryCommandState('italic'));
24106 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24108 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24109 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24110 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24112 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24113 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24116 var ans = this.editorcore.getAllAncestors();
24117 if (this.formatCombo) {
24120 var store = this.formatCombo.store;
24121 this.formatCombo.setValue("");
24122 for (var i =0; i < ans.length;i++) {
24123 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24125 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24133 // hides menus... - so this cant be on a menu...
24134 Roo.bootstrap.MenuMgr.hideAll();
24136 Roo.bootstrap.MenuMgr.hideAll();
24137 //this.editorsyncValue();
24139 onFirstFocus: function() {
24140 this.buttons.each(function(item){
24144 toggleSourceEdit : function(sourceEditMode){
24147 if(sourceEditMode){
24148 Roo.log("disabling buttons");
24149 this.buttons.each( function(item){
24150 if(item.cmd != 'pencil'){
24156 Roo.log("enabling buttons");
24157 if(this.editorcore.initialized){
24158 this.buttons.each( function(item){
24164 Roo.log("calling toggole on editor");
24165 // tell the editor that it's been pressed..
24166 this.editor.toggleSourceEdit(sourceEditMode);
24176 * @class Roo.bootstrap.Table.AbstractSelectionModel
24177 * @extends Roo.util.Observable
24178 * Abstract base class for grid SelectionModels. It provides the interface that should be
24179 * implemented by descendant classes. This class should not be directly instantiated.
24182 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24183 this.locked = false;
24184 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24188 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24189 /** @ignore Called by the grid automatically. Do not call directly. */
24190 init : function(grid){
24196 * Locks the selections.
24199 this.locked = true;
24203 * Unlocks the selections.
24205 unlock : function(){
24206 this.locked = false;
24210 * Returns true if the selections are locked.
24211 * @return {Boolean}
24213 isLocked : function(){
24214 return this.locked;
24218 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24219 * @class Roo.bootstrap.Table.RowSelectionModel
24220 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24221 * It supports multiple selections and keyboard selection/navigation.
24223 * @param {Object} config
24226 Roo.bootstrap.Table.RowSelectionModel = function(config){
24227 Roo.apply(this, config);
24228 this.selections = new Roo.util.MixedCollection(false, function(o){
24233 this.lastActive = false;
24237 * @event selectionchange
24238 * Fires when the selection changes
24239 * @param {SelectionModel} this
24241 "selectionchange" : true,
24243 * @event afterselectionchange
24244 * Fires after the selection changes (eg. by key press or clicking)
24245 * @param {SelectionModel} this
24247 "afterselectionchange" : true,
24249 * @event beforerowselect
24250 * Fires when a row is selected being selected, return false to cancel.
24251 * @param {SelectionModel} this
24252 * @param {Number} rowIndex The selected index
24253 * @param {Boolean} keepExisting False if other selections will be cleared
24255 "beforerowselect" : true,
24258 * Fires when a row is selected.
24259 * @param {SelectionModel} this
24260 * @param {Number} rowIndex The selected index
24261 * @param {Roo.data.Record} r The record
24263 "rowselect" : true,
24265 * @event rowdeselect
24266 * Fires when a row is deselected.
24267 * @param {SelectionModel} this
24268 * @param {Number} rowIndex The selected index
24270 "rowdeselect" : true
24272 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24273 this.locked = false;
24276 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24278 * @cfg {Boolean} singleSelect
24279 * True to allow selection of only one row at a time (defaults to false)
24281 singleSelect : false,
24284 initEvents : function()
24287 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24288 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24289 //}else{ // allow click to work like normal
24290 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24292 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24293 this.grid.on("rowclick", this.handleMouseDown, this);
24295 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24296 "up" : function(e){
24298 this.selectPrevious(e.shiftKey);
24299 }else if(this.last !== false && this.lastActive !== false){
24300 var last = this.last;
24301 this.selectRange(this.last, this.lastActive-1);
24302 this.grid.getView().focusRow(this.lastActive);
24303 if(last !== false){
24307 this.selectFirstRow();
24309 this.fireEvent("afterselectionchange", this);
24311 "down" : function(e){
24313 this.selectNext(e.shiftKey);
24314 }else if(this.last !== false && this.lastActive !== false){
24315 var last = this.last;
24316 this.selectRange(this.last, this.lastActive+1);
24317 this.grid.getView().focusRow(this.lastActive);
24318 if(last !== false){
24322 this.selectFirstRow();
24324 this.fireEvent("afterselectionchange", this);
24328 this.grid.store.on('load', function(){
24329 this.selections.clear();
24332 var view = this.grid.view;
24333 view.on("refresh", this.onRefresh, this);
24334 view.on("rowupdated", this.onRowUpdated, this);
24335 view.on("rowremoved", this.onRemove, this);
24340 onRefresh : function()
24342 var ds = this.grid.store, i, v = this.grid.view;
24343 var s = this.selections;
24344 s.each(function(r){
24345 if((i = ds.indexOfId(r.id)) != -1){
24354 onRemove : function(v, index, r){
24355 this.selections.remove(r);
24359 onRowUpdated : function(v, index, r){
24360 if(this.isSelected(r)){
24361 v.onRowSelect(index);
24367 * @param {Array} records The records to select
24368 * @param {Boolean} keepExisting (optional) True to keep existing selections
24370 selectRecords : function(records, keepExisting)
24373 this.clearSelections();
24375 var ds = this.grid.store;
24376 for(var i = 0, len = records.length; i < len; i++){
24377 this.selectRow(ds.indexOf(records[i]), true);
24382 * Gets the number of selected rows.
24385 getCount : function(){
24386 return this.selections.length;
24390 * Selects the first row in the grid.
24392 selectFirstRow : function(){
24397 * Select the last row.
24398 * @param {Boolean} keepExisting (optional) True to keep existing selections
24400 selectLastRow : function(keepExisting){
24401 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24402 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24406 * Selects the row immediately following the last selected row.
24407 * @param {Boolean} keepExisting (optional) True to keep existing selections
24409 selectNext : function(keepExisting)
24411 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24412 this.selectRow(this.last+1, keepExisting);
24413 this.grid.getView().focusRow(this.last);
24418 * Selects the row that precedes the last selected row.
24419 * @param {Boolean} keepExisting (optional) True to keep existing selections
24421 selectPrevious : function(keepExisting){
24423 this.selectRow(this.last-1, keepExisting);
24424 this.grid.getView().focusRow(this.last);
24429 * Returns the selected records
24430 * @return {Array} Array of selected records
24432 getSelections : function(){
24433 return [].concat(this.selections.items);
24437 * Returns the first selected record.
24440 getSelected : function(){
24441 return this.selections.itemAt(0);
24446 * Clears all selections.
24448 clearSelections : function(fast)
24454 var ds = this.grid.store;
24455 var s = this.selections;
24456 s.each(function(r){
24457 this.deselectRow(ds.indexOfId(r.id));
24461 this.selections.clear();
24468 * Selects all rows.
24470 selectAll : function(){
24474 this.selections.clear();
24475 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24476 this.selectRow(i, true);
24481 * Returns True if there is a selection.
24482 * @return {Boolean}
24484 hasSelection : function(){
24485 return this.selections.length > 0;
24489 * Returns True if the specified row is selected.
24490 * @param {Number/Record} record The record or index of the record to check
24491 * @return {Boolean}
24493 isSelected : function(index){
24494 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24495 return (r && this.selections.key(r.id) ? true : false);
24499 * Returns True if the specified record id is selected.
24500 * @param {String} id The id of record to check
24501 * @return {Boolean}
24503 isIdSelected : function(id){
24504 return (this.selections.key(id) ? true : false);
24509 handleMouseDBClick : function(e, t){
24513 handleMouseDown : function(e, t)
24515 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24516 if(this.isLocked() || rowIndex < 0 ){
24519 if(e.shiftKey && this.last !== false){
24520 var last = this.last;
24521 this.selectRange(last, rowIndex, e.ctrlKey);
24522 this.last = last; // reset the last
24526 var isSelected = this.isSelected(rowIndex);
24527 //Roo.log("select row:" + rowIndex);
24529 this.deselectRow(rowIndex);
24531 this.selectRow(rowIndex, true);
24535 if(e.button !== 0 && isSelected){
24536 alert('rowIndex 2: ' + rowIndex);
24537 view.focusRow(rowIndex);
24538 }else if(e.ctrlKey && isSelected){
24539 this.deselectRow(rowIndex);
24540 }else if(!isSelected){
24541 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24542 view.focusRow(rowIndex);
24546 this.fireEvent("afterselectionchange", this);
24549 handleDragableRowClick : function(grid, rowIndex, e)
24551 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24552 this.selectRow(rowIndex, false);
24553 grid.view.focusRow(rowIndex);
24554 this.fireEvent("afterselectionchange", this);
24559 * Selects multiple rows.
24560 * @param {Array} rows Array of the indexes of the row to select
24561 * @param {Boolean} keepExisting (optional) True to keep existing selections
24563 selectRows : function(rows, keepExisting){
24565 this.clearSelections();
24567 for(var i = 0, len = rows.length; i < len; i++){
24568 this.selectRow(rows[i], true);
24573 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24574 * @param {Number} startRow The index of the first row in the range
24575 * @param {Number} endRow The index of the last row in the range
24576 * @param {Boolean} keepExisting (optional) True to retain existing selections
24578 selectRange : function(startRow, endRow, keepExisting){
24583 this.clearSelections();
24585 if(startRow <= endRow){
24586 for(var i = startRow; i <= endRow; i++){
24587 this.selectRow(i, true);
24590 for(var i = startRow; i >= endRow; i--){
24591 this.selectRow(i, true);
24597 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24598 * @param {Number} startRow The index of the first row in the range
24599 * @param {Number} endRow The index of the last row in the range
24601 deselectRange : function(startRow, endRow, preventViewNotify){
24605 for(var i = startRow; i <= endRow; i++){
24606 this.deselectRow(i, preventViewNotify);
24612 * @param {Number} row The index of the row to select
24613 * @param {Boolean} keepExisting (optional) True to keep existing selections
24615 selectRow : function(index, keepExisting, preventViewNotify)
24617 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24620 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24621 if(!keepExisting || this.singleSelect){
24622 this.clearSelections();
24625 var r = this.grid.store.getAt(index);
24626 //console.log('selectRow - record id :' + r.id);
24628 this.selections.add(r);
24629 this.last = this.lastActive = index;
24630 if(!preventViewNotify){
24631 var proxy = new Roo.Element(
24632 this.grid.getRowDom(index)
24634 proxy.addClass('bg-info info');
24636 this.fireEvent("rowselect", this, index, r);
24637 this.fireEvent("selectionchange", this);
24643 * @param {Number} row The index of the row to deselect
24645 deselectRow : function(index, preventViewNotify)
24650 if(this.last == index){
24653 if(this.lastActive == index){
24654 this.lastActive = false;
24657 var r = this.grid.store.getAt(index);
24662 this.selections.remove(r);
24663 //.console.log('deselectRow - record id :' + r.id);
24664 if(!preventViewNotify){
24666 var proxy = new Roo.Element(
24667 this.grid.getRowDom(index)
24669 proxy.removeClass('bg-info info');
24671 this.fireEvent("rowdeselect", this, index);
24672 this.fireEvent("selectionchange", this);
24676 restoreLast : function(){
24678 this.last = this._last;
24683 acceptsNav : function(row, col, cm){
24684 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24688 onEditorKey : function(field, e){
24689 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24694 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24696 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24698 }else if(k == e.ENTER && !e.ctrlKey){
24702 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24704 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24706 }else if(k == e.ESC){
24710 g.startEditing(newCell[0], newCell[1]);
24716 * Ext JS Library 1.1.1
24717 * Copyright(c) 2006-2007, Ext JS, LLC.
24719 * Originally Released Under LGPL - original licence link has changed is not relivant.
24722 * <script type="text/javascript">
24726 * @class Roo.bootstrap.PagingToolbar
24727 * @extends Roo.bootstrap.NavSimplebar
24728 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24730 * Create a new PagingToolbar
24731 * @param {Object} config The config object
24732 * @param {Roo.data.Store} store
24734 Roo.bootstrap.PagingToolbar = function(config)
24736 // old args format still supported... - xtype is prefered..
24737 // created from xtype...
24739 this.ds = config.dataSource;
24741 if (config.store && !this.ds) {
24742 this.store= Roo.factory(config.store, Roo.data);
24743 this.ds = this.store;
24744 this.ds.xmodule = this.xmodule || false;
24747 this.toolbarItems = [];
24748 if (config.items) {
24749 this.toolbarItems = config.items;
24752 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24757 this.bind(this.ds);
24760 if (Roo.bootstrap.version == 4) {
24761 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24763 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24768 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24770 * @cfg {Roo.data.Store} dataSource
24771 * The underlying data store providing the paged data
24774 * @cfg {String/HTMLElement/Element} container
24775 * container The id or element that will contain the toolbar
24778 * @cfg {Boolean} displayInfo
24779 * True to display the displayMsg (defaults to false)
24782 * @cfg {Number} pageSize
24783 * The number of records to display per page (defaults to 20)
24787 * @cfg {String} displayMsg
24788 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24790 displayMsg : 'Displaying {0} - {1} of {2}',
24792 * @cfg {String} emptyMsg
24793 * The message to display when no records are found (defaults to "No data to display")
24795 emptyMsg : 'No data to display',
24797 * Customizable piece of the default paging text (defaults to "Page")
24800 beforePageText : "Page",
24802 * Customizable piece of the default paging text (defaults to "of %0")
24805 afterPageText : "of {0}",
24807 * Customizable piece of the default paging text (defaults to "First Page")
24810 firstText : "First Page",
24812 * Customizable piece of the default paging text (defaults to "Previous Page")
24815 prevText : "Previous Page",
24817 * Customizable piece of the default paging text (defaults to "Next Page")
24820 nextText : "Next Page",
24822 * Customizable piece of the default paging text (defaults to "Last Page")
24825 lastText : "Last Page",
24827 * Customizable piece of the default paging text (defaults to "Refresh")
24830 refreshText : "Refresh",
24834 onRender : function(ct, position)
24836 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24837 this.navgroup.parentId = this.id;
24838 this.navgroup.onRender(this.el, null);
24839 // add the buttons to the navgroup
24841 if(this.displayInfo){
24842 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24843 this.displayEl = this.el.select('.x-paging-info', true).first();
24844 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24845 // this.displayEl = navel.el.select('span',true).first();
24851 Roo.each(_this.buttons, function(e){ // this might need to use render????
24852 Roo.factory(e).render(_this.el);
24856 Roo.each(_this.toolbarItems, function(e) {
24857 _this.navgroup.addItem(e);
24861 this.first = this.navgroup.addItem({
24862 tooltip: this.firstText,
24863 cls: "prev btn-outline-secondary",
24864 html : ' <i class="fa fa-step-backward"></i>',
24866 preventDefault: true,
24867 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24870 this.prev = this.navgroup.addItem({
24871 tooltip: this.prevText,
24872 cls: "prev btn-outline-secondary",
24873 html : ' <i class="fa fa-backward"></i>',
24875 preventDefault: true,
24876 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24878 //this.addSeparator();
24881 var field = this.navgroup.addItem( {
24883 cls : 'x-paging-position btn-outline-secondary',
24885 html : this.beforePageText +
24886 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24887 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24890 this.field = field.el.select('input', true).first();
24891 this.field.on("keydown", this.onPagingKeydown, this);
24892 this.field.on("focus", function(){this.dom.select();});
24895 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24896 //this.field.setHeight(18);
24897 //this.addSeparator();
24898 this.next = this.navgroup.addItem({
24899 tooltip: this.nextText,
24900 cls: "next btn-outline-secondary",
24901 html : ' <i class="fa fa-forward"></i>',
24903 preventDefault: true,
24904 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24906 this.last = this.navgroup.addItem({
24907 tooltip: this.lastText,
24908 html : ' <i class="fa fa-step-forward"></i>',
24909 cls: "next btn-outline-secondary",
24911 preventDefault: true,
24912 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24914 //this.addSeparator();
24915 this.loading = this.navgroup.addItem({
24916 tooltip: this.refreshText,
24917 cls: "btn-outline-secondary",
24918 html : ' <i class="fa fa-refresh"></i>',
24919 preventDefault: true,
24920 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24926 updateInfo : function(){
24927 if(this.displayEl){
24928 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24929 var msg = count == 0 ?
24933 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24935 this.displayEl.update(msg);
24940 onLoad : function(ds, r, o)
24942 this.cursor = o.params.start ? o.params.start : 0;
24944 var d = this.getPageData(),
24949 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24950 this.field.dom.value = ap;
24951 this.first.setDisabled(ap == 1);
24952 this.prev.setDisabled(ap == 1);
24953 this.next.setDisabled(ap == ps);
24954 this.last.setDisabled(ap == ps);
24955 this.loading.enable();
24960 getPageData : function(){
24961 var total = this.ds.getTotalCount();
24964 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24965 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24970 onLoadError : function(){
24971 this.loading.enable();
24975 onPagingKeydown : function(e){
24976 var k = e.getKey();
24977 var d = this.getPageData();
24979 var v = this.field.dom.value, pageNum;
24980 if(!v || isNaN(pageNum = parseInt(v, 10))){
24981 this.field.dom.value = d.activePage;
24984 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24985 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24988 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))
24990 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24991 this.field.dom.value = pageNum;
24992 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24995 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24997 var v = this.field.dom.value, pageNum;
24998 var increment = (e.shiftKey) ? 10 : 1;
24999 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25002 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25003 this.field.dom.value = d.activePage;
25006 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25008 this.field.dom.value = parseInt(v, 10) + increment;
25009 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25010 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25017 beforeLoad : function(){
25019 this.loading.disable();
25024 onClick : function(which){
25033 ds.load({params:{start: 0, limit: this.pageSize}});
25036 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25039 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25042 var total = ds.getTotalCount();
25043 var extra = total % this.pageSize;
25044 var lastStart = extra ? (total - extra) : total-this.pageSize;
25045 ds.load({params:{start: lastStart, limit: this.pageSize}});
25048 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25054 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25055 * @param {Roo.data.Store} store The data store to unbind
25057 unbind : function(ds){
25058 ds.un("beforeload", this.beforeLoad, this);
25059 ds.un("load", this.onLoad, this);
25060 ds.un("loadexception", this.onLoadError, this);
25061 ds.un("remove", this.updateInfo, this);
25062 ds.un("add", this.updateInfo, this);
25063 this.ds = undefined;
25067 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25068 * @param {Roo.data.Store} store The data store to bind
25070 bind : function(ds){
25071 ds.on("beforeload", this.beforeLoad, this);
25072 ds.on("load", this.onLoad, this);
25073 ds.on("loadexception", this.onLoadError, this);
25074 ds.on("remove", this.updateInfo, this);
25075 ds.on("add", this.updateInfo, this);
25086 * @class Roo.bootstrap.MessageBar
25087 * @extends Roo.bootstrap.Component
25088 * Bootstrap MessageBar class
25089 * @cfg {String} html contents of the MessageBar
25090 * @cfg {String} weight (info | success | warning | danger) default info
25091 * @cfg {String} beforeClass insert the bar before the given class
25092 * @cfg {Boolean} closable (true | false) default false
25093 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25096 * Create a new Element
25097 * @param {Object} config The config object
25100 Roo.bootstrap.MessageBar = function(config){
25101 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25104 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25110 beforeClass: 'bootstrap-sticky-wrap',
25112 getAutoCreate : function(){
25116 cls: 'alert alert-dismissable alert-' + this.weight,
25121 html: this.html || ''
25127 cfg.cls += ' alert-messages-fixed';
25141 onRender : function(ct, position)
25143 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25146 var cfg = Roo.apply({}, this.getAutoCreate());
25150 cfg.cls += ' ' + this.cls;
25153 cfg.style = this.style;
25155 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25157 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25160 this.el.select('>button.close').on('click', this.hide, this);
25166 if (!this.rendered) {
25172 this.fireEvent('show', this);
25178 if (!this.rendered) {
25184 this.fireEvent('hide', this);
25187 update : function()
25189 // var e = this.el.dom.firstChild;
25191 // if(this.closable){
25192 // e = e.nextSibling;
25195 // e.data = this.html || '';
25197 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25213 * @class Roo.bootstrap.Graph
25214 * @extends Roo.bootstrap.Component
25215 * Bootstrap Graph class
25219 @cfg {String} graphtype bar | vbar | pie
25220 @cfg {number} g_x coodinator | centre x (pie)
25221 @cfg {number} g_y coodinator | centre y (pie)
25222 @cfg {number} g_r radius (pie)
25223 @cfg {number} g_height height of the chart (respected by all elements in the set)
25224 @cfg {number} g_width width of the chart (respected by all elements in the set)
25225 @cfg {Object} title The title of the chart
25228 -opts (object) options for the chart
25230 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25231 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25233 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.
25234 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25236 o stretch (boolean)
25238 -opts (object) options for the pie
25241 o startAngle (number)
25242 o endAngle (number)
25246 * Create a new Input
25247 * @param {Object} config The config object
25250 Roo.bootstrap.Graph = function(config){
25251 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25257 * The img click event for the img.
25258 * @param {Roo.EventObject} e
25264 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25275 //g_colors: this.colors,
25282 getAutoCreate : function(){
25293 onRender : function(ct,position){
25296 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25298 if (typeof(Raphael) == 'undefined') {
25299 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25303 this.raphael = Raphael(this.el.dom);
25305 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25306 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25307 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25308 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25310 r.text(160, 10, "Single Series Chart").attr(txtattr);
25311 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25312 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25313 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25315 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25316 r.barchart(330, 10, 300, 220, data1);
25317 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25318 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25321 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25322 // r.barchart(30, 30, 560, 250, xdata, {
25323 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25324 // axis : "0 0 1 1",
25325 // axisxlabels : xdata
25326 // //yvalues : cols,
25329 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25331 // this.load(null,xdata,{
25332 // axis : "0 0 1 1",
25333 // axisxlabels : xdata
25338 load : function(graphtype,xdata,opts)
25340 this.raphael.clear();
25342 graphtype = this.graphtype;
25347 var r = this.raphael,
25348 fin = function () {
25349 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25351 fout = function () {
25352 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25354 pfin = function() {
25355 this.sector.stop();
25356 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25359 this.label[0].stop();
25360 this.label[0].attr({ r: 7.5 });
25361 this.label[1].attr({ "font-weight": 800 });
25364 pfout = function() {
25365 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25368 this.label[0].animate({ r: 5 }, 500, "bounce");
25369 this.label[1].attr({ "font-weight": 400 });
25375 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25378 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25381 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25382 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25384 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25391 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25396 setTitle: function(o)
25401 initEvents: function() {
25404 this.el.on('click', this.onClick, this);
25408 onClick : function(e)
25410 Roo.log('img onclick');
25411 this.fireEvent('click', this, e);
25423 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25426 * @class Roo.bootstrap.dash.NumberBox
25427 * @extends Roo.bootstrap.Component
25428 * Bootstrap NumberBox class
25429 * @cfg {String} headline Box headline
25430 * @cfg {String} content Box content
25431 * @cfg {String} icon Box icon
25432 * @cfg {String} footer Footer text
25433 * @cfg {String} fhref Footer href
25436 * Create a new NumberBox
25437 * @param {Object} config The config object
25441 Roo.bootstrap.dash.NumberBox = function(config){
25442 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25446 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25455 getAutoCreate : function(){
25459 cls : 'small-box ',
25467 cls : 'roo-headline',
25468 html : this.headline
25472 cls : 'roo-content',
25473 html : this.content
25487 cls : 'ion ' + this.icon
25496 cls : 'small-box-footer',
25497 href : this.fhref || '#',
25501 cfg.cn.push(footer);
25508 onRender : function(ct,position){
25509 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25516 setHeadline: function (value)
25518 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25521 setFooter: function (value, href)
25523 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25526 this.el.select('a.small-box-footer',true).first().attr('href', href);
25531 setContent: function (value)
25533 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25536 initEvents: function()
25550 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25553 * @class Roo.bootstrap.dash.TabBox
25554 * @extends Roo.bootstrap.Component
25555 * Bootstrap TabBox class
25556 * @cfg {String} title Title of the TabBox
25557 * @cfg {String} icon Icon of the TabBox
25558 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25559 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25562 * Create a new TabBox
25563 * @param {Object} config The config object
25567 Roo.bootstrap.dash.TabBox = function(config){
25568 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25573 * When a pane is added
25574 * @param {Roo.bootstrap.dash.TabPane} pane
25578 * @event activatepane
25579 * When a pane is activated
25580 * @param {Roo.bootstrap.dash.TabPane} pane
25582 "activatepane" : true
25590 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25595 tabScrollable : false,
25597 getChildContainer : function()
25599 return this.el.select('.tab-content', true).first();
25602 getAutoCreate : function(){
25606 cls: 'pull-left header',
25614 cls: 'fa ' + this.icon
25620 cls: 'nav nav-tabs pull-right',
25626 if(this.tabScrollable){
25633 cls: 'nav nav-tabs pull-right',
25644 cls: 'nav-tabs-custom',
25649 cls: 'tab-content no-padding',
25657 initEvents : function()
25659 //Roo.log('add add pane handler');
25660 this.on('addpane', this.onAddPane, this);
25663 * Updates the box title
25664 * @param {String} html to set the title to.
25666 setTitle : function(value)
25668 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25670 onAddPane : function(pane)
25672 this.panes.push(pane);
25673 //Roo.log('addpane');
25675 // tabs are rendere left to right..
25676 if(!this.showtabs){
25680 var ctr = this.el.select('.nav-tabs', true).first();
25683 var existing = ctr.select('.nav-tab',true);
25684 var qty = existing.getCount();;
25687 var tab = ctr.createChild({
25689 cls : 'nav-tab' + (qty ? '' : ' active'),
25697 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25700 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25702 pane.el.addClass('active');
25707 onTabClick : function(ev,un,ob,pane)
25709 //Roo.log('tab - prev default');
25710 ev.preventDefault();
25713 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25714 pane.tab.addClass('active');
25715 //Roo.log(pane.title);
25716 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25717 // technically we should have a deactivate event.. but maybe add later.
25718 // and it should not de-activate the selected tab...
25719 this.fireEvent('activatepane', pane);
25720 pane.el.addClass('active');
25721 pane.fireEvent('activate');
25726 getActivePane : function()
25729 Roo.each(this.panes, function(p) {
25730 if(p.el.hasClass('active')){
25751 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25753 * @class Roo.bootstrap.TabPane
25754 * @extends Roo.bootstrap.Component
25755 * Bootstrap TabPane class
25756 * @cfg {Boolean} active (false | true) Default false
25757 * @cfg {String} title title of panel
25761 * Create a new TabPane
25762 * @param {Object} config The config object
25765 Roo.bootstrap.dash.TabPane = function(config){
25766 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25772 * When a pane is activated
25773 * @param {Roo.bootstrap.dash.TabPane} pane
25780 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25785 // the tabBox that this is attached to.
25788 getAutoCreate : function()
25796 cfg.cls += ' active';
25801 initEvents : function()
25803 //Roo.log('trigger add pane handler');
25804 this.parent().fireEvent('addpane', this)
25808 * Updates the tab title
25809 * @param {String} html to set the title to.
25811 setTitle: function(str)
25817 this.tab.select('a', true).first().dom.innerHTML = str;
25834 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25837 * @class Roo.bootstrap.menu.Menu
25838 * @extends Roo.bootstrap.Component
25839 * Bootstrap Menu class - container for Menu
25840 * @cfg {String} html Text of the menu
25841 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25842 * @cfg {String} icon Font awesome icon
25843 * @cfg {String} pos Menu align to (top | bottom) default bottom
25847 * Create a new Menu
25848 * @param {Object} config The config object
25852 Roo.bootstrap.menu.Menu = function(config){
25853 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25857 * @event beforeshow
25858 * Fires before this menu is displayed
25859 * @param {Roo.bootstrap.menu.Menu} this
25863 * @event beforehide
25864 * Fires before this menu is hidden
25865 * @param {Roo.bootstrap.menu.Menu} this
25870 * Fires after this menu is displayed
25871 * @param {Roo.bootstrap.menu.Menu} this
25876 * Fires after this menu is hidden
25877 * @param {Roo.bootstrap.menu.Menu} this
25882 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25883 * @param {Roo.bootstrap.menu.Menu} this
25884 * @param {Roo.EventObject} e
25891 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25895 weight : 'default',
25900 getChildContainer : function() {
25901 if(this.isSubMenu){
25905 return this.el.select('ul.dropdown-menu', true).first();
25908 getAutoCreate : function()
25913 cls : 'roo-menu-text',
25921 cls : 'fa ' + this.icon
25932 cls : 'dropdown-button btn btn-' + this.weight,
25937 cls : 'dropdown-toggle btn btn-' + this.weight,
25947 cls : 'dropdown-menu'
25953 if(this.pos == 'top'){
25954 cfg.cls += ' dropup';
25957 if(this.isSubMenu){
25960 cls : 'dropdown-menu'
25967 onRender : function(ct, position)
25969 this.isSubMenu = ct.hasClass('dropdown-submenu');
25971 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25974 initEvents : function()
25976 if(this.isSubMenu){
25980 this.hidden = true;
25982 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25983 this.triggerEl.on('click', this.onTriggerPress, this);
25985 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25986 this.buttonEl.on('click', this.onClick, this);
25992 if(this.isSubMenu){
25996 return this.el.select('ul.dropdown-menu', true).first();
25999 onClick : function(e)
26001 this.fireEvent("click", this, e);
26004 onTriggerPress : function(e)
26006 if (this.isVisible()) {
26013 isVisible : function(){
26014 return !this.hidden;
26019 this.fireEvent("beforeshow", this);
26021 this.hidden = false;
26022 this.el.addClass('open');
26024 Roo.get(document).on("mouseup", this.onMouseUp, this);
26026 this.fireEvent("show", this);
26033 this.fireEvent("beforehide", this);
26035 this.hidden = true;
26036 this.el.removeClass('open');
26038 Roo.get(document).un("mouseup", this.onMouseUp);
26040 this.fireEvent("hide", this);
26043 onMouseUp : function()
26057 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26060 * @class Roo.bootstrap.menu.Item
26061 * @extends Roo.bootstrap.Component
26062 * Bootstrap MenuItem class
26063 * @cfg {Boolean} submenu (true | false) default false
26064 * @cfg {String} html text of the item
26065 * @cfg {String} href the link
26066 * @cfg {Boolean} disable (true | false) default false
26067 * @cfg {Boolean} preventDefault (true | false) default true
26068 * @cfg {String} icon Font awesome icon
26069 * @cfg {String} pos Submenu align to (left | right) default right
26073 * Create a new Item
26074 * @param {Object} config The config object
26078 Roo.bootstrap.menu.Item = function(config){
26079 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26083 * Fires when the mouse is hovering over this menu
26084 * @param {Roo.bootstrap.menu.Item} this
26085 * @param {Roo.EventObject} e
26090 * Fires when the mouse exits this menu
26091 * @param {Roo.bootstrap.menu.Item} this
26092 * @param {Roo.EventObject} e
26098 * The raw click event for the entire grid.
26099 * @param {Roo.EventObject} e
26105 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26110 preventDefault: true,
26115 getAutoCreate : function()
26120 cls : 'roo-menu-item-text',
26128 cls : 'fa ' + this.icon
26137 href : this.href || '#',
26144 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26148 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26150 if(this.pos == 'left'){
26151 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26158 initEvents : function()
26160 this.el.on('mouseover', this.onMouseOver, this);
26161 this.el.on('mouseout', this.onMouseOut, this);
26163 this.el.select('a', true).first().on('click', this.onClick, this);
26167 onClick : function(e)
26169 if(this.preventDefault){
26170 e.preventDefault();
26173 this.fireEvent("click", this, e);
26176 onMouseOver : function(e)
26178 if(this.submenu && this.pos == 'left'){
26179 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26182 this.fireEvent("mouseover", this, e);
26185 onMouseOut : function(e)
26187 this.fireEvent("mouseout", this, e);
26199 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26202 * @class Roo.bootstrap.menu.Separator
26203 * @extends Roo.bootstrap.Component
26204 * Bootstrap Separator class
26207 * Create a new Separator
26208 * @param {Object} config The config object
26212 Roo.bootstrap.menu.Separator = function(config){
26213 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26216 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26218 getAutoCreate : function(){
26239 * @class Roo.bootstrap.Tooltip
26240 * Bootstrap Tooltip class
26241 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26242 * to determine which dom element triggers the tooltip.
26244 * It needs to add support for additional attributes like tooltip-position
26247 * Create a new Toolti
26248 * @param {Object} config The config object
26251 Roo.bootstrap.Tooltip = function(config){
26252 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26254 this.alignment = Roo.bootstrap.Tooltip.alignment;
26256 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26257 this.alignment = config.alignment;
26262 Roo.apply(Roo.bootstrap.Tooltip, {
26264 * @function init initialize tooltip monitoring.
26268 currentTip : false,
26269 currentRegion : false,
26275 Roo.get(document).on('mouseover', this.enter ,this);
26276 Roo.get(document).on('mouseout', this.leave, this);
26279 this.currentTip = new Roo.bootstrap.Tooltip();
26282 enter : function(ev)
26284 var dom = ev.getTarget();
26286 //Roo.log(['enter',dom]);
26287 var el = Roo.fly(dom);
26288 if (this.currentEl) {
26290 //Roo.log(this.currentEl);
26291 //Roo.log(this.currentEl.contains(dom));
26292 if (this.currentEl == el) {
26295 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26301 if (this.currentTip.el) {
26302 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26306 if(!el || el.dom == document){
26312 // you can not look for children, as if el is the body.. then everythign is the child..
26313 if (!el.attr('tooltip')) { //
26314 if (!el.select("[tooltip]").elements.length) {
26317 // is the mouse over this child...?
26318 bindEl = el.select("[tooltip]").first();
26319 var xy = ev.getXY();
26320 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26321 //Roo.log("not in region.");
26324 //Roo.log("child element over..");
26327 this.currentEl = bindEl;
26328 this.currentTip.bind(bindEl);
26329 this.currentRegion = Roo.lib.Region.getRegion(dom);
26330 this.currentTip.enter();
26333 leave : function(ev)
26335 var dom = ev.getTarget();
26336 //Roo.log(['leave',dom]);
26337 if (!this.currentEl) {
26342 if (dom != this.currentEl.dom) {
26345 var xy = ev.getXY();
26346 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26349 // only activate leave if mouse cursor is outside... bounding box..
26354 if (this.currentTip) {
26355 this.currentTip.leave();
26357 //Roo.log('clear currentEl');
26358 this.currentEl = false;
26363 'left' : ['r-l', [-2,0], 'right'],
26364 'right' : ['l-r', [2,0], 'left'],
26365 'bottom' : ['t-b', [0,2], 'top'],
26366 'top' : [ 'b-t', [0,-2], 'bottom']
26372 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26377 delay : null, // can be { show : 300 , hide: 500}
26381 hoverState : null, //???
26383 placement : 'bottom',
26387 getAutoCreate : function(){
26394 cls : 'tooltip-arrow'
26397 cls : 'tooltip-inner'
26404 bind : function(el)
26410 enter : function () {
26412 if (this.timeout != null) {
26413 clearTimeout(this.timeout);
26416 this.hoverState = 'in';
26417 //Roo.log("enter - show");
26418 if (!this.delay || !this.delay.show) {
26423 this.timeout = setTimeout(function () {
26424 if (_t.hoverState == 'in') {
26427 }, this.delay.show);
26431 clearTimeout(this.timeout);
26433 this.hoverState = 'out';
26434 if (!this.delay || !this.delay.hide) {
26440 this.timeout = setTimeout(function () {
26441 //Roo.log("leave - timeout");
26443 if (_t.hoverState == 'out') {
26445 Roo.bootstrap.Tooltip.currentEl = false;
26450 show : function (msg)
26453 this.render(document.body);
26456 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26458 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26460 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26462 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26464 var placement = typeof this.placement == 'function' ?
26465 this.placement.call(this, this.el, on_el) :
26468 var autoToken = /\s?auto?\s?/i;
26469 var autoPlace = autoToken.test(placement);
26471 placement = placement.replace(autoToken, '') || 'top';
26475 //this.el.setXY([0,0]);
26477 //this.el.dom.style.display='block';
26479 //this.el.appendTo(on_el);
26481 var p = this.getPosition();
26482 var box = this.el.getBox();
26488 var align = this.alignment[placement];
26490 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26492 if(placement == 'top' || placement == 'bottom'){
26494 placement = 'right';
26497 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26498 placement = 'left';
26501 var scroll = Roo.select('body', true).first().getScroll();
26503 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26507 align = this.alignment[placement];
26510 this.el.alignTo(this.bindEl, align[0],align[1]);
26511 //var arrow = this.el.select('.arrow',true).first();
26512 //arrow.set(align[2],
26514 this.el.addClass(placement);
26516 this.el.addClass('in fade');
26518 this.hoverState = null;
26520 if (this.el.hasClass('fade')) {
26531 //this.el.setXY([0,0]);
26532 this.el.removeClass('in');
26548 * @class Roo.bootstrap.LocationPicker
26549 * @extends Roo.bootstrap.Component
26550 * Bootstrap LocationPicker class
26551 * @cfg {Number} latitude Position when init default 0
26552 * @cfg {Number} longitude Position when init default 0
26553 * @cfg {Number} zoom default 15
26554 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26555 * @cfg {Boolean} mapTypeControl default false
26556 * @cfg {Boolean} disableDoubleClickZoom default false
26557 * @cfg {Boolean} scrollwheel default true
26558 * @cfg {Boolean} streetViewControl default false
26559 * @cfg {Number} radius default 0
26560 * @cfg {String} locationName
26561 * @cfg {Boolean} draggable default true
26562 * @cfg {Boolean} enableAutocomplete default false
26563 * @cfg {Boolean} enableReverseGeocode default true
26564 * @cfg {String} markerTitle
26567 * Create a new LocationPicker
26568 * @param {Object} config The config object
26572 Roo.bootstrap.LocationPicker = function(config){
26574 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26579 * Fires when the picker initialized.
26580 * @param {Roo.bootstrap.LocationPicker} this
26581 * @param {Google Location} location
26585 * @event positionchanged
26586 * Fires when the picker position changed.
26587 * @param {Roo.bootstrap.LocationPicker} this
26588 * @param {Google Location} location
26590 positionchanged : true,
26593 * Fires when the map resize.
26594 * @param {Roo.bootstrap.LocationPicker} this
26599 * Fires when the map show.
26600 * @param {Roo.bootstrap.LocationPicker} this
26605 * Fires when the map hide.
26606 * @param {Roo.bootstrap.LocationPicker} this
26611 * Fires when click the map.
26612 * @param {Roo.bootstrap.LocationPicker} this
26613 * @param {Map event} e
26617 * @event mapRightClick
26618 * Fires when right click the map.
26619 * @param {Roo.bootstrap.LocationPicker} this
26620 * @param {Map event} e
26622 mapRightClick : true,
26624 * @event markerClick
26625 * Fires when click the marker.
26626 * @param {Roo.bootstrap.LocationPicker} this
26627 * @param {Map event} e
26629 markerClick : true,
26631 * @event markerRightClick
26632 * Fires when right click the marker.
26633 * @param {Roo.bootstrap.LocationPicker} this
26634 * @param {Map event} e
26636 markerRightClick : true,
26638 * @event OverlayViewDraw
26639 * Fires when OverlayView Draw
26640 * @param {Roo.bootstrap.LocationPicker} this
26642 OverlayViewDraw : true,
26644 * @event OverlayViewOnAdd
26645 * Fires when OverlayView Draw
26646 * @param {Roo.bootstrap.LocationPicker} this
26648 OverlayViewOnAdd : true,
26650 * @event OverlayViewOnRemove
26651 * Fires when OverlayView Draw
26652 * @param {Roo.bootstrap.LocationPicker} this
26654 OverlayViewOnRemove : true,
26656 * @event OverlayViewShow
26657 * Fires when OverlayView Draw
26658 * @param {Roo.bootstrap.LocationPicker} this
26659 * @param {Pixel} cpx
26661 OverlayViewShow : true,
26663 * @event OverlayViewHide
26664 * Fires when OverlayView Draw
26665 * @param {Roo.bootstrap.LocationPicker} this
26667 OverlayViewHide : true,
26669 * @event loadexception
26670 * Fires when load google lib failed.
26671 * @param {Roo.bootstrap.LocationPicker} this
26673 loadexception : true
26678 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26680 gMapContext: false,
26686 mapTypeControl: false,
26687 disableDoubleClickZoom: false,
26689 streetViewControl: false,
26693 enableAutocomplete: false,
26694 enableReverseGeocode: true,
26697 getAutoCreate: function()
26702 cls: 'roo-location-picker'
26708 initEvents: function(ct, position)
26710 if(!this.el.getWidth() || this.isApplied()){
26714 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26719 initial: function()
26721 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26722 this.fireEvent('loadexception', this);
26726 if(!this.mapTypeId){
26727 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26730 this.gMapContext = this.GMapContext();
26732 this.initOverlayView();
26734 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26738 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26739 _this.setPosition(_this.gMapContext.marker.position);
26742 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26743 _this.fireEvent('mapClick', this, event);
26747 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26748 _this.fireEvent('mapRightClick', this, event);
26752 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26753 _this.fireEvent('markerClick', this, event);
26757 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26758 _this.fireEvent('markerRightClick', this, event);
26762 this.setPosition(this.gMapContext.location);
26764 this.fireEvent('initial', this, this.gMapContext.location);
26767 initOverlayView: function()
26771 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26775 _this.fireEvent('OverlayViewDraw', _this);
26780 _this.fireEvent('OverlayViewOnAdd', _this);
26783 onRemove: function()
26785 _this.fireEvent('OverlayViewOnRemove', _this);
26788 show: function(cpx)
26790 _this.fireEvent('OverlayViewShow', _this, cpx);
26795 _this.fireEvent('OverlayViewHide', _this);
26801 fromLatLngToContainerPixel: function(event)
26803 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26806 isApplied: function()
26808 return this.getGmapContext() == false ? false : true;
26811 getGmapContext: function()
26813 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26816 GMapContext: function()
26818 var position = new google.maps.LatLng(this.latitude, this.longitude);
26820 var _map = new google.maps.Map(this.el.dom, {
26823 mapTypeId: this.mapTypeId,
26824 mapTypeControl: this.mapTypeControl,
26825 disableDoubleClickZoom: this.disableDoubleClickZoom,
26826 scrollwheel: this.scrollwheel,
26827 streetViewControl: this.streetViewControl,
26828 locationName: this.locationName,
26829 draggable: this.draggable,
26830 enableAutocomplete: this.enableAutocomplete,
26831 enableReverseGeocode: this.enableReverseGeocode
26834 var _marker = new google.maps.Marker({
26835 position: position,
26837 title: this.markerTitle,
26838 draggable: this.draggable
26845 location: position,
26846 radius: this.radius,
26847 locationName: this.locationName,
26848 addressComponents: {
26849 formatted_address: null,
26850 addressLine1: null,
26851 addressLine2: null,
26853 streetNumber: null,
26857 stateOrProvince: null
26860 domContainer: this.el.dom,
26861 geodecoder: new google.maps.Geocoder()
26865 drawCircle: function(center, radius, options)
26867 if (this.gMapContext.circle != null) {
26868 this.gMapContext.circle.setMap(null);
26872 options = Roo.apply({}, options, {
26873 strokeColor: "#0000FF",
26874 strokeOpacity: .35,
26876 fillColor: "#0000FF",
26880 options.map = this.gMapContext.map;
26881 options.radius = radius;
26882 options.center = center;
26883 this.gMapContext.circle = new google.maps.Circle(options);
26884 return this.gMapContext.circle;
26890 setPosition: function(location)
26892 this.gMapContext.location = location;
26893 this.gMapContext.marker.setPosition(location);
26894 this.gMapContext.map.panTo(location);
26895 this.drawCircle(location, this.gMapContext.radius, {});
26899 if (this.gMapContext.settings.enableReverseGeocode) {
26900 this.gMapContext.geodecoder.geocode({
26901 latLng: this.gMapContext.location
26902 }, function(results, status) {
26904 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26905 _this.gMapContext.locationName = results[0].formatted_address;
26906 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26908 _this.fireEvent('positionchanged', this, location);
26915 this.fireEvent('positionchanged', this, location);
26920 google.maps.event.trigger(this.gMapContext.map, "resize");
26922 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26924 this.fireEvent('resize', this);
26927 setPositionByLatLng: function(latitude, longitude)
26929 this.setPosition(new google.maps.LatLng(latitude, longitude));
26932 getCurrentPosition: function()
26935 latitude: this.gMapContext.location.lat(),
26936 longitude: this.gMapContext.location.lng()
26940 getAddressName: function()
26942 return this.gMapContext.locationName;
26945 getAddressComponents: function()
26947 return this.gMapContext.addressComponents;
26950 address_component_from_google_geocode: function(address_components)
26954 for (var i = 0; i < address_components.length; i++) {
26955 var component = address_components[i];
26956 if (component.types.indexOf("postal_code") >= 0) {
26957 result.postalCode = component.short_name;
26958 } else if (component.types.indexOf("street_number") >= 0) {
26959 result.streetNumber = component.short_name;
26960 } else if (component.types.indexOf("route") >= 0) {
26961 result.streetName = component.short_name;
26962 } else if (component.types.indexOf("neighborhood") >= 0) {
26963 result.city = component.short_name;
26964 } else if (component.types.indexOf("locality") >= 0) {
26965 result.city = component.short_name;
26966 } else if (component.types.indexOf("sublocality") >= 0) {
26967 result.district = component.short_name;
26968 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26969 result.stateOrProvince = component.short_name;
26970 } else if (component.types.indexOf("country") >= 0) {
26971 result.country = component.short_name;
26975 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26976 result.addressLine2 = "";
26980 setZoomLevel: function(zoom)
26982 this.gMapContext.map.setZoom(zoom);
26995 this.fireEvent('show', this);
27006 this.fireEvent('hide', this);
27011 Roo.apply(Roo.bootstrap.LocationPicker, {
27013 OverlayView : function(map, options)
27015 options = options || {};
27029 * @class Roo.bootstrap.Alert
27030 * @extends Roo.bootstrap.Component
27031 * Bootstrap Alert class
27032 * @cfg {String} title The title of alert
27033 * @cfg {String} html The content of alert
27034 * @cfg {String} weight ( success | info | warning | danger )
27035 * @cfg {String} faicon font-awesomeicon
27038 * Create a new alert
27039 * @param {Object} config The config object
27043 Roo.bootstrap.Alert = function(config){
27044 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27048 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27055 getAutoCreate : function()
27064 cls : 'roo-alert-icon'
27069 cls : 'roo-alert-title',
27074 cls : 'roo-alert-text',
27081 cfg.cn[0].cls += ' fa ' + this.faicon;
27085 cfg.cls += ' alert-' + this.weight;
27091 initEvents: function()
27093 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27096 setTitle : function(str)
27098 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27101 setText : function(str)
27103 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27106 setWeight : function(weight)
27109 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27112 this.weight = weight;
27114 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27117 setIcon : function(icon)
27120 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27123 this.faicon = icon;
27125 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27146 * @class Roo.bootstrap.UploadCropbox
27147 * @extends Roo.bootstrap.Component
27148 * Bootstrap UploadCropbox class
27149 * @cfg {String} emptyText show when image has been loaded
27150 * @cfg {String} rotateNotify show when image too small to rotate
27151 * @cfg {Number} errorTimeout default 3000
27152 * @cfg {Number} minWidth default 300
27153 * @cfg {Number} minHeight default 300
27154 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27155 * @cfg {Boolean} isDocument (true|false) default false
27156 * @cfg {String} url action url
27157 * @cfg {String} paramName default 'imageUpload'
27158 * @cfg {String} method default POST
27159 * @cfg {Boolean} loadMask (true|false) default true
27160 * @cfg {Boolean} loadingText default 'Loading...'
27163 * Create a new UploadCropbox
27164 * @param {Object} config The config object
27167 Roo.bootstrap.UploadCropbox = function(config){
27168 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27172 * @event beforeselectfile
27173 * Fire before select file
27174 * @param {Roo.bootstrap.UploadCropbox} this
27176 "beforeselectfile" : true,
27179 * Fire after initEvent
27180 * @param {Roo.bootstrap.UploadCropbox} this
27185 * Fire after initEvent
27186 * @param {Roo.bootstrap.UploadCropbox} this
27187 * @param {String} data
27192 * Fire when preparing the file data
27193 * @param {Roo.bootstrap.UploadCropbox} this
27194 * @param {Object} file
27199 * Fire when get exception
27200 * @param {Roo.bootstrap.UploadCropbox} this
27201 * @param {XMLHttpRequest} xhr
27203 "exception" : true,
27205 * @event beforeloadcanvas
27206 * Fire before load the canvas
27207 * @param {Roo.bootstrap.UploadCropbox} this
27208 * @param {String} src
27210 "beforeloadcanvas" : true,
27213 * Fire when trash image
27214 * @param {Roo.bootstrap.UploadCropbox} this
27219 * Fire when download the image
27220 * @param {Roo.bootstrap.UploadCropbox} this
27224 * @event footerbuttonclick
27225 * Fire when footerbuttonclick
27226 * @param {Roo.bootstrap.UploadCropbox} this
27227 * @param {String} type
27229 "footerbuttonclick" : true,
27233 * @param {Roo.bootstrap.UploadCropbox} this
27238 * Fire when rotate the image
27239 * @param {Roo.bootstrap.UploadCropbox} this
27240 * @param {String} pos
27245 * Fire when inspect the file
27246 * @param {Roo.bootstrap.UploadCropbox} this
27247 * @param {Object} file
27252 * Fire when xhr upload the file
27253 * @param {Roo.bootstrap.UploadCropbox} this
27254 * @param {Object} data
27259 * Fire when arrange the file data
27260 * @param {Roo.bootstrap.UploadCropbox} this
27261 * @param {Object} formData
27266 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27269 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27271 emptyText : 'Click to upload image',
27272 rotateNotify : 'Image is too small to rotate',
27273 errorTimeout : 3000,
27287 cropType : 'image/jpeg',
27289 canvasLoaded : false,
27290 isDocument : false,
27292 paramName : 'imageUpload',
27294 loadingText : 'Loading...',
27297 getAutoCreate : function()
27301 cls : 'roo-upload-cropbox',
27305 cls : 'roo-upload-cropbox-selector',
27310 cls : 'roo-upload-cropbox-body',
27311 style : 'cursor:pointer',
27315 cls : 'roo-upload-cropbox-preview'
27319 cls : 'roo-upload-cropbox-thumb'
27323 cls : 'roo-upload-cropbox-empty-notify',
27324 html : this.emptyText
27328 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27329 html : this.rotateNotify
27335 cls : 'roo-upload-cropbox-footer',
27338 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27348 onRender : function(ct, position)
27350 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27352 if (this.buttons.length) {
27354 Roo.each(this.buttons, function(bb) {
27356 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27358 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27364 this.maskEl = this.el;
27368 initEvents : function()
27370 this.urlAPI = (window.createObjectURL && window) ||
27371 (window.URL && URL.revokeObjectURL && URL) ||
27372 (window.webkitURL && webkitURL);
27374 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27375 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27377 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27378 this.selectorEl.hide();
27380 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27381 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27383 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27384 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27385 this.thumbEl.hide();
27387 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27388 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27390 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27391 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27392 this.errorEl.hide();
27394 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27395 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27396 this.footerEl.hide();
27398 this.setThumbBoxSize();
27404 this.fireEvent('initial', this);
27411 window.addEventListener("resize", function() { _this.resize(); } );
27413 this.bodyEl.on('click', this.beforeSelectFile, this);
27416 this.bodyEl.on('touchstart', this.onTouchStart, this);
27417 this.bodyEl.on('touchmove', this.onTouchMove, this);
27418 this.bodyEl.on('touchend', this.onTouchEnd, this);
27422 this.bodyEl.on('mousedown', this.onMouseDown, this);
27423 this.bodyEl.on('mousemove', this.onMouseMove, this);
27424 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27425 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27426 Roo.get(document).on('mouseup', this.onMouseUp, this);
27429 this.selectorEl.on('change', this.onFileSelected, this);
27435 this.baseScale = 1;
27437 this.baseRotate = 1;
27438 this.dragable = false;
27439 this.pinching = false;
27442 this.cropData = false;
27443 this.notifyEl.dom.innerHTML = this.emptyText;
27445 this.selectorEl.dom.value = '';
27449 resize : function()
27451 if(this.fireEvent('resize', this) != false){
27452 this.setThumbBoxPosition();
27453 this.setCanvasPosition();
27457 onFooterButtonClick : function(e, el, o, type)
27460 case 'rotate-left' :
27461 this.onRotateLeft(e);
27463 case 'rotate-right' :
27464 this.onRotateRight(e);
27467 this.beforeSelectFile(e);
27482 this.fireEvent('footerbuttonclick', this, type);
27485 beforeSelectFile : function(e)
27487 e.preventDefault();
27489 if(this.fireEvent('beforeselectfile', this) != false){
27490 this.selectorEl.dom.click();
27494 onFileSelected : function(e)
27496 e.preventDefault();
27498 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27502 var file = this.selectorEl.dom.files[0];
27504 if(this.fireEvent('inspect', this, file) != false){
27505 this.prepare(file);
27510 trash : function(e)
27512 this.fireEvent('trash', this);
27515 download : function(e)
27517 this.fireEvent('download', this);
27520 loadCanvas : function(src)
27522 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27526 this.imageEl = document.createElement('img');
27530 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27532 this.imageEl.src = src;
27536 onLoadCanvas : function()
27538 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27539 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27541 this.bodyEl.un('click', this.beforeSelectFile, this);
27543 this.notifyEl.hide();
27544 this.thumbEl.show();
27545 this.footerEl.show();
27547 this.baseRotateLevel();
27549 if(this.isDocument){
27550 this.setThumbBoxSize();
27553 this.setThumbBoxPosition();
27555 this.baseScaleLevel();
27561 this.canvasLoaded = true;
27564 this.maskEl.unmask();
27569 setCanvasPosition : function()
27571 if(!this.canvasEl){
27575 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27576 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27578 this.previewEl.setLeft(pw);
27579 this.previewEl.setTop(ph);
27583 onMouseDown : function(e)
27587 this.dragable = true;
27588 this.pinching = false;
27590 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27591 this.dragable = false;
27595 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27596 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27600 onMouseMove : function(e)
27604 if(!this.canvasLoaded){
27608 if (!this.dragable){
27612 var minX = Math.ceil(this.thumbEl.getLeft(true));
27613 var minY = Math.ceil(this.thumbEl.getTop(true));
27615 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27616 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27618 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27619 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27621 x = x - this.mouseX;
27622 y = y - this.mouseY;
27624 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27625 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27627 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27628 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27630 this.previewEl.setLeft(bgX);
27631 this.previewEl.setTop(bgY);
27633 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27634 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27637 onMouseUp : function(e)
27641 this.dragable = false;
27644 onMouseWheel : function(e)
27648 this.startScale = this.scale;
27650 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27652 if(!this.zoomable()){
27653 this.scale = this.startScale;
27662 zoomable : function()
27664 var minScale = this.thumbEl.getWidth() / this.minWidth;
27666 if(this.minWidth < this.minHeight){
27667 minScale = this.thumbEl.getHeight() / this.minHeight;
27670 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27671 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27675 (this.rotate == 0 || this.rotate == 180) &&
27677 width > this.imageEl.OriginWidth ||
27678 height > this.imageEl.OriginHeight ||
27679 (width < this.minWidth && height < this.minHeight)
27687 (this.rotate == 90 || this.rotate == 270) &&
27689 width > this.imageEl.OriginWidth ||
27690 height > this.imageEl.OriginHeight ||
27691 (width < this.minHeight && height < this.minWidth)
27698 !this.isDocument &&
27699 (this.rotate == 0 || this.rotate == 180) &&
27701 width < this.minWidth ||
27702 width > this.imageEl.OriginWidth ||
27703 height < this.minHeight ||
27704 height > this.imageEl.OriginHeight
27711 !this.isDocument &&
27712 (this.rotate == 90 || this.rotate == 270) &&
27714 width < this.minHeight ||
27715 width > this.imageEl.OriginWidth ||
27716 height < this.minWidth ||
27717 height > this.imageEl.OriginHeight
27727 onRotateLeft : function(e)
27729 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27731 var minScale = this.thumbEl.getWidth() / this.minWidth;
27733 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27734 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27736 this.startScale = this.scale;
27738 while (this.getScaleLevel() < minScale){
27740 this.scale = this.scale + 1;
27742 if(!this.zoomable()){
27747 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27748 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27753 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27760 this.scale = this.startScale;
27762 this.onRotateFail();
27767 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27769 if(this.isDocument){
27770 this.setThumbBoxSize();
27771 this.setThumbBoxPosition();
27772 this.setCanvasPosition();
27777 this.fireEvent('rotate', this, 'left');
27781 onRotateRight : function(e)
27783 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27785 var minScale = this.thumbEl.getWidth() / this.minWidth;
27787 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27788 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27790 this.startScale = this.scale;
27792 while (this.getScaleLevel() < minScale){
27794 this.scale = this.scale + 1;
27796 if(!this.zoomable()){
27801 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27802 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27807 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27814 this.scale = this.startScale;
27816 this.onRotateFail();
27821 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27823 if(this.isDocument){
27824 this.setThumbBoxSize();
27825 this.setThumbBoxPosition();
27826 this.setCanvasPosition();
27831 this.fireEvent('rotate', this, 'right');
27834 onRotateFail : function()
27836 this.errorEl.show(true);
27840 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27845 this.previewEl.dom.innerHTML = '';
27847 var canvasEl = document.createElement("canvas");
27849 var contextEl = canvasEl.getContext("2d");
27851 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27852 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27853 var center = this.imageEl.OriginWidth / 2;
27855 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27856 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27857 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27858 center = this.imageEl.OriginHeight / 2;
27861 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27863 contextEl.translate(center, center);
27864 contextEl.rotate(this.rotate * Math.PI / 180);
27866 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27868 this.canvasEl = document.createElement("canvas");
27870 this.contextEl = this.canvasEl.getContext("2d");
27872 switch (this.rotate) {
27875 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27876 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27878 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27883 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27884 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27886 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27887 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);
27891 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27896 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27897 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27899 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27900 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);
27904 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);
27909 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27910 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27912 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27913 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27917 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);
27924 this.previewEl.appendChild(this.canvasEl);
27926 this.setCanvasPosition();
27931 if(!this.canvasLoaded){
27935 var imageCanvas = document.createElement("canvas");
27937 var imageContext = imageCanvas.getContext("2d");
27939 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27940 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27942 var center = imageCanvas.width / 2;
27944 imageContext.translate(center, center);
27946 imageContext.rotate(this.rotate * Math.PI / 180);
27948 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27950 var canvas = document.createElement("canvas");
27952 var context = canvas.getContext("2d");
27954 canvas.width = this.minWidth;
27955 canvas.height = this.minHeight;
27957 switch (this.rotate) {
27960 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27961 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27963 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27964 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27966 var targetWidth = this.minWidth - 2 * x;
27967 var targetHeight = this.minHeight - 2 * y;
27971 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27972 scale = targetWidth / width;
27975 if(x > 0 && y == 0){
27976 scale = targetHeight / height;
27979 if(x > 0 && y > 0){
27980 scale = targetWidth / width;
27982 if(width < height){
27983 scale = targetHeight / height;
27987 context.scale(scale, scale);
27989 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27990 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27992 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27993 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27995 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28000 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28001 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28003 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28004 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28006 var targetWidth = this.minWidth - 2 * x;
28007 var targetHeight = this.minHeight - 2 * y;
28011 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28012 scale = targetWidth / width;
28015 if(x > 0 && y == 0){
28016 scale = targetHeight / height;
28019 if(x > 0 && y > 0){
28020 scale = targetWidth / width;
28022 if(width < height){
28023 scale = targetHeight / height;
28027 context.scale(scale, scale);
28029 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28030 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28032 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28033 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28035 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28037 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28042 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28043 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28045 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28046 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28048 var targetWidth = this.minWidth - 2 * x;
28049 var targetHeight = this.minHeight - 2 * y;
28053 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28054 scale = targetWidth / width;
28057 if(x > 0 && y == 0){
28058 scale = targetHeight / height;
28061 if(x > 0 && y > 0){
28062 scale = targetWidth / width;
28064 if(width < height){
28065 scale = targetHeight / height;
28069 context.scale(scale, scale);
28071 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28072 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28074 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28075 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28077 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28078 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28080 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28085 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28086 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28088 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28089 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28091 var targetWidth = this.minWidth - 2 * x;
28092 var targetHeight = this.minHeight - 2 * y;
28096 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28097 scale = targetWidth / width;
28100 if(x > 0 && y == 0){
28101 scale = targetHeight / height;
28104 if(x > 0 && y > 0){
28105 scale = targetWidth / width;
28107 if(width < height){
28108 scale = targetHeight / height;
28112 context.scale(scale, scale);
28114 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28115 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28117 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28118 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28120 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28122 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28129 this.cropData = canvas.toDataURL(this.cropType);
28131 if(this.fireEvent('crop', this, this.cropData) !== false){
28132 this.process(this.file, this.cropData);
28139 setThumbBoxSize : function()
28143 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28144 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28145 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28147 this.minWidth = width;
28148 this.minHeight = height;
28150 if(this.rotate == 90 || this.rotate == 270){
28151 this.minWidth = height;
28152 this.minHeight = width;
28157 width = Math.ceil(this.minWidth * height / this.minHeight);
28159 if(this.minWidth > this.minHeight){
28161 height = Math.ceil(this.minHeight * width / this.minWidth);
28164 this.thumbEl.setStyle({
28165 width : width + 'px',
28166 height : height + 'px'
28173 setThumbBoxPosition : function()
28175 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28176 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28178 this.thumbEl.setLeft(x);
28179 this.thumbEl.setTop(y);
28183 baseRotateLevel : function()
28185 this.baseRotate = 1;
28188 typeof(this.exif) != 'undefined' &&
28189 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28190 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28192 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28195 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28199 baseScaleLevel : function()
28203 if(this.isDocument){
28205 if(this.baseRotate == 6 || this.baseRotate == 8){
28207 height = this.thumbEl.getHeight();
28208 this.baseScale = height / this.imageEl.OriginWidth;
28210 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28211 width = this.thumbEl.getWidth();
28212 this.baseScale = width / this.imageEl.OriginHeight;
28218 height = this.thumbEl.getHeight();
28219 this.baseScale = height / this.imageEl.OriginHeight;
28221 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28222 width = this.thumbEl.getWidth();
28223 this.baseScale = width / this.imageEl.OriginWidth;
28229 if(this.baseRotate == 6 || this.baseRotate == 8){
28231 width = this.thumbEl.getHeight();
28232 this.baseScale = width / this.imageEl.OriginHeight;
28234 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28235 height = this.thumbEl.getWidth();
28236 this.baseScale = height / this.imageEl.OriginHeight;
28239 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28240 height = this.thumbEl.getWidth();
28241 this.baseScale = height / this.imageEl.OriginHeight;
28243 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28244 width = this.thumbEl.getHeight();
28245 this.baseScale = width / this.imageEl.OriginWidth;
28252 width = this.thumbEl.getWidth();
28253 this.baseScale = width / this.imageEl.OriginWidth;
28255 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28256 height = this.thumbEl.getHeight();
28257 this.baseScale = height / this.imageEl.OriginHeight;
28260 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28262 height = this.thumbEl.getHeight();
28263 this.baseScale = height / this.imageEl.OriginHeight;
28265 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28266 width = this.thumbEl.getWidth();
28267 this.baseScale = width / this.imageEl.OriginWidth;
28275 getScaleLevel : function()
28277 return this.baseScale * Math.pow(1.1, this.scale);
28280 onTouchStart : function(e)
28282 if(!this.canvasLoaded){
28283 this.beforeSelectFile(e);
28287 var touches = e.browserEvent.touches;
28293 if(touches.length == 1){
28294 this.onMouseDown(e);
28298 if(touches.length != 2){
28304 for(var i = 0, finger; finger = touches[i]; i++){
28305 coords.push(finger.pageX, finger.pageY);
28308 var x = Math.pow(coords[0] - coords[2], 2);
28309 var y = Math.pow(coords[1] - coords[3], 2);
28311 this.startDistance = Math.sqrt(x + y);
28313 this.startScale = this.scale;
28315 this.pinching = true;
28316 this.dragable = false;
28320 onTouchMove : function(e)
28322 if(!this.pinching && !this.dragable){
28326 var touches = e.browserEvent.touches;
28333 this.onMouseMove(e);
28339 for(var i = 0, finger; finger = touches[i]; i++){
28340 coords.push(finger.pageX, finger.pageY);
28343 var x = Math.pow(coords[0] - coords[2], 2);
28344 var y = Math.pow(coords[1] - coords[3], 2);
28346 this.endDistance = Math.sqrt(x + y);
28348 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28350 if(!this.zoomable()){
28351 this.scale = this.startScale;
28359 onTouchEnd : function(e)
28361 this.pinching = false;
28362 this.dragable = false;
28366 process : function(file, crop)
28369 this.maskEl.mask(this.loadingText);
28372 this.xhr = new XMLHttpRequest();
28374 file.xhr = this.xhr;
28376 this.xhr.open(this.method, this.url, true);
28379 "Accept": "application/json",
28380 "Cache-Control": "no-cache",
28381 "X-Requested-With": "XMLHttpRequest"
28384 for (var headerName in headers) {
28385 var headerValue = headers[headerName];
28387 this.xhr.setRequestHeader(headerName, headerValue);
28393 this.xhr.onload = function()
28395 _this.xhrOnLoad(_this.xhr);
28398 this.xhr.onerror = function()
28400 _this.xhrOnError(_this.xhr);
28403 var formData = new FormData();
28405 formData.append('returnHTML', 'NO');
28408 formData.append('crop', crop);
28411 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28412 formData.append(this.paramName, file, file.name);
28415 if(typeof(file.filename) != 'undefined'){
28416 formData.append('filename', file.filename);
28419 if(typeof(file.mimetype) != 'undefined'){
28420 formData.append('mimetype', file.mimetype);
28423 if(this.fireEvent('arrange', this, formData) != false){
28424 this.xhr.send(formData);
28428 xhrOnLoad : function(xhr)
28431 this.maskEl.unmask();
28434 if (xhr.readyState !== 4) {
28435 this.fireEvent('exception', this, xhr);
28439 var response = Roo.decode(xhr.responseText);
28441 if(!response.success){
28442 this.fireEvent('exception', this, xhr);
28446 var response = Roo.decode(xhr.responseText);
28448 this.fireEvent('upload', this, response);
28452 xhrOnError : function()
28455 this.maskEl.unmask();
28458 Roo.log('xhr on error');
28460 var response = Roo.decode(xhr.responseText);
28466 prepare : function(file)
28469 this.maskEl.mask(this.loadingText);
28475 if(typeof(file) === 'string'){
28476 this.loadCanvas(file);
28480 if(!file || !this.urlAPI){
28485 this.cropType = file.type;
28489 if(this.fireEvent('prepare', this, this.file) != false){
28491 var reader = new FileReader();
28493 reader.onload = function (e) {
28494 if (e.target.error) {
28495 Roo.log(e.target.error);
28499 var buffer = e.target.result,
28500 dataView = new DataView(buffer),
28502 maxOffset = dataView.byteLength - 4,
28506 if (dataView.getUint16(0) === 0xffd8) {
28507 while (offset < maxOffset) {
28508 markerBytes = dataView.getUint16(offset);
28510 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28511 markerLength = dataView.getUint16(offset + 2) + 2;
28512 if (offset + markerLength > dataView.byteLength) {
28513 Roo.log('Invalid meta data: Invalid segment size.');
28517 if(markerBytes == 0xffe1){
28518 _this.parseExifData(
28525 offset += markerLength;
28535 var url = _this.urlAPI.createObjectURL(_this.file);
28537 _this.loadCanvas(url);
28542 reader.readAsArrayBuffer(this.file);
28548 parseExifData : function(dataView, offset, length)
28550 var tiffOffset = offset + 10,
28554 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28555 // No Exif data, might be XMP data instead
28559 // Check for the ASCII code for "Exif" (0x45786966):
28560 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28561 // No Exif data, might be XMP data instead
28564 if (tiffOffset + 8 > dataView.byteLength) {
28565 Roo.log('Invalid Exif data: Invalid segment size.');
28568 // Check for the two null bytes:
28569 if (dataView.getUint16(offset + 8) !== 0x0000) {
28570 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28573 // Check the byte alignment:
28574 switch (dataView.getUint16(tiffOffset)) {
28576 littleEndian = true;
28579 littleEndian = false;
28582 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28585 // Check for the TIFF tag marker (0x002A):
28586 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28587 Roo.log('Invalid Exif data: Missing TIFF marker.');
28590 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28591 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28593 this.parseExifTags(
28596 tiffOffset + dirOffset,
28601 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28606 if (dirOffset + 6 > dataView.byteLength) {
28607 Roo.log('Invalid Exif data: Invalid directory offset.');
28610 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28611 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28612 if (dirEndOffset + 4 > dataView.byteLength) {
28613 Roo.log('Invalid Exif data: Invalid directory size.');
28616 for (i = 0; i < tagsNumber; i += 1) {
28620 dirOffset + 2 + 12 * i, // tag offset
28624 // Return the offset to the next directory:
28625 return dataView.getUint32(dirEndOffset, littleEndian);
28628 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28630 var tag = dataView.getUint16(offset, littleEndian);
28632 this.exif[tag] = this.getExifValue(
28636 dataView.getUint16(offset + 2, littleEndian), // tag type
28637 dataView.getUint32(offset + 4, littleEndian), // tag length
28642 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28644 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28653 Roo.log('Invalid Exif data: Invalid tag type.');
28657 tagSize = tagType.size * length;
28658 // Determine if the value is contained in the dataOffset bytes,
28659 // or if the value at the dataOffset is a pointer to the actual data:
28660 dataOffset = tagSize > 4 ?
28661 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28662 if (dataOffset + tagSize > dataView.byteLength) {
28663 Roo.log('Invalid Exif data: Invalid data offset.');
28666 if (length === 1) {
28667 return tagType.getValue(dataView, dataOffset, littleEndian);
28670 for (i = 0; i < length; i += 1) {
28671 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28674 if (tagType.ascii) {
28676 // Concatenate the chars:
28677 for (i = 0; i < values.length; i += 1) {
28679 // Ignore the terminating NULL byte(s):
28680 if (c === '\u0000') {
28692 Roo.apply(Roo.bootstrap.UploadCropbox, {
28694 'Orientation': 0x0112
28698 1: 0, //'top-left',
28700 3: 180, //'bottom-right',
28701 // 4: 'bottom-left',
28703 6: 90, //'right-top',
28704 // 7: 'right-bottom',
28705 8: 270 //'left-bottom'
28709 // byte, 8-bit unsigned int:
28711 getValue: function (dataView, dataOffset) {
28712 return dataView.getUint8(dataOffset);
28716 // ascii, 8-bit byte:
28718 getValue: function (dataView, dataOffset) {
28719 return String.fromCharCode(dataView.getUint8(dataOffset));
28724 // short, 16 bit int:
28726 getValue: function (dataView, dataOffset, littleEndian) {
28727 return dataView.getUint16(dataOffset, littleEndian);
28731 // long, 32 bit int:
28733 getValue: function (dataView, dataOffset, littleEndian) {
28734 return dataView.getUint32(dataOffset, littleEndian);
28738 // rational = two long values, first is numerator, second is denominator:
28740 getValue: function (dataView, dataOffset, littleEndian) {
28741 return dataView.getUint32(dataOffset, littleEndian) /
28742 dataView.getUint32(dataOffset + 4, littleEndian);
28746 // slong, 32 bit signed int:
28748 getValue: function (dataView, dataOffset, littleEndian) {
28749 return dataView.getInt32(dataOffset, littleEndian);
28753 // srational, two slongs, first is numerator, second is denominator:
28755 getValue: function (dataView, dataOffset, littleEndian) {
28756 return dataView.getInt32(dataOffset, littleEndian) /
28757 dataView.getInt32(dataOffset + 4, littleEndian);
28767 cls : 'btn-group roo-upload-cropbox-rotate-left',
28768 action : 'rotate-left',
28772 cls : 'btn btn-default',
28773 html : '<i class="fa fa-undo"></i>'
28779 cls : 'btn-group roo-upload-cropbox-picture',
28780 action : 'picture',
28784 cls : 'btn btn-default',
28785 html : '<i class="fa fa-picture-o"></i>'
28791 cls : 'btn-group roo-upload-cropbox-rotate-right',
28792 action : 'rotate-right',
28796 cls : 'btn btn-default',
28797 html : '<i class="fa fa-repeat"></i>'
28805 cls : 'btn-group roo-upload-cropbox-rotate-left',
28806 action : 'rotate-left',
28810 cls : 'btn btn-default',
28811 html : '<i class="fa fa-undo"></i>'
28817 cls : 'btn-group roo-upload-cropbox-download',
28818 action : 'download',
28822 cls : 'btn btn-default',
28823 html : '<i class="fa fa-download"></i>'
28829 cls : 'btn-group roo-upload-cropbox-crop',
28834 cls : 'btn btn-default',
28835 html : '<i class="fa fa-crop"></i>'
28841 cls : 'btn-group roo-upload-cropbox-trash',
28846 cls : 'btn btn-default',
28847 html : '<i class="fa fa-trash"></i>'
28853 cls : 'btn-group roo-upload-cropbox-rotate-right',
28854 action : 'rotate-right',
28858 cls : 'btn btn-default',
28859 html : '<i class="fa fa-repeat"></i>'
28867 cls : 'btn-group roo-upload-cropbox-rotate-left',
28868 action : 'rotate-left',
28872 cls : 'btn btn-default',
28873 html : '<i class="fa fa-undo"></i>'
28879 cls : 'btn-group roo-upload-cropbox-rotate-right',
28880 action : 'rotate-right',
28884 cls : 'btn btn-default',
28885 html : '<i class="fa fa-repeat"></i>'
28898 * @class Roo.bootstrap.DocumentManager
28899 * @extends Roo.bootstrap.Component
28900 * Bootstrap DocumentManager class
28901 * @cfg {String} paramName default 'imageUpload'
28902 * @cfg {String} toolTipName default 'filename'
28903 * @cfg {String} method default POST
28904 * @cfg {String} url action url
28905 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28906 * @cfg {Boolean} multiple multiple upload default true
28907 * @cfg {Number} thumbSize default 300
28908 * @cfg {String} fieldLabel
28909 * @cfg {Number} labelWidth default 4
28910 * @cfg {String} labelAlign (left|top) default left
28911 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28912 * @cfg {Number} labellg set the width of label (1-12)
28913 * @cfg {Number} labelmd set the width of label (1-12)
28914 * @cfg {Number} labelsm set the width of label (1-12)
28915 * @cfg {Number} labelxs set the width of label (1-12)
28918 * Create a new DocumentManager
28919 * @param {Object} config The config object
28922 Roo.bootstrap.DocumentManager = function(config){
28923 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28926 this.delegates = [];
28931 * Fire when initial the DocumentManager
28932 * @param {Roo.bootstrap.DocumentManager} this
28937 * inspect selected file
28938 * @param {Roo.bootstrap.DocumentManager} this
28939 * @param {File} file
28944 * Fire when xhr load exception
28945 * @param {Roo.bootstrap.DocumentManager} this
28946 * @param {XMLHttpRequest} xhr
28948 "exception" : true,
28950 * @event afterupload
28951 * Fire when xhr load exception
28952 * @param {Roo.bootstrap.DocumentManager} this
28953 * @param {XMLHttpRequest} xhr
28955 "afterupload" : true,
28958 * prepare the form data
28959 * @param {Roo.bootstrap.DocumentManager} this
28960 * @param {Object} formData
28965 * Fire when remove the file
28966 * @param {Roo.bootstrap.DocumentManager} this
28967 * @param {Object} file
28972 * Fire after refresh the file
28973 * @param {Roo.bootstrap.DocumentManager} this
28978 * Fire after click the image
28979 * @param {Roo.bootstrap.DocumentManager} this
28980 * @param {Object} file
28985 * Fire when upload a image and editable set to true
28986 * @param {Roo.bootstrap.DocumentManager} this
28987 * @param {Object} file
28991 * @event beforeselectfile
28992 * Fire before select file
28993 * @param {Roo.bootstrap.DocumentManager} this
28995 "beforeselectfile" : true,
28998 * Fire before process file
28999 * @param {Roo.bootstrap.DocumentManager} this
29000 * @param {Object} file
29004 * @event previewrendered
29005 * Fire when preview rendered
29006 * @param {Roo.bootstrap.DocumentManager} this
29007 * @param {Object} file
29009 "previewrendered" : true,
29012 "previewResize" : true
29017 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29026 paramName : 'imageUpload',
29027 toolTipName : 'filename',
29030 labelAlign : 'left',
29040 getAutoCreate : function()
29042 var managerWidget = {
29044 cls : 'roo-document-manager',
29048 cls : 'roo-document-manager-selector',
29053 cls : 'roo-document-manager-uploader',
29057 cls : 'roo-document-manager-upload-btn',
29058 html : '<i class="fa fa-plus"></i>'
29069 cls : 'column col-md-12',
29074 if(this.fieldLabel.length){
29079 cls : 'column col-md-12',
29080 html : this.fieldLabel
29084 cls : 'column col-md-12',
29089 if(this.labelAlign == 'left'){
29094 html : this.fieldLabel
29103 if(this.labelWidth > 12){
29104 content[0].style = "width: " + this.labelWidth + 'px';
29107 if(this.labelWidth < 13 && this.labelmd == 0){
29108 this.labelmd = this.labelWidth;
29111 if(this.labellg > 0){
29112 content[0].cls += ' col-lg-' + this.labellg;
29113 content[1].cls += ' col-lg-' + (12 - this.labellg);
29116 if(this.labelmd > 0){
29117 content[0].cls += ' col-md-' + this.labelmd;
29118 content[1].cls += ' col-md-' + (12 - this.labelmd);
29121 if(this.labelsm > 0){
29122 content[0].cls += ' col-sm-' + this.labelsm;
29123 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29126 if(this.labelxs > 0){
29127 content[0].cls += ' col-xs-' + this.labelxs;
29128 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29136 cls : 'row clearfix',
29144 initEvents : function()
29146 this.managerEl = this.el.select('.roo-document-manager', true).first();
29147 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29149 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29150 this.selectorEl.hide();
29153 this.selectorEl.attr('multiple', 'multiple');
29156 this.selectorEl.on('change', this.onFileSelected, this);
29158 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29159 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29161 this.uploader.on('click', this.onUploaderClick, this);
29163 this.renderProgressDialog();
29167 window.addEventListener("resize", function() { _this.refresh(); } );
29169 this.fireEvent('initial', this);
29172 renderProgressDialog : function()
29176 this.progressDialog = new Roo.bootstrap.Modal({
29177 cls : 'roo-document-manager-progress-dialog',
29178 allow_close : false,
29189 btnclick : function() {
29190 _this.uploadCancel();
29196 this.progressDialog.render(Roo.get(document.body));
29198 this.progress = new Roo.bootstrap.Progress({
29199 cls : 'roo-document-manager-progress',
29204 this.progress.render(this.progressDialog.getChildContainer());
29206 this.progressBar = new Roo.bootstrap.ProgressBar({
29207 cls : 'roo-document-manager-progress-bar',
29210 aria_valuemax : 12,
29214 this.progressBar.render(this.progress.getChildContainer());
29217 onUploaderClick : function(e)
29219 e.preventDefault();
29221 if(this.fireEvent('beforeselectfile', this) != false){
29222 this.selectorEl.dom.click();
29227 onFileSelected : function(e)
29229 e.preventDefault();
29231 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29235 Roo.each(this.selectorEl.dom.files, function(file){
29236 if(this.fireEvent('inspect', this, file) != false){
29237 this.files.push(file);
29247 this.selectorEl.dom.value = '';
29249 if(!this.files || !this.files.length){
29253 if(this.boxes > 0 && this.files.length > this.boxes){
29254 this.files = this.files.slice(0, this.boxes);
29257 this.uploader.show();
29259 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29260 this.uploader.hide();
29269 Roo.each(this.files, function(file){
29271 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29272 var f = this.renderPreview(file);
29277 if(file.type.indexOf('image') != -1){
29278 this.delegates.push(
29280 _this.process(file);
29281 }).createDelegate(this)
29289 _this.process(file);
29290 }).createDelegate(this)
29295 this.files = files;
29297 this.delegates = this.delegates.concat(docs);
29299 if(!this.delegates.length){
29304 this.progressBar.aria_valuemax = this.delegates.length;
29311 arrange : function()
29313 if(!this.delegates.length){
29314 this.progressDialog.hide();
29319 var delegate = this.delegates.shift();
29321 this.progressDialog.show();
29323 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29325 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29330 refresh : function()
29332 this.uploader.show();
29334 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29335 this.uploader.hide();
29338 Roo.isTouch ? this.closable(false) : this.closable(true);
29340 this.fireEvent('refresh', this);
29343 onRemove : function(e, el, o)
29345 e.preventDefault();
29347 this.fireEvent('remove', this, o);
29351 remove : function(o)
29355 Roo.each(this.files, function(file){
29356 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29365 this.files = files;
29372 Roo.each(this.files, function(file){
29377 file.target.remove();
29386 onClick : function(e, el, o)
29388 e.preventDefault();
29390 this.fireEvent('click', this, o);
29394 closable : function(closable)
29396 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29398 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29410 xhrOnLoad : function(xhr)
29412 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29416 if (xhr.readyState !== 4) {
29418 this.fireEvent('exception', this, xhr);
29422 var response = Roo.decode(xhr.responseText);
29424 if(!response.success){
29426 this.fireEvent('exception', this, xhr);
29430 var file = this.renderPreview(response.data);
29432 this.files.push(file);
29436 this.fireEvent('afterupload', this, xhr);
29440 xhrOnError : function(xhr)
29442 Roo.log('xhr on error');
29444 var response = Roo.decode(xhr.responseText);
29451 process : function(file)
29453 if(this.fireEvent('process', this, file) !== false){
29454 if(this.editable && file.type.indexOf('image') != -1){
29455 this.fireEvent('edit', this, file);
29459 this.uploadStart(file, false);
29466 uploadStart : function(file, crop)
29468 this.xhr = new XMLHttpRequest();
29470 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29475 file.xhr = this.xhr;
29477 this.managerEl.createChild({
29479 cls : 'roo-document-manager-loading',
29483 tooltip : file.name,
29484 cls : 'roo-document-manager-thumb',
29485 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29491 this.xhr.open(this.method, this.url, true);
29494 "Accept": "application/json",
29495 "Cache-Control": "no-cache",
29496 "X-Requested-With": "XMLHttpRequest"
29499 for (var headerName in headers) {
29500 var headerValue = headers[headerName];
29502 this.xhr.setRequestHeader(headerName, headerValue);
29508 this.xhr.onload = function()
29510 _this.xhrOnLoad(_this.xhr);
29513 this.xhr.onerror = function()
29515 _this.xhrOnError(_this.xhr);
29518 var formData = new FormData();
29520 formData.append('returnHTML', 'NO');
29523 formData.append('crop', crop);
29526 formData.append(this.paramName, file, file.name);
29533 if(this.fireEvent('prepare', this, formData, options) != false){
29535 if(options.manually){
29539 this.xhr.send(formData);
29543 this.uploadCancel();
29546 uploadCancel : function()
29552 this.delegates = [];
29554 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29561 renderPreview : function(file)
29563 if(typeof(file.target) != 'undefined' && file.target){
29567 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29569 var previewEl = this.managerEl.createChild({
29571 cls : 'roo-document-manager-preview',
29575 tooltip : file[this.toolTipName],
29576 cls : 'roo-document-manager-thumb',
29577 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29582 html : '<i class="fa fa-times-circle"></i>'
29587 var close = previewEl.select('button.close', true).first();
29589 close.on('click', this.onRemove, this, file);
29591 file.target = previewEl;
29593 var image = previewEl.select('img', true).first();
29597 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29599 image.on('click', this.onClick, this, file);
29601 this.fireEvent('previewrendered', this, file);
29607 onPreviewLoad : function(file, image)
29609 if(typeof(file.target) == 'undefined' || !file.target){
29613 var width = image.dom.naturalWidth || image.dom.width;
29614 var height = image.dom.naturalHeight || image.dom.height;
29616 if(!this.previewResize) {
29620 if(width > height){
29621 file.target.addClass('wide');
29625 file.target.addClass('tall');
29630 uploadFromSource : function(file, crop)
29632 this.xhr = new XMLHttpRequest();
29634 this.managerEl.createChild({
29636 cls : 'roo-document-manager-loading',
29640 tooltip : file.name,
29641 cls : 'roo-document-manager-thumb',
29642 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29648 this.xhr.open(this.method, this.url, true);
29651 "Accept": "application/json",
29652 "Cache-Control": "no-cache",
29653 "X-Requested-With": "XMLHttpRequest"
29656 for (var headerName in headers) {
29657 var headerValue = headers[headerName];
29659 this.xhr.setRequestHeader(headerName, headerValue);
29665 this.xhr.onload = function()
29667 _this.xhrOnLoad(_this.xhr);
29670 this.xhr.onerror = function()
29672 _this.xhrOnError(_this.xhr);
29675 var formData = new FormData();
29677 formData.append('returnHTML', 'NO');
29679 formData.append('crop', crop);
29681 if(typeof(file.filename) != 'undefined'){
29682 formData.append('filename', file.filename);
29685 if(typeof(file.mimetype) != 'undefined'){
29686 formData.append('mimetype', file.mimetype);
29691 if(this.fireEvent('prepare', this, formData) != false){
29692 this.xhr.send(formData);
29702 * @class Roo.bootstrap.DocumentViewer
29703 * @extends Roo.bootstrap.Component
29704 * Bootstrap DocumentViewer class
29705 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29706 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29709 * Create a new DocumentViewer
29710 * @param {Object} config The config object
29713 Roo.bootstrap.DocumentViewer = function(config){
29714 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29719 * Fire after initEvent
29720 * @param {Roo.bootstrap.DocumentViewer} this
29726 * @param {Roo.bootstrap.DocumentViewer} this
29731 * Fire after download button
29732 * @param {Roo.bootstrap.DocumentViewer} this
29737 * Fire after trash button
29738 * @param {Roo.bootstrap.DocumentViewer} this
29745 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29747 showDownload : true,
29751 getAutoCreate : function()
29755 cls : 'roo-document-viewer',
29759 cls : 'roo-document-viewer-body',
29763 cls : 'roo-document-viewer-thumb',
29767 cls : 'roo-document-viewer-image'
29775 cls : 'roo-document-viewer-footer',
29778 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29782 cls : 'btn-group roo-document-viewer-download',
29786 cls : 'btn btn-default',
29787 html : '<i class="fa fa-download"></i>'
29793 cls : 'btn-group roo-document-viewer-trash',
29797 cls : 'btn btn-default',
29798 html : '<i class="fa fa-trash"></i>'
29811 initEvents : function()
29813 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29814 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29816 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29817 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29819 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29820 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29822 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29823 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29825 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29826 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29828 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29829 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29831 this.bodyEl.on('click', this.onClick, this);
29832 this.downloadBtn.on('click', this.onDownload, this);
29833 this.trashBtn.on('click', this.onTrash, this);
29835 this.downloadBtn.hide();
29836 this.trashBtn.hide();
29838 if(this.showDownload){
29839 this.downloadBtn.show();
29842 if(this.showTrash){
29843 this.trashBtn.show();
29846 if(!this.showDownload && !this.showTrash) {
29847 this.footerEl.hide();
29852 initial : function()
29854 this.fireEvent('initial', this);
29858 onClick : function(e)
29860 e.preventDefault();
29862 this.fireEvent('click', this);
29865 onDownload : function(e)
29867 e.preventDefault();
29869 this.fireEvent('download', this);
29872 onTrash : function(e)
29874 e.preventDefault();
29876 this.fireEvent('trash', this);
29888 * @class Roo.bootstrap.NavProgressBar
29889 * @extends Roo.bootstrap.Component
29890 * Bootstrap NavProgressBar class
29893 * Create a new nav progress bar
29894 * @param {Object} config The config object
29897 Roo.bootstrap.NavProgressBar = function(config){
29898 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29900 this.bullets = this.bullets || [];
29902 // Roo.bootstrap.NavProgressBar.register(this);
29906 * Fires when the active item changes
29907 * @param {Roo.bootstrap.NavProgressBar} this
29908 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29909 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29916 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29921 getAutoCreate : function()
29923 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29927 cls : 'roo-navigation-bar-group',
29931 cls : 'roo-navigation-top-bar'
29935 cls : 'roo-navigation-bullets-bar',
29939 cls : 'roo-navigation-bar'
29946 cls : 'roo-navigation-bottom-bar'
29956 initEvents: function()
29961 onRender : function(ct, position)
29963 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29965 if(this.bullets.length){
29966 Roo.each(this.bullets, function(b){
29975 addItem : function(cfg)
29977 var item = new Roo.bootstrap.NavProgressItem(cfg);
29979 item.parentId = this.id;
29980 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29983 var top = new Roo.bootstrap.Element({
29985 cls : 'roo-navigation-bar-text'
29988 var bottom = new Roo.bootstrap.Element({
29990 cls : 'roo-navigation-bar-text'
29993 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29994 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29996 var topText = new Roo.bootstrap.Element({
29998 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30001 var bottomText = new Roo.bootstrap.Element({
30003 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30006 topText.onRender(top.el, null);
30007 bottomText.onRender(bottom.el, null);
30010 item.bottomEl = bottom;
30013 this.barItems.push(item);
30018 getActive : function()
30020 var active = false;
30022 Roo.each(this.barItems, function(v){
30024 if (!v.isActive()) {
30036 setActiveItem : function(item)
30040 Roo.each(this.barItems, function(v){
30041 if (v.rid == item.rid) {
30045 if (v.isActive()) {
30046 v.setActive(false);
30051 item.setActive(true);
30053 this.fireEvent('changed', this, item, prev);
30056 getBarItem: function(rid)
30060 Roo.each(this.barItems, function(e) {
30061 if (e.rid != rid) {
30072 indexOfItem : function(item)
30076 Roo.each(this.barItems, function(v, i){
30078 if (v.rid != item.rid) {
30089 setActiveNext : function()
30091 var i = this.indexOfItem(this.getActive());
30093 if (i > this.barItems.length) {
30097 this.setActiveItem(this.barItems[i+1]);
30100 setActivePrev : function()
30102 var i = this.indexOfItem(this.getActive());
30108 this.setActiveItem(this.barItems[i-1]);
30111 format : function()
30113 if(!this.barItems.length){
30117 var width = 100 / this.barItems.length;
30119 Roo.each(this.barItems, function(i){
30120 i.el.setStyle('width', width + '%');
30121 i.topEl.el.setStyle('width', width + '%');
30122 i.bottomEl.el.setStyle('width', width + '%');
30131 * Nav Progress Item
30136 * @class Roo.bootstrap.NavProgressItem
30137 * @extends Roo.bootstrap.Component
30138 * Bootstrap NavProgressItem class
30139 * @cfg {String} rid the reference id
30140 * @cfg {Boolean} active (true|false) Is item active default false
30141 * @cfg {Boolean} disabled (true|false) Is item active default false
30142 * @cfg {String} html
30143 * @cfg {String} position (top|bottom) text position default bottom
30144 * @cfg {String} icon show icon instead of number
30147 * Create a new NavProgressItem
30148 * @param {Object} config The config object
30150 Roo.bootstrap.NavProgressItem = function(config){
30151 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30156 * The raw click event for the entire grid.
30157 * @param {Roo.bootstrap.NavProgressItem} this
30158 * @param {Roo.EventObject} e
30165 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30171 position : 'bottom',
30174 getAutoCreate : function()
30176 var iconCls = 'roo-navigation-bar-item-icon';
30178 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30182 cls: 'roo-navigation-bar-item',
30192 cfg.cls += ' active';
30195 cfg.cls += ' disabled';
30201 disable : function()
30203 this.setDisabled(true);
30206 enable : function()
30208 this.setDisabled(false);
30211 initEvents: function()
30213 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30215 this.iconEl.on('click', this.onClick, this);
30218 onClick : function(e)
30220 e.preventDefault();
30226 if(this.fireEvent('click', this, e) === false){
30230 this.parent().setActiveItem(this);
30233 isActive: function ()
30235 return this.active;
30238 setActive : function(state)
30240 if(this.active == state){
30244 this.active = state;
30247 this.el.addClass('active');
30251 this.el.removeClass('active');
30256 setDisabled : function(state)
30258 if(this.disabled == state){
30262 this.disabled = state;
30265 this.el.addClass('disabled');
30269 this.el.removeClass('disabled');
30272 tooltipEl : function()
30274 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30287 * @class Roo.bootstrap.FieldLabel
30288 * @extends Roo.bootstrap.Component
30289 * Bootstrap FieldLabel class
30290 * @cfg {String} html contents of the element
30291 * @cfg {String} tag tag of the element default label
30292 * @cfg {String} cls class of the element
30293 * @cfg {String} target label target
30294 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30295 * @cfg {String} invalidClass default "text-warning"
30296 * @cfg {String} validClass default "text-success"
30297 * @cfg {String} iconTooltip default "This field is required"
30298 * @cfg {String} indicatorpos (left|right) default left
30301 * Create a new FieldLabel
30302 * @param {Object} config The config object
30305 Roo.bootstrap.FieldLabel = function(config){
30306 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30311 * Fires after the field has been marked as invalid.
30312 * @param {Roo.form.FieldLabel} this
30313 * @param {String} msg The validation message
30318 * Fires after the field has been validated with no errors.
30319 * @param {Roo.form.FieldLabel} this
30325 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30332 invalidClass : 'has-warning',
30333 validClass : 'has-success',
30334 iconTooltip : 'This field is required',
30335 indicatorpos : 'left',
30337 getAutoCreate : function(){
30340 if (!this.allowBlank) {
30346 cls : 'roo-bootstrap-field-label ' + this.cls,
30351 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30352 tooltip : this.iconTooltip
30361 if(this.indicatorpos == 'right'){
30364 cls : 'roo-bootstrap-field-label ' + this.cls,
30373 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30374 tooltip : this.iconTooltip
30383 initEvents: function()
30385 Roo.bootstrap.Element.superclass.initEvents.call(this);
30387 this.indicator = this.indicatorEl();
30389 if(this.indicator){
30390 this.indicator.removeClass('visible');
30391 this.indicator.addClass('invisible');
30394 Roo.bootstrap.FieldLabel.register(this);
30397 indicatorEl : function()
30399 var indicator = this.el.select('i.roo-required-indicator',true).first();
30410 * Mark this field as valid
30412 markValid : function()
30414 if(this.indicator){
30415 this.indicator.removeClass('visible');
30416 this.indicator.addClass('invisible');
30419 this.el.removeClass(this.invalidClass);
30421 this.el.addClass(this.validClass);
30423 this.fireEvent('valid', this);
30427 * Mark this field as invalid
30428 * @param {String} msg The validation message
30430 markInvalid : function(msg)
30432 if(this.indicator){
30433 this.indicator.removeClass('invisible');
30434 this.indicator.addClass('visible');
30437 this.el.removeClass(this.validClass);
30439 this.el.addClass(this.invalidClass);
30441 this.fireEvent('invalid', this, msg);
30447 Roo.apply(Roo.bootstrap.FieldLabel, {
30452 * register a FieldLabel Group
30453 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30455 register : function(label)
30457 if(this.groups.hasOwnProperty(label.target)){
30461 this.groups[label.target] = label;
30465 * fetch a FieldLabel Group based on the target
30466 * @param {string} target
30467 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30469 get: function(target) {
30470 if (typeof(this.groups[target]) == 'undefined') {
30474 return this.groups[target] ;
30483 * page DateSplitField.
30489 * @class Roo.bootstrap.DateSplitField
30490 * @extends Roo.bootstrap.Component
30491 * Bootstrap DateSplitField class
30492 * @cfg {string} fieldLabel - the label associated
30493 * @cfg {Number} labelWidth set the width of label (0-12)
30494 * @cfg {String} labelAlign (top|left)
30495 * @cfg {Boolean} dayAllowBlank (true|false) default false
30496 * @cfg {Boolean} monthAllowBlank (true|false) default false
30497 * @cfg {Boolean} yearAllowBlank (true|false) default false
30498 * @cfg {string} dayPlaceholder
30499 * @cfg {string} monthPlaceholder
30500 * @cfg {string} yearPlaceholder
30501 * @cfg {string} dayFormat default 'd'
30502 * @cfg {string} monthFormat default 'm'
30503 * @cfg {string} yearFormat default 'Y'
30504 * @cfg {Number} labellg set the width of label (1-12)
30505 * @cfg {Number} labelmd set the width of label (1-12)
30506 * @cfg {Number} labelsm set the width of label (1-12)
30507 * @cfg {Number} labelxs set the width of label (1-12)
30511 * Create a new DateSplitField
30512 * @param {Object} config The config object
30515 Roo.bootstrap.DateSplitField = function(config){
30516 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30522 * getting the data of years
30523 * @param {Roo.bootstrap.DateSplitField} this
30524 * @param {Object} years
30529 * getting the data of days
30530 * @param {Roo.bootstrap.DateSplitField} this
30531 * @param {Object} days
30536 * Fires after the field has been marked as invalid.
30537 * @param {Roo.form.Field} this
30538 * @param {String} msg The validation message
30543 * Fires after the field has been validated with no errors.
30544 * @param {Roo.form.Field} this
30550 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30553 labelAlign : 'top',
30555 dayAllowBlank : false,
30556 monthAllowBlank : false,
30557 yearAllowBlank : false,
30558 dayPlaceholder : '',
30559 monthPlaceholder : '',
30560 yearPlaceholder : '',
30564 isFormField : true,
30570 getAutoCreate : function()
30574 cls : 'row roo-date-split-field-group',
30579 cls : 'form-hidden-field roo-date-split-field-group-value',
30585 var labelCls = 'col-md-12';
30586 var contentCls = 'col-md-4';
30588 if(this.fieldLabel){
30592 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30596 html : this.fieldLabel
30601 if(this.labelAlign == 'left'){
30603 if(this.labelWidth > 12){
30604 label.style = "width: " + this.labelWidth + 'px';
30607 if(this.labelWidth < 13 && this.labelmd == 0){
30608 this.labelmd = this.labelWidth;
30611 if(this.labellg > 0){
30612 labelCls = ' col-lg-' + this.labellg;
30613 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30616 if(this.labelmd > 0){
30617 labelCls = ' col-md-' + this.labelmd;
30618 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30621 if(this.labelsm > 0){
30622 labelCls = ' col-sm-' + this.labelsm;
30623 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30626 if(this.labelxs > 0){
30627 labelCls = ' col-xs-' + this.labelxs;
30628 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30632 label.cls += ' ' + labelCls;
30634 cfg.cn.push(label);
30637 Roo.each(['day', 'month', 'year'], function(t){
30640 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30647 inputEl: function ()
30649 return this.el.select('.roo-date-split-field-group-value', true).first();
30652 onRender : function(ct, position)
30656 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30658 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30660 this.dayField = new Roo.bootstrap.ComboBox({
30661 allowBlank : this.dayAllowBlank,
30662 alwaysQuery : true,
30663 displayField : 'value',
30666 forceSelection : true,
30668 placeholder : this.dayPlaceholder,
30669 selectOnFocus : true,
30670 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30671 triggerAction : 'all',
30673 valueField : 'value',
30674 store : new Roo.data.SimpleStore({
30675 data : (function() {
30677 _this.fireEvent('days', _this, days);
30680 fields : [ 'value' ]
30683 select : function (_self, record, index)
30685 _this.setValue(_this.getValue());
30690 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30692 this.monthField = new Roo.bootstrap.MonthField({
30693 after : '<i class=\"fa fa-calendar\"></i>',
30694 allowBlank : this.monthAllowBlank,
30695 placeholder : this.monthPlaceholder,
30698 render : function (_self)
30700 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30701 e.preventDefault();
30705 select : function (_self, oldvalue, newvalue)
30707 _this.setValue(_this.getValue());
30712 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30714 this.yearField = new Roo.bootstrap.ComboBox({
30715 allowBlank : this.yearAllowBlank,
30716 alwaysQuery : true,
30717 displayField : 'value',
30720 forceSelection : true,
30722 placeholder : this.yearPlaceholder,
30723 selectOnFocus : true,
30724 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30725 triggerAction : 'all',
30727 valueField : 'value',
30728 store : new Roo.data.SimpleStore({
30729 data : (function() {
30731 _this.fireEvent('years', _this, years);
30734 fields : [ 'value' ]
30737 select : function (_self, record, index)
30739 _this.setValue(_this.getValue());
30744 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30747 setValue : function(v, format)
30749 this.inputEl.dom.value = v;
30751 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30753 var d = Date.parseDate(v, f);
30760 this.setDay(d.format(this.dayFormat));
30761 this.setMonth(d.format(this.monthFormat));
30762 this.setYear(d.format(this.yearFormat));
30769 setDay : function(v)
30771 this.dayField.setValue(v);
30772 this.inputEl.dom.value = this.getValue();
30777 setMonth : function(v)
30779 this.monthField.setValue(v, true);
30780 this.inputEl.dom.value = this.getValue();
30785 setYear : function(v)
30787 this.yearField.setValue(v);
30788 this.inputEl.dom.value = this.getValue();
30793 getDay : function()
30795 return this.dayField.getValue();
30798 getMonth : function()
30800 return this.monthField.getValue();
30803 getYear : function()
30805 return this.yearField.getValue();
30808 getValue : function()
30810 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30812 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30822 this.inputEl.dom.value = '';
30827 validate : function()
30829 var d = this.dayField.validate();
30830 var m = this.monthField.validate();
30831 var y = this.yearField.validate();
30836 (!this.dayAllowBlank && !d) ||
30837 (!this.monthAllowBlank && !m) ||
30838 (!this.yearAllowBlank && !y)
30843 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30852 this.markInvalid();
30857 markValid : function()
30860 var label = this.el.select('label', true).first();
30861 var icon = this.el.select('i.fa-star', true).first();
30867 this.fireEvent('valid', this);
30871 * Mark this field as invalid
30872 * @param {String} msg The validation message
30874 markInvalid : function(msg)
30877 var label = this.el.select('label', true).first();
30878 var icon = this.el.select('i.fa-star', true).first();
30880 if(label && !icon){
30881 this.el.select('.roo-date-split-field-label', true).createChild({
30883 cls : 'text-danger fa fa-lg fa-star',
30884 tooltip : 'This field is required',
30885 style : 'margin-right:5px;'
30889 this.fireEvent('invalid', this, msg);
30892 clearInvalid : function()
30894 var label = this.el.select('label', true).first();
30895 var icon = this.el.select('i.fa-star', true).first();
30901 this.fireEvent('valid', this);
30904 getName: function()
30914 * http://masonry.desandro.com
30916 * The idea is to render all the bricks based on vertical width...
30918 * The original code extends 'outlayer' - we might need to use that....
30924 * @class Roo.bootstrap.LayoutMasonry
30925 * @extends Roo.bootstrap.Component
30926 * Bootstrap Layout Masonry class
30929 * Create a new Element
30930 * @param {Object} config The config object
30933 Roo.bootstrap.LayoutMasonry = function(config){
30935 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30939 Roo.bootstrap.LayoutMasonry.register(this);
30945 * Fire after layout the items
30946 * @param {Roo.bootstrap.LayoutMasonry} this
30947 * @param {Roo.EventObject} e
30954 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30957 * @cfg {Boolean} isLayoutInstant = no animation?
30959 isLayoutInstant : false, // needed?
30962 * @cfg {Number} boxWidth width of the columns
30967 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30972 * @cfg {Number} padWidth padding below box..
30977 * @cfg {Number} gutter gutter width..
30982 * @cfg {Number} maxCols maximum number of columns
30988 * @cfg {Boolean} isAutoInitial defalut true
30990 isAutoInitial : true,
30995 * @cfg {Boolean} isHorizontal defalut false
30997 isHorizontal : false,
30999 currentSize : null,
31005 bricks: null, //CompositeElement
31009 _isLayoutInited : false,
31011 // isAlternative : false, // only use for vertical layout...
31014 * @cfg {Number} alternativePadWidth padding below box..
31016 alternativePadWidth : 50,
31018 selectedBrick : [],
31020 getAutoCreate : function(){
31022 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31026 cls: 'blog-masonary-wrapper ' + this.cls,
31028 cls : 'mas-boxes masonary'
31035 getChildContainer: function( )
31037 if (this.boxesEl) {
31038 return this.boxesEl;
31041 this.boxesEl = this.el.select('.mas-boxes').first();
31043 return this.boxesEl;
31047 initEvents : function()
31051 if(this.isAutoInitial){
31052 Roo.log('hook children rendered');
31053 this.on('childrenrendered', function() {
31054 Roo.log('children rendered');
31060 initial : function()
31062 this.selectedBrick = [];
31064 this.currentSize = this.el.getBox(true);
31066 Roo.EventManager.onWindowResize(this.resize, this);
31068 if(!this.isAutoInitial){
31076 //this.layout.defer(500,this);
31080 resize : function()
31082 var cs = this.el.getBox(true);
31085 this.currentSize.width == cs.width &&
31086 this.currentSize.x == cs.x &&
31087 this.currentSize.height == cs.height &&
31088 this.currentSize.y == cs.y
31090 Roo.log("no change in with or X or Y");
31094 this.currentSize = cs;
31100 layout : function()
31102 this._resetLayout();
31104 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31106 this.layoutItems( isInstant );
31108 this._isLayoutInited = true;
31110 this.fireEvent('layout', this);
31114 _resetLayout : function()
31116 if(this.isHorizontal){
31117 this.horizontalMeasureColumns();
31121 this.verticalMeasureColumns();
31125 verticalMeasureColumns : function()
31127 this.getContainerWidth();
31129 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31130 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31134 var boxWidth = this.boxWidth + this.padWidth;
31136 if(this.containerWidth < this.boxWidth){
31137 boxWidth = this.containerWidth
31140 var containerWidth = this.containerWidth;
31142 var cols = Math.floor(containerWidth / boxWidth);
31144 this.cols = Math.max( cols, 1 );
31146 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31148 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31150 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31152 this.colWidth = boxWidth + avail - this.padWidth;
31154 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31155 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31158 horizontalMeasureColumns : function()
31160 this.getContainerWidth();
31162 var boxWidth = this.boxWidth;
31164 if(this.containerWidth < boxWidth){
31165 boxWidth = this.containerWidth;
31168 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31170 this.el.setHeight(boxWidth);
31174 getContainerWidth : function()
31176 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31179 layoutItems : function( isInstant )
31181 Roo.log(this.bricks);
31183 var items = Roo.apply([], this.bricks);
31185 if(this.isHorizontal){
31186 this._horizontalLayoutItems( items , isInstant );
31190 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31191 // this._verticalAlternativeLayoutItems( items , isInstant );
31195 this._verticalLayoutItems( items , isInstant );
31199 _verticalLayoutItems : function ( items , isInstant)
31201 if ( !items || !items.length ) {
31206 ['xs', 'xs', 'xs', 'tall'],
31207 ['xs', 'xs', 'tall'],
31208 ['xs', 'xs', 'sm'],
31209 ['xs', 'xs', 'xs'],
31215 ['sm', 'xs', 'xs'],
31219 ['tall', 'xs', 'xs', 'xs'],
31220 ['tall', 'xs', 'xs'],
31232 Roo.each(items, function(item, k){
31234 switch (item.size) {
31235 // these layouts take up a full box,
31246 boxes.push([item]);
31269 var filterPattern = function(box, length)
31277 var pattern = box.slice(0, length);
31281 Roo.each(pattern, function(i){
31282 format.push(i.size);
31285 Roo.each(standard, function(s){
31287 if(String(s) != String(format)){
31296 if(!match && length == 1){
31301 filterPattern(box, length - 1);
31305 queue.push(pattern);
31307 box = box.slice(length, box.length);
31309 filterPattern(box, 4);
31315 Roo.each(boxes, function(box, k){
31321 if(box.length == 1){
31326 filterPattern(box, 4);
31330 this._processVerticalLayoutQueue( queue, isInstant );
31334 // _verticalAlternativeLayoutItems : function( items , isInstant )
31336 // if ( !items || !items.length ) {
31340 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31344 _horizontalLayoutItems : function ( items , isInstant)
31346 if ( !items || !items.length || items.length < 3) {
31352 var eItems = items.slice(0, 3);
31354 items = items.slice(3, items.length);
31357 ['xs', 'xs', 'xs', 'wide'],
31358 ['xs', 'xs', 'wide'],
31359 ['xs', 'xs', 'sm'],
31360 ['xs', 'xs', 'xs'],
31366 ['sm', 'xs', 'xs'],
31370 ['wide', 'xs', 'xs', 'xs'],
31371 ['wide', 'xs', 'xs'],
31384 Roo.each(items, function(item, k){
31386 switch (item.size) {
31397 boxes.push([item]);
31421 var filterPattern = function(box, length)
31429 var pattern = box.slice(0, length);
31433 Roo.each(pattern, function(i){
31434 format.push(i.size);
31437 Roo.each(standard, function(s){
31439 if(String(s) != String(format)){
31448 if(!match && length == 1){
31453 filterPattern(box, length - 1);
31457 queue.push(pattern);
31459 box = box.slice(length, box.length);
31461 filterPattern(box, 4);
31467 Roo.each(boxes, function(box, k){
31473 if(box.length == 1){
31478 filterPattern(box, 4);
31485 var pos = this.el.getBox(true);
31489 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31491 var hit_end = false;
31493 Roo.each(queue, function(box){
31497 Roo.each(box, function(b){
31499 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31509 Roo.each(box, function(b){
31511 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31514 mx = Math.max(mx, b.x);
31518 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31522 Roo.each(box, function(b){
31524 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31538 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31541 /** Sets position of item in DOM
31542 * @param {Element} item
31543 * @param {Number} x - horizontal position
31544 * @param {Number} y - vertical position
31545 * @param {Boolean} isInstant - disables transitions
31547 _processVerticalLayoutQueue : function( queue, isInstant )
31549 var pos = this.el.getBox(true);
31554 for (var i = 0; i < this.cols; i++){
31558 Roo.each(queue, function(box, k){
31560 var col = k % this.cols;
31562 Roo.each(box, function(b,kk){
31564 b.el.position('absolute');
31566 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31567 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31569 if(b.size == 'md-left' || b.size == 'md-right'){
31570 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31571 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31574 b.el.setWidth(width);
31575 b.el.setHeight(height);
31577 b.el.select('iframe',true).setSize(width,height);
31581 for (var i = 0; i < this.cols; i++){
31583 if(maxY[i] < maxY[col]){
31588 col = Math.min(col, i);
31592 x = pos.x + col * (this.colWidth + this.padWidth);
31596 var positions = [];
31598 switch (box.length){
31600 positions = this.getVerticalOneBoxColPositions(x, y, box);
31603 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31606 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31609 positions = this.getVerticalFourBoxColPositions(x, y, box);
31615 Roo.each(box, function(b,kk){
31617 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31619 var sz = b.el.getSize();
31621 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31629 for (var i = 0; i < this.cols; i++){
31630 mY = Math.max(mY, maxY[i]);
31633 this.el.setHeight(mY - pos.y);
31637 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31639 // var pos = this.el.getBox(true);
31642 // var maxX = pos.right;
31644 // var maxHeight = 0;
31646 // Roo.each(items, function(item, k){
31650 // item.el.position('absolute');
31652 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31654 // item.el.setWidth(width);
31656 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31658 // item.el.setHeight(height);
31661 // item.el.setXY([x, y], isInstant ? false : true);
31663 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31666 // y = y + height + this.alternativePadWidth;
31668 // maxHeight = maxHeight + height + this.alternativePadWidth;
31672 // this.el.setHeight(maxHeight);
31676 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31678 var pos = this.el.getBox(true);
31683 var maxX = pos.right;
31685 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31687 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31689 Roo.each(queue, function(box, k){
31691 Roo.each(box, function(b, kk){
31693 b.el.position('absolute');
31695 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31696 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31698 if(b.size == 'md-left' || b.size == 'md-right'){
31699 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31700 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31703 b.el.setWidth(width);
31704 b.el.setHeight(height);
31712 var positions = [];
31714 switch (box.length){
31716 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31719 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31722 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31725 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31731 Roo.each(box, function(b,kk){
31733 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31735 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31743 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31745 Roo.each(eItems, function(b,k){
31747 b.size = (k == 0) ? 'sm' : 'xs';
31748 b.x = (k == 0) ? 2 : 1;
31749 b.y = (k == 0) ? 2 : 1;
31751 b.el.position('absolute');
31753 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31755 b.el.setWidth(width);
31757 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31759 b.el.setHeight(height);
31763 var positions = [];
31766 x : maxX - this.unitWidth * 2 - this.gutter,
31771 x : maxX - this.unitWidth,
31772 y : minY + (this.unitWidth + this.gutter) * 2
31776 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31780 Roo.each(eItems, function(b,k){
31782 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31788 getVerticalOneBoxColPositions : function(x, y, box)
31792 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31794 if(box[0].size == 'md-left'){
31798 if(box[0].size == 'md-right'){
31803 x : x + (this.unitWidth + this.gutter) * rand,
31810 getVerticalTwoBoxColPositions : function(x, y, box)
31814 if(box[0].size == 'xs'){
31818 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31822 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31836 x : x + (this.unitWidth + this.gutter) * 2,
31837 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31844 getVerticalThreeBoxColPositions : function(x, y, box)
31848 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31856 x : x + (this.unitWidth + this.gutter) * 1,
31861 x : x + (this.unitWidth + this.gutter) * 2,
31869 if(box[0].size == 'xs' && box[1].size == 'xs'){
31878 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31882 x : x + (this.unitWidth + this.gutter) * 1,
31896 x : x + (this.unitWidth + this.gutter) * 2,
31901 x : x + (this.unitWidth + this.gutter) * 2,
31902 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31909 getVerticalFourBoxColPositions : function(x, y, box)
31913 if(box[0].size == 'xs'){
31922 y : y + (this.unitHeight + this.gutter) * 1
31927 y : y + (this.unitHeight + this.gutter) * 2
31931 x : x + (this.unitWidth + this.gutter) * 1,
31945 x : x + (this.unitWidth + this.gutter) * 2,
31950 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31951 y : y + (this.unitHeight + this.gutter) * 1
31955 x : x + (this.unitWidth + this.gutter) * 2,
31956 y : y + (this.unitWidth + this.gutter) * 2
31963 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31967 if(box[0].size == 'md-left'){
31969 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31976 if(box[0].size == 'md-right'){
31978 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31979 y : minY + (this.unitWidth + this.gutter) * 1
31985 var rand = Math.floor(Math.random() * (4 - box[0].y));
31988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31989 y : minY + (this.unitWidth + this.gutter) * rand
31996 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32000 if(box[0].size == 'xs'){
32003 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32008 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32009 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32017 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32022 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32023 y : minY + (this.unitWidth + this.gutter) * 2
32030 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32034 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32037 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32042 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32043 y : minY + (this.unitWidth + this.gutter) * 1
32047 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32048 y : minY + (this.unitWidth + this.gutter) * 2
32055 if(box[0].size == 'xs' && box[1].size == 'xs'){
32058 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32063 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32068 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32069 y : minY + (this.unitWidth + this.gutter) * 1
32077 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32082 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32083 y : minY + (this.unitWidth + this.gutter) * 2
32087 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32088 y : minY + (this.unitWidth + this.gutter) * 2
32095 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32099 if(box[0].size == 'xs'){
32102 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32107 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32112 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),
32117 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32118 y : minY + (this.unitWidth + this.gutter) * 1
32126 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32131 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32132 y : minY + (this.unitWidth + this.gutter) * 2
32136 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32137 y : minY + (this.unitWidth + this.gutter) * 2
32141 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),
32142 y : minY + (this.unitWidth + this.gutter) * 2
32150 * remove a Masonry Brick
32151 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32153 removeBrick : function(brick_id)
32159 for (var i = 0; i<this.bricks.length; i++) {
32160 if (this.bricks[i].id == brick_id) {
32161 this.bricks.splice(i,1);
32162 this.el.dom.removeChild(Roo.get(brick_id).dom);
32169 * adds a Masonry Brick
32170 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32172 addBrick : function(cfg)
32174 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32175 //this.register(cn);
32176 cn.parentId = this.id;
32177 cn.render(this.el);
32182 * register a Masonry Brick
32183 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32186 register : function(brick)
32188 this.bricks.push(brick);
32189 brick.masonryId = this.id;
32193 * clear all the Masonry Brick
32195 clearAll : function()
32198 //this.getChildContainer().dom.innerHTML = "";
32199 this.el.dom.innerHTML = '';
32202 getSelected : function()
32204 if (!this.selectedBrick) {
32208 return this.selectedBrick;
32212 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32216 * register a Masonry Layout
32217 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32220 register : function(layout)
32222 this.groups[layout.id] = layout;
32225 * fetch a Masonry Layout based on the masonry layout ID
32226 * @param {string} the masonry layout to add
32227 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32230 get: function(layout_id) {
32231 if (typeof(this.groups[layout_id]) == 'undefined') {
32234 return this.groups[layout_id] ;
32246 * http://masonry.desandro.com
32248 * The idea is to render all the bricks based on vertical width...
32250 * The original code extends 'outlayer' - we might need to use that....
32256 * @class Roo.bootstrap.LayoutMasonryAuto
32257 * @extends Roo.bootstrap.Component
32258 * Bootstrap Layout Masonry class
32261 * Create a new Element
32262 * @param {Object} config The config object
32265 Roo.bootstrap.LayoutMasonryAuto = function(config){
32266 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32269 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32272 * @cfg {Boolean} isFitWidth - resize the width..
32274 isFitWidth : false, // options..
32276 * @cfg {Boolean} isOriginLeft = left align?
32278 isOriginLeft : true,
32280 * @cfg {Boolean} isOriginTop = top align?
32282 isOriginTop : false,
32284 * @cfg {Boolean} isLayoutInstant = no animation?
32286 isLayoutInstant : false, // needed?
32288 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32290 isResizingContainer : true,
32292 * @cfg {Number} columnWidth width of the columns
32298 * @cfg {Number} maxCols maximum number of columns
32303 * @cfg {Number} padHeight padding below box..
32309 * @cfg {Boolean} isAutoInitial defalut true
32312 isAutoInitial : true,
32318 initialColumnWidth : 0,
32319 currentSize : null,
32321 colYs : null, // array.
32328 bricks: null, //CompositeElement
32329 cols : 0, // array?
32330 // element : null, // wrapped now this.el
32331 _isLayoutInited : null,
32334 getAutoCreate : function(){
32338 cls: 'blog-masonary-wrapper ' + this.cls,
32340 cls : 'mas-boxes masonary'
32347 getChildContainer: function( )
32349 if (this.boxesEl) {
32350 return this.boxesEl;
32353 this.boxesEl = this.el.select('.mas-boxes').first();
32355 return this.boxesEl;
32359 initEvents : function()
32363 if(this.isAutoInitial){
32364 Roo.log('hook children rendered');
32365 this.on('childrenrendered', function() {
32366 Roo.log('children rendered');
32373 initial : function()
32375 this.reloadItems();
32377 this.currentSize = this.el.getBox(true);
32379 /// was window resize... - let's see if this works..
32380 Roo.EventManager.onWindowResize(this.resize, this);
32382 if(!this.isAutoInitial){
32387 this.layout.defer(500,this);
32390 reloadItems: function()
32392 this.bricks = this.el.select('.masonry-brick', true);
32394 this.bricks.each(function(b) {
32395 //Roo.log(b.getSize());
32396 if (!b.attr('originalwidth')) {
32397 b.attr('originalwidth', b.getSize().width);
32402 Roo.log(this.bricks.elements.length);
32405 resize : function()
32408 var cs = this.el.getBox(true);
32410 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32411 Roo.log("no change in with or X");
32414 this.currentSize = cs;
32418 layout : function()
32421 this._resetLayout();
32422 //this._manageStamps();
32424 // don't animate first layout
32425 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32426 this.layoutItems( isInstant );
32428 // flag for initalized
32429 this._isLayoutInited = true;
32432 layoutItems : function( isInstant )
32434 //var items = this._getItemsForLayout( this.items );
32435 // original code supports filtering layout items.. we just ignore it..
32437 this._layoutItems( this.bricks , isInstant );
32439 this._postLayout();
32441 _layoutItems : function ( items , isInstant)
32443 //this.fireEvent( 'layout', this, items );
32446 if ( !items || !items.elements.length ) {
32447 // no items, emit event with empty array
32452 items.each(function(item) {
32453 Roo.log("layout item");
32455 // get x/y object from method
32456 var position = this._getItemLayoutPosition( item );
32458 position.item = item;
32459 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32460 queue.push( position );
32463 this._processLayoutQueue( queue );
32465 /** Sets position of item in DOM
32466 * @param {Element} item
32467 * @param {Number} x - horizontal position
32468 * @param {Number} y - vertical position
32469 * @param {Boolean} isInstant - disables transitions
32471 _processLayoutQueue : function( queue )
32473 for ( var i=0, len = queue.length; i < len; i++ ) {
32474 var obj = queue[i];
32475 obj.item.position('absolute');
32476 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32482 * Any logic you want to do after each layout,
32483 * i.e. size the container
32485 _postLayout : function()
32487 this.resizeContainer();
32490 resizeContainer : function()
32492 if ( !this.isResizingContainer ) {
32495 var size = this._getContainerSize();
32497 this.el.setSize(size.width,size.height);
32498 this.boxesEl.setSize(size.width,size.height);
32504 _resetLayout : function()
32506 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32507 this.colWidth = this.el.getWidth();
32508 //this.gutter = this.el.getWidth();
32510 this.measureColumns();
32516 this.colYs.push( 0 );
32522 measureColumns : function()
32524 this.getContainerWidth();
32525 // if columnWidth is 0, default to outerWidth of first item
32526 if ( !this.columnWidth ) {
32527 var firstItem = this.bricks.first();
32528 Roo.log(firstItem);
32529 this.columnWidth = this.containerWidth;
32530 if (firstItem && firstItem.attr('originalwidth') ) {
32531 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32533 // columnWidth fall back to item of first element
32534 Roo.log("set column width?");
32535 this.initialColumnWidth = this.columnWidth ;
32537 // if first elem has no width, default to size of container
32542 if (this.initialColumnWidth) {
32543 this.columnWidth = this.initialColumnWidth;
32548 // column width is fixed at the top - however if container width get's smaller we should
32551 // this bit calcs how man columns..
32553 var columnWidth = this.columnWidth += this.gutter;
32555 // calculate columns
32556 var containerWidth = this.containerWidth + this.gutter;
32558 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32559 // fix rounding errors, typically with gutters
32560 var excess = columnWidth - containerWidth % columnWidth;
32563 // if overshoot is less than a pixel, round up, otherwise floor it
32564 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32565 cols = Math[ mathMethod ]( cols );
32566 this.cols = Math.max( cols, 1 );
32567 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32569 // padding positioning..
32570 var totalColWidth = this.cols * this.columnWidth;
32571 var padavail = this.containerWidth - totalColWidth;
32572 // so for 2 columns - we need 3 'pads'
32574 var padNeeded = (1+this.cols) * this.padWidth;
32576 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32578 this.columnWidth += padExtra
32579 //this.padWidth = Math.floor(padavail / ( this.cols));
32581 // adjust colum width so that padding is fixed??
32583 // we have 3 columns ... total = width * 3
32584 // we have X left over... that should be used by
32586 //if (this.expandC) {
32594 getContainerWidth : function()
32596 /* // container is parent if fit width
32597 var container = this.isFitWidth ? this.element.parentNode : this.element;
32598 // check that this.size and size are there
32599 // IE8 triggers resize on body size change, so they might not be
32601 var size = getSize( container ); //FIXME
32602 this.containerWidth = size && size.innerWidth; //FIXME
32605 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32609 _getItemLayoutPosition : function( item ) // what is item?
32611 // we resize the item to our columnWidth..
32613 item.setWidth(this.columnWidth);
32614 item.autoBoxAdjust = false;
32616 var sz = item.getSize();
32618 // how many columns does this brick span
32619 var remainder = this.containerWidth % this.columnWidth;
32621 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32622 // round if off by 1 pixel, otherwise use ceil
32623 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32624 colSpan = Math.min( colSpan, this.cols );
32626 // normally this should be '1' as we dont' currently allow multi width columns..
32628 var colGroup = this._getColGroup( colSpan );
32629 // get the minimum Y value from the columns
32630 var minimumY = Math.min.apply( Math, colGroup );
32631 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32633 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32635 // position the brick
32637 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32638 y: this.currentSize.y + minimumY + this.padHeight
32642 // apply setHeight to necessary columns
32643 var setHeight = minimumY + sz.height + this.padHeight;
32644 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32646 var setSpan = this.cols + 1 - colGroup.length;
32647 for ( var i = 0; i < setSpan; i++ ) {
32648 this.colYs[ shortColIndex + i ] = setHeight ;
32655 * @param {Number} colSpan - number of columns the element spans
32656 * @returns {Array} colGroup
32658 _getColGroup : function( colSpan )
32660 if ( colSpan < 2 ) {
32661 // if brick spans only one column, use all the column Ys
32666 // how many different places could this brick fit horizontally
32667 var groupCount = this.cols + 1 - colSpan;
32668 // for each group potential horizontal position
32669 for ( var i = 0; i < groupCount; i++ ) {
32670 // make an array of colY values for that one group
32671 var groupColYs = this.colYs.slice( i, i + colSpan );
32672 // and get the max value of the array
32673 colGroup[i] = Math.max.apply( Math, groupColYs );
32678 _manageStamp : function( stamp )
32680 var stampSize = stamp.getSize();
32681 var offset = stamp.getBox();
32682 // get the columns that this stamp affects
32683 var firstX = this.isOriginLeft ? offset.x : offset.right;
32684 var lastX = firstX + stampSize.width;
32685 var firstCol = Math.floor( firstX / this.columnWidth );
32686 firstCol = Math.max( 0, firstCol );
32688 var lastCol = Math.floor( lastX / this.columnWidth );
32689 // lastCol should not go over if multiple of columnWidth #425
32690 lastCol -= lastX % this.columnWidth ? 0 : 1;
32691 lastCol = Math.min( this.cols - 1, lastCol );
32693 // set colYs to bottom of the stamp
32694 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32697 for ( var i = firstCol; i <= lastCol; i++ ) {
32698 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32703 _getContainerSize : function()
32705 this.maxY = Math.max.apply( Math, this.colYs );
32710 if ( this.isFitWidth ) {
32711 size.width = this._getContainerFitWidth();
32717 _getContainerFitWidth : function()
32719 var unusedCols = 0;
32720 // count unused columns
32723 if ( this.colYs[i] !== 0 ) {
32728 // fit container to columns that have been used
32729 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32732 needsResizeLayout : function()
32734 var previousWidth = this.containerWidth;
32735 this.getContainerWidth();
32736 return previousWidth !== this.containerWidth;
32751 * @class Roo.bootstrap.MasonryBrick
32752 * @extends Roo.bootstrap.Component
32753 * Bootstrap MasonryBrick class
32756 * Create a new MasonryBrick
32757 * @param {Object} config The config object
32760 Roo.bootstrap.MasonryBrick = function(config){
32762 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32764 Roo.bootstrap.MasonryBrick.register(this);
32770 * When a MasonryBrick is clcik
32771 * @param {Roo.bootstrap.MasonryBrick} this
32772 * @param {Roo.EventObject} e
32778 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32781 * @cfg {String} title
32785 * @cfg {String} html
32789 * @cfg {String} bgimage
32793 * @cfg {String} videourl
32797 * @cfg {String} cls
32801 * @cfg {String} href
32805 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32810 * @cfg {String} placetitle (center|bottom)
32815 * @cfg {Boolean} isFitContainer defalut true
32817 isFitContainer : true,
32820 * @cfg {Boolean} preventDefault defalut false
32822 preventDefault : false,
32825 * @cfg {Boolean} inverse defalut false
32827 maskInverse : false,
32829 getAutoCreate : function()
32831 if(!this.isFitContainer){
32832 return this.getSplitAutoCreate();
32835 var cls = 'masonry-brick masonry-brick-full';
32837 if(this.href.length){
32838 cls += ' masonry-brick-link';
32841 if(this.bgimage.length){
32842 cls += ' masonry-brick-image';
32845 if(this.maskInverse){
32846 cls += ' mask-inverse';
32849 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32850 cls += ' enable-mask';
32854 cls += ' masonry-' + this.size + '-brick';
32857 if(this.placetitle.length){
32859 switch (this.placetitle) {
32861 cls += ' masonry-center-title';
32864 cls += ' masonry-bottom-title';
32871 if(!this.html.length && !this.bgimage.length){
32872 cls += ' masonry-center-title';
32875 if(!this.html.length && this.bgimage.length){
32876 cls += ' masonry-bottom-title';
32881 cls += ' ' + this.cls;
32885 tag: (this.href.length) ? 'a' : 'div',
32890 cls: 'masonry-brick-mask'
32894 cls: 'masonry-brick-paragraph',
32900 if(this.href.length){
32901 cfg.href = this.href;
32904 var cn = cfg.cn[1].cn;
32906 if(this.title.length){
32909 cls: 'masonry-brick-title',
32914 if(this.html.length){
32917 cls: 'masonry-brick-text',
32922 if (!this.title.length && !this.html.length) {
32923 cfg.cn[1].cls += ' hide';
32926 if(this.bgimage.length){
32929 cls: 'masonry-brick-image-view',
32934 if(this.videourl.length){
32935 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32936 // youtube support only?
32939 cls: 'masonry-brick-image-view',
32942 allowfullscreen : true
32950 getSplitAutoCreate : function()
32952 var cls = 'masonry-brick masonry-brick-split';
32954 if(this.href.length){
32955 cls += ' masonry-brick-link';
32958 if(this.bgimage.length){
32959 cls += ' masonry-brick-image';
32963 cls += ' masonry-' + this.size + '-brick';
32966 switch (this.placetitle) {
32968 cls += ' masonry-center-title';
32971 cls += ' masonry-bottom-title';
32974 if(!this.bgimage.length){
32975 cls += ' masonry-center-title';
32978 if(this.bgimage.length){
32979 cls += ' masonry-bottom-title';
32985 cls += ' ' + this.cls;
32989 tag: (this.href.length) ? 'a' : 'div',
32994 cls: 'masonry-brick-split-head',
32998 cls: 'masonry-brick-paragraph',
33005 cls: 'masonry-brick-split-body',
33011 if(this.href.length){
33012 cfg.href = this.href;
33015 if(this.title.length){
33016 cfg.cn[0].cn[0].cn.push({
33018 cls: 'masonry-brick-title',
33023 if(this.html.length){
33024 cfg.cn[1].cn.push({
33026 cls: 'masonry-brick-text',
33031 if(this.bgimage.length){
33032 cfg.cn[0].cn.push({
33034 cls: 'masonry-brick-image-view',
33039 if(this.videourl.length){
33040 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33041 // youtube support only?
33042 cfg.cn[0].cn.cn.push({
33044 cls: 'masonry-brick-image-view',
33047 allowfullscreen : true
33054 initEvents: function()
33056 switch (this.size) {
33089 this.el.on('touchstart', this.onTouchStart, this);
33090 this.el.on('touchmove', this.onTouchMove, this);
33091 this.el.on('touchend', this.onTouchEnd, this);
33092 this.el.on('contextmenu', this.onContextMenu, this);
33094 this.el.on('mouseenter' ,this.enter, this);
33095 this.el.on('mouseleave', this.leave, this);
33096 this.el.on('click', this.onClick, this);
33099 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33100 this.parent().bricks.push(this);
33105 onClick: function(e, el)
33107 var time = this.endTimer - this.startTimer;
33108 // Roo.log(e.preventDefault());
33111 e.preventDefault();
33116 if(!this.preventDefault){
33120 e.preventDefault();
33122 if (this.activeClass != '') {
33123 this.selectBrick();
33126 this.fireEvent('click', this, e);
33129 enter: function(e, el)
33131 e.preventDefault();
33133 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33137 if(this.bgimage.length && this.html.length){
33138 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33142 leave: function(e, el)
33144 e.preventDefault();
33146 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33150 if(this.bgimage.length && this.html.length){
33151 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33155 onTouchStart: function(e, el)
33157 // e.preventDefault();
33159 this.touchmoved = false;
33161 if(!this.isFitContainer){
33165 if(!this.bgimage.length || !this.html.length){
33169 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33171 this.timer = new Date().getTime();
33175 onTouchMove: function(e, el)
33177 this.touchmoved = true;
33180 onContextMenu : function(e,el)
33182 e.preventDefault();
33183 e.stopPropagation();
33187 onTouchEnd: function(e, el)
33189 // e.preventDefault();
33191 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33198 if(!this.bgimage.length || !this.html.length){
33200 if(this.href.length){
33201 window.location.href = this.href;
33207 if(!this.isFitContainer){
33211 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33213 window.location.href = this.href;
33216 //selection on single brick only
33217 selectBrick : function() {
33219 if (!this.parentId) {
33223 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33224 var index = m.selectedBrick.indexOf(this.id);
33227 m.selectedBrick.splice(index,1);
33228 this.el.removeClass(this.activeClass);
33232 for(var i = 0; i < m.selectedBrick.length; i++) {
33233 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33234 b.el.removeClass(b.activeClass);
33237 m.selectedBrick = [];
33239 m.selectedBrick.push(this.id);
33240 this.el.addClass(this.activeClass);
33244 isSelected : function(){
33245 return this.el.hasClass(this.activeClass);
33250 Roo.apply(Roo.bootstrap.MasonryBrick, {
33253 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33255 * register a Masonry Brick
33256 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33259 register : function(brick)
33261 //this.groups[brick.id] = brick;
33262 this.groups.add(brick.id, brick);
33265 * fetch a masonry brick based on the masonry brick ID
33266 * @param {string} the masonry brick to add
33267 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33270 get: function(brick_id)
33272 // if (typeof(this.groups[brick_id]) == 'undefined') {
33275 // return this.groups[brick_id] ;
33277 if(this.groups.key(brick_id)) {
33278 return this.groups.key(brick_id);
33296 * @class Roo.bootstrap.Brick
33297 * @extends Roo.bootstrap.Component
33298 * Bootstrap Brick class
33301 * Create a new Brick
33302 * @param {Object} config The config object
33305 Roo.bootstrap.Brick = function(config){
33306 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33312 * When a Brick is click
33313 * @param {Roo.bootstrap.Brick} this
33314 * @param {Roo.EventObject} e
33320 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33323 * @cfg {String} title
33327 * @cfg {String} html
33331 * @cfg {String} bgimage
33335 * @cfg {String} cls
33339 * @cfg {String} href
33343 * @cfg {String} video
33347 * @cfg {Boolean} square
33351 getAutoCreate : function()
33353 var cls = 'roo-brick';
33355 if(this.href.length){
33356 cls += ' roo-brick-link';
33359 if(this.bgimage.length){
33360 cls += ' roo-brick-image';
33363 if(!this.html.length && !this.bgimage.length){
33364 cls += ' roo-brick-center-title';
33367 if(!this.html.length && this.bgimage.length){
33368 cls += ' roo-brick-bottom-title';
33372 cls += ' ' + this.cls;
33376 tag: (this.href.length) ? 'a' : 'div',
33381 cls: 'roo-brick-paragraph',
33387 if(this.href.length){
33388 cfg.href = this.href;
33391 var cn = cfg.cn[0].cn;
33393 if(this.title.length){
33396 cls: 'roo-brick-title',
33401 if(this.html.length){
33404 cls: 'roo-brick-text',
33411 if(this.bgimage.length){
33414 cls: 'roo-brick-image-view',
33422 initEvents: function()
33424 if(this.title.length || this.html.length){
33425 this.el.on('mouseenter' ,this.enter, this);
33426 this.el.on('mouseleave', this.leave, this);
33429 Roo.EventManager.onWindowResize(this.resize, this);
33431 if(this.bgimage.length){
33432 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33433 this.imageEl.on('load', this.onImageLoad, this);
33440 onImageLoad : function()
33445 resize : function()
33447 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33449 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33451 if(this.bgimage.length){
33452 var image = this.el.select('.roo-brick-image-view', true).first();
33454 image.setWidth(paragraph.getWidth());
33457 image.setHeight(paragraph.getWidth());
33460 this.el.setHeight(image.getHeight());
33461 paragraph.setHeight(image.getHeight());
33467 enter: function(e, el)
33469 e.preventDefault();
33471 if(this.bgimage.length){
33472 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33473 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33477 leave: function(e, el)
33479 e.preventDefault();
33481 if(this.bgimage.length){
33482 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33483 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33498 * @class Roo.bootstrap.NumberField
33499 * @extends Roo.bootstrap.Input
33500 * Bootstrap NumberField class
33506 * Create a new NumberField
33507 * @param {Object} config The config object
33510 Roo.bootstrap.NumberField = function(config){
33511 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33514 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33517 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33519 allowDecimals : true,
33521 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33523 decimalSeparator : ".",
33525 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33527 decimalPrecision : 2,
33529 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33531 allowNegative : true,
33534 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33538 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33540 minValue : Number.NEGATIVE_INFINITY,
33542 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33544 maxValue : Number.MAX_VALUE,
33546 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33548 minText : "The minimum value for this field is {0}",
33550 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33552 maxText : "The maximum value for this field is {0}",
33554 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33555 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33557 nanText : "{0} is not a valid number",
33559 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33561 thousandsDelimiter : false,
33563 * @cfg {String} valueAlign alignment of value
33565 valueAlign : "left",
33567 getAutoCreate : function()
33569 var hiddenInput = {
33573 cls: 'hidden-number-input'
33577 hiddenInput.name = this.name;
33582 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33584 this.name = hiddenInput.name;
33586 if(cfg.cn.length > 0) {
33587 cfg.cn.push(hiddenInput);
33594 initEvents : function()
33596 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33598 var allowed = "0123456789";
33600 if(this.allowDecimals){
33601 allowed += this.decimalSeparator;
33604 if(this.allowNegative){
33608 if(this.thousandsDelimiter) {
33612 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33614 var keyPress = function(e){
33616 var k = e.getKey();
33618 var c = e.getCharCode();
33621 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33622 allowed.indexOf(String.fromCharCode(c)) === -1
33628 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33632 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33637 this.el.on("keypress", keyPress, this);
33640 validateValue : function(value)
33643 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33647 var num = this.parseValue(value);
33650 this.markInvalid(String.format(this.nanText, value));
33654 if(num < this.minValue){
33655 this.markInvalid(String.format(this.minText, this.minValue));
33659 if(num > this.maxValue){
33660 this.markInvalid(String.format(this.maxText, this.maxValue));
33667 getValue : function()
33669 var v = this.hiddenEl().getValue();
33671 return this.fixPrecision(this.parseValue(v));
33674 parseValue : function(value)
33676 if(this.thousandsDelimiter) {
33678 r = new RegExp(",", "g");
33679 value = value.replace(r, "");
33682 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33683 return isNaN(value) ? '' : value;
33686 fixPrecision : function(value)
33688 if(this.thousandsDelimiter) {
33690 r = new RegExp(",", "g");
33691 value = value.replace(r, "");
33694 var nan = isNaN(value);
33696 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33697 return nan ? '' : value;
33699 return parseFloat(value).toFixed(this.decimalPrecision);
33702 setValue : function(v)
33704 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33710 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33712 this.inputEl().dom.value = (v == '') ? '' :
33713 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33715 if(!this.allowZero && v === '0') {
33716 this.hiddenEl().dom.value = '';
33717 this.inputEl().dom.value = '';
33724 decimalPrecisionFcn : function(v)
33726 return Math.floor(v);
33729 beforeBlur : function()
33731 var v = this.parseValue(this.getRawValue());
33733 if(v || v === 0 || v === ''){
33738 hiddenEl : function()
33740 return this.el.select('input.hidden-number-input',true).first();
33752 * @class Roo.bootstrap.DocumentSlider
33753 * @extends Roo.bootstrap.Component
33754 * Bootstrap DocumentSlider class
33757 * Create a new DocumentViewer
33758 * @param {Object} config The config object
33761 Roo.bootstrap.DocumentSlider = function(config){
33762 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33769 * Fire after initEvent
33770 * @param {Roo.bootstrap.DocumentSlider} this
33775 * Fire after update
33776 * @param {Roo.bootstrap.DocumentSlider} this
33782 * @param {Roo.bootstrap.DocumentSlider} this
33788 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33794 getAutoCreate : function()
33798 cls : 'roo-document-slider',
33802 cls : 'roo-document-slider-header',
33806 cls : 'roo-document-slider-header-title'
33812 cls : 'roo-document-slider-body',
33816 cls : 'roo-document-slider-prev',
33820 cls : 'fa fa-chevron-left'
33826 cls : 'roo-document-slider-thumb',
33830 cls : 'roo-document-slider-image'
33836 cls : 'roo-document-slider-next',
33840 cls : 'fa fa-chevron-right'
33852 initEvents : function()
33854 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33855 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33857 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33858 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33860 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33861 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33863 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33864 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33866 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33867 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33869 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33870 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33872 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33873 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33875 this.thumbEl.on('click', this.onClick, this);
33877 this.prevIndicator.on('click', this.prev, this);
33879 this.nextIndicator.on('click', this.next, this);
33883 initial : function()
33885 if(this.files.length){
33886 this.indicator = 1;
33890 this.fireEvent('initial', this);
33893 update : function()
33895 this.imageEl.attr('src', this.files[this.indicator - 1]);
33897 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33899 this.prevIndicator.show();
33901 if(this.indicator == 1){
33902 this.prevIndicator.hide();
33905 this.nextIndicator.show();
33907 if(this.indicator == this.files.length){
33908 this.nextIndicator.hide();
33911 this.thumbEl.scrollTo('top');
33913 this.fireEvent('update', this);
33916 onClick : function(e)
33918 e.preventDefault();
33920 this.fireEvent('click', this);
33925 e.preventDefault();
33927 this.indicator = Math.max(1, this.indicator - 1);
33934 e.preventDefault();
33936 this.indicator = Math.min(this.files.length, this.indicator + 1);
33950 * @class Roo.bootstrap.RadioSet
33951 * @extends Roo.bootstrap.Input
33952 * Bootstrap RadioSet class
33953 * @cfg {String} indicatorpos (left|right) default left
33954 * @cfg {Boolean} inline (true|false) inline the element (default true)
33955 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33957 * Create a new RadioSet
33958 * @param {Object} config The config object
33961 Roo.bootstrap.RadioSet = function(config){
33963 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33967 Roo.bootstrap.RadioSet.register(this);
33972 * Fires when the element is checked or unchecked.
33973 * @param {Roo.bootstrap.RadioSet} this This radio
33974 * @param {Roo.bootstrap.Radio} item The checked item
33979 * Fires when the element is click.
33980 * @param {Roo.bootstrap.RadioSet} this This radio set
33981 * @param {Roo.bootstrap.Radio} item The checked item
33982 * @param {Roo.EventObject} e The event object
33989 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33997 indicatorpos : 'left',
33999 getAutoCreate : function()
34003 cls : 'roo-radio-set-label',
34007 html : this.fieldLabel
34012 if(this.indicatorpos == 'left'){
34015 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34016 tooltip : 'This field is required'
34021 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34022 tooltip : 'This field is required'
34028 cls : 'roo-radio-set-items'
34031 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34033 if (align === 'left' && this.fieldLabel.length) {
34036 cls : "roo-radio-set-right",
34042 if(this.labelWidth > 12){
34043 label.style = "width: " + this.labelWidth + 'px';
34046 if(this.labelWidth < 13 && this.labelmd == 0){
34047 this.labelmd = this.labelWidth;
34050 if(this.labellg > 0){
34051 label.cls += ' col-lg-' + this.labellg;
34052 items.cls += ' col-lg-' + (12 - this.labellg);
34055 if(this.labelmd > 0){
34056 label.cls += ' col-md-' + this.labelmd;
34057 items.cls += ' col-md-' + (12 - this.labelmd);
34060 if(this.labelsm > 0){
34061 label.cls += ' col-sm-' + this.labelsm;
34062 items.cls += ' col-sm-' + (12 - this.labelsm);
34065 if(this.labelxs > 0){
34066 label.cls += ' col-xs-' + this.labelxs;
34067 items.cls += ' col-xs-' + (12 - this.labelxs);
34073 cls : 'roo-radio-set',
34077 cls : 'roo-radio-set-input',
34080 value : this.value ? this.value : ''
34087 if(this.weight.length){
34088 cfg.cls += ' roo-radio-' + this.weight;
34092 cfg.cls += ' roo-radio-set-inline';
34096 ['xs','sm','md','lg'].map(function(size){
34097 if (settings[size]) {
34098 cfg.cls += ' col-' + size + '-' + settings[size];
34106 initEvents : function()
34108 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34109 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34111 if(!this.fieldLabel.length){
34112 this.labelEl.hide();
34115 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34116 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34118 this.indicator = this.indicatorEl();
34120 if(this.indicator){
34121 this.indicator.addClass('invisible');
34124 this.originalValue = this.getValue();
34128 inputEl: function ()
34130 return this.el.select('.roo-radio-set-input', true).first();
34133 getChildContainer : function()
34135 return this.itemsEl;
34138 register : function(item)
34140 this.radioes.push(item);
34144 validate : function()
34146 if(this.getVisibilityEl().hasClass('hidden')){
34152 Roo.each(this.radioes, function(i){
34161 if(this.allowBlank) {
34165 if(this.disabled || valid){
34170 this.markInvalid();
34175 markValid : function()
34177 if(this.labelEl.isVisible(true)){
34178 this.indicatorEl().removeClass('visible');
34179 this.indicatorEl().addClass('invisible');
34182 this.el.removeClass([this.invalidClass, this.validClass]);
34183 this.el.addClass(this.validClass);
34185 this.fireEvent('valid', this);
34188 markInvalid : function(msg)
34190 if(this.allowBlank || this.disabled){
34194 if(this.labelEl.isVisible(true)){
34195 this.indicatorEl().removeClass('invisible');
34196 this.indicatorEl().addClass('visible');
34199 this.el.removeClass([this.invalidClass, this.validClass]);
34200 this.el.addClass(this.invalidClass);
34202 this.fireEvent('invalid', this, msg);
34206 setValue : function(v, suppressEvent)
34208 if(this.value === v){
34215 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34218 Roo.each(this.radioes, function(i){
34220 i.el.removeClass('checked');
34223 Roo.each(this.radioes, function(i){
34225 if(i.value === v || i.value.toString() === v.toString()){
34227 i.el.addClass('checked');
34229 if(suppressEvent !== true){
34230 this.fireEvent('check', this, i);
34241 clearInvalid : function(){
34243 if(!this.el || this.preventMark){
34247 this.el.removeClass([this.invalidClass]);
34249 this.fireEvent('valid', this);
34254 Roo.apply(Roo.bootstrap.RadioSet, {
34258 register : function(set)
34260 this.groups[set.name] = set;
34263 get: function(name)
34265 if (typeof(this.groups[name]) == 'undefined') {
34269 return this.groups[name] ;
34275 * Ext JS Library 1.1.1
34276 * Copyright(c) 2006-2007, Ext JS, LLC.
34278 * Originally Released Under LGPL - original licence link has changed is not relivant.
34281 * <script type="text/javascript">
34286 * @class Roo.bootstrap.SplitBar
34287 * @extends Roo.util.Observable
34288 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34292 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34293 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34294 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34295 split.minSize = 100;
34296 split.maxSize = 600;
34297 split.animate = true;
34298 split.on('moved', splitterMoved);
34301 * Create a new SplitBar
34302 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34303 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34304 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34305 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34306 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34307 position of the SplitBar).
34309 Roo.bootstrap.SplitBar = function(cfg){
34314 // dragElement : elm
34315 // resizingElement: el,
34317 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34318 // placement : Roo.bootstrap.SplitBar.LEFT ,
34319 // existingProxy ???
34322 this.el = Roo.get(cfg.dragElement, true);
34323 this.el.dom.unselectable = "on";
34325 this.resizingEl = Roo.get(cfg.resizingElement, true);
34329 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34330 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34333 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34336 * The minimum size of the resizing element. (Defaults to 0)
34342 * The maximum size of the resizing element. (Defaults to 2000)
34345 this.maxSize = 2000;
34348 * Whether to animate the transition to the new size
34351 this.animate = false;
34354 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34357 this.useShim = false;
34362 if(!cfg.existingProxy){
34364 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34366 this.proxy = Roo.get(cfg.existingProxy).dom;
34369 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34372 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34375 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34378 this.dragSpecs = {};
34381 * @private The adapter to use to positon and resize elements
34383 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34384 this.adapter.init(this);
34386 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34388 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34389 this.el.addClass("roo-splitbar-h");
34392 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34393 this.el.addClass("roo-splitbar-v");
34399 * Fires when the splitter is moved (alias for {@link #event-moved})
34400 * @param {Roo.bootstrap.SplitBar} this
34401 * @param {Number} newSize the new width or height
34406 * Fires when the splitter is moved
34407 * @param {Roo.bootstrap.SplitBar} this
34408 * @param {Number} newSize the new width or height
34412 * @event beforeresize
34413 * Fires before the splitter is dragged
34414 * @param {Roo.bootstrap.SplitBar} this
34416 "beforeresize" : true,
34418 "beforeapply" : true
34421 Roo.util.Observable.call(this);
34424 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34425 onStartProxyDrag : function(x, y){
34426 this.fireEvent("beforeresize", this);
34428 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34430 o.enableDisplayMode("block");
34431 // all splitbars share the same overlay
34432 Roo.bootstrap.SplitBar.prototype.overlay = o;
34434 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34435 this.overlay.show();
34436 Roo.get(this.proxy).setDisplayed("block");
34437 var size = this.adapter.getElementSize(this);
34438 this.activeMinSize = this.getMinimumSize();;
34439 this.activeMaxSize = this.getMaximumSize();;
34440 var c1 = size - this.activeMinSize;
34441 var c2 = Math.max(this.activeMaxSize - size, 0);
34442 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34443 this.dd.resetConstraints();
34444 this.dd.setXConstraint(
34445 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34446 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34448 this.dd.setYConstraint(0, 0);
34450 this.dd.resetConstraints();
34451 this.dd.setXConstraint(0, 0);
34452 this.dd.setYConstraint(
34453 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34454 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34457 this.dragSpecs.startSize = size;
34458 this.dragSpecs.startPoint = [x, y];
34459 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34463 * @private Called after the drag operation by the DDProxy
34465 onEndProxyDrag : function(e){
34466 Roo.get(this.proxy).setDisplayed(false);
34467 var endPoint = Roo.lib.Event.getXY(e);
34469 this.overlay.hide();
34472 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34473 newSize = this.dragSpecs.startSize +
34474 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34475 endPoint[0] - this.dragSpecs.startPoint[0] :
34476 this.dragSpecs.startPoint[0] - endPoint[0]
34479 newSize = this.dragSpecs.startSize +
34480 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34481 endPoint[1] - this.dragSpecs.startPoint[1] :
34482 this.dragSpecs.startPoint[1] - endPoint[1]
34485 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34486 if(newSize != this.dragSpecs.startSize){
34487 if(this.fireEvent('beforeapply', this, newSize) !== false){
34488 this.adapter.setElementSize(this, newSize);
34489 this.fireEvent("moved", this, newSize);
34490 this.fireEvent("resize", this, newSize);
34496 * Get the adapter this SplitBar uses
34497 * @return The adapter object
34499 getAdapter : function(){
34500 return this.adapter;
34504 * Set the adapter this SplitBar uses
34505 * @param {Object} adapter A SplitBar adapter object
34507 setAdapter : function(adapter){
34508 this.adapter = adapter;
34509 this.adapter.init(this);
34513 * Gets the minimum size for the resizing element
34514 * @return {Number} The minimum size
34516 getMinimumSize : function(){
34517 return this.minSize;
34521 * Sets the minimum size for the resizing element
34522 * @param {Number} minSize The minimum size
34524 setMinimumSize : function(minSize){
34525 this.minSize = minSize;
34529 * Gets the maximum size for the resizing element
34530 * @return {Number} The maximum size
34532 getMaximumSize : function(){
34533 return this.maxSize;
34537 * Sets the maximum size for the resizing element
34538 * @param {Number} maxSize The maximum size
34540 setMaximumSize : function(maxSize){
34541 this.maxSize = maxSize;
34545 * Sets the initialize size for the resizing element
34546 * @param {Number} size The initial size
34548 setCurrentSize : function(size){
34549 var oldAnimate = this.animate;
34550 this.animate = false;
34551 this.adapter.setElementSize(this, size);
34552 this.animate = oldAnimate;
34556 * Destroy this splitbar.
34557 * @param {Boolean} removeEl True to remove the element
34559 destroy : function(removeEl){
34561 this.shim.remove();
34564 this.proxy.parentNode.removeChild(this.proxy);
34572 * @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.
34574 Roo.bootstrap.SplitBar.createProxy = function(dir){
34575 var proxy = new Roo.Element(document.createElement("div"));
34576 proxy.unselectable();
34577 var cls = 'roo-splitbar-proxy';
34578 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34579 document.body.appendChild(proxy.dom);
34584 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34585 * Default Adapter. It assumes the splitter and resizing element are not positioned
34586 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34588 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34591 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34592 // do nothing for now
34593 init : function(s){
34597 * Called before drag operations to get the current size of the resizing element.
34598 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34600 getElementSize : function(s){
34601 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34602 return s.resizingEl.getWidth();
34604 return s.resizingEl.getHeight();
34609 * Called after drag operations to set the size of the resizing element.
34610 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34611 * @param {Number} newSize The new size to set
34612 * @param {Function} onComplete A function to be invoked when resizing is complete
34614 setElementSize : function(s, newSize, onComplete){
34615 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34617 s.resizingEl.setWidth(newSize);
34619 onComplete(s, newSize);
34622 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34627 s.resizingEl.setHeight(newSize);
34629 onComplete(s, newSize);
34632 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34639 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34640 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34641 * Adapter that moves the splitter element to align with the resized sizing element.
34642 * Used with an absolute positioned SplitBar.
34643 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34644 * document.body, make sure you assign an id to the body element.
34646 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34647 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34648 this.container = Roo.get(container);
34651 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34652 init : function(s){
34653 this.basic.init(s);
34656 getElementSize : function(s){
34657 return this.basic.getElementSize(s);
34660 setElementSize : function(s, newSize, onComplete){
34661 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34664 moveSplitter : function(s){
34665 var yes = Roo.bootstrap.SplitBar;
34666 switch(s.placement){
34668 s.el.setX(s.resizingEl.getRight());
34671 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34674 s.el.setY(s.resizingEl.getBottom());
34677 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34684 * Orientation constant - Create a vertical SplitBar
34688 Roo.bootstrap.SplitBar.VERTICAL = 1;
34691 * Orientation constant - Create a horizontal SplitBar
34695 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34698 * Placement constant - The resizing element is to the left of the splitter element
34702 Roo.bootstrap.SplitBar.LEFT = 1;
34705 * Placement constant - The resizing element is to the right of the splitter element
34709 Roo.bootstrap.SplitBar.RIGHT = 2;
34712 * Placement constant - The resizing element is positioned above the splitter element
34716 Roo.bootstrap.SplitBar.TOP = 3;
34719 * Placement constant - The resizing element is positioned under splitter element
34723 Roo.bootstrap.SplitBar.BOTTOM = 4;
34724 Roo.namespace("Roo.bootstrap.layout");/*
34726 * Ext JS Library 1.1.1
34727 * Copyright(c) 2006-2007, Ext JS, LLC.
34729 * Originally Released Under LGPL - original licence link has changed is not relivant.
34732 * <script type="text/javascript">
34736 * @class Roo.bootstrap.layout.Manager
34737 * @extends Roo.bootstrap.Component
34738 * Base class for layout managers.
34740 Roo.bootstrap.layout.Manager = function(config)
34742 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34748 /** false to disable window resize monitoring @type Boolean */
34749 this.monitorWindowResize = true;
34754 * Fires when a layout is performed.
34755 * @param {Roo.LayoutManager} this
34759 * @event regionresized
34760 * Fires when the user resizes a region.
34761 * @param {Roo.LayoutRegion} region The resized region
34762 * @param {Number} newSize The new size (width for east/west, height for north/south)
34764 "regionresized" : true,
34766 * @event regioncollapsed
34767 * Fires when a region is collapsed.
34768 * @param {Roo.LayoutRegion} region The collapsed region
34770 "regioncollapsed" : true,
34772 * @event regionexpanded
34773 * Fires when a region is expanded.
34774 * @param {Roo.LayoutRegion} region The expanded region
34776 "regionexpanded" : true
34778 this.updating = false;
34781 this.el = Roo.get(config.el);
34787 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34792 monitorWindowResize : true,
34798 onRender : function(ct, position)
34801 this.el = Roo.get(ct);
34804 //this.fireEvent('render',this);
34808 initEvents: function()
34812 // ie scrollbar fix
34813 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34814 document.body.scroll = "no";
34815 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34816 this.el.position('relative');
34818 this.id = this.el.id;
34819 this.el.addClass("roo-layout-container");
34820 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34821 if(this.el.dom != document.body ) {
34822 this.el.on('resize', this.layout,this);
34823 this.el.on('show', this.layout,this);
34829 * Returns true if this layout is currently being updated
34830 * @return {Boolean}
34832 isUpdating : function(){
34833 return this.updating;
34837 * Suspend the LayoutManager from doing auto-layouts while
34838 * making multiple add or remove calls
34840 beginUpdate : function(){
34841 this.updating = true;
34845 * Restore auto-layouts and optionally disable the manager from performing a layout
34846 * @param {Boolean} noLayout true to disable a layout update
34848 endUpdate : function(noLayout){
34849 this.updating = false;
34855 layout: function(){
34859 onRegionResized : function(region, newSize){
34860 this.fireEvent("regionresized", region, newSize);
34864 onRegionCollapsed : function(region){
34865 this.fireEvent("regioncollapsed", region);
34868 onRegionExpanded : function(region){
34869 this.fireEvent("regionexpanded", region);
34873 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34874 * performs box-model adjustments.
34875 * @return {Object} The size as an object {width: (the width), height: (the height)}
34877 getViewSize : function()
34880 if(this.el.dom != document.body){
34881 size = this.el.getSize();
34883 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34885 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34886 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34891 * Returns the Element this layout is bound to.
34892 * @return {Roo.Element}
34894 getEl : function(){
34899 * Returns the specified region.
34900 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34901 * @return {Roo.LayoutRegion}
34903 getRegion : function(target){
34904 return this.regions[target.toLowerCase()];
34907 onWindowResize : function(){
34908 if(this.monitorWindowResize){
34915 * Ext JS Library 1.1.1
34916 * Copyright(c) 2006-2007, Ext JS, LLC.
34918 * Originally Released Under LGPL - original licence link has changed is not relivant.
34921 * <script type="text/javascript">
34924 * @class Roo.bootstrap.layout.Border
34925 * @extends Roo.bootstrap.layout.Manager
34926 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34927 * please see: examples/bootstrap/nested.html<br><br>
34929 <b>The container the layout is rendered into can be either the body element or any other element.
34930 If it is not the body element, the container needs to either be an absolute positioned element,
34931 or you will need to add "position:relative" to the css of the container. You will also need to specify
34932 the container size if it is not the body element.</b>
34935 * Create a new Border
34936 * @param {Object} config Configuration options
34938 Roo.bootstrap.layout.Border = function(config){
34939 config = config || {};
34940 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34944 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34945 if(config[region]){
34946 config[region].region = region;
34947 this.addRegion(config[region]);
34953 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34955 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34957 * Creates and adds a new region if it doesn't already exist.
34958 * @param {String} target The target region key (north, south, east, west or center).
34959 * @param {Object} config The regions config object
34960 * @return {BorderLayoutRegion} The new region
34962 addRegion : function(config)
34964 if(!this.regions[config.region]){
34965 var r = this.factory(config);
34966 this.bindRegion(r);
34968 return this.regions[config.region];
34972 bindRegion : function(r){
34973 this.regions[r.config.region] = r;
34975 r.on("visibilitychange", this.layout, this);
34976 r.on("paneladded", this.layout, this);
34977 r.on("panelremoved", this.layout, this);
34978 r.on("invalidated", this.layout, this);
34979 r.on("resized", this.onRegionResized, this);
34980 r.on("collapsed", this.onRegionCollapsed, this);
34981 r.on("expanded", this.onRegionExpanded, this);
34985 * Performs a layout update.
34987 layout : function()
34989 if(this.updating) {
34993 // render all the rebions if they have not been done alreayd?
34994 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34995 if(this.regions[region] && !this.regions[region].bodyEl){
34996 this.regions[region].onRender(this.el)
35000 var size = this.getViewSize();
35001 var w = size.width;
35002 var h = size.height;
35007 //var x = 0, y = 0;
35009 var rs = this.regions;
35010 var north = rs["north"];
35011 var south = rs["south"];
35012 var west = rs["west"];
35013 var east = rs["east"];
35014 var center = rs["center"];
35015 //if(this.hideOnLayout){ // not supported anymore
35016 //c.el.setStyle("display", "none");
35018 if(north && north.isVisible()){
35019 var b = north.getBox();
35020 var m = north.getMargins();
35021 b.width = w - (m.left+m.right);
35024 centerY = b.height + b.y + m.bottom;
35025 centerH -= centerY;
35026 north.updateBox(this.safeBox(b));
35028 if(south && south.isVisible()){
35029 var b = south.getBox();
35030 var m = south.getMargins();
35031 b.width = w - (m.left+m.right);
35033 var totalHeight = (b.height + m.top + m.bottom);
35034 b.y = h - totalHeight + m.top;
35035 centerH -= totalHeight;
35036 south.updateBox(this.safeBox(b));
35038 if(west && west.isVisible()){
35039 var b = west.getBox();
35040 var m = west.getMargins();
35041 b.height = centerH - (m.top+m.bottom);
35043 b.y = centerY + m.top;
35044 var totalWidth = (b.width + m.left + m.right);
35045 centerX += totalWidth;
35046 centerW -= totalWidth;
35047 west.updateBox(this.safeBox(b));
35049 if(east && east.isVisible()){
35050 var b = east.getBox();
35051 var m = east.getMargins();
35052 b.height = centerH - (m.top+m.bottom);
35053 var totalWidth = (b.width + m.left + m.right);
35054 b.x = w - totalWidth + m.left;
35055 b.y = centerY + m.top;
35056 centerW -= totalWidth;
35057 east.updateBox(this.safeBox(b));
35060 var m = center.getMargins();
35062 x: centerX + m.left,
35063 y: centerY + m.top,
35064 width: centerW - (m.left+m.right),
35065 height: centerH - (m.top+m.bottom)
35067 //if(this.hideOnLayout){
35068 //center.el.setStyle("display", "block");
35070 center.updateBox(this.safeBox(centerBox));
35073 this.fireEvent("layout", this);
35077 safeBox : function(box){
35078 box.width = Math.max(0, box.width);
35079 box.height = Math.max(0, box.height);
35084 * Adds a ContentPanel (or subclass) to this layout.
35085 * @param {String} target The target region key (north, south, east, west or center).
35086 * @param {Roo.ContentPanel} panel The panel to add
35087 * @return {Roo.ContentPanel} The added panel
35089 add : function(target, panel){
35091 target = target.toLowerCase();
35092 return this.regions[target].add(panel);
35096 * Remove a ContentPanel (or subclass) to this layout.
35097 * @param {String} target The target region key (north, south, east, west or center).
35098 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35099 * @return {Roo.ContentPanel} The removed panel
35101 remove : function(target, panel){
35102 target = target.toLowerCase();
35103 return this.regions[target].remove(panel);
35107 * Searches all regions for a panel with the specified id
35108 * @param {String} panelId
35109 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35111 findPanel : function(panelId){
35112 var rs = this.regions;
35113 for(var target in rs){
35114 if(typeof rs[target] != "function"){
35115 var p = rs[target].getPanel(panelId);
35125 * Searches all regions for a panel with the specified id and activates (shows) it.
35126 * @param {String/ContentPanel} panelId The panels id or the panel itself
35127 * @return {Roo.ContentPanel} The shown panel or null
35129 showPanel : function(panelId) {
35130 var rs = this.regions;
35131 for(var target in rs){
35132 var r = rs[target];
35133 if(typeof r != "function"){
35134 if(r.hasPanel(panelId)){
35135 return r.showPanel(panelId);
35143 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35144 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35147 restoreState : function(provider){
35149 provider = Roo.state.Manager;
35151 var sm = new Roo.LayoutStateManager();
35152 sm.init(this, provider);
35158 * Adds a xtype elements to the layout.
35162 xtype : 'ContentPanel',
35169 xtype : 'NestedLayoutPanel',
35175 items : [ ... list of content panels or nested layout panels.. ]
35179 * @param {Object} cfg Xtype definition of item to add.
35181 addxtype : function(cfg)
35183 // basically accepts a pannel...
35184 // can accept a layout region..!?!?
35185 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35188 // theory? children can only be panels??
35190 //if (!cfg.xtype.match(/Panel$/)) {
35195 if (typeof(cfg.region) == 'undefined') {
35196 Roo.log("Failed to add Panel, region was not set");
35200 var region = cfg.region;
35206 xitems = cfg.items;
35213 case 'Content': // ContentPanel (el, cfg)
35214 case 'Scroll': // ContentPanel (el, cfg)
35216 cfg.autoCreate = true;
35217 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35219 // var el = this.el.createChild();
35220 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35223 this.add(region, ret);
35227 case 'TreePanel': // our new panel!
35228 cfg.el = this.el.createChild();
35229 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35230 this.add(region, ret);
35235 // create a new Layout (which is a Border Layout...
35237 var clayout = cfg.layout;
35238 clayout.el = this.el.createChild();
35239 clayout.items = clayout.items || [];
35243 // replace this exitems with the clayout ones..
35244 xitems = clayout.items;
35246 // force background off if it's in center...
35247 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35248 cfg.background = false;
35250 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35253 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35254 //console.log('adding nested layout panel ' + cfg.toSource());
35255 this.add(region, ret);
35256 nb = {}; /// find first...
35261 // needs grid and region
35263 //var el = this.getRegion(region).el.createChild();
35265 *var el = this.el.createChild();
35266 // create the grid first...
35267 cfg.grid.container = el;
35268 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35271 if (region == 'center' && this.active ) {
35272 cfg.background = false;
35275 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35277 this.add(region, ret);
35279 if (cfg.background) {
35280 // render grid on panel activation (if panel background)
35281 ret.on('activate', function(gp) {
35282 if (!gp.grid.rendered) {
35283 // gp.grid.render(el);
35287 // cfg.grid.render(el);
35293 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35294 // it was the old xcomponent building that caused this before.
35295 // espeically if border is the top element in the tree.
35305 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35307 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35308 this.add(region, ret);
35312 throw "Can not add '" + cfg.xtype + "' to Border";
35318 this.beginUpdate();
35322 Roo.each(xitems, function(i) {
35323 region = nb && i.region ? i.region : false;
35325 var add = ret.addxtype(i);
35328 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35329 if (!i.background) {
35330 abn[region] = nb[region] ;
35337 // make the last non-background panel active..
35338 //if (nb) { Roo.log(abn); }
35341 for(var r in abn) {
35342 region = this.getRegion(r);
35344 // tried using nb[r], but it does not work..
35346 region.showPanel(abn[r]);
35357 factory : function(cfg)
35360 var validRegions = Roo.bootstrap.layout.Border.regions;
35362 var target = cfg.region;
35365 var r = Roo.bootstrap.layout;
35369 return new r.North(cfg);
35371 return new r.South(cfg);
35373 return new r.East(cfg);
35375 return new r.West(cfg);
35377 return new r.Center(cfg);
35379 throw 'Layout region "'+target+'" not supported.';
35386 * Ext JS Library 1.1.1
35387 * Copyright(c) 2006-2007, Ext JS, LLC.
35389 * Originally Released Under LGPL - original licence link has changed is not relivant.
35392 * <script type="text/javascript">
35396 * @class Roo.bootstrap.layout.Basic
35397 * @extends Roo.util.Observable
35398 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35399 * and does not have a titlebar, tabs or any other features. All it does is size and position
35400 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35401 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35402 * @cfg {string} region the region that it inhabits..
35403 * @cfg {bool} skipConfig skip config?
35407 Roo.bootstrap.layout.Basic = function(config){
35409 this.mgr = config.mgr;
35411 this.position = config.region;
35413 var skipConfig = config.skipConfig;
35417 * @scope Roo.BasicLayoutRegion
35421 * @event beforeremove
35422 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35423 * @param {Roo.LayoutRegion} this
35424 * @param {Roo.ContentPanel} panel The panel
35425 * @param {Object} e The cancel event object
35427 "beforeremove" : true,
35429 * @event invalidated
35430 * Fires when the layout for this region is changed.
35431 * @param {Roo.LayoutRegion} this
35433 "invalidated" : true,
35435 * @event visibilitychange
35436 * Fires when this region is shown or hidden
35437 * @param {Roo.LayoutRegion} this
35438 * @param {Boolean} visibility true or false
35440 "visibilitychange" : true,
35442 * @event paneladded
35443 * Fires when a panel is added.
35444 * @param {Roo.LayoutRegion} this
35445 * @param {Roo.ContentPanel} panel The panel
35447 "paneladded" : true,
35449 * @event panelremoved
35450 * Fires when a panel is removed.
35451 * @param {Roo.LayoutRegion} this
35452 * @param {Roo.ContentPanel} panel The panel
35454 "panelremoved" : true,
35456 * @event beforecollapse
35457 * Fires when this region before collapse.
35458 * @param {Roo.LayoutRegion} this
35460 "beforecollapse" : true,
35463 * Fires when this region is collapsed.
35464 * @param {Roo.LayoutRegion} this
35466 "collapsed" : true,
35469 * Fires when this region is expanded.
35470 * @param {Roo.LayoutRegion} this
35475 * Fires when this region is slid into view.
35476 * @param {Roo.LayoutRegion} this
35478 "slideshow" : true,
35481 * Fires when this region slides out of view.
35482 * @param {Roo.LayoutRegion} this
35484 "slidehide" : true,
35486 * @event panelactivated
35487 * Fires when a panel is activated.
35488 * @param {Roo.LayoutRegion} this
35489 * @param {Roo.ContentPanel} panel The activated panel
35491 "panelactivated" : true,
35494 * Fires when the user resizes this region.
35495 * @param {Roo.LayoutRegion} this
35496 * @param {Number} newSize The new size (width for east/west, height for north/south)
35500 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35501 this.panels = new Roo.util.MixedCollection();
35502 this.panels.getKey = this.getPanelId.createDelegate(this);
35504 this.activePanel = null;
35505 // ensure listeners are added...
35507 if (config.listeners || config.events) {
35508 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35509 listeners : config.listeners || {},
35510 events : config.events || {}
35514 if(skipConfig !== true){
35515 this.applyConfig(config);
35519 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35521 getPanelId : function(p){
35525 applyConfig : function(config){
35526 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35527 this.config = config;
35532 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35533 * the width, for horizontal (north, south) the height.
35534 * @param {Number} newSize The new width or height
35536 resizeTo : function(newSize){
35537 var el = this.el ? this.el :
35538 (this.activePanel ? this.activePanel.getEl() : null);
35540 switch(this.position){
35543 el.setWidth(newSize);
35544 this.fireEvent("resized", this, newSize);
35548 el.setHeight(newSize);
35549 this.fireEvent("resized", this, newSize);
35555 getBox : function(){
35556 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35559 getMargins : function(){
35560 return this.margins;
35563 updateBox : function(box){
35565 var el = this.activePanel.getEl();
35566 el.dom.style.left = box.x + "px";
35567 el.dom.style.top = box.y + "px";
35568 this.activePanel.setSize(box.width, box.height);
35572 * Returns the container element for this region.
35573 * @return {Roo.Element}
35575 getEl : function(){
35576 return this.activePanel;
35580 * Returns true if this region is currently visible.
35581 * @return {Boolean}
35583 isVisible : function(){
35584 return this.activePanel ? true : false;
35587 setActivePanel : function(panel){
35588 panel = this.getPanel(panel);
35589 if(this.activePanel && this.activePanel != panel){
35590 this.activePanel.setActiveState(false);
35591 this.activePanel.getEl().setLeftTop(-10000,-10000);
35593 this.activePanel = panel;
35594 panel.setActiveState(true);
35596 panel.setSize(this.box.width, this.box.height);
35598 this.fireEvent("panelactivated", this, panel);
35599 this.fireEvent("invalidated");
35603 * Show the specified panel.
35604 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35605 * @return {Roo.ContentPanel} The shown panel or null
35607 showPanel : function(panel){
35608 panel = this.getPanel(panel);
35610 this.setActivePanel(panel);
35616 * Get the active panel for this region.
35617 * @return {Roo.ContentPanel} The active panel or null
35619 getActivePanel : function(){
35620 return this.activePanel;
35624 * Add the passed ContentPanel(s)
35625 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35626 * @return {Roo.ContentPanel} The panel added (if only one was added)
35628 add : function(panel){
35629 if(arguments.length > 1){
35630 for(var i = 0, len = arguments.length; i < len; i++) {
35631 this.add(arguments[i]);
35635 if(this.hasPanel(panel)){
35636 this.showPanel(panel);
35639 var el = panel.getEl();
35640 if(el.dom.parentNode != this.mgr.el.dom){
35641 this.mgr.el.dom.appendChild(el.dom);
35643 if(panel.setRegion){
35644 panel.setRegion(this);
35646 this.panels.add(panel);
35647 el.setStyle("position", "absolute");
35648 if(!panel.background){
35649 this.setActivePanel(panel);
35650 if(this.config.initialSize && this.panels.getCount()==1){
35651 this.resizeTo(this.config.initialSize);
35654 this.fireEvent("paneladded", this, panel);
35659 * Returns true if the panel is in this region.
35660 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35661 * @return {Boolean}
35663 hasPanel : function(panel){
35664 if(typeof panel == "object"){ // must be panel obj
35665 panel = panel.getId();
35667 return this.getPanel(panel) ? true : false;
35671 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35672 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35673 * @param {Boolean} preservePanel Overrides the config preservePanel option
35674 * @return {Roo.ContentPanel} The panel that was removed
35676 remove : function(panel, preservePanel){
35677 panel = this.getPanel(panel);
35682 this.fireEvent("beforeremove", this, panel, e);
35683 if(e.cancel === true){
35686 var panelId = panel.getId();
35687 this.panels.removeKey(panelId);
35692 * Returns the panel specified or null if it's not in this region.
35693 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35694 * @return {Roo.ContentPanel}
35696 getPanel : function(id){
35697 if(typeof id == "object"){ // must be panel obj
35700 return this.panels.get(id);
35704 * Returns this regions position (north/south/east/west/center).
35707 getPosition: function(){
35708 return this.position;
35712 * Ext JS Library 1.1.1
35713 * Copyright(c) 2006-2007, Ext JS, LLC.
35715 * Originally Released Under LGPL - original licence link has changed is not relivant.
35718 * <script type="text/javascript">
35722 * @class Roo.bootstrap.layout.Region
35723 * @extends Roo.bootstrap.layout.Basic
35724 * This class represents a region in a layout manager.
35726 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35727 * @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})
35728 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35729 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35730 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35731 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35732 * @cfg {String} title The title for the region (overrides panel titles)
35733 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35734 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35735 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35736 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35737 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35738 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35739 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35740 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35741 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35742 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35744 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35745 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35746 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35747 * @cfg {Number} width For East/West panels
35748 * @cfg {Number} height For North/South panels
35749 * @cfg {Boolean} split To show the splitter
35750 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35752 * @cfg {string} cls Extra CSS classes to add to region
35754 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35755 * @cfg {string} region the region that it inhabits..
35758 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35759 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35761 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35762 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35763 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35765 Roo.bootstrap.layout.Region = function(config)
35767 this.applyConfig(config);
35769 var mgr = config.mgr;
35770 var pos = config.region;
35771 config.skipConfig = true;
35772 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35775 this.onRender(mgr.el);
35778 this.visible = true;
35779 this.collapsed = false;
35780 this.unrendered_panels = [];
35783 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35785 position: '', // set by wrapper (eg. north/south etc..)
35786 unrendered_panels : null, // unrendered panels.
35787 createBody : function(){
35788 /** This region's body element
35789 * @type Roo.Element */
35790 this.bodyEl = this.el.createChild({
35792 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35796 onRender: function(ctr, pos)
35798 var dh = Roo.DomHelper;
35799 /** This region's container element
35800 * @type Roo.Element */
35801 this.el = dh.append(ctr.dom, {
35803 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35805 /** This region's title element
35806 * @type Roo.Element */
35808 this.titleEl = dh.append(this.el.dom,
35811 unselectable: "on",
35812 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35814 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35815 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35818 this.titleEl.enableDisplayMode();
35819 /** This region's title text element
35820 * @type HTMLElement */
35821 this.titleTextEl = this.titleEl.dom.firstChild;
35822 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35824 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35825 this.closeBtn.enableDisplayMode();
35826 this.closeBtn.on("click", this.closeClicked, this);
35827 this.closeBtn.hide();
35829 this.createBody(this.config);
35830 if(this.config.hideWhenEmpty){
35832 this.on("paneladded", this.validateVisibility, this);
35833 this.on("panelremoved", this.validateVisibility, this);
35835 if(this.autoScroll){
35836 this.bodyEl.setStyle("overflow", "auto");
35838 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35840 //if(c.titlebar !== false){
35841 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35842 this.titleEl.hide();
35844 this.titleEl.show();
35845 if(this.config.title){
35846 this.titleTextEl.innerHTML = this.config.title;
35850 if(this.config.collapsed){
35851 this.collapse(true);
35853 if(this.config.hidden){
35857 if (this.unrendered_panels && this.unrendered_panels.length) {
35858 for (var i =0;i< this.unrendered_panels.length; i++) {
35859 this.add(this.unrendered_panels[i]);
35861 this.unrendered_panels = null;
35867 applyConfig : function(c)
35870 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35871 var dh = Roo.DomHelper;
35872 if(c.titlebar !== false){
35873 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35874 this.collapseBtn.on("click", this.collapse, this);
35875 this.collapseBtn.enableDisplayMode();
35877 if(c.showPin === true || this.showPin){
35878 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35879 this.stickBtn.enableDisplayMode();
35880 this.stickBtn.on("click", this.expand, this);
35881 this.stickBtn.hide();
35886 /** This region's collapsed element
35887 * @type Roo.Element */
35890 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35891 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35894 if(c.floatable !== false){
35895 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35896 this.collapsedEl.on("click", this.collapseClick, this);
35899 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35900 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35901 id: "message", unselectable: "on", style:{"float":"left"}});
35902 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35904 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35905 this.expandBtn.on("click", this.expand, this);
35909 if(this.collapseBtn){
35910 this.collapseBtn.setVisible(c.collapsible == true);
35913 this.cmargins = c.cmargins || this.cmargins ||
35914 (this.position == "west" || this.position == "east" ?
35915 {top: 0, left: 2, right:2, bottom: 0} :
35916 {top: 2, left: 0, right:0, bottom: 2});
35918 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35921 this.bottomTabs = c.tabPosition != "top";
35923 this.autoScroll = c.autoScroll || false;
35928 this.duration = c.duration || .30;
35929 this.slideDuration = c.slideDuration || .45;
35934 * Returns true if this region is currently visible.
35935 * @return {Boolean}
35937 isVisible : function(){
35938 return this.visible;
35942 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35943 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35945 //setCollapsedTitle : function(title){
35946 // title = title || " ";
35947 // if(this.collapsedTitleTextEl){
35948 // this.collapsedTitleTextEl.innerHTML = title;
35952 getBox : function(){
35954 // if(!this.collapsed){
35955 b = this.el.getBox(false, true);
35957 // b = this.collapsedEl.getBox(false, true);
35962 getMargins : function(){
35963 return this.margins;
35964 //return this.collapsed ? this.cmargins : this.margins;
35967 highlight : function(){
35968 this.el.addClass("x-layout-panel-dragover");
35971 unhighlight : function(){
35972 this.el.removeClass("x-layout-panel-dragover");
35975 updateBox : function(box)
35977 if (!this.bodyEl) {
35978 return; // not rendered yet..
35982 if(!this.collapsed){
35983 this.el.dom.style.left = box.x + "px";
35984 this.el.dom.style.top = box.y + "px";
35985 this.updateBody(box.width, box.height);
35987 this.collapsedEl.dom.style.left = box.x + "px";
35988 this.collapsedEl.dom.style.top = box.y + "px";
35989 this.collapsedEl.setSize(box.width, box.height);
35992 this.tabs.autoSizeTabs();
35996 updateBody : function(w, h)
35999 this.el.setWidth(w);
36000 w -= this.el.getBorderWidth("rl");
36001 if(this.config.adjustments){
36002 w += this.config.adjustments[0];
36005 if(h !== null && h > 0){
36006 this.el.setHeight(h);
36007 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36008 h -= this.el.getBorderWidth("tb");
36009 if(this.config.adjustments){
36010 h += this.config.adjustments[1];
36012 this.bodyEl.setHeight(h);
36014 h = this.tabs.syncHeight(h);
36017 if(this.panelSize){
36018 w = w !== null ? w : this.panelSize.width;
36019 h = h !== null ? h : this.panelSize.height;
36021 if(this.activePanel){
36022 var el = this.activePanel.getEl();
36023 w = w !== null ? w : el.getWidth();
36024 h = h !== null ? h : el.getHeight();
36025 this.panelSize = {width: w, height: h};
36026 this.activePanel.setSize(w, h);
36028 if(Roo.isIE && this.tabs){
36029 this.tabs.el.repaint();
36034 * Returns the container element for this region.
36035 * @return {Roo.Element}
36037 getEl : function(){
36042 * Hides this region.
36045 //if(!this.collapsed){
36046 this.el.dom.style.left = "-2000px";
36049 // this.collapsedEl.dom.style.left = "-2000px";
36050 // this.collapsedEl.hide();
36052 this.visible = false;
36053 this.fireEvent("visibilitychange", this, false);
36057 * Shows this region if it was previously hidden.
36060 //if(!this.collapsed){
36063 // this.collapsedEl.show();
36065 this.visible = true;
36066 this.fireEvent("visibilitychange", this, true);
36069 closeClicked : function(){
36070 if(this.activePanel){
36071 this.remove(this.activePanel);
36075 collapseClick : function(e){
36077 e.stopPropagation();
36080 e.stopPropagation();
36086 * Collapses this region.
36087 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36090 collapse : function(skipAnim, skipCheck = false){
36091 if(this.collapsed) {
36095 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36097 this.collapsed = true;
36099 this.split.el.hide();
36101 if(this.config.animate && skipAnim !== true){
36102 this.fireEvent("invalidated", this);
36103 this.animateCollapse();
36105 this.el.setLocation(-20000,-20000);
36107 this.collapsedEl.show();
36108 this.fireEvent("collapsed", this);
36109 this.fireEvent("invalidated", this);
36115 animateCollapse : function(){
36120 * Expands this region if it was previously collapsed.
36121 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36122 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36125 expand : function(e, skipAnim){
36127 e.stopPropagation();
36129 if(!this.collapsed || this.el.hasActiveFx()) {
36133 this.afterSlideIn();
36136 this.collapsed = false;
36137 if(this.config.animate && skipAnim !== true){
36138 this.animateExpand();
36142 this.split.el.show();
36144 this.collapsedEl.setLocation(-2000,-2000);
36145 this.collapsedEl.hide();
36146 this.fireEvent("invalidated", this);
36147 this.fireEvent("expanded", this);
36151 animateExpand : function(){
36155 initTabs : function()
36157 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36159 var ts = new Roo.bootstrap.panel.Tabs({
36160 el: this.bodyEl.dom,
36161 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36162 disableTooltips: this.config.disableTabTips,
36163 toolbar : this.config.toolbar
36166 if(this.config.hideTabs){
36167 ts.stripWrap.setDisplayed(false);
36170 ts.resizeTabs = this.config.resizeTabs === true;
36171 ts.minTabWidth = this.config.minTabWidth || 40;
36172 ts.maxTabWidth = this.config.maxTabWidth || 250;
36173 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36174 ts.monitorResize = false;
36175 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36176 ts.bodyEl.addClass('roo-layout-tabs-body');
36177 this.panels.each(this.initPanelAsTab, this);
36180 initPanelAsTab : function(panel){
36181 var ti = this.tabs.addTab(
36185 this.config.closeOnTab && panel.isClosable(),
36188 if(panel.tabTip !== undefined){
36189 ti.setTooltip(panel.tabTip);
36191 ti.on("activate", function(){
36192 this.setActivePanel(panel);
36195 if(this.config.closeOnTab){
36196 ti.on("beforeclose", function(t, e){
36198 this.remove(panel);
36202 panel.tabItem = ti;
36207 updatePanelTitle : function(panel, title)
36209 if(this.activePanel == panel){
36210 this.updateTitle(title);
36213 var ti = this.tabs.getTab(panel.getEl().id);
36215 if(panel.tabTip !== undefined){
36216 ti.setTooltip(panel.tabTip);
36221 updateTitle : function(title){
36222 if(this.titleTextEl && !this.config.title){
36223 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36227 setActivePanel : function(panel)
36229 panel = this.getPanel(panel);
36230 if(this.activePanel && this.activePanel != panel){
36231 if(this.activePanel.setActiveState(false) === false){
36235 this.activePanel = panel;
36236 panel.setActiveState(true);
36237 if(this.panelSize){
36238 panel.setSize(this.panelSize.width, this.panelSize.height);
36241 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36243 this.updateTitle(panel.getTitle());
36245 this.fireEvent("invalidated", this);
36247 this.fireEvent("panelactivated", this, panel);
36251 * Shows the specified panel.
36252 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36253 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36255 showPanel : function(panel)
36257 panel = this.getPanel(panel);
36260 var tab = this.tabs.getTab(panel.getEl().id);
36261 if(tab.isHidden()){
36262 this.tabs.unhideTab(tab.id);
36266 this.setActivePanel(panel);
36273 * Get the active panel for this region.
36274 * @return {Roo.ContentPanel} The active panel or null
36276 getActivePanel : function(){
36277 return this.activePanel;
36280 validateVisibility : function(){
36281 if(this.panels.getCount() < 1){
36282 this.updateTitle(" ");
36283 this.closeBtn.hide();
36286 if(!this.isVisible()){
36293 * Adds the passed ContentPanel(s) to this region.
36294 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36295 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36297 add : function(panel)
36299 if(arguments.length > 1){
36300 for(var i = 0, len = arguments.length; i < len; i++) {
36301 this.add(arguments[i]);
36306 // if we have not been rendered yet, then we can not really do much of this..
36307 if (!this.bodyEl) {
36308 this.unrendered_panels.push(panel);
36315 if(this.hasPanel(panel)){
36316 this.showPanel(panel);
36319 panel.setRegion(this);
36320 this.panels.add(panel);
36321 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36322 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36323 // and hide them... ???
36324 this.bodyEl.dom.appendChild(panel.getEl().dom);
36325 if(panel.background !== true){
36326 this.setActivePanel(panel);
36328 this.fireEvent("paneladded", this, panel);
36335 this.initPanelAsTab(panel);
36339 if(panel.background !== true){
36340 this.tabs.activate(panel.getEl().id);
36342 this.fireEvent("paneladded", this, panel);
36347 * Hides the tab for the specified panel.
36348 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36350 hidePanel : function(panel){
36351 if(this.tabs && (panel = this.getPanel(panel))){
36352 this.tabs.hideTab(panel.getEl().id);
36357 * Unhides the tab for a previously hidden panel.
36358 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36360 unhidePanel : function(panel){
36361 if(this.tabs && (panel = this.getPanel(panel))){
36362 this.tabs.unhideTab(panel.getEl().id);
36366 clearPanels : function(){
36367 while(this.panels.getCount() > 0){
36368 this.remove(this.panels.first());
36373 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36374 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36375 * @param {Boolean} preservePanel Overrides the config preservePanel option
36376 * @return {Roo.ContentPanel} The panel that was removed
36378 remove : function(panel, preservePanel)
36380 panel = this.getPanel(panel);
36385 this.fireEvent("beforeremove", this, panel, e);
36386 if(e.cancel === true){
36389 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36390 var panelId = panel.getId();
36391 this.panels.removeKey(panelId);
36393 document.body.appendChild(panel.getEl().dom);
36396 this.tabs.removeTab(panel.getEl().id);
36397 }else if (!preservePanel){
36398 this.bodyEl.dom.removeChild(panel.getEl().dom);
36400 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36401 var p = this.panels.first();
36402 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36403 tempEl.appendChild(p.getEl().dom);
36404 this.bodyEl.update("");
36405 this.bodyEl.dom.appendChild(p.getEl().dom);
36407 this.updateTitle(p.getTitle());
36409 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36410 this.setActivePanel(p);
36412 panel.setRegion(null);
36413 if(this.activePanel == panel){
36414 this.activePanel = null;
36416 if(this.config.autoDestroy !== false && preservePanel !== true){
36417 try{panel.destroy();}catch(e){}
36419 this.fireEvent("panelremoved", this, panel);
36424 * Returns the TabPanel component used by this region
36425 * @return {Roo.TabPanel}
36427 getTabs : function(){
36431 createTool : function(parentEl, className){
36432 var btn = Roo.DomHelper.append(parentEl, {
36434 cls: "x-layout-tools-button",
36437 cls: "roo-layout-tools-button-inner " + className,
36441 btn.addClassOnOver("roo-layout-tools-button-over");
36446 * Ext JS Library 1.1.1
36447 * Copyright(c) 2006-2007, Ext JS, LLC.
36449 * Originally Released Under LGPL - original licence link has changed is not relivant.
36452 * <script type="text/javascript">
36458 * @class Roo.SplitLayoutRegion
36459 * @extends Roo.LayoutRegion
36460 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36462 Roo.bootstrap.layout.Split = function(config){
36463 this.cursor = config.cursor;
36464 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36467 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36469 splitTip : "Drag to resize.",
36470 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36471 useSplitTips : false,
36473 applyConfig : function(config){
36474 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36477 onRender : function(ctr,pos) {
36479 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36480 if(!this.config.split){
36485 var splitEl = Roo.DomHelper.append(ctr.dom, {
36487 id: this.el.id + "-split",
36488 cls: "roo-layout-split roo-layout-split-"+this.position,
36491 /** The SplitBar for this region
36492 * @type Roo.SplitBar */
36493 // does not exist yet...
36494 Roo.log([this.position, this.orientation]);
36496 this.split = new Roo.bootstrap.SplitBar({
36497 dragElement : splitEl,
36498 resizingElement: this.el,
36499 orientation : this.orientation
36502 this.split.on("moved", this.onSplitMove, this);
36503 this.split.useShim = this.config.useShim === true;
36504 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36505 if(this.useSplitTips){
36506 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36508 //if(config.collapsible){
36509 // this.split.el.on("dblclick", this.collapse, this);
36512 if(typeof this.config.minSize != "undefined"){
36513 this.split.minSize = this.config.minSize;
36515 if(typeof this.config.maxSize != "undefined"){
36516 this.split.maxSize = this.config.maxSize;
36518 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36519 this.hideSplitter();
36524 getHMaxSize : function(){
36525 var cmax = this.config.maxSize || 10000;
36526 var center = this.mgr.getRegion("center");
36527 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36530 getVMaxSize : function(){
36531 var cmax = this.config.maxSize || 10000;
36532 var center = this.mgr.getRegion("center");
36533 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36536 onSplitMove : function(split, newSize){
36537 this.fireEvent("resized", this, newSize);
36541 * Returns the {@link Roo.SplitBar} for this region.
36542 * @return {Roo.SplitBar}
36544 getSplitBar : function(){
36549 this.hideSplitter();
36550 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36553 hideSplitter : function(){
36555 this.split.el.setLocation(-2000,-2000);
36556 this.split.el.hide();
36562 this.split.el.show();
36564 Roo.bootstrap.layout.Split.superclass.show.call(this);
36567 beforeSlide: function(){
36568 if(Roo.isGecko){// firefox overflow auto bug workaround
36569 this.bodyEl.clip();
36571 this.tabs.bodyEl.clip();
36573 if(this.activePanel){
36574 this.activePanel.getEl().clip();
36576 if(this.activePanel.beforeSlide){
36577 this.activePanel.beforeSlide();
36583 afterSlide : function(){
36584 if(Roo.isGecko){// firefox overflow auto bug workaround
36585 this.bodyEl.unclip();
36587 this.tabs.bodyEl.unclip();
36589 if(this.activePanel){
36590 this.activePanel.getEl().unclip();
36591 if(this.activePanel.afterSlide){
36592 this.activePanel.afterSlide();
36598 initAutoHide : function(){
36599 if(this.autoHide !== false){
36600 if(!this.autoHideHd){
36601 var st = new Roo.util.DelayedTask(this.slideIn, this);
36602 this.autoHideHd = {
36603 "mouseout": function(e){
36604 if(!e.within(this.el, true)){
36608 "mouseover" : function(e){
36614 this.el.on(this.autoHideHd);
36618 clearAutoHide : function(){
36619 if(this.autoHide !== false){
36620 this.el.un("mouseout", this.autoHideHd.mouseout);
36621 this.el.un("mouseover", this.autoHideHd.mouseover);
36625 clearMonitor : function(){
36626 Roo.get(document).un("click", this.slideInIf, this);
36629 // these names are backwards but not changed for compat
36630 slideOut : function(){
36631 if(this.isSlid || this.el.hasActiveFx()){
36634 this.isSlid = true;
36635 if(this.collapseBtn){
36636 this.collapseBtn.hide();
36638 this.closeBtnState = this.closeBtn.getStyle('display');
36639 this.closeBtn.hide();
36641 this.stickBtn.show();
36644 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36645 this.beforeSlide();
36646 this.el.setStyle("z-index", 10001);
36647 this.el.slideIn(this.getSlideAnchor(), {
36648 callback: function(){
36650 this.initAutoHide();
36651 Roo.get(document).on("click", this.slideInIf, this);
36652 this.fireEvent("slideshow", this);
36659 afterSlideIn : function(){
36660 this.clearAutoHide();
36661 this.isSlid = false;
36662 this.clearMonitor();
36663 this.el.setStyle("z-index", "");
36664 if(this.collapseBtn){
36665 this.collapseBtn.show();
36667 this.closeBtn.setStyle('display', this.closeBtnState);
36669 this.stickBtn.hide();
36671 this.fireEvent("slidehide", this);
36674 slideIn : function(cb){
36675 if(!this.isSlid || this.el.hasActiveFx()){
36679 this.isSlid = false;
36680 this.beforeSlide();
36681 this.el.slideOut(this.getSlideAnchor(), {
36682 callback: function(){
36683 this.el.setLeftTop(-10000, -10000);
36685 this.afterSlideIn();
36693 slideInIf : function(e){
36694 if(!e.within(this.el)){
36699 animateCollapse : function(){
36700 this.beforeSlide();
36701 this.el.setStyle("z-index", 20000);
36702 var anchor = this.getSlideAnchor();
36703 this.el.slideOut(anchor, {
36704 callback : function(){
36705 this.el.setStyle("z-index", "");
36706 this.collapsedEl.slideIn(anchor, {duration:.3});
36708 this.el.setLocation(-10000,-10000);
36710 this.fireEvent("collapsed", this);
36717 animateExpand : function(){
36718 this.beforeSlide();
36719 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36720 this.el.setStyle("z-index", 20000);
36721 this.collapsedEl.hide({
36724 this.el.slideIn(this.getSlideAnchor(), {
36725 callback : function(){
36726 this.el.setStyle("z-index", "");
36729 this.split.el.show();
36731 this.fireEvent("invalidated", this);
36732 this.fireEvent("expanded", this);
36760 getAnchor : function(){
36761 return this.anchors[this.position];
36764 getCollapseAnchor : function(){
36765 return this.canchors[this.position];
36768 getSlideAnchor : function(){
36769 return this.sanchors[this.position];
36772 getAlignAdj : function(){
36773 var cm = this.cmargins;
36774 switch(this.position){
36790 getExpandAdj : function(){
36791 var c = this.collapsedEl, cm = this.cmargins;
36792 switch(this.position){
36794 return [-(cm.right+c.getWidth()+cm.left), 0];
36797 return [cm.right+c.getWidth()+cm.left, 0];
36800 return [0, -(cm.top+cm.bottom+c.getHeight())];
36803 return [0, cm.top+cm.bottom+c.getHeight()];
36809 * Ext JS Library 1.1.1
36810 * Copyright(c) 2006-2007, Ext JS, LLC.
36812 * Originally Released Under LGPL - original licence link has changed is not relivant.
36815 * <script type="text/javascript">
36818 * These classes are private internal classes
36820 Roo.bootstrap.layout.Center = function(config){
36821 config.region = "center";
36822 Roo.bootstrap.layout.Region.call(this, config);
36823 this.visible = true;
36824 this.minWidth = config.minWidth || 20;
36825 this.minHeight = config.minHeight || 20;
36828 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36830 // center panel can't be hidden
36834 // center panel can't be hidden
36837 getMinWidth: function(){
36838 return this.minWidth;
36841 getMinHeight: function(){
36842 return this.minHeight;
36855 Roo.bootstrap.layout.North = function(config)
36857 config.region = 'north';
36858 config.cursor = 'n-resize';
36860 Roo.bootstrap.layout.Split.call(this, config);
36864 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36865 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36866 this.split.el.addClass("roo-layout-split-v");
36868 var size = config.initialSize || config.height;
36869 if(typeof size != "undefined"){
36870 this.el.setHeight(size);
36873 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36875 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36879 getBox : function(){
36880 if(this.collapsed){
36881 return this.collapsedEl.getBox();
36883 var box = this.el.getBox();
36885 box.height += this.split.el.getHeight();
36890 updateBox : function(box){
36891 if(this.split && !this.collapsed){
36892 box.height -= this.split.el.getHeight();
36893 this.split.el.setLeft(box.x);
36894 this.split.el.setTop(box.y+box.height);
36895 this.split.el.setWidth(box.width);
36897 if(this.collapsed){
36898 this.updateBody(box.width, null);
36900 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36908 Roo.bootstrap.layout.South = function(config){
36909 config.region = 'south';
36910 config.cursor = 's-resize';
36911 Roo.bootstrap.layout.Split.call(this, config);
36913 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36914 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36915 this.split.el.addClass("roo-layout-split-v");
36917 var size = config.initialSize || config.height;
36918 if(typeof size != "undefined"){
36919 this.el.setHeight(size);
36923 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36924 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36925 getBox : function(){
36926 if(this.collapsed){
36927 return this.collapsedEl.getBox();
36929 var box = this.el.getBox();
36931 var sh = this.split.el.getHeight();
36938 updateBox : function(box){
36939 if(this.split && !this.collapsed){
36940 var sh = this.split.el.getHeight();
36943 this.split.el.setLeft(box.x);
36944 this.split.el.setTop(box.y-sh);
36945 this.split.el.setWidth(box.width);
36947 if(this.collapsed){
36948 this.updateBody(box.width, null);
36950 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36954 Roo.bootstrap.layout.East = function(config){
36955 config.region = "east";
36956 config.cursor = "e-resize";
36957 Roo.bootstrap.layout.Split.call(this, config);
36959 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36960 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36961 this.split.el.addClass("roo-layout-split-h");
36963 var size = config.initialSize || config.width;
36964 if(typeof size != "undefined"){
36965 this.el.setWidth(size);
36968 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36969 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36970 getBox : function(){
36971 if(this.collapsed){
36972 return this.collapsedEl.getBox();
36974 var box = this.el.getBox();
36976 var sw = this.split.el.getWidth();
36983 updateBox : function(box){
36984 if(this.split && !this.collapsed){
36985 var sw = this.split.el.getWidth();
36987 this.split.el.setLeft(box.x);
36988 this.split.el.setTop(box.y);
36989 this.split.el.setHeight(box.height);
36992 if(this.collapsed){
36993 this.updateBody(null, box.height);
36995 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36999 Roo.bootstrap.layout.West = function(config){
37000 config.region = "west";
37001 config.cursor = "w-resize";
37003 Roo.bootstrap.layout.Split.call(this, config);
37005 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37006 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37007 this.split.el.addClass("roo-layout-split-h");
37011 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37012 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37014 onRender: function(ctr, pos)
37016 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37017 var size = this.config.initialSize || this.config.width;
37018 if(typeof size != "undefined"){
37019 this.el.setWidth(size);
37023 getBox : function(){
37024 if(this.collapsed){
37025 return this.collapsedEl.getBox();
37027 var box = this.el.getBox();
37029 box.width += this.split.el.getWidth();
37034 updateBox : function(box){
37035 if(this.split && !this.collapsed){
37036 var sw = this.split.el.getWidth();
37038 this.split.el.setLeft(box.x+box.width);
37039 this.split.el.setTop(box.y);
37040 this.split.el.setHeight(box.height);
37042 if(this.collapsed){
37043 this.updateBody(null, box.height);
37045 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37048 Roo.namespace("Roo.bootstrap.panel");/*
37050 * Ext JS Library 1.1.1
37051 * Copyright(c) 2006-2007, Ext JS, LLC.
37053 * Originally Released Under LGPL - original licence link has changed is not relivant.
37056 * <script type="text/javascript">
37059 * @class Roo.ContentPanel
37060 * @extends Roo.util.Observable
37061 * A basic ContentPanel element.
37062 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37063 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37064 * @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
37065 * @cfg {Boolean} closable True if the panel can be closed/removed
37066 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37067 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37068 * @cfg {Toolbar} toolbar A toolbar for this panel
37069 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37070 * @cfg {String} title The title for this panel
37071 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37072 * @cfg {String} url Calls {@link #setUrl} with this value
37073 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37074 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37075 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37076 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37077 * @cfg {Boolean} badges render the badges
37080 * Create a new ContentPanel.
37081 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37082 * @param {String/Object} config A string to set only the title or a config object
37083 * @param {String} content (optional) Set the HTML content for this panel
37084 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37086 Roo.bootstrap.panel.Content = function( config){
37088 this.tpl = config.tpl || false;
37090 var el = config.el;
37091 var content = config.content;
37093 if(config.autoCreate){ // xtype is available if this is called from factory
37096 this.el = Roo.get(el);
37097 if(!this.el && config && config.autoCreate){
37098 if(typeof config.autoCreate == "object"){
37099 if(!config.autoCreate.id){
37100 config.autoCreate.id = config.id||el;
37102 this.el = Roo.DomHelper.append(document.body,
37103 config.autoCreate, true);
37105 var elcfg = { tag: "div",
37106 cls: "roo-layout-inactive-content",
37110 elcfg.html = config.html;
37114 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37117 this.closable = false;
37118 this.loaded = false;
37119 this.active = false;
37122 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37124 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37126 this.wrapEl = this.el; //this.el.wrap();
37128 if (config.toolbar.items) {
37129 ti = config.toolbar.items ;
37130 delete config.toolbar.items ;
37134 this.toolbar.render(this.wrapEl, 'before');
37135 for(var i =0;i < ti.length;i++) {
37136 // Roo.log(['add child', items[i]]);
37137 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37139 this.toolbar.items = nitems;
37140 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37141 delete config.toolbar;
37145 // xtype created footer. - not sure if will work as we normally have to render first..
37146 if (this.footer && !this.footer.el && this.footer.xtype) {
37147 if (!this.wrapEl) {
37148 this.wrapEl = this.el.wrap();
37151 this.footer.container = this.wrapEl.createChild();
37153 this.footer = Roo.factory(this.footer, Roo);
37158 if(typeof config == "string"){
37159 this.title = config;
37161 Roo.apply(this, config);
37165 this.resizeEl = Roo.get(this.resizeEl, true);
37167 this.resizeEl = this.el;
37169 // handle view.xtype
37177 * Fires when this panel is activated.
37178 * @param {Roo.ContentPanel} this
37182 * @event deactivate
37183 * Fires when this panel is activated.
37184 * @param {Roo.ContentPanel} this
37186 "deactivate" : true,
37190 * Fires when this panel is resized if fitToFrame is true.
37191 * @param {Roo.ContentPanel} this
37192 * @param {Number} width The width after any component adjustments
37193 * @param {Number} height The height after any component adjustments
37199 * Fires when this tab is created
37200 * @param {Roo.ContentPanel} this
37211 if(this.autoScroll){
37212 this.resizeEl.setStyle("overflow", "auto");
37214 // fix randome scrolling
37215 //this.el.on('scroll', function() {
37216 // Roo.log('fix random scolling');
37217 // this.scrollTo('top',0);
37220 content = content || this.content;
37222 this.setContent(content);
37224 if(config && config.url){
37225 this.setUrl(this.url, this.params, this.loadOnce);
37230 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37232 if (this.view && typeof(this.view.xtype) != 'undefined') {
37233 this.view.el = this.el.appendChild(document.createElement("div"));
37234 this.view = Roo.factory(this.view);
37235 this.view.render && this.view.render(false, '');
37239 this.fireEvent('render', this);
37242 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37246 setRegion : function(region){
37247 this.region = region;
37248 this.setActiveClass(region && !this.background);
37252 setActiveClass: function(state)
37255 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37256 this.el.setStyle('position','relative');
37258 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37259 this.el.setStyle('position', 'absolute');
37264 * Returns the toolbar for this Panel if one was configured.
37265 * @return {Roo.Toolbar}
37267 getToolbar : function(){
37268 return this.toolbar;
37271 setActiveState : function(active)
37273 this.active = active;
37274 this.setActiveClass(active);
37276 if(this.fireEvent("deactivate", this) === false){
37281 this.fireEvent("activate", this);
37285 * Updates this panel's element
37286 * @param {String} content The new content
37287 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37289 setContent : function(content, loadScripts){
37290 this.el.update(content, loadScripts);
37293 ignoreResize : function(w, h){
37294 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37297 this.lastSize = {width: w, height: h};
37302 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37303 * @return {Roo.UpdateManager} The UpdateManager
37305 getUpdateManager : function(){
37306 return this.el.getUpdateManager();
37309 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37310 * @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:
37313 url: "your-url.php",
37314 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37315 callback: yourFunction,
37316 scope: yourObject, //(optional scope)
37319 text: "Loading...",
37324 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37325 * 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.
37326 * @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}
37327 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37328 * @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.
37329 * @return {Roo.ContentPanel} this
37332 var um = this.el.getUpdateManager();
37333 um.update.apply(um, arguments);
37339 * 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.
37340 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37341 * @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)
37342 * @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)
37343 * @return {Roo.UpdateManager} The UpdateManager
37345 setUrl : function(url, params, loadOnce){
37346 if(this.refreshDelegate){
37347 this.removeListener("activate", this.refreshDelegate);
37349 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37350 this.on("activate", this.refreshDelegate);
37351 return this.el.getUpdateManager();
37354 _handleRefresh : function(url, params, loadOnce){
37355 if(!loadOnce || !this.loaded){
37356 var updater = this.el.getUpdateManager();
37357 updater.update(url, params, this._setLoaded.createDelegate(this));
37361 _setLoaded : function(){
37362 this.loaded = true;
37366 * Returns this panel's id
37369 getId : function(){
37374 * Returns this panel's element - used by regiosn to add.
37375 * @return {Roo.Element}
37377 getEl : function(){
37378 return this.wrapEl || this.el;
37383 adjustForComponents : function(width, height)
37385 //Roo.log('adjustForComponents ');
37386 if(this.resizeEl != this.el){
37387 width -= this.el.getFrameWidth('lr');
37388 height -= this.el.getFrameWidth('tb');
37391 var te = this.toolbar.getEl();
37392 te.setWidth(width);
37393 height -= te.getHeight();
37396 var te = this.footer.getEl();
37397 te.setWidth(width);
37398 height -= te.getHeight();
37402 if(this.adjustments){
37403 width += this.adjustments[0];
37404 height += this.adjustments[1];
37406 return {"width": width, "height": height};
37409 setSize : function(width, height){
37410 if(this.fitToFrame && !this.ignoreResize(width, height)){
37411 if(this.fitContainer && this.resizeEl != this.el){
37412 this.el.setSize(width, height);
37414 var size = this.adjustForComponents(width, height);
37415 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37416 this.fireEvent('resize', this, size.width, size.height);
37421 * Returns this panel's title
37424 getTitle : function(){
37426 if (typeof(this.title) != 'object') {
37431 for (var k in this.title) {
37432 if (!this.title.hasOwnProperty(k)) {
37436 if (k.indexOf('-') >= 0) {
37437 var s = k.split('-');
37438 for (var i = 0; i<s.length; i++) {
37439 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37442 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37449 * Set this panel's title
37450 * @param {String} title
37452 setTitle : function(title){
37453 this.title = title;
37455 this.region.updatePanelTitle(this, title);
37460 * Returns true is this panel was configured to be closable
37461 * @return {Boolean}
37463 isClosable : function(){
37464 return this.closable;
37467 beforeSlide : function(){
37469 this.resizeEl.clip();
37472 afterSlide : function(){
37474 this.resizeEl.unclip();
37478 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37479 * Will fail silently if the {@link #setUrl} method has not been called.
37480 * This does not activate the panel, just updates its content.
37482 refresh : function(){
37483 if(this.refreshDelegate){
37484 this.loaded = false;
37485 this.refreshDelegate();
37490 * Destroys this panel
37492 destroy : function(){
37493 this.el.removeAllListeners();
37494 var tempEl = document.createElement("span");
37495 tempEl.appendChild(this.el.dom);
37496 tempEl.innerHTML = "";
37502 * form - if the content panel contains a form - this is a reference to it.
37503 * @type {Roo.form.Form}
37507 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37508 * This contains a reference to it.
37514 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37524 * @param {Object} cfg Xtype definition of item to add.
37528 getChildContainer: function () {
37529 return this.getEl();
37534 var ret = new Roo.factory(cfg);
37539 if (cfg.xtype.match(/^Form$/)) {
37542 //if (this.footer) {
37543 // el = this.footer.container.insertSibling(false, 'before');
37545 el = this.el.createChild();
37548 this.form = new Roo.form.Form(cfg);
37551 if ( this.form.allItems.length) {
37552 this.form.render(el.dom);
37556 // should only have one of theses..
37557 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37558 // views.. should not be just added - used named prop 'view''
37560 cfg.el = this.el.appendChild(document.createElement("div"));
37563 var ret = new Roo.factory(cfg);
37565 ret.render && ret.render(false, ''); // render blank..
37575 * @class Roo.bootstrap.panel.Grid
37576 * @extends Roo.bootstrap.panel.Content
37578 * Create a new GridPanel.
37579 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37580 * @param {Object} config A the config object
37586 Roo.bootstrap.panel.Grid = function(config)
37590 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37591 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37593 config.el = this.wrapper;
37594 //this.el = this.wrapper;
37596 if (config.container) {
37597 // ctor'ed from a Border/panel.grid
37600 this.wrapper.setStyle("overflow", "hidden");
37601 this.wrapper.addClass('roo-grid-container');
37606 if(config.toolbar){
37607 var tool_el = this.wrapper.createChild();
37608 this.toolbar = Roo.factory(config.toolbar);
37610 if (config.toolbar.items) {
37611 ti = config.toolbar.items ;
37612 delete config.toolbar.items ;
37616 this.toolbar.render(tool_el);
37617 for(var i =0;i < ti.length;i++) {
37618 // Roo.log(['add child', items[i]]);
37619 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37621 this.toolbar.items = nitems;
37623 delete config.toolbar;
37626 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37627 config.grid.scrollBody = true;;
37628 config.grid.monitorWindowResize = false; // turn off autosizing
37629 config.grid.autoHeight = false;
37630 config.grid.autoWidth = false;
37632 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37634 if (config.background) {
37635 // render grid on panel activation (if panel background)
37636 this.on('activate', function(gp) {
37637 if (!gp.grid.rendered) {
37638 gp.grid.render(this.wrapper);
37639 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37644 this.grid.render(this.wrapper);
37645 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37648 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37649 // ??? needed ??? config.el = this.wrapper;
37654 // xtype created footer. - not sure if will work as we normally have to render first..
37655 if (this.footer && !this.footer.el && this.footer.xtype) {
37657 var ctr = this.grid.getView().getFooterPanel(true);
37658 this.footer.dataSource = this.grid.dataSource;
37659 this.footer = Roo.factory(this.footer, Roo);
37660 this.footer.render(ctr);
37670 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37671 getId : function(){
37672 return this.grid.id;
37676 * Returns the grid for this panel
37677 * @return {Roo.bootstrap.Table}
37679 getGrid : function(){
37683 setSize : function(width, height){
37684 if(!this.ignoreResize(width, height)){
37685 var grid = this.grid;
37686 var size = this.adjustForComponents(width, height);
37687 var gridel = grid.getGridEl();
37688 gridel.setSize(size.width, size.height);
37690 var thd = grid.getGridEl().select('thead',true).first();
37691 var tbd = grid.getGridEl().select('tbody', true).first();
37693 tbd.setSize(width, height - thd.getHeight());
37702 beforeSlide : function(){
37703 this.grid.getView().scroller.clip();
37706 afterSlide : function(){
37707 this.grid.getView().scroller.unclip();
37710 destroy : function(){
37711 this.grid.destroy();
37713 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37718 * @class Roo.bootstrap.panel.Nest
37719 * @extends Roo.bootstrap.panel.Content
37721 * Create a new Panel, that can contain a layout.Border.
37724 * @param {Roo.BorderLayout} layout The layout for this panel
37725 * @param {String/Object} config A string to set only the title or a config object
37727 Roo.bootstrap.panel.Nest = function(config)
37729 // construct with only one argument..
37730 /* FIXME - implement nicer consturctors
37731 if (layout.layout) {
37733 layout = config.layout;
37734 delete config.layout;
37736 if (layout.xtype && !layout.getEl) {
37737 // then layout needs constructing..
37738 layout = Roo.factory(layout, Roo);
37742 config.el = config.layout.getEl();
37744 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37746 config.layout.monitorWindowResize = false; // turn off autosizing
37747 this.layout = config.layout;
37748 this.layout.getEl().addClass("roo-layout-nested-layout");
37755 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37757 setSize : function(width, height){
37758 if(!this.ignoreResize(width, height)){
37759 var size = this.adjustForComponents(width, height);
37760 var el = this.layout.getEl();
37761 if (size.height < 1) {
37762 el.setWidth(size.width);
37764 el.setSize(size.width, size.height);
37766 var touch = el.dom.offsetWidth;
37767 this.layout.layout();
37768 // ie requires a double layout on the first pass
37769 if(Roo.isIE && !this.initialized){
37770 this.initialized = true;
37771 this.layout.layout();
37776 // activate all subpanels if not currently active..
37778 setActiveState : function(active){
37779 this.active = active;
37780 this.setActiveClass(active);
37783 this.fireEvent("deactivate", this);
37787 this.fireEvent("activate", this);
37788 // not sure if this should happen before or after..
37789 if (!this.layout) {
37790 return; // should not happen..
37793 for (var r in this.layout.regions) {
37794 reg = this.layout.getRegion(r);
37795 if (reg.getActivePanel()) {
37796 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37797 reg.setActivePanel(reg.getActivePanel());
37800 if (!reg.panels.length) {
37803 reg.showPanel(reg.getPanel(0));
37812 * Returns the nested BorderLayout for this panel
37813 * @return {Roo.BorderLayout}
37815 getLayout : function(){
37816 return this.layout;
37820 * Adds a xtype elements to the layout of the nested panel
37824 xtype : 'ContentPanel',
37831 xtype : 'NestedLayoutPanel',
37837 items : [ ... list of content panels or nested layout panels.. ]
37841 * @param {Object} cfg Xtype definition of item to add.
37843 addxtype : function(cfg) {
37844 return this.layout.addxtype(cfg);
37849 * Ext JS Library 1.1.1
37850 * Copyright(c) 2006-2007, Ext JS, LLC.
37852 * Originally Released Under LGPL - original licence link has changed is not relivant.
37855 * <script type="text/javascript">
37858 * @class Roo.TabPanel
37859 * @extends Roo.util.Observable
37860 * A lightweight tab container.
37864 // basic tabs 1, built from existing content
37865 var tabs = new Roo.TabPanel("tabs1");
37866 tabs.addTab("script", "View Script");
37867 tabs.addTab("markup", "View Markup");
37868 tabs.activate("script");
37870 // more advanced tabs, built from javascript
37871 var jtabs = new Roo.TabPanel("jtabs");
37872 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37874 // set up the UpdateManager
37875 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37876 var updater = tab2.getUpdateManager();
37877 updater.setDefaultUrl("ajax1.htm");
37878 tab2.on('activate', updater.refresh, updater, true);
37880 // Use setUrl for Ajax loading
37881 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37882 tab3.setUrl("ajax2.htm", null, true);
37885 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37888 jtabs.activate("jtabs-1");
37891 * Create a new TabPanel.
37892 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37893 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37895 Roo.bootstrap.panel.Tabs = function(config){
37897 * The container element for this TabPanel.
37898 * @type Roo.Element
37900 this.el = Roo.get(config.el);
37903 if(typeof config == "boolean"){
37904 this.tabPosition = config ? "bottom" : "top";
37906 Roo.apply(this, config);
37910 if(this.tabPosition == "bottom"){
37911 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37912 this.el.addClass("roo-tabs-bottom");
37914 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37915 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37916 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37918 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37920 if(this.tabPosition != "bottom"){
37921 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37922 * @type Roo.Element
37924 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37925 this.el.addClass("roo-tabs-top");
37929 this.bodyEl.setStyle("position", "relative");
37931 this.active = null;
37932 this.activateDelegate = this.activate.createDelegate(this);
37937 * Fires when the active tab changes
37938 * @param {Roo.TabPanel} this
37939 * @param {Roo.TabPanelItem} activePanel The new active tab
37943 * @event beforetabchange
37944 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37945 * @param {Roo.TabPanel} this
37946 * @param {Object} e Set cancel to true on this object to cancel the tab change
37947 * @param {Roo.TabPanelItem} tab The tab being changed to
37949 "beforetabchange" : true
37952 Roo.EventManager.onWindowResize(this.onResize, this);
37953 this.cpad = this.el.getPadding("lr");
37954 this.hiddenCount = 0;
37957 // toolbar on the tabbar support...
37958 if (this.toolbar) {
37959 alert("no toolbar support yet");
37960 this.toolbar = false;
37962 var tcfg = this.toolbar;
37963 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37964 this.toolbar = new Roo.Toolbar(tcfg);
37965 if (Roo.isSafari) {
37966 var tbl = tcfg.container.child('table', true);
37967 tbl.setAttribute('width', '100%');
37975 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37978 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37980 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37982 tabPosition : "top",
37984 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37986 currentTabWidth : 0,
37988 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37992 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37996 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37998 preferredTabWidth : 175,
38000 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38002 resizeTabs : false,
38004 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38006 monitorResize : true,
38008 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38013 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38014 * @param {String} id The id of the div to use <b>or create</b>
38015 * @param {String} text The text for the tab
38016 * @param {String} content (optional) Content to put in the TabPanelItem body
38017 * @param {Boolean} closable (optional) True to create a close icon on the tab
38018 * @return {Roo.TabPanelItem} The created TabPanelItem
38020 addTab : function(id, text, content, closable, tpl)
38022 var item = new Roo.bootstrap.panel.TabItem({
38026 closable : closable,
38029 this.addTabItem(item);
38031 item.setContent(content);
38037 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38038 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38039 * @return {Roo.TabPanelItem}
38041 getTab : function(id){
38042 return this.items[id];
38046 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38047 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38049 hideTab : function(id){
38050 var t = this.items[id];
38053 this.hiddenCount++;
38054 this.autoSizeTabs();
38059 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38060 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38062 unhideTab : function(id){
38063 var t = this.items[id];
38065 t.setHidden(false);
38066 this.hiddenCount--;
38067 this.autoSizeTabs();
38072 * Adds an existing {@link Roo.TabPanelItem}.
38073 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38075 addTabItem : function(item){
38076 this.items[item.id] = item;
38077 this.items.push(item);
38078 // if(this.resizeTabs){
38079 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38080 // this.autoSizeTabs();
38082 // item.autoSize();
38087 * Removes a {@link Roo.TabPanelItem}.
38088 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38090 removeTab : function(id){
38091 var items = this.items;
38092 var tab = items[id];
38093 if(!tab) { return; }
38094 var index = items.indexOf(tab);
38095 if(this.active == tab && items.length > 1){
38096 var newTab = this.getNextAvailable(index);
38101 this.stripEl.dom.removeChild(tab.pnode.dom);
38102 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38103 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38105 items.splice(index, 1);
38106 delete this.items[tab.id];
38107 tab.fireEvent("close", tab);
38108 tab.purgeListeners();
38109 this.autoSizeTabs();
38112 getNextAvailable : function(start){
38113 var items = this.items;
38115 // look for a next tab that will slide over to
38116 // replace the one being removed
38117 while(index < items.length){
38118 var item = items[++index];
38119 if(item && !item.isHidden()){
38123 // if one isn't found select the previous tab (on the left)
38126 var item = items[--index];
38127 if(item && !item.isHidden()){
38135 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38136 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38138 disableTab : function(id){
38139 var tab = this.items[id];
38140 if(tab && this.active != tab){
38146 * Enables a {@link Roo.TabPanelItem} that is disabled.
38147 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38149 enableTab : function(id){
38150 var tab = this.items[id];
38155 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38156 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38157 * @return {Roo.TabPanelItem} The TabPanelItem.
38159 activate : function(id){
38160 var tab = this.items[id];
38164 if(tab == this.active || tab.disabled){
38168 this.fireEvent("beforetabchange", this, e, tab);
38169 if(e.cancel !== true && !tab.disabled){
38171 this.active.hide();
38173 this.active = this.items[id];
38174 this.active.show();
38175 this.fireEvent("tabchange", this, this.active);
38181 * Gets the active {@link Roo.TabPanelItem}.
38182 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38184 getActiveTab : function(){
38185 return this.active;
38189 * Updates the tab body element to fit the height of the container element
38190 * for overflow scrolling
38191 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38193 syncHeight : function(targetHeight){
38194 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38195 var bm = this.bodyEl.getMargins();
38196 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38197 this.bodyEl.setHeight(newHeight);
38201 onResize : function(){
38202 if(this.monitorResize){
38203 this.autoSizeTabs();
38208 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38210 beginUpdate : function(){
38211 this.updating = true;
38215 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38217 endUpdate : function(){
38218 this.updating = false;
38219 this.autoSizeTabs();
38223 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38225 autoSizeTabs : function(){
38226 var count = this.items.length;
38227 var vcount = count - this.hiddenCount;
38228 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38231 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38232 var availWidth = Math.floor(w / vcount);
38233 var b = this.stripBody;
38234 if(b.getWidth() > w){
38235 var tabs = this.items;
38236 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38237 if(availWidth < this.minTabWidth){
38238 /*if(!this.sleft){ // incomplete scrolling code
38239 this.createScrollButtons();
38242 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38245 if(this.currentTabWidth < this.preferredTabWidth){
38246 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38252 * Returns the number of tabs in this TabPanel.
38255 getCount : function(){
38256 return this.items.length;
38260 * Resizes all the tabs to the passed width
38261 * @param {Number} The new width
38263 setTabWidth : function(width){
38264 this.currentTabWidth = width;
38265 for(var i = 0, len = this.items.length; i < len; i++) {
38266 if(!this.items[i].isHidden()) {
38267 this.items[i].setWidth(width);
38273 * Destroys this TabPanel
38274 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38276 destroy : function(removeEl){
38277 Roo.EventManager.removeResizeListener(this.onResize, this);
38278 for(var i = 0, len = this.items.length; i < len; i++){
38279 this.items[i].purgeListeners();
38281 if(removeEl === true){
38282 this.el.update("");
38287 createStrip : function(container)
38289 var strip = document.createElement("nav");
38290 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38291 container.appendChild(strip);
38295 createStripList : function(strip)
38297 // div wrapper for retard IE
38298 // returns the "tr" element.
38299 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38300 //'<div class="x-tabs-strip-wrap">'+
38301 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38302 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38303 return strip.firstChild; //.firstChild.firstChild.firstChild;
38305 createBody : function(container)
38307 var body = document.createElement("div");
38308 Roo.id(body, "tab-body");
38309 //Roo.fly(body).addClass("x-tabs-body");
38310 Roo.fly(body).addClass("tab-content");
38311 container.appendChild(body);
38314 createItemBody :function(bodyEl, id){
38315 var body = Roo.getDom(id);
38317 body = document.createElement("div");
38320 //Roo.fly(body).addClass("x-tabs-item-body");
38321 Roo.fly(body).addClass("tab-pane");
38322 bodyEl.insertBefore(body, bodyEl.firstChild);
38326 createStripElements : function(stripEl, text, closable, tpl)
38328 var td = document.createElement("li"); // was td..
38331 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38334 stripEl.appendChild(td);
38336 td.className = "x-tabs-closable";
38337 if(!this.closeTpl){
38338 this.closeTpl = new Roo.Template(
38339 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38340 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38341 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38344 var el = this.closeTpl.overwrite(td, {"text": text});
38345 var close = el.getElementsByTagName("div")[0];
38346 var inner = el.getElementsByTagName("em")[0];
38347 return {"el": el, "close": close, "inner": inner};
38350 // not sure what this is..
38351 // if(!this.tabTpl){
38352 //this.tabTpl = new Roo.Template(
38353 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38354 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38356 // this.tabTpl = new Roo.Template(
38357 // '<a href="#">' +
38358 // '<span unselectable="on"' +
38359 // (this.disableTooltips ? '' : ' title="{text}"') +
38360 // ' >{text}</span></a>'
38366 var template = tpl || this.tabTpl || false;
38370 template = new Roo.Template(
38372 '<span unselectable="on"' +
38373 (this.disableTooltips ? '' : ' title="{text}"') +
38374 ' >{text}</span></a>'
38378 switch (typeof(template)) {
38382 template = new Roo.Template(template);
38388 var el = template.overwrite(td, {"text": text});
38390 var inner = el.getElementsByTagName("span")[0];
38392 return {"el": el, "inner": inner};
38400 * @class Roo.TabPanelItem
38401 * @extends Roo.util.Observable
38402 * Represents an individual item (tab plus body) in a TabPanel.
38403 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38404 * @param {String} id The id of this TabPanelItem
38405 * @param {String} text The text for the tab of this TabPanelItem
38406 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38408 Roo.bootstrap.panel.TabItem = function(config){
38410 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38411 * @type Roo.TabPanel
38413 this.tabPanel = config.panel;
38415 * The id for this TabPanelItem
38418 this.id = config.id;
38420 this.disabled = false;
38422 this.text = config.text;
38424 this.loaded = false;
38425 this.closable = config.closable;
38428 * The body element for this TabPanelItem.
38429 * @type Roo.Element
38431 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38432 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38433 this.bodyEl.setStyle("display", "block");
38434 this.bodyEl.setStyle("zoom", "1");
38435 //this.hideAction();
38437 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38439 this.el = Roo.get(els.el);
38440 this.inner = Roo.get(els.inner, true);
38441 this.textEl = Roo.get(this.el.dom.firstChild, true);
38442 this.pnode = Roo.get(els.el.parentNode, true);
38443 // this.el.on("mousedown", this.onTabMouseDown, this);
38444 this.el.on("click", this.onTabClick, this);
38446 if(config.closable){
38447 var c = Roo.get(els.close, true);
38448 c.dom.title = this.closeText;
38449 c.addClassOnOver("close-over");
38450 c.on("click", this.closeClick, this);
38456 * Fires when this tab becomes the active tab.
38457 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38458 * @param {Roo.TabPanelItem} this
38462 * @event beforeclose
38463 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38464 * @param {Roo.TabPanelItem} this
38465 * @param {Object} e Set cancel to true on this object to cancel the close.
38467 "beforeclose": true,
38470 * Fires when this tab is closed.
38471 * @param {Roo.TabPanelItem} this
38475 * @event deactivate
38476 * Fires when this tab is no longer the active tab.
38477 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38478 * @param {Roo.TabPanelItem} this
38480 "deactivate" : true
38482 this.hidden = false;
38484 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38487 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38489 purgeListeners : function(){
38490 Roo.util.Observable.prototype.purgeListeners.call(this);
38491 this.el.removeAllListeners();
38494 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38497 this.pnode.addClass("active");
38500 this.tabPanel.stripWrap.repaint();
38502 this.fireEvent("activate", this.tabPanel, this);
38506 * Returns true if this tab is the active tab.
38507 * @return {Boolean}
38509 isActive : function(){
38510 return this.tabPanel.getActiveTab() == this;
38514 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38517 this.pnode.removeClass("active");
38519 this.fireEvent("deactivate", this.tabPanel, this);
38522 hideAction : function(){
38523 this.bodyEl.hide();
38524 this.bodyEl.setStyle("position", "absolute");
38525 this.bodyEl.setLeft("-20000px");
38526 this.bodyEl.setTop("-20000px");
38529 showAction : function(){
38530 this.bodyEl.setStyle("position", "relative");
38531 this.bodyEl.setTop("");
38532 this.bodyEl.setLeft("");
38533 this.bodyEl.show();
38537 * Set the tooltip for the tab.
38538 * @param {String} tooltip The tab's tooltip
38540 setTooltip : function(text){
38541 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38542 this.textEl.dom.qtip = text;
38543 this.textEl.dom.removeAttribute('title');
38545 this.textEl.dom.title = text;
38549 onTabClick : function(e){
38550 e.preventDefault();
38551 this.tabPanel.activate(this.id);
38554 onTabMouseDown : function(e){
38555 e.preventDefault();
38556 this.tabPanel.activate(this.id);
38559 getWidth : function(){
38560 return this.inner.getWidth();
38563 setWidth : function(width){
38564 var iwidth = width - this.pnode.getPadding("lr");
38565 this.inner.setWidth(iwidth);
38566 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38567 this.pnode.setWidth(width);
38571 * Show or hide the tab
38572 * @param {Boolean} hidden True to hide or false to show.
38574 setHidden : function(hidden){
38575 this.hidden = hidden;
38576 this.pnode.setStyle("display", hidden ? "none" : "");
38580 * Returns true if this tab is "hidden"
38581 * @return {Boolean}
38583 isHidden : function(){
38584 return this.hidden;
38588 * Returns the text for this tab
38591 getText : function(){
38595 autoSize : function(){
38596 //this.el.beginMeasure();
38597 this.textEl.setWidth(1);
38599 * #2804 [new] Tabs in Roojs
38600 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38602 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38603 //this.el.endMeasure();
38607 * Sets the text for the tab (Note: this also sets the tooltip text)
38608 * @param {String} text The tab's text and tooltip
38610 setText : function(text){
38612 this.textEl.update(text);
38613 this.setTooltip(text);
38614 //if(!this.tabPanel.resizeTabs){
38615 // this.autoSize();
38619 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38621 activate : function(){
38622 this.tabPanel.activate(this.id);
38626 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38628 disable : function(){
38629 if(this.tabPanel.active != this){
38630 this.disabled = true;
38631 this.pnode.addClass("disabled");
38636 * Enables this TabPanelItem if it was previously disabled.
38638 enable : function(){
38639 this.disabled = false;
38640 this.pnode.removeClass("disabled");
38644 * Sets the content for this TabPanelItem.
38645 * @param {String} content The content
38646 * @param {Boolean} loadScripts true to look for and load scripts
38648 setContent : function(content, loadScripts){
38649 this.bodyEl.update(content, loadScripts);
38653 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38654 * @return {Roo.UpdateManager} The UpdateManager
38656 getUpdateManager : function(){
38657 return this.bodyEl.getUpdateManager();
38661 * Set a URL to be used to load the content for this TabPanelItem.
38662 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38663 * @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)
38664 * @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)
38665 * @return {Roo.UpdateManager} The UpdateManager
38667 setUrl : function(url, params, loadOnce){
38668 if(this.refreshDelegate){
38669 this.un('activate', this.refreshDelegate);
38671 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38672 this.on("activate", this.refreshDelegate);
38673 return this.bodyEl.getUpdateManager();
38677 _handleRefresh : function(url, params, loadOnce){
38678 if(!loadOnce || !this.loaded){
38679 var updater = this.bodyEl.getUpdateManager();
38680 updater.update(url, params, this._setLoaded.createDelegate(this));
38685 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38686 * Will fail silently if the setUrl method has not been called.
38687 * This does not activate the panel, just updates its content.
38689 refresh : function(){
38690 if(this.refreshDelegate){
38691 this.loaded = false;
38692 this.refreshDelegate();
38697 _setLoaded : function(){
38698 this.loaded = true;
38702 closeClick : function(e){
38705 this.fireEvent("beforeclose", this, o);
38706 if(o.cancel !== true){
38707 this.tabPanel.removeTab(this.id);
38711 * The text displayed in the tooltip for the close icon.
38714 closeText : "Close this tab"
38717 * This script refer to:
38718 * Title: International Telephone Input
38719 * Author: Jack O'Connor
38720 * Code version: v12.1.12
38721 * Availability: https://github.com/jackocnr/intl-tel-input.git
38724 Roo.bootstrap.PhoneInputData = function() {
38727 "Afghanistan (افغانستان)",
38732 "Albania (Shqipëri)",
38737 "Algeria (الجزائر)",
38762 "Antigua and Barbuda",
38772 "Armenia (Հայաստան)",
38788 "Austria (Österreich)",
38793 "Azerbaijan (Azərbaycan)",
38803 "Bahrain (البحرين)",
38808 "Bangladesh (বাংলাদেশ)",
38818 "Belarus (Беларусь)",
38823 "Belgium (België)",
38853 "Bosnia and Herzegovina (Босна и Херцеговина)",
38868 "British Indian Ocean Territory",
38873 "British Virgin Islands",
38883 "Bulgaria (България)",
38893 "Burundi (Uburundi)",
38898 "Cambodia (កម្ពុជា)",
38903 "Cameroon (Cameroun)",
38912 ["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"]
38915 "Cape Verde (Kabu Verdi)",
38920 "Caribbean Netherlands",
38931 "Central African Republic (République centrafricaine)",
38951 "Christmas Island",
38957 "Cocos (Keeling) Islands",
38968 "Comoros (جزر القمر)",
38973 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38978 "Congo (Republic) (Congo-Brazzaville)",
38998 "Croatia (Hrvatska)",
39019 "Czech Republic (Česká republika)",
39024 "Denmark (Danmark)",
39039 "Dominican Republic (República Dominicana)",
39043 ["809", "829", "849"]
39061 "Equatorial Guinea (Guinea Ecuatorial)",
39081 "Falkland Islands (Islas Malvinas)",
39086 "Faroe Islands (Føroyar)",
39107 "French Guiana (Guyane française)",
39112 "French Polynesia (Polynésie française)",
39127 "Georgia (საქართველო)",
39132 "Germany (Deutschland)",
39152 "Greenland (Kalaallit Nunaat)",
39189 "Guinea-Bissau (Guiné Bissau)",
39214 "Hungary (Magyarország)",
39219 "Iceland (Ísland)",
39239 "Iraq (العراق)",
39255 "Israel (ישראל)",
39282 "Jordan (الأردن)",
39287 "Kazakhstan (Казахстан)",
39308 "Kuwait (الكويت)",
39313 "Kyrgyzstan (Кыргызстан)",
39323 "Latvia (Latvija)",
39328 "Lebanon (لبنان)",
39343 "Libya (ليبيا)",
39353 "Lithuania (Lietuva)",
39368 "Macedonia (FYROM) (Македонија)",
39373 "Madagascar (Madagasikara)",
39403 "Marshall Islands",
39413 "Mauritania (موريتانيا)",
39418 "Mauritius (Moris)",
39439 "Moldova (Republica Moldova)",
39449 "Mongolia (Монгол)",
39454 "Montenegro (Crna Gora)",
39464 "Morocco (المغرب)",
39470 "Mozambique (Moçambique)",
39475 "Myanmar (Burma) (မြန်မာ)",
39480 "Namibia (Namibië)",
39495 "Netherlands (Nederland)",
39500 "New Caledonia (Nouvelle-Calédonie)",
39535 "North Korea (조선 민주주의 인민 공화국)",
39540 "Northern Mariana Islands",
39556 "Pakistan (پاکستان)",
39566 "Palestine (فلسطين)",
39576 "Papua New Guinea",
39618 "Réunion (La Réunion)",
39624 "Romania (România)",
39640 "Saint Barthélemy",
39651 "Saint Kitts and Nevis",
39661 "Saint Martin (Saint-Martin (partie française))",
39667 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39672 "Saint Vincent and the Grenadines",
39687 "São Tomé and Príncipe (São Tomé e Príncipe)",
39692 "Saudi Arabia (المملكة العربية السعودية)",
39697 "Senegal (Sénégal)",
39727 "Slovakia (Slovensko)",
39732 "Slovenia (Slovenija)",
39742 "Somalia (Soomaaliya)",
39752 "South Korea (대한민국)",
39757 "South Sudan (جنوب السودان)",
39767 "Sri Lanka (ශ්රී ලංකාව)",
39772 "Sudan (السودان)",
39782 "Svalbard and Jan Mayen",
39793 "Sweden (Sverige)",
39798 "Switzerland (Schweiz)",
39803 "Syria (سوريا)",
39848 "Trinidad and Tobago",
39853 "Tunisia (تونس)",
39858 "Turkey (Türkiye)",
39868 "Turks and Caicos Islands",
39878 "U.S. Virgin Islands",
39888 "Ukraine (Україна)",
39893 "United Arab Emirates (الإمارات العربية المتحدة)",
39915 "Uzbekistan (Oʻzbekiston)",
39925 "Vatican City (Città del Vaticano)",
39936 "Vietnam (Việt Nam)",
39941 "Wallis and Futuna (Wallis-et-Futuna)",
39946 "Western Sahara (الصحراء الغربية)",
39952 "Yemen (اليمن)",
39976 * This script refer to:
39977 * Title: International Telephone Input
39978 * Author: Jack O'Connor
39979 * Code version: v12.1.12
39980 * Availability: https://github.com/jackocnr/intl-tel-input.git
39984 * @class Roo.bootstrap.PhoneInput
39985 * @extends Roo.bootstrap.TriggerField
39986 * An input with International dial-code selection
39988 * @cfg {String} defaultDialCode default '+852'
39989 * @cfg {Array} preferedCountries default []
39992 * Create a new PhoneInput.
39993 * @param {Object} config Configuration options
39996 Roo.bootstrap.PhoneInput = function(config) {
39997 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40000 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40002 listWidth: undefined,
40004 selectedClass: 'active',
40006 invalidClass : "has-warning",
40008 validClass: 'has-success',
40010 allowed: '0123456789',
40015 * @cfg {String} defaultDialCode The default dial code when initializing the input
40017 defaultDialCode: '+852',
40020 * @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
40022 preferedCountries: false,
40024 getAutoCreate : function()
40026 var data = Roo.bootstrap.PhoneInputData();
40027 var align = this.labelAlign || this.parentLabelAlign();
40030 this.allCountries = [];
40031 this.dialCodeMapping = [];
40033 for (var i = 0; i < data.length; i++) {
40035 this.allCountries[i] = {
40039 priority: c[3] || 0,
40040 areaCodes: c[4] || null
40042 this.dialCodeMapping[c[2]] = {
40045 priority: c[3] || 0,
40046 areaCodes: c[4] || null
40058 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40059 maxlength: this.max_length,
40060 cls : 'form-control tel-input',
40061 autocomplete: 'new-password'
40064 var hiddenInput = {
40067 cls: 'hidden-tel-input'
40071 hiddenInput.name = this.name;
40074 if (this.disabled) {
40075 input.disabled = true;
40078 var flag_container = {
40095 cls: this.hasFeedback ? 'has-feedback' : '',
40101 cls: 'dial-code-holder',
40108 cls: 'roo-select2-container input-group',
40115 if (this.fieldLabel.length) {
40118 tooltip: 'This field is required'
40124 cls: 'control-label',
40130 html: this.fieldLabel
40133 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40139 if(this.indicatorpos == 'right') {
40140 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40147 if(align == 'left') {
40155 if(this.labelWidth > 12){
40156 label.style = "width: " + this.labelWidth + 'px';
40158 if(this.labelWidth < 13 && this.labelmd == 0){
40159 this.labelmd = this.labelWidth;
40161 if(this.labellg > 0){
40162 label.cls += ' col-lg-' + this.labellg;
40163 input.cls += ' col-lg-' + (12 - this.labellg);
40165 if(this.labelmd > 0){
40166 label.cls += ' col-md-' + this.labelmd;
40167 container.cls += ' col-md-' + (12 - this.labelmd);
40169 if(this.labelsm > 0){
40170 label.cls += ' col-sm-' + this.labelsm;
40171 container.cls += ' col-sm-' + (12 - this.labelsm);
40173 if(this.labelxs > 0){
40174 label.cls += ' col-xs-' + this.labelxs;
40175 container.cls += ' col-xs-' + (12 - this.labelxs);
40185 var settings = this;
40187 ['xs','sm','md','lg'].map(function(size){
40188 if (settings[size]) {
40189 cfg.cls += ' col-' + size + '-' + settings[size];
40193 this.store = new Roo.data.Store({
40194 proxy : new Roo.data.MemoryProxy({}),
40195 reader : new Roo.data.JsonReader({
40206 'name' : 'dialCode',
40210 'name' : 'priority',
40214 'name' : 'areaCodes',
40221 if(!this.preferedCountries) {
40222 this.preferedCountries = [
40229 var p = this.preferedCountries.reverse();
40232 for (var i = 0; i < p.length; i++) {
40233 for (var j = 0; j < this.allCountries.length; j++) {
40234 if(this.allCountries[j].iso2 == p[i]) {
40235 var t = this.allCountries[j];
40236 this.allCountries.splice(j,1);
40237 this.allCountries.unshift(t);
40243 this.store.proxy.data = {
40245 data: this.allCountries
40251 initEvents : function()
40254 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40256 this.indicator = this.indicatorEl();
40257 this.flag = this.flagEl();
40258 this.dialCodeHolder = this.dialCodeHolderEl();
40260 this.trigger = this.el.select('div.flag-box',true).first();
40261 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40266 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40267 _this.list.setWidth(lw);
40270 this.list.on('mouseover', this.onViewOver, this);
40271 this.list.on('mousemove', this.onViewMove, this);
40272 this.inputEl().on("keyup", this.onKeyUp, this);
40273 this.inputEl().on("keypress", this.onKeyPress, this);
40275 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40277 this.view = new Roo.View(this.list, this.tpl, {
40278 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40281 this.view.on('click', this.onViewClick, this);
40282 this.setValue(this.defaultDialCode);
40285 onTriggerClick : function(e)
40287 Roo.log('trigger click');
40292 if(this.isExpanded()){
40294 this.hasFocus = false;
40296 this.store.load({});
40297 this.hasFocus = true;
40302 isExpanded : function()
40304 return this.list.isVisible();
40307 collapse : function()
40309 if(!this.isExpanded()){
40313 Roo.get(document).un('mousedown', this.collapseIf, this);
40314 Roo.get(document).un('mousewheel', this.collapseIf, this);
40315 this.fireEvent('collapse', this);
40319 expand : function()
40323 if(this.isExpanded() || !this.hasFocus){
40327 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40328 this.list.setWidth(lw);
40331 this.restrictHeight();
40333 Roo.get(document).on('mousedown', this.collapseIf, this);
40334 Roo.get(document).on('mousewheel', this.collapseIf, this);
40336 this.fireEvent('expand', this);
40339 restrictHeight : function()
40341 this.list.alignTo(this.inputEl(), this.listAlign);
40342 this.list.alignTo(this.inputEl(), this.listAlign);
40345 onViewOver : function(e, t)
40347 if(this.inKeyMode){
40350 var item = this.view.findItemFromChild(t);
40353 var index = this.view.indexOf(item);
40354 this.select(index, false);
40359 onViewClick : function(view, doFocus, el, e)
40361 var index = this.view.getSelectedIndexes()[0];
40363 var r = this.store.getAt(index);
40366 this.onSelect(r, index);
40368 if(doFocus !== false && !this.blockFocus){
40369 this.inputEl().focus();
40373 onViewMove : function(e, t)
40375 this.inKeyMode = false;
40378 select : function(index, scrollIntoView)
40380 this.selectedIndex = index;
40381 this.view.select(index);
40382 if(scrollIntoView !== false){
40383 var el = this.view.getNode(index);
40385 this.list.scrollChildIntoView(el, false);
40390 createList : function()
40392 this.list = Roo.get(document.body).createChild({
40394 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40395 style: 'display:none'
40398 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40401 collapseIf : function(e)
40403 var in_combo = e.within(this.el);
40404 var in_list = e.within(this.list);
40405 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40407 if (in_combo || in_list || is_list) {
40413 onSelect : function(record, index)
40415 if(this.fireEvent('beforeselect', this, record, index) !== false){
40417 this.setFlagClass(record.data.iso2);
40418 this.setDialCode(record.data.dialCode);
40419 this.hasFocus = false;
40421 this.fireEvent('select', this, record, index);
40425 flagEl : function()
40427 var flag = this.el.select('div.flag',true).first();
40434 dialCodeHolderEl : function()
40436 var d = this.el.select('input.dial-code-holder',true).first();
40443 setDialCode : function(v)
40445 this.dialCodeHolder.dom.value = '+'+v;
40448 setFlagClass : function(n)
40450 this.flag.dom.className = 'flag '+n;
40453 getValue : function()
40455 var v = this.inputEl().getValue();
40456 if(this.dialCodeHolder) {
40457 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40462 setValue : function(v)
40464 var d = this.getDialCode(v);
40466 //invalid dial code
40467 if(v.length == 0 || !d || d.length == 0) {
40469 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40470 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40476 this.setFlagClass(this.dialCodeMapping[d].iso2);
40477 this.setDialCode(d);
40478 this.inputEl().dom.value = v.replace('+'+d,'');
40479 this.hiddenEl().dom.value = this.getValue();
40484 getDialCode : function(v)
40488 if (v.length == 0) {
40489 return this.dialCodeHolder.dom.value;
40493 if (v.charAt(0) != "+") {
40496 var numericChars = "";
40497 for (var i = 1; i < v.length; i++) {
40498 var c = v.charAt(i);
40501 if (this.dialCodeMapping[numericChars]) {
40502 dialCode = v.substr(1, i);
40504 if (numericChars.length == 4) {
40514 this.setValue(this.defaultDialCode);
40518 hiddenEl : function()
40520 return this.el.select('input.hidden-tel-input',true).first();
40523 // after setting val
40524 onKeyUp : function(e){
40525 this.setValue(this.getValue());
40528 onKeyPress : function(e){
40529 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40536 * @class Roo.bootstrap.MoneyField
40537 * @extends Roo.bootstrap.ComboBox
40538 * Bootstrap MoneyField class
40541 * Create a new MoneyField.
40542 * @param {Object} config Configuration options
40545 Roo.bootstrap.MoneyField = function(config) {
40547 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40551 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40554 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40556 allowDecimals : true,
40558 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40560 decimalSeparator : ".",
40562 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40564 decimalPrecision : 0,
40566 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40568 allowNegative : true,
40570 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40574 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40576 minValue : Number.NEGATIVE_INFINITY,
40578 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40580 maxValue : Number.MAX_VALUE,
40582 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40584 minText : "The minimum value for this field is {0}",
40586 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40588 maxText : "The maximum value for this field is {0}",
40590 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40591 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40593 nanText : "{0} is not a valid number",
40595 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40599 * @cfg {String} defaults currency of the MoneyField
40600 * value should be in lkey
40602 defaultCurrency : false,
40604 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40606 thousandsDelimiter : false,
40608 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40619 getAutoCreate : function()
40621 var align = this.labelAlign || this.parentLabelAlign();
40633 cls : 'form-control roo-money-amount-input',
40634 autocomplete: 'new-password'
40637 var hiddenInput = {
40641 cls: 'hidden-number-input'
40644 if(this.max_length) {
40645 input.maxlength = this.max_length;
40649 hiddenInput.name = this.name;
40652 if (this.disabled) {
40653 input.disabled = true;
40656 var clg = 12 - this.inputlg;
40657 var cmd = 12 - this.inputmd;
40658 var csm = 12 - this.inputsm;
40659 var cxs = 12 - this.inputxs;
40663 cls : 'row roo-money-field',
40667 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40671 cls: 'roo-select2-container input-group',
40675 cls : 'form-control roo-money-currency-input',
40676 autocomplete: 'new-password',
40678 name : this.currencyName
40682 cls : 'input-group-addon',
40696 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40700 cls: this.hasFeedback ? 'has-feedback' : '',
40711 if (this.fieldLabel.length) {
40714 tooltip: 'This field is required'
40720 cls: 'control-label',
40726 html: this.fieldLabel
40729 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40735 if(this.indicatorpos == 'right') {
40736 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40743 if(align == 'left') {
40751 if(this.labelWidth > 12){
40752 label.style = "width: " + this.labelWidth + 'px';
40754 if(this.labelWidth < 13 && this.labelmd == 0){
40755 this.labelmd = this.labelWidth;
40757 if(this.labellg > 0){
40758 label.cls += ' col-lg-' + this.labellg;
40759 input.cls += ' col-lg-' + (12 - this.labellg);
40761 if(this.labelmd > 0){
40762 label.cls += ' col-md-' + this.labelmd;
40763 container.cls += ' col-md-' + (12 - this.labelmd);
40765 if(this.labelsm > 0){
40766 label.cls += ' col-sm-' + this.labelsm;
40767 container.cls += ' col-sm-' + (12 - this.labelsm);
40769 if(this.labelxs > 0){
40770 label.cls += ' col-xs-' + this.labelxs;
40771 container.cls += ' col-xs-' + (12 - this.labelxs);
40782 var settings = this;
40784 ['xs','sm','md','lg'].map(function(size){
40785 if (settings[size]) {
40786 cfg.cls += ' col-' + size + '-' + settings[size];
40793 initEvents : function()
40795 this.indicator = this.indicatorEl();
40797 this.initCurrencyEvent();
40799 this.initNumberEvent();
40802 initCurrencyEvent : function()
40805 throw "can not find store for combo";
40808 this.store = Roo.factory(this.store, Roo.data);
40809 this.store.parent = this;
40813 this.triggerEl = this.el.select('.input-group-addon', true).first();
40815 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40820 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40821 _this.list.setWidth(lw);
40824 this.list.on('mouseover', this.onViewOver, this);
40825 this.list.on('mousemove', this.onViewMove, this);
40826 this.list.on('scroll', this.onViewScroll, this);
40829 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40832 this.view = new Roo.View(this.list, this.tpl, {
40833 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40836 this.view.on('click', this.onViewClick, this);
40838 this.store.on('beforeload', this.onBeforeLoad, this);
40839 this.store.on('load', this.onLoad, this);
40840 this.store.on('loadexception', this.onLoadException, this);
40842 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40843 "up" : function(e){
40844 this.inKeyMode = true;
40848 "down" : function(e){
40849 if(!this.isExpanded()){
40850 this.onTriggerClick();
40852 this.inKeyMode = true;
40857 "enter" : function(e){
40860 if(this.fireEvent("specialkey", this, e)){
40861 this.onViewClick(false);
40867 "esc" : function(e){
40871 "tab" : function(e){
40874 if(this.fireEvent("specialkey", this, e)){
40875 this.onViewClick(false);
40883 doRelay : function(foo, bar, hname){
40884 if(hname == 'down' || this.scope.isExpanded()){
40885 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40893 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40897 initNumberEvent : function(e)
40899 this.inputEl().on("keydown" , this.fireKey, this);
40900 this.inputEl().on("focus", this.onFocus, this);
40901 this.inputEl().on("blur", this.onBlur, this);
40903 this.inputEl().relayEvent('keyup', this);
40905 if(this.indicator){
40906 this.indicator.addClass('invisible');
40909 this.originalValue = this.getValue();
40911 if(this.validationEvent == 'keyup'){
40912 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40913 this.inputEl().on('keyup', this.filterValidation, this);
40915 else if(this.validationEvent !== false){
40916 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40919 if(this.selectOnFocus){
40920 this.on("focus", this.preFocus, this);
40923 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40924 this.inputEl().on("keypress", this.filterKeys, this);
40926 this.inputEl().relayEvent('keypress', this);
40929 var allowed = "0123456789";
40931 if(this.allowDecimals){
40932 allowed += this.decimalSeparator;
40935 if(this.allowNegative){
40939 if(this.thousandsDelimiter) {
40943 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40945 var keyPress = function(e){
40947 var k = e.getKey();
40949 var c = e.getCharCode();
40952 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40953 allowed.indexOf(String.fromCharCode(c)) === -1
40959 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40963 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40968 this.inputEl().on("keypress", keyPress, this);
40972 onTriggerClick : function(e)
40979 this.loadNext = false;
40981 if(this.isExpanded()){
40986 this.hasFocus = true;
40988 if(this.triggerAction == 'all') {
40989 this.doQuery(this.allQuery, true);
40993 this.doQuery(this.getRawValue());
40996 getCurrency : function()
40998 var v = this.currencyEl().getValue();
41003 restrictHeight : function()
41005 this.list.alignTo(this.currencyEl(), this.listAlign);
41006 this.list.alignTo(this.currencyEl(), this.listAlign);
41009 onViewClick : function(view, doFocus, el, e)
41011 var index = this.view.getSelectedIndexes()[0];
41013 var r = this.store.getAt(index);
41016 this.onSelect(r, index);
41020 onSelect : function(record, index){
41022 if(this.fireEvent('beforeselect', this, record, index) !== false){
41024 this.setFromCurrencyData(index > -1 ? record.data : false);
41028 this.fireEvent('select', this, record, index);
41032 setFromCurrencyData : function(o)
41036 this.lastCurrency = o;
41038 if (this.currencyField) {
41039 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41041 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41044 this.lastSelectionText = currency;
41046 //setting default currency
41047 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41048 this.setCurrency(this.defaultCurrency);
41052 this.setCurrency(currency);
41055 setFromData : function(o)
41059 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41061 this.setFromCurrencyData(c);
41066 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41068 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41071 this.setValue(value);
41075 setCurrency : function(v)
41077 this.currencyValue = v;
41080 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41085 setValue : function(v)
41087 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41093 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41095 this.inputEl().dom.value = (v == '') ? '' :
41096 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41098 if(!this.allowZero && v === '0') {
41099 this.hiddenEl().dom.value = '';
41100 this.inputEl().dom.value = '';
41107 getRawValue : function()
41109 var v = this.inputEl().getValue();
41114 getValue : function()
41116 return this.fixPrecision(this.parseValue(this.getRawValue()));
41119 parseValue : function(value)
41121 if(this.thousandsDelimiter) {
41123 r = new RegExp(",", "g");
41124 value = value.replace(r, "");
41127 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41128 return isNaN(value) ? '' : value;
41132 fixPrecision : function(value)
41134 if(this.thousandsDelimiter) {
41136 r = new RegExp(",", "g");
41137 value = value.replace(r, "");
41140 var nan = isNaN(value);
41142 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41143 return nan ? '' : value;
41145 return parseFloat(value).toFixed(this.decimalPrecision);
41148 decimalPrecisionFcn : function(v)
41150 return Math.floor(v);
41153 validateValue : function(value)
41155 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41159 var num = this.parseValue(value);
41162 this.markInvalid(String.format(this.nanText, value));
41166 if(num < this.minValue){
41167 this.markInvalid(String.format(this.minText, this.minValue));
41171 if(num > this.maxValue){
41172 this.markInvalid(String.format(this.maxText, this.maxValue));
41179 validate : function()
41181 if(this.disabled || this.allowBlank){
41186 var currency = this.getCurrency();
41188 if(this.validateValue(this.getRawValue()) && currency.length){
41193 this.markInvalid();
41197 getName: function()
41202 beforeBlur : function()
41208 var v = this.parseValue(this.getRawValue());
41215 onBlur : function()
41219 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41220 //this.el.removeClass(this.focusClass);
41223 this.hasFocus = false;
41225 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41229 var v = this.getValue();
41231 if(String(v) !== String(this.startValue)){
41232 this.fireEvent('change', this, v, this.startValue);
41235 this.fireEvent("blur", this);
41238 inputEl : function()
41240 return this.el.select('.roo-money-amount-input', true).first();
41243 currencyEl : function()
41245 return this.el.select('.roo-money-currency-input', true).first();
41248 hiddenEl : function()
41250 return this.el.select('input.hidden-number-input',true).first();